summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorsherbrecher <oss@herbrecher.de>2013-03-15 17:50:02 +0100
committersherbrecher <oss@herbrecher.de>2013-03-15 17:50:02 +0100
commitec45a8433dba8140565728500050035ddad0a20e (patch)
treefff9bf7677cf66dabdc2906f54e334c66e3d6dda /apps
parentd2b3a9fb3bfa4d434d7d797019ec0cadec392a2b (diff)
parentebb2cb340349929e5e1d8e73e0f4523756f69d73 (diff)
downloadnextcloud-server-ec45a8433dba8140565728500050035ddad0a20e.tar.gz
nextcloud-server-ec45a8433dba8140565728500050035ddad0a20e.zip
Merge remote-tracking branch 'upstream/master'
Conflicts: apps/files_external/templates/settings.php
Diffstat (limited to 'apps')
-rw-r--r--apps/files/admin.php17
-rw-r--r--apps/files/ajax/autocomplete.php54
-rw-r--r--apps/files/ajax/delete.php17
-rw-r--r--apps/files/ajax/download.php8
-rw-r--r--apps/files/ajax/getstoragestats.php9
-rw-r--r--apps/files/ajax/list.php4
-rw-r--r--apps/files/ajax/move.php26
-rw-r--r--apps/files/ajax/newfile.php22
-rw-r--r--apps/files/ajax/newfolder.php5
-rw-r--r--apps/files/ajax/rawlist.php2
-rw-r--r--apps/files/ajax/rename.php18
-rw-r--r--apps/files/ajax/scan.php90
-rw-r--r--apps/files/ajax/timezone.php5
-rw-r--r--apps/files/ajax/upgrade.php44
-rw-r--r--apps/files/ajax/upload.php80
-rw-r--r--apps/files/appinfo/app.php12
-rw-r--r--apps/files/appinfo/filesync.php30
-rw-r--r--apps/files/appinfo/info.xml2
-rw-r--r--apps/files/appinfo/remote.php13
-rw-r--r--apps/files/appinfo/routes.php14
-rw-r--r--apps/files/appinfo/update.php22
-rw-r--r--apps/files/appinfo/version2
-rw-r--r--apps/files/css/files.css168
-rw-r--r--apps/files/download.php22
-rw-r--r--apps/files/index.php189
-rw-r--r--apps/files/js/fileactions.js246
-rw-r--r--apps/files/js/filelist.js322
-rw-r--r--apps/files/js/files.js510
-rw-r--r--apps/files/js/jquery-visibility.js31
-rw-r--r--apps/files/js/keyboardshortcuts.js168
-rw-r--r--apps/files/js/timezone.js12
-rw-r--r--apps/files/js/upgrade.js17
-rw-r--r--apps/files/js/upload.js8
-rw-r--r--apps/files/l10n/ar.php6
-rw-r--r--apps/files/l10n/bg_BG.php48
-rw-r--r--apps/files/l10n/bn_BD.php64
-rw-r--r--apps/files/l10n/ca.php64
-rw-r--r--apps/files/l10n/cs_CZ.php64
-rw-r--r--apps/files/l10n/da.php64
-rw-r--r--apps/files/l10n/de.php68
-rw-r--r--apps/files/l10n/de_DE.php73
-rw-r--r--apps/files/l10n/el.php87
-rw-r--r--apps/files/l10n/eo.php56
-rw-r--r--apps/files/l10n/es.php64
-rw-r--r--apps/files/l10n/es_AR.php76
-rw-r--r--apps/files/l10n/et_EE.php41
-rw-r--r--apps/files/l10n/eu.php64
-rw-r--r--apps/files/l10n/fa.php46
-rw-r--r--apps/files/l10n/fi_FI.php55
-rw-r--r--apps/files/l10n/fr.php66
-rw-r--r--apps/files/l10n/gl.php87
-rw-r--r--apps/files/l10n/he.php37
-rw-r--r--apps/files/l10n/hr.php34
-rw-r--r--apps/files/l10n/hu_HU.php90
-rw-r--r--apps/files/l10n/hy.php6
-rw-r--r--apps/files/l10n/ia.php5
-rw-r--r--apps/files/l10n/id.php25
-rw-r--r--apps/files/l10n/is.php64
-rw-r--r--apps/files/l10n/it.php64
-rw-r--r--apps/files/l10n/ja_JP.php66
-rw-r--r--apps/files/l10n/ka.php4
-rw-r--r--apps/files/l10n/ka_GE.php52
-rw-r--r--apps/files/l10n/ko.php71
-rw-r--r--apps/files/l10n/ku_IQ.php9
-rw-r--r--apps/files/l10n/lb.php18
-rw-r--r--apps/files/l10n/lt_LT.php31
-rw-r--r--apps/files/l10n/lv.php99
-rw-r--r--apps/files/l10n/mk.php35
-rw-r--r--apps/files/l10n/ms_MY.php20
-rw-r--r--apps/files/l10n/my_MM.php4
-rw-r--r--apps/files/l10n/nb_NO.php35
-rw-r--r--apps/files/l10n/nl.php66
-rw-r--r--apps/files/l10n/nn_NO.php5
-rw-r--r--apps/files/l10n/oc.php33
-rw-r--r--apps/files/l10n/pl.php94
-rw-r--r--apps/files/l10n/pl_PL.php3
-rw-r--r--apps/files/l10n/pt_BR.php68
-rw-r--r--apps/files/l10n/pt_PT.php68
-rw-r--r--apps/files/l10n/ro.php54
-rw-r--r--apps/files/l10n/ru.php53
-rw-r--r--apps/files/l10n/ru_RU.php62
-rw-r--r--apps/files/l10n/si_LK.php48
-rw-r--r--apps/files/l10n/sk_SK.php76
-rw-r--r--apps/files/l10n/sl.php96
-rw-r--r--apps/files/l10n/sr.php63
-rw-r--r--apps/files/l10n/sr@latin.php5
-rw-r--r--apps/files/l10n/sv.php64
-rw-r--r--apps/files/l10n/ta_LK.php56
-rw-r--r--apps/files/l10n/th_TH.php61
-rw-r--r--apps/files/l10n/tr.php53
-rw-r--r--apps/files/l10n/uk.php60
-rw-r--r--apps/files/l10n/vi.php60
-rw-r--r--apps/files/l10n/zh_CN.GB2312.php44
-rw-r--r--apps/files/l10n/zh_CN.php68
-rw-r--r--apps/files/l10n/zh_HK.php9
-rw-r--r--apps/files/l10n/zh_TW.php62
-rw-r--r--apps/files/lib/capabilities.php24
-rw-r--r--apps/files/lib/helper.php20
-rw-r--r--apps/files/settings.php6
-rw-r--r--apps/files/templates/admin.php26
-rw-r--r--apps/files/templates/index.php112
-rw-r--r--apps/files/templates/part.breadcrumb.php22
-rw-r--r--apps/files/templates/part.list.php104
-rw-r--r--apps/files/templates/upgrade.php4
-rw-r--r--apps/files_encryption/appinfo/app.php48
-rw-r--r--apps/files_encryption/appinfo/database.xml24
-rw-r--r--apps/files_encryption/appinfo/info.xml6
-rw-r--r--apps/files_encryption/appinfo/routes.php9
-rw-r--r--apps/files_encryption/appinfo/spec.txt19
-rw-r--r--apps/files_encryption/appinfo/version2
-rw-r--r--apps/files_encryption/hooks/hooks.php191
-rw-r--r--apps/files_encryption/js/settings.js11
-rw-r--r--apps/files_encryption/l10n/ar.php4
-rw-r--r--apps/files_encryption/l10n/bg_BG.php4
-rw-r--r--apps/files_encryption/l10n/bn_BD.php4
-rw-r--r--apps/files_encryption/l10n/ca.php7
-rw-r--r--apps/files_encryption/l10n/cs_CZ.php7
-rw-r--r--apps/files_encryption/l10n/da.php7
-rw-r--r--apps/files_encryption/l10n/de.php7
-rw-r--r--apps/files_encryption/l10n/de_DE.php7
-rw-r--r--apps/files_encryption/l10n/el.php7
-rw-r--r--apps/files_encryption/l10n/eo.php4
-rw-r--r--apps/files_encryption/l10n/es.php7
-rw-r--r--apps/files_encryption/l10n/es_AR.php7
-rw-r--r--apps/files_encryption/l10n/et_EE.php7
-rw-r--r--apps/files_encryption/l10n/eu.php7
-rw-r--r--apps/files_encryption/l10n/fa.php3
-rw-r--r--apps/files_encryption/l10n/fi_FI.php7
-rw-r--r--apps/files_encryption/l10n/fr.php7
-rw-r--r--apps/files_encryption/l10n/gl.php9
-rw-r--r--apps/files_encryption/l10n/he.php4
-rw-r--r--apps/files_encryption/l10n/hu_HU.php7
-rw-r--r--apps/files_encryption/l10n/id.php7
-rw-r--r--apps/files_encryption/l10n/is.php4
-rw-r--r--apps/files_encryption/l10n/it.php7
-rw-r--r--apps/files_encryption/l10n/ja_JP.php7
-rw-r--r--apps/files_encryption/l10n/ko.php4
-rw-r--r--apps/files_encryption/l10n/ku_IQ.php4
-rw-r--r--apps/files_encryption/l10n/lt_LT.php4
-rw-r--r--apps/files_encryption/l10n/lv.php7
-rw-r--r--apps/files_encryption/l10n/mk.php4
-rw-r--r--apps/files_encryption/l10n/nb_NO.php7
-rw-r--r--apps/files_encryption/l10n/nl.php7
-rw-r--r--apps/files_encryption/l10n/pl.php7
-rw-r--r--apps/files_encryption/l10n/pt_BR.php7
-rw-r--r--apps/files_encryption/l10n/pt_PT.php7
-rw-r--r--apps/files_encryption/l10n/ro.php4
-rw-r--r--apps/files_encryption/l10n/ru.php7
-rw-r--r--apps/files_encryption/l10n/ru_RU.php4
-rw-r--r--apps/files_encryption/l10n/si_LK.php4
-rw-r--r--apps/files_encryption/l10n/sk_SK.php7
-rw-r--r--apps/files_encryption/l10n/sl.php7
-rw-r--r--apps/files_encryption/l10n/sr.php4
-rw-r--r--apps/files_encryption/l10n/sv.php7
-rw-r--r--apps/files_encryption/l10n/ta_LK.php4
-rw-r--r--apps/files_encryption/l10n/th_TH.php4
-rw-r--r--apps/files_encryption/l10n/tr.php7
-rw-r--r--apps/files_encryption/l10n/uk.php7
-rw-r--r--apps/files_encryption/l10n/vi.php7
-rw-r--r--apps/files_encryption/l10n/zh_CN.GB2312.php4
-rw-r--r--apps/files_encryption/l10n/zh_CN.php7
-rw-r--r--apps/files_encryption/l10n/zh_HK.php6
-rw-r--r--apps/files_encryption/l10n/zh_TW.php4
-rw-r--r--apps/files_encryption/lib/capabilities.php23
-rwxr-xr-x[-rw-r--r--]apps/files_encryption/lib/crypt.php912
-rw-r--r--apps/files_encryption/lib/cryptstream.php174
-rwxr-xr-xapps/files_encryption/lib/keymanager.php323
-rw-r--r--apps/files_encryption/lib/proxy.php389
-rw-r--r--apps/files_encryption/lib/session.php103
-rw-r--r--apps/files_encryption/lib/stream.php495
-rw-r--r--apps/files_encryption/lib/util.php476
-rw-r--r--apps/files_encryption/settings-personal.php17
-rw-r--r--apps/files_encryption/settings.php17
-rw-r--r--apps/files_encryption/templates/settings-personal.php22
-rw-r--r--apps/files_encryption/templates/settings.php22
-rw-r--r--apps/files_encryption/test/binary (renamed from apps/files_encryption/tests/binary)bin9734 -> 9734 bytes
-rwxr-xr-xapps/files_encryption/test/crypt.php667
-rw-r--r--apps/files_encryption/test/keymanager.php130
-rw-r--r--apps/files_encryption/test/legacy-encrypted-text.txtbin0 -> 3360 bytes
-rw-r--r--apps/files_encryption/test/proxy.php220
-rw-r--r--apps/files_encryption/test/stream.php226
-rwxr-xr-xapps/files_encryption/test/util.php225
-rw-r--r--apps/files_encryption/test/zeros (renamed from apps/files_encryption/tests/zeros)bin10238 -> 10238 bytes
-rw-r--r--apps/files_encryption/tests/encryption.php72
-rw-r--r--apps/files_encryption/tests/proxy.php117
-rw-r--r--apps/files_encryption/tests/stream.php85
-rw-r--r--apps/files_external/3rdparty/phpseclib/AUTHORS3
-rw-r--r--apps/files_external/3rdparty/phpseclib/LICENSE21
-rw-r--r--apps/files_external/3rdparty/phpseclib/README.md16
-rw-r--r--apps/files_external/3rdparty/phpseclib/composer.json48
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php946
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php1334
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php825
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php519
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php2646
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php243
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php1525
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php1080
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php540
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php1277
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php4323
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php3633
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php2029
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php1577
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php3009
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf6
-rw-r--r--apps/files_external/3rdparty/phpseclib/phpunit.xml.dist18
-rw-r--r--apps/files_external/ajax/addMountPoint.php8
-rw-r--r--apps/files_external/ajax/addRootCertificate.php25
-rw-r--r--apps/files_external/ajax/dropbox.php17
-rw-r--r--apps/files_external/ajax/google.php29
-rw-r--r--apps/files_external/ajax/removeMountPoint.php10
-rw-r--r--apps/files_external/ajax/removeRootCertificate.php1
-rw-r--r--apps/files_external/appinfo/app.php19
-rw-r--r--apps/files_external/appinfo/info.xml2
-rw-r--r--apps/files_external/css/settings.css5
-rw-r--r--apps/files_external/img/error.pngbin0 -> 533 bytes
-rw-r--r--apps/files_external/img/success.pngbin0 -> 545 bytes
-rw-r--r--apps/files_external/img/waiting.pngbin0 -> 512 bytes
-rw-r--r--apps/files_external/js/dropbox.js70
-rw-r--r--apps/files_external/js/google.js94
-rw-r--r--apps/files_external/js/settings.js162
-rw-r--r--apps/files_external/l10n/af_ZA.php3
-rw-r--r--apps/files_external/l10n/ar.php5
-rw-r--r--apps/files_external/l10n/bg_BG.php17
-rw-r--r--apps/files_external/l10n/bn_BD.php20
-rw-r--r--apps/files_external/l10n/ca.php9
-rw-r--r--apps/files_external/l10n/cs_CZ.php11
-rw-r--r--apps/files_external/l10n/da.php7
-rw-r--r--apps/files_external/l10n/de.php9
-rw-r--r--apps/files_external/l10n/de_DE.php25
-rw-r--r--apps/files_external/l10n/el.php7
-rw-r--r--apps/files_external/l10n/eo.php5
-rw-r--r--apps/files_external/l10n/es.php9
-rw-r--r--apps/files_external/l10n/es_AR.php9
-rw-r--r--apps/files_external/l10n/et_EE.php9
-rw-r--r--apps/files_external/l10n/eu.php9
-rw-r--r--apps/files_external/l10n/fa.php11
-rw-r--r--apps/files_external/l10n/fi_FI.php8
-rw-r--r--apps/files_external/l10n/fr.php9
-rw-r--r--apps/files_external/l10n/gl.php25
-rw-r--r--apps/files_external/l10n/he.php8
-rw-r--r--apps/files_external/l10n/hi.php3
-rw-r--r--apps/files_external/l10n/hr.php5
-rw-r--r--apps/files_external/l10n/hu_HU.php25
-rw-r--r--apps/files_external/l10n/hy.php3
-rw-r--r--apps/files_external/l10n/ia.php5
-rw-r--r--apps/files_external/l10n/id.php22
-rw-r--r--apps/files_external/l10n/is.php23
-rw-r--r--apps/files_external/l10n/it.php9
-rw-r--r--apps/files_external/l10n/ja_JP.php9
-rw-r--r--apps/files_external/l10n/ka.php3
-rw-r--r--apps/files_external/l10n/ka_GE.php5
-rw-r--r--apps/files_external/l10n/ko.php23
-rw-r--r--apps/files_external/l10n/ku_IQ.php4
-rw-r--r--apps/files_external/l10n/lb.php5
-rw-r--r--apps/files_external/l10n/lt_LT.php14
-rw-r--r--apps/files_external/l10n/lv.php25
-rw-r--r--apps/files_external/l10n/mk.php23
-rw-r--r--apps/files_external/l10n/ms_MY.php5
-rw-r--r--apps/files_external/l10n/my_MM.php3
-rw-r--r--apps/files_external/l10n/nb_NO.php15
-rw-r--r--apps/files_external/l10n/nl.php11
-rw-r--r--apps/files_external/l10n/nn_NO.php5
-rw-r--r--apps/files_external/l10n/oc.php5
-rw-r--r--apps/files_external/l10n/pl.php9
-rw-r--r--apps/files_external/l10n/pt_BR.php9
-rw-r--r--apps/files_external/l10n/pt_PT.php13
-rw-r--r--apps/files_external/l10n/ro.php11
-rw-r--r--apps/files_external/l10n/ru.php13
-rw-r--r--apps/files_external/l10n/ru_RU.php7
-rw-r--r--apps/files_external/l10n/si_LK.php21
-rw-r--r--apps/files_external/l10n/sk_SK.php16
-rw-r--r--apps/files_external/l10n/sl.php19
-rw-r--r--apps/files_external/l10n/sr.php5
-rw-r--r--apps/files_external/l10n/sr@latin.php5
-rw-r--r--apps/files_external/l10n/sv.php9
-rw-r--r--apps/files_external/l10n/ta_LK.php21
-rw-r--r--apps/files_external/l10n/th_TH.php7
-rw-r--r--apps/files_external/l10n/tr.php19
-rw-r--r--apps/files_external/l10n/uk.php22
-rw-r--r--apps/files_external/l10n/ur_PK.php3
-rw-r--r--apps/files_external/l10n/vi.php11
-rw-r--r--apps/files_external/l10n/zh_CN.GB2312.php5
-rw-r--r--apps/files_external/l10n/zh_CN.php23
-rw-r--r--apps/files_external/l10n/zh_HK.php5
-rw-r--r--apps/files_external/l10n/zh_TW.php10
-rw-r--r--apps/files_external/lib/amazons3.php113
-rwxr-xr-xapps/files_external/lib/config.php288
-rwxr-xr-xapps/files_external/lib/dropbox.php98
-rw-r--r--apps/files_external/lib/ftp.php72
-rw-r--r--apps/files_external/lib/google.php204
-rw-r--r--apps/files_external/lib/sftp.php288
-rw-r--r--apps/files_external/lib/smb.php75
-rw-r--r--apps/files_external/lib/streamwrapper.php65
-rw-r--r--apps/files_external/lib/swift.php313
-rw-r--r--apps/files_external/lib/webdav.php226
-rwxr-xr-xapps/files_external/personal.php5
-rw-r--r--apps/files_external/settings.php5
-rw-r--r--apps/files_external/templates/settings.php160
-rw-r--r--apps/files_external/tests/amazons3.php11
-rw-r--r--apps/files_external/tests/config.php7
-rw-r--r--apps/files_external/tests/dropbox.php8
-rw-r--r--apps/files_external/tests/ftp.php32
-rw-r--r--apps/files_external/tests/google.php7
-rw-r--r--apps/files_external/tests/sftp.php43
-rw-r--r--apps/files_external/tests/smb.php11
-rw-r--r--apps/files_external/tests/swift.php8
-rw-r--r--apps/files_external/tests/webdav.php9
-rw-r--r--apps/files_sharing/appinfo/app.php17
-rw-r--r--apps/files_sharing/appinfo/info.xml2
-rw-r--r--apps/files_sharing/appinfo/update.php22
-rw-r--r--apps/files_sharing/css/public.css86
-rw-r--r--apps/files_sharing/js/share.js40
-rw-r--r--apps/files_sharing/l10n/af_ZA.php4
-rw-r--r--apps/files_sharing/l10n/ar.php9
-rw-r--r--apps/files_sharing/l10n/bg_BG.php9
-rw-r--r--apps/files_sharing/l10n/bn_BD.php9
-rw-r--r--apps/files_sharing/l10n/de.php4
-rw-r--r--apps/files_sharing/l10n/de_DE.php9
-rw-r--r--apps/files_sharing/l10n/et_EE.php2
-rw-r--r--apps/files_sharing/l10n/fa.php11
-rw-r--r--apps/files_sharing/l10n/gl.php6
-rw-r--r--apps/files_sharing/l10n/hu_HU.php11
-rw-r--r--apps/files_sharing/l10n/id.php9
-rw-r--r--apps/files_sharing/l10n/is.php9
-rw-r--r--apps/files_sharing/l10n/ja_JP.php4
-rw-r--r--apps/files_sharing/l10n/ka.php4
-rw-r--r--apps/files_sharing/l10n/ka_GE.php6
-rw-r--r--apps/files_sharing/l10n/ko.php9
-rw-r--r--apps/files_sharing/l10n/lb.php3
-rw-r--r--apps/files_sharing/l10n/lv.php9
-rw-r--r--apps/files_sharing/l10n/mk.php9
-rw-r--r--apps/files_sharing/l10n/my_MM.php6
-rw-r--r--apps/files_sharing/l10n/nb_NO.php11
-rw-r--r--apps/files_sharing/l10n/ru.php2
-rw-r--r--apps/files_sharing/l10n/si_LK.php9
-rw-r--r--apps/files_sharing/l10n/sl.php6
-rw-r--r--apps/files_sharing/l10n/sr.php5
-rw-r--r--apps/files_sharing/l10n/ta_LK.php9
-rw-r--r--apps/files_sharing/l10n/tr.php9
-rw-r--r--apps/files_sharing/l10n/uk.php7
-rw-r--r--apps/files_sharing/l10n/ur_PK.php4
-rw-r--r--apps/files_sharing/l10n/vi.php2
-rw-r--r--apps/files_sharing/l10n/zh_HK.php4
-rw-r--r--apps/files_sharing/l10n/zh_TW.php5
-rw-r--r--apps/files_sharing/lib/cache.php272
-rw-r--r--apps/files_sharing/lib/permissions.php86
-rw-r--r--apps/files_sharing/lib/share/file.php103
-rw-r--r--apps/files_sharing/lib/share/folder.php48
-rw-r--r--apps/files_sharing/lib/sharedstorage.php329
-rw-r--r--apps/files_sharing/lib/updater.php102
-rw-r--r--apps/files_sharing/lib/watcher.php51
-rw-r--r--apps/files_sharing/public.php360
-rw-r--r--apps/files_sharing/templates/authenticate.php12
-rw-r--r--apps/files_sharing/templates/public.php36
-rw-r--r--apps/files_trashbin/ajax/delete.php45
-rw-r--r--apps/files_trashbin/ajax/undelete.php46
-rw-r--r--apps/files_trashbin/appinfo/app.php7
-rw-r--r--apps/files_trashbin/appinfo/database.xml118
-rw-r--r--apps/files_trashbin/appinfo/info.xml14
-rw-r--r--apps/files_trashbin/appinfo/update.php40
-rw-r--r--apps/files_trashbin/appinfo/version1
-rw-r--r--apps/files_trashbin/download.php51
-rw-r--r--apps/files_trashbin/index.php116
-rw-r--r--apps/files_trashbin/js/disableDefaultActions.js4
-rw-r--r--apps/files_trashbin/js/trash.js236
-rw-r--r--apps/files_trashbin/l10n/.gitkeep0
-rw-r--r--apps/files_trashbin/l10n/ar.php4
-rw-r--r--apps/files_trashbin/l10n/bg_BG.php16
-rw-r--r--apps/files_trashbin/l10n/bn_BD.php8
-rw-r--r--apps/files_trashbin/l10n/ca.php17
-rw-r--r--apps/files_trashbin/l10n/cs_CZ.php17
-rw-r--r--apps/files_trashbin/l10n/da.php16
-rw-r--r--apps/files_trashbin/l10n/de.php17
-rw-r--r--apps/files_trashbin/l10n/de_DE.php17
-rw-r--r--apps/files_trashbin/l10n/el.php17
-rw-r--r--apps/files_trashbin/l10n/eo.php10
-rw-r--r--apps/files_trashbin/l10n/es.php17
-rw-r--r--apps/files_trashbin/l10n/es_AR.php17
-rw-r--r--apps/files_trashbin/l10n/et_EE.php16
-rw-r--r--apps/files_trashbin/l10n/eu.php17
-rw-r--r--apps/files_trashbin/l10n/fa.php10
-rw-r--r--apps/files_trashbin/l10n/fi_FI.php17
-rw-r--r--apps/files_trashbin/l10n/fr.php17
-rw-r--r--apps/files_trashbin/l10n/gl.php17
-rw-r--r--apps/files_trashbin/l10n/he.php16
-rw-r--r--apps/files_trashbin/l10n/hr.php4
-rw-r--r--apps/files_trashbin/l10n/hu_HU.php17
-rw-r--r--apps/files_trashbin/l10n/hy.php3
-rw-r--r--apps/files_trashbin/l10n/ia.php4
-rw-r--r--apps/files_trashbin/l10n/id.php15
-rw-r--r--apps/files_trashbin/l10n/is.php8
-rw-r--r--apps/files_trashbin/l10n/it.php17
-rw-r--r--apps/files_trashbin/l10n/ja_JP.php17
-rw-r--r--apps/files_trashbin/l10n/ka_GE.php8
-rw-r--r--apps/files_trashbin/l10n/ko.php9
-rw-r--r--apps/files_trashbin/l10n/ku_IQ.php3
-rw-r--r--apps/files_trashbin/l10n/lb.php4
-rw-r--r--apps/files_trashbin/l10n/lt_LT.php8
-rw-r--r--apps/files_trashbin/l10n/lv.php17
-rw-r--r--apps/files_trashbin/l10n/mk.php8
-rw-r--r--apps/files_trashbin/l10n/ms_MY.php4
-rw-r--r--apps/files_trashbin/l10n/nb_NO.php16
-rw-r--r--apps/files_trashbin/l10n/nl.php17
-rw-r--r--apps/files_trashbin/l10n/nn_NO.php4
-rw-r--r--apps/files_trashbin/l10n/oc.php4
-rw-r--r--apps/files_trashbin/l10n/pl.php17
-rw-r--r--apps/files_trashbin/l10n/pt_BR.php17
-rw-r--r--apps/files_trashbin/l10n/pt_PT.php17
-rw-r--r--apps/files_trashbin/l10n/ro.php8
-rw-r--r--apps/files_trashbin/l10n/ru.php17
-rw-r--r--apps/files_trashbin/l10n/ru_RU.php17
-rw-r--r--apps/files_trashbin/l10n/si_LK.php6
-rw-r--r--apps/files_trashbin/l10n/sk_SK.php17
-rw-r--r--apps/files_trashbin/l10n/sl.php17
-rw-r--r--apps/files_trashbin/l10n/sr.php12
-rw-r--r--apps/files_trashbin/l10n/sr@latin.php4
-rw-r--r--apps/files_trashbin/l10n/sv.php17
-rw-r--r--apps/files_trashbin/l10n/ta_LK.php8
-rw-r--r--apps/files_trashbin/l10n/th_TH.php13
-rw-r--r--apps/files_trashbin/l10n/tr.php16
-rw-r--r--apps/files_trashbin/l10n/uk.php17
-rw-r--r--apps/files_trashbin/l10n/vi.php17
-rw-r--r--apps/files_trashbin/l10n/zh_CN.GB2312.php8
-rw-r--r--apps/files_trashbin/l10n/zh_CN.php17
-rw-r--r--apps/files_trashbin/l10n/zh_HK.php4
-rw-r--r--apps/files_trashbin/l10n/zh_TW.php10
-rw-r--r--apps/files_trashbin/lib/hooks.php45
-rw-r--r--apps/files_trashbin/lib/trash.php517
-rw-r--r--apps/files_trashbin/templates/index.php41
-rw-r--r--apps/files_trashbin/templates/part.breadcrumb.php20
-rw-r--r--apps/files_trashbin/templates/part.list.php71
-rw-r--r--apps/files_versions/ajax/getVersions.php8
-rw-r--r--apps/files_versions/ajax/rollbackVersion.php11
-rw-r--r--apps/files_versions/ajax/togglesettings.php10
-rw-r--r--apps/files_versions/appinfo/app.php14
-rw-r--r--apps/files_versions/appinfo/database.xml35
-rw-r--r--apps/files_versions/appinfo/info.xml2
-rw-r--r--apps/files_versions/appinfo/routes.php9
-rw-r--r--apps/files_versions/appinfo/version2
-rw-r--r--apps/files_versions/history.php33
-rw-r--r--apps/files_versions/js/settings-personal.js39
-rw-r--r--apps/files_versions/js/versions.js31
-rw-r--r--apps/files_versions/l10n/ar.php3
-rw-r--r--apps/files_versions/l10n/bg_BG.php3
-rw-r--r--apps/files_versions/l10n/bn_BD.php3
-rw-r--r--apps/files_versions/l10n/ca.php13
-rw-r--r--apps/files_versions/l10n/cs_CZ.php13
-rw-r--r--apps/files_versions/l10n/da.php13
-rw-r--r--apps/files_versions/l10n/de.php13
-rw-r--r--apps/files_versions/l10n/de_DE.php11
-rw-r--r--apps/files_versions/l10n/el.php14
-rw-r--r--apps/files_versions/l10n/eo.php3
-rw-r--r--apps/files_versions/l10n/es.php15
-rw-r--r--apps/files_versions/l10n/es_AR.php13
-rw-r--r--apps/files_versions/l10n/et_EE.php7
-rw-r--r--apps/files_versions/l10n/eu.php13
-rw-r--r--apps/files_versions/l10n/fa.php7
-rw-r--r--apps/files_versions/l10n/fi_FI.php14
-rw-r--r--apps/files_versions/l10n/fr.php13
-rw-r--r--apps/files_versions/l10n/gl.php12
-rw-r--r--apps/files_versions/l10n/he.php6
-rw-r--r--apps/files_versions/l10n/hu_HU.php11
-rw-r--r--apps/files_versions/l10n/id.php10
-rw-r--r--apps/files_versions/l10n/is.php3
-rw-r--r--apps/files_versions/l10n/it.php13
-rw-r--r--apps/files_versions/l10n/ja_JP.php13
-rw-r--r--apps/files_versions/l10n/ko.php5
-rw-r--r--apps/files_versions/l10n/ku_IQ.php3
-rw-r--r--apps/files_versions/l10n/lb.php5
-rw-r--r--apps/files_versions/l10n/lt_LT.php4
-rw-r--r--apps/files_versions/l10n/lv.php11
-rw-r--r--apps/files_versions/l10n/mk.php3
-rw-r--r--apps/files_versions/l10n/nb_NO.php4
-rw-r--r--apps/files_versions/l10n/nl.php13
-rw-r--r--apps/files_versions/l10n/pl.php13
-rw-r--r--apps/files_versions/l10n/pt_BR.php13
-rw-r--r--apps/files_versions/l10n/pt_PT.php13
-rw-r--r--apps/files_versions/l10n/ro.php3
-rw-r--r--apps/files_versions/l10n/ru.php10
-rw-r--r--apps/files_versions/l10n/ru_RU.php3
-rw-r--r--apps/files_versions/l10n/si_LK.php5
-rw-r--r--apps/files_versions/l10n/sk_SK.php13
-rw-r--r--apps/files_versions/l10n/sl.php13
-rw-r--r--apps/files_versions/l10n/sr.php5
-rw-r--r--apps/files_versions/l10n/sv.php14
-rw-r--r--apps/files_versions/l10n/ta_LK.php5
-rw-r--r--apps/files_versions/l10n/th_TH.php3
-rw-r--r--apps/files_versions/l10n/tr.php10
-rw-r--r--apps/files_versions/l10n/uk.php11
-rw-r--r--apps/files_versions/l10n/vi.php11
-rw-r--r--apps/files_versions/l10n/zh_CN.GB2312.php3
-rw-r--r--apps/files_versions/l10n/zh_CN.php13
-rw-r--r--apps/files_versions/l10n/zh_HK.php6
-rw-r--r--apps/files_versions/l10n/zh_TW.php5
-rw-r--r--apps/files_versions/lib/capabilities.php23
-rw-r--r--apps/files_versions/lib/hooks.php41
-rw-r--r--apps/files_versions/lib/versions.php490
-rw-r--r--apps/files_versions/settings-personal.php7
-rw-r--r--apps/files_versions/settings.php9
-rw-r--r--apps/files_versions/templates/history.php23
-rw-r--r--apps/files_versions/templates/settings-personal.php14
-rw-r--r--apps/files_versions/templates/settings.php6
-rw-r--r--apps/user_ldap/ajax/deleteConfiguration.php (renamed from apps/files_versions/ajax/expireAll.php)29
-rw-r--r--apps/user_ldap/ajax/getConfiguration.php31
-rw-r--r--apps/user_ldap/ajax/getNewServerConfigPrefix.php34
-rw-r--r--apps/user_ldap/ajax/setConfiguration.php33
-rw-r--r--apps/user_ldap/ajax/testConfiguration.php15
-rw-r--r--apps/user_ldap/appinfo/app.php29
-rw-r--r--apps/user_ldap/appinfo/database.xml2
-rw-r--r--apps/user_ldap/appinfo/info.xml8
-rw-r--r--apps/user_ldap/appinfo/update.php59
-rw-r--r--apps/user_ldap/appinfo/version2
-rw-r--r--apps/user_ldap/css/settings.css9
-rw-r--r--apps/user_ldap/group_ldap.php140
-rw-r--r--apps/user_ldap/group_proxy.php194
-rw-r--r--apps/user_ldap/js/settings.js157
-rw-r--r--apps/user_ldap/l10n/af_ZA.php4
-rw-r--r--apps/user_ldap/l10n/ar.php5
-rw-r--r--apps/user_ldap/l10n/bg_BG.php4
-rw-r--r--apps/user_ldap/l10n/bn_BD.php36
-rw-r--r--apps/user_ldap/l10n/ca.php48
-rw-r--r--apps/user_ldap/l10n/cs_CZ.php48
-rw-r--r--apps/user_ldap/l10n/da.php15
-rw-r--r--apps/user_ldap/l10n/de.php48
-rw-r--r--apps/user_ldap/l10n/de_DE.php75
-rw-r--r--apps/user_ldap/l10n/el.php41
-rw-r--r--apps/user_ldap/l10n/eo.php12
-rw-r--r--apps/user_ldap/l10n/es.php47
-rw-r--r--apps/user_ldap/l10n/es_AR.php48
-rw-r--r--apps/user_ldap/l10n/et_EE.php16
-rw-r--r--apps/user_ldap/l10n/eu.php48
-rw-r--r--apps/user_ldap/l10n/fa.php12
-rw-r--r--apps/user_ldap/l10n/fi_FI.php18
-rw-r--r--apps/user_ldap/l10n/fr.php52
-rw-r--r--apps/user_ldap/l10n/gl.php75
-rw-r--r--apps/user_ldap/l10n/he.php14
-rw-r--r--apps/user_ldap/l10n/hi.php3
-rw-r--r--apps/user_ldap/l10n/hr.php3
-rw-r--r--apps/user_ldap/l10n/hu_HU.php75
-rw-r--r--apps/user_ldap/l10n/ia.php3
-rw-r--r--apps/user_ldap/l10n/id.php69
-rw-r--r--apps/user_ldap/l10n/is.php5
-rw-r--r--apps/user_ldap/l10n/it.php48
-rw-r--r--apps/user_ldap/l10n/ja_JP.php48
-rw-r--r--apps/user_ldap/l10n/ka.php4
-rw-r--r--apps/user_ldap/l10n/ka_GE.php4
-rw-r--r--apps/user_ldap/l10n/ko.php53
-rw-r--r--apps/user_ldap/l10n/ku_IQ.php3
-rw-r--r--apps/user_ldap/l10n/lb.php5
-rw-r--r--apps/user_ldap/l10n/lt_LT.php1
-rw-r--r--apps/user_ldap/l10n/lv.php75
-rw-r--r--apps/user_ldap/l10n/mk.php7
-rw-r--r--apps/user_ldap/l10n/ms_MY.php4
-rw-r--r--apps/user_ldap/l10n/my_MM.php4
-rw-r--r--apps/user_ldap/l10n/nb_NO.php58
-rw-r--r--apps/user_ldap/l10n/nl.php75
-rw-r--r--apps/user_ldap/l10n/nn_NO.php3
-rw-r--r--apps/user_ldap/l10n/oc.php4
-rw-r--r--apps/user_ldap/l10n/pl.php48
-rw-r--r--apps/user_ldap/l10n/pt_BR.php52
-rw-r--r--apps/user_ldap/l10n/pt_PT.php67
-rw-r--r--apps/user_ldap/l10n/ro.php15
-rw-r--r--apps/user_ldap/l10n/ru.php48
-rw-r--r--apps/user_ldap/l10n/ru_RU.php21
-rw-r--r--apps/user_ldap/l10n/si_LK.php14
-rw-r--r--apps/user_ldap/l10n/sk_SK.php75
-rw-r--r--apps/user_ldap/l10n/sl.php78
-rw-r--r--apps/user_ldap/l10n/sr.php35
-rw-r--r--apps/user_ldap/l10n/sr@latin.php3
-rw-r--r--apps/user_ldap/l10n/sv.php44
-rw-r--r--apps/user_ldap/l10n/ta_LK.php27
-rw-r--r--apps/user_ldap/l10n/th_TH.php34
-rw-r--r--apps/user_ldap/l10n/tr.php30
-rw-r--r--apps/user_ldap/l10n/uk.php71
-rw-r--r--apps/user_ldap/l10n/ur_PK.php4
-rw-r--r--apps/user_ldap/l10n/vi.php34
-rw-r--r--apps/user_ldap/l10n/zh_CN.GB2312.php10
-rw-r--r--apps/user_ldap/l10n/zh_CN.php29
-rw-r--r--apps/user_ldap/l10n/zh_HK.php5
-rw-r--r--apps/user_ldap/l10n/zh_TW.php9
-rw-r--r--apps/user_ldap/lib/access.php577
-rw-r--r--apps/user_ldap/lib/connection.php397
-rw-r--r--apps/user_ldap/lib/helper.php104
-rw-r--r--apps/user_ldap/lib/jobs.php34
-rw-r--r--apps/user_ldap/lib/proxy.php104
-rw-r--r--apps/user_ldap/settings.php73
-rw-r--r--apps/user_ldap/templates/settings.php111
-rw-r--r--apps/user_ldap/tests/group_ldap.php18
-rw-r--r--apps/user_ldap/user_ldap.php160
-rw-r--r--apps/user_ldap/user_proxy.php194
-rwxr-xr-xapps/user_webdavauth/appinfo/app.php4
-rwxr-xr-xapps/user_webdavauth/appinfo/info.xml10
-rw-r--r--apps/user_webdavauth/appinfo/version1
-rw-r--r--apps/user_webdavauth/l10n/.gitkeep0
-rw-r--r--apps/user_webdavauth/l10n/ar.php3
-rw-r--r--apps/user_webdavauth/l10n/bn_BD.php3
-rw-r--r--apps/user_webdavauth/l10n/ca.php5
-rw-r--r--apps/user_webdavauth/l10n/cs_CZ.php5
-rw-r--r--apps/user_webdavauth/l10n/da.php5
-rw-r--r--apps/user_webdavauth/l10n/de.php5
-rw-r--r--apps/user_webdavauth/l10n/de_DE.php5
-rw-r--r--apps/user_webdavauth/l10n/el.php5
-rw-r--r--apps/user_webdavauth/l10n/eo.php4
-rw-r--r--apps/user_webdavauth/l10n/es.php5
-rw-r--r--apps/user_webdavauth/l10n/es_AR.php5
-rw-r--r--apps/user_webdavauth/l10n/et_EE.php4
-rw-r--r--apps/user_webdavauth/l10n/eu.php5
-rw-r--r--apps/user_webdavauth/l10n/fi_FI.php4
-rw-r--r--apps/user_webdavauth/l10n/fr.php5
-rw-r--r--apps/user_webdavauth/l10n/gl.php5
-rw-r--r--apps/user_webdavauth/l10n/hu_HU.php5
-rw-r--r--apps/user_webdavauth/l10n/id.php5
-rw-r--r--apps/user_webdavauth/l10n/is.php3
-rw-r--r--apps/user_webdavauth/l10n/it.php5
-rw-r--r--apps/user_webdavauth/l10n/ja_JP.php5
-rw-r--r--apps/user_webdavauth/l10n/ko.php5
-rw-r--r--apps/user_webdavauth/l10n/lv.php5
-rw-r--r--apps/user_webdavauth/l10n/mk.php3
-rw-r--r--apps/user_webdavauth/l10n/nb_NO.php3
-rw-r--r--apps/user_webdavauth/l10n/nl.php5
-rw-r--r--apps/user_webdavauth/l10n/pl.php5
-rw-r--r--apps/user_webdavauth/l10n/pt_BR.php5
-rw-r--r--apps/user_webdavauth/l10n/pt_PT.php5
-rw-r--r--apps/user_webdavauth/l10n/ro.php5
-rw-r--r--apps/user_webdavauth/l10n/ru.php5
-rw-r--r--apps/user_webdavauth/l10n/ru_RU.php4
-rw-r--r--apps/user_webdavauth/l10n/si_LK.php3
-rw-r--r--apps/user_webdavauth/l10n/sk_SK.php5
-rw-r--r--apps/user_webdavauth/l10n/sl.php5
-rw-r--r--apps/user_webdavauth/l10n/sr.php5
-rw-r--r--apps/user_webdavauth/l10n/sv.php5
-rw-r--r--apps/user_webdavauth/l10n/ta_LK.php3
-rw-r--r--apps/user_webdavauth/l10n/th_TH.php5
-rw-r--r--apps/user_webdavauth/l10n/tr.php4
-rw-r--r--apps/user_webdavauth/l10n/uk.php5
-rw-r--r--apps/user_webdavauth/l10n/vi.php5
-rw-r--r--apps/user_webdavauth/l10n/zh_CN.php5
-rw-r--r--apps/user_webdavauth/l10n/zh_TW.php3
-rwxr-xr-xapps/user_webdavauth/settings.php11
-rwxr-xr-xapps/user_webdavauth/templates/settings.php6
-rwxr-xr-xapps/user_webdavauth/user_webdavauth.php34
643 files changed, 44385 insertions, 5393 deletions
diff --git a/apps/files/admin.php b/apps/files/admin.php
index 547f2bd7ddb..02c3147dba5 100644
--- a/apps/files/admin.php
+++ b/apps/files/admin.php
@@ -21,21 +21,14 @@
*
*/
-
-// Init owncloud
-
-
OCP\User::checkAdminUser();
$htaccessWorking=(getenv('htaccessWorking')=='true');
$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
-$upload_max_filesize_possible = OCP\Util::computerFileSize(get_cfg_var('upload_max_filesize'));
$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
-$post_max_size_possible = OCP\Util::computerFileSize(get_cfg_var('post_max_size'));
$maxUploadFilesize = OCP\Util::humanFileSize(min($upload_max_filesize, $post_max_size));
-$maxUploadFilesizePossible = OCP\Util::humanFileSize(min($upload_max_filesize_possible, $post_max_size_possible));
-if($_POST) {
+if($_POST && OC_Util::isCallRegistered()) {
if(isset($_POST['maxUploadSize'])) {
if(($setMaxSize = OC_Files::setUploadLimit(OCP\Util::computerFileSize($_POST['maxUploadSize']))) !== false) {
$maxUploadFilesize = OCP\Util::humanFileSize($setMaxSize);
@@ -49,7 +42,8 @@ if($_POST) {
OCP\Config::setSystemValue('allowZipDownload', isset($_POST['allowZipDownload']));
}
}
-$maxZipInputSize = OCP\Util::humanFileSize(OCP\Config::getSystemValue('maxZipInputSize', OCP\Util::computerFileSize('800 MB')));
+$maxZipInputSizeDefault = OCP\Util::computerFileSize('800 MB');
+$maxZipInputSize = OCP\Util::humanFileSize(OCP\Config::getSystemValue('maxZipInputSize', $maxZipInputSizeDefault));
$allowZipDownload = intval(OCP\Config::getSystemValue('allowZipDownload', true));
OCP\App::setActiveNavigationEntry( "files_administration" );
@@ -59,8 +53,9 @@ $htaccessWritable=is_writable(OC::$SERVERROOT.'/.htaccess');
$tmpl = new OCP\Template( 'files', 'admin' );
$tmpl->assign( 'uploadChangable', $htaccessWorking and $htaccessWritable );
$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
-$tmpl->assign( 'maxPossibleUploadSize', $maxUploadFilesizePossible);
+// max possible makes only sense on a 32 bit system
+$tmpl->assign( 'displayMaxPossibleUploadSize', PHP_INT_SIZE===4);
+$tmpl->assign( 'maxPossibleUploadSize', OCP\Util::humanFileSize(PHP_INT_MAX));
$tmpl->assign( 'allowZipDownload', $allowZipDownload);
$tmpl->assign( 'maxZipInputSize', $maxZipInputSize);
return $tmpl->fetchPage();
-
diff --git a/apps/files/ajax/autocomplete.php b/apps/files/ajax/autocomplete.php
deleted file mode 100644
index fae38368a85..00000000000
--- a/apps/files/ajax/autocomplete.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-//provide auto completion of paths for use with jquer ui autocomplete
-
-
-// Init owncloud
-
-
-OCP\JSON::checkLoggedIn();
-
-// Get data
-$query = $_GET['term'];
-$dirOnly=(isset($_GET['dironly']))?($_GET['dironly']=='true'):false;
-
-if($query[0]!='/') {
- $query='/'.$query;
-}
-
-if(substr($query, -1, 1)=='/') {
- $base=$query;
-} else {
- $base=dirname($query);
-}
-
-$query=substr($query, strlen($base));
-
-if($base!='/') {
- $query=substr($query, 1);
-}
-$queryLen=strlen($query);
-$query=strtolower($query);
-
-// echo "$base - $query";
-
-$files=array();
-
-if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)) {
- $dh = OC_Filesystem::opendir($base);
- if($dh) {
- if(substr($base, -1, 1)!='/') {
- $base=$base.'/';
- }
- while (($file = readdir($dh)) !== false) {
- if ($file != "." && $file != "..") {
- if(substr(strtolower($file), 0, $queryLen)==$query) {
- $item=$base.$file;
- if((!$dirOnly or OC_Filesystem::is_dir($item))) {
- $files[]=(object)array('id'=>$item,'label'=>$item,'name'=>$item);
- }
- }
- }
- }
- }
-}
-OCP\JSON::encodedPrint($files);
diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php
index 57c8c15c197..da7e9d6b2aa 100644
--- a/apps/files/ajax/delete.php
+++ b/apps/files/ajax/delete.php
@@ -10,19 +10,24 @@ OCP\JSON::callCheck();
$dir = stripslashes($_POST["dir"]);
$files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_POST["files"]);
-$files = explode(';', $files);
+$files = json_decode($files);
$filesWithError = '';
+
$success = true;
+
//Now delete
-foreach($files as $file) {
- if( !OC_Files::delete( $dir, $file )) {
+foreach ($files as $file) {
+ if (($dir === '' && $file === 'Shared') || !\OC\Files\Filesystem::unlink($dir . '/' . $file)) {
$filesWithError .= $file . "\n";
$success = false;
}
}
-if($success) {
- OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $files )));
+// get array with updated storage stats (e.g. max file size) after upload
+$storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir);
+
+if ($success) {
+ OCP\JSON::success(array("data" => array_merge(array("dir" => $dir, "files" => $files), $storageStats)));
} else {
- OCP\JSON::error(array("data" => array( "message" => "Could not delete:\n" . $filesWithError )));
+ OCP\JSON::error(array("data" => array_merge(array("message" => "Could not delete:\n" . $filesWithError), $storageStats)));
}
diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php
index b9a4ddaf5e7..7c8dcb372e2 100644
--- a/apps/files/ajax/download.php
+++ b/apps/files/ajax/download.php
@@ -33,4 +33,10 @@ OCP\User::checkLoggedIn();
$files = $_GET["files"];
$dir = $_GET["dir"];
-OC_Files::get($dir, $files, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
+$files_list = json_decode($files);
+// in case we get only a single file
+if ($files_list === NULL ) {
+ $files_list = array($files);
+}
+
+OC_Files::get($dir, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
diff --git a/apps/files/ajax/getstoragestats.php b/apps/files/ajax/getstoragestats.php
new file mode 100644
index 00000000000..7a2b642a9bd
--- /dev/null
+++ b/apps/files/ajax/getstoragestats.php
@@ -0,0 +1,9 @@
+<?php
+
+// only need filesystem apps
+$RUNTIME_APPTYPES = array('filesystem');
+
+OCP\JSON::checkLoggedIn();
+
+// send back json
+OCP\JSON::success(array('data' => \OCA\files\lib\Helper::buildFileStorageStatistics('/')));
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index 568fe754c02..878e4cb2159 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -25,14 +25,14 @@ if($doBreadcrumb) {
}
$breadcrumbNav = new OCP\Template( "files", "part.breadcrumb", "" );
- $breadcrumbNav->assign( "breadcrumb", $breadcrumb );
+ $breadcrumbNav->assign( "breadcrumb", $breadcrumb, false );
$data['breadcrumb'] = $breadcrumbNav->fetchPage();
}
// make filelist
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir ) as $i ) {
+foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) {
$i["date"] = OCP\Util::formatDate($i["mtime"] );
$files[] = $i;
}
diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php
index 8b3149ef14e..93063e52eb0 100644
--- a/apps/files/ajax/move.php
+++ b/apps/files/ajax/move.php
@@ -7,13 +7,25 @@ OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
// Get data
-$dir = stripslashes($_GET["dir"]);
-$file = stripslashes($_GET["file"]);
-$target = stripslashes($_GET["target"]);
+$dir = stripslashes($_POST["dir"]);
+$file = stripslashes($_POST["file"]);
+$target = stripslashes(rawurldecode($_POST["target"]));
+$l = OC_L10N::get('files');
-if(OC_Files::move($dir, $file, $target, $file)) {
- OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
-} else {
- OCP\JSON::error(array("data" => array( "message" => "Could not move $file" )));
+if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) {
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s - File with this name already exists", array($file)) )));
+ exit;
+}
+
+if ($dir != '' || $file != 'Shared') {
+ $targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file);
+ $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
+ if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
+ OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
+ } else {
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
+ }
+}else{
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
}
diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php
index 77d866979c3..38714f34a63 100644
--- a/apps/files/ajax/newfile.php
+++ b/apps/files/ajax/newfile.php
@@ -9,7 +9,7 @@ if(!OC_User::isLoggedIn()) {
session_write_close();
// Get the params
-$dir = isset( $_REQUEST['dir'] ) ? trim($_REQUEST['dir'], '/\\') : '';
+$dir = isset( $_REQUEST['dir'] ) ? '/'.trim($_REQUEST['dir'], '/\\') : '';
$filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : '';
$content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : '';
$source = isset( $_REQUEST['source'] ) ? trim($_REQUEST['source'], '/\\') : '';
@@ -63,12 +63,12 @@ if($source) {
$ctx = stream_context_create(null, array('notification' =>'progress'));
$sourceStream=fopen($source, 'rb', false, $ctx);
$target=$dir.'/'.$filename;
- $result=OC_Filesystem::file_put_contents($target, $sourceStream);
+ $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream);
if($result) {
- $meta = OC_FileCache::get($target);
+ $meta = \OC\Files\Filesystem::getFileInfo($target);
$mime=$meta['mimetype'];
- $id = OC_FileCache::getId($target);
- $eventSource->send('success', array('mime'=>$mime, 'size'=>OC_Filesystem::filesize($target), 'id' => $id));
+ $id = $meta['fileid'];
+ $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id));
} else {
$eventSource->send('error', "Error while downloading ".$source. ' to '.$target);
}
@@ -76,15 +76,15 @@ if($source) {
exit();
} else {
if($content) {
- if(OC_Filesystem::file_put_contents($dir.'/'.$filename, $content)) {
- $meta = OC_FileCache::get($dir.'/'.$filename);
- $id = OC_FileCache::getId($dir.'/'.$filename);
+ if(\OC\Files\Filesystem::file_put_contents($dir.'/'.$filename, $content)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id)));
exit();
}
- }elseif(OC_Files::newFile($dir, $filename, 'file')) {
- $meta = OC_FileCache::get($dir.'/'.$filename);
- $id = OC_FileCache::getId($dir.'/'.$filename);
+ }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id)));
exit();
}
diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php
index 0f1f2f14eb0..e26e1238bc6 100644
--- a/apps/files/ajax/newfolder.php
+++ b/apps/files/ajax/newfolder.php
@@ -19,13 +19,14 @@ if(strpos($foldername, '/')!==false) {
exit();
}
-if(OC_Files::newFile($dir, stripslashes($foldername), 'dir')) {
+if(\OC\Files\Filesystem::mkdir($dir . '/' . stripslashes($foldername))) {
if ( $dir != '/') {
$path = $dir.'/'.$foldername;
} else {
$path = '/'.$foldername;
}
- $id = OC_FileCache::getId($path);
+ $meta = \OC\Files\Filesystem::getFileInfo($path);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('id'=>$id)));
exit();
}
diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php
index e0aa0bdac52..1cd2944483c 100644
--- a/apps/files/ajax/rawlist.php
+++ b/apps/files/ajax/rawlist.php
@@ -15,7 +15,7 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : '';
// make filelist
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ) {
+foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $i ) {
$i["date"] = OCP\Util::formatDate($i["mtime"] );
$i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']);
$files[] = $i;
diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php
index 45448279fa1..9fd2ce3ad4b 100644
--- a/apps/files/ajax/rename.php
+++ b/apps/files/ajax/rename.php
@@ -11,10 +11,16 @@ $dir = stripslashes($_GET["dir"]);
$file = stripslashes($_GET["file"]);
$newname = stripslashes($_GET["newname"]);
-// Delete
-if( OC_Files::move( $dir, $file, $dir, $newname )) {
- OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname )));
-}
-else{
- OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" )));
+$l = OC_L10N::get('files');
+
+if ( $newname !== '.' and ($dir != '' || $file != 'Shared') and $newname !== '.') {
+ $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname);
+ $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
+ if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
+ OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname )));
+ } else {
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") )));
+ }
+}else{
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") )));
}
diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php
index d2ec1ab5161..391b98608bd 100644
--- a/apps/files/ajax/scan.php
+++ b/apps/files/ajax/scan.php
@@ -1,43 +1,71 @@
<?php
+set_time_limit(0); //scanning can take ages
+session_write_close();
-set_time_limit(0);//scanning can take ages
+$force = (isset($_GET['force']) and ($_GET['force'] === 'true'));
+$dir = isset($_GET['dir']) ? $_GET['dir'] : '';
-$force=isset($_GET['force']) and $_GET['force']=='true';
-$dir=isset($_GET['dir'])?$_GET['dir']:'';
-$checkOnly=isset($_GET['checkonly']) and $_GET['checkonly']=='true';
+$eventSource = new OC_EventSource();
+ScanListener::$eventSource = $eventSource;
+ScanListener::$view = \OC\Files\Filesystem::getView();
-if(!$checkOnly) {
- $eventSource=new OC_EventSource();
-}
+OC_Hook::connect('\OC\Files\Cache\Scanner', 'scan_folder', 'ScanListener', 'folder');
+OC_Hook::connect('\OC\Files\Cache\Scanner', 'scan_file', 'ScanListener', 'file');
-session_write_close();
+$absolutePath = \OC\Files\Filesystem::getView()->getAbsolutePath($dir);
-//create the file cache if necesary
-if($force or !OC_FileCache::inCache('')) {
- if(!$checkOnly) {
- OCP\DB::beginTransaction();
+$mountPoints = \OC\Files\Filesystem::getMountPoints($absolutePath);
+$mountPoints[] = \OC\Files\Filesystem::getMountPoint($absolutePath);
+$mountPoints = array_reverse($mountPoints); //start with the mount point of $dir
- if(OC_Cache::isFast()) {
- OC_Cache::clear('fileid/'); //make sure the old fileid's don't mess things up
+foreach ($mountPoints as $mountPoint) {
+ $storage = \OC\Files\Filesystem::getStorage($mountPoint);
+ if ($storage) {
+ ScanListener::$mountPoints[$storage->getId()] = $mountPoint;
+ $scanner = $storage->getScanner();
+ if ($force) {
+ $scanner->scan('');
+ } else {
+ $scanner->backgroundScan();
}
-
- OC_FileCache::scan($dir,$eventSource);
- OC_FileCache::clean();
- OCP\DB::commit();
- $eventSource->send('success', true);
- } else {
- OCP\JSON::success(array('data'=>array('done'=>true)));
- exit;
}
-} else {
- if($checkOnly) {
- OCP\JSON::success(array('data'=>array('done'=>false)));
- exit;
+}
+
+$eventSource->send('done', ScanListener::$fileCount);
+$eventSource->close();
+
+class ScanListener {
+
+ static public $fileCount = 0;
+ static public $lastCount = 0;
+
+ /**
+ * @var \OC\Files\View $view
+ */
+ static public $view;
+
+ /**
+ * @var array $mountPoints map storage ids to mountpoints
+ */
+ static public $mountPoints = array();
+
+ /**
+ * @var \OC_EventSource event source to pass events to
+ */
+ static public $eventSource;
+
+ static function folder($params) {
+ $internalPath = $params['path'];
+ $mountPoint = self::$mountPoints[$params['storage']];
+ $path = self::$view->getRelativePath($mountPoint . $internalPath);
+ self::$eventSource->send('folder', $path);
}
- if(isset($eventSource)) {
- $eventSource->send('success', false);
- } else {
- exit;
+
+ static function file() {
+ self::$fileCount++;
+ if (self::$fileCount > self::$lastCount + 20) { //send a count update every 20 files
+ self::$lastCount = self::$fileCount;
+ self::$eventSource->send('count', self::$fileCount);
+ }
}
}
-$eventSource->close();
diff --git a/apps/files/ajax/timezone.php b/apps/files/ajax/timezone.php
deleted file mode 100644
index b71fa3940cb..00000000000
--- a/apps/files/ajax/timezone.php
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
- // FIXME: this should start a secure session if forcessl is enabled
- // see lib/base.php for an example
- //session_start();
- $_SESSION['timezone'] = $_GET['time'];
diff --git a/apps/files/ajax/upgrade.php b/apps/files/ajax/upgrade.php
new file mode 100644
index 00000000000..7237b02c0b0
--- /dev/null
+++ b/apps/files/ajax/upgrade.php
@@ -0,0 +1,44 @@
+<?php
+set_time_limit(0); //scanning can take ages
+session_write_close();
+
+$user = OC_User::getUser();
+$eventSource = new OC_EventSource();
+$listener = new UpgradeListener($eventSource);
+$legacy = new \OC\Files\Cache\Legacy($user);
+
+if ($legacy->hasItems()) {
+ OC_Hook::connect('\OC\Files\Cache\Upgrade', 'migrate_path', $listener, 'upgradePath');
+
+ OC_DB::beginTransaction();
+ $upgrade = new \OC\Files\Cache\Upgrade($legacy);
+ $count = $legacy->getCount();
+ $eventSource->send('total', $count);
+ $upgrade->upgradePath('/' . $user . '/files');
+ OC_DB::commit();
+}
+\OC\Files\Cache\Upgrade::upgradeDone($user);
+$eventSource->send('done', true);
+$eventSource->close();
+
+class UpgradeListener {
+ /**
+ * @var OC_EventSource $eventSource
+ */
+ private $eventSource;
+
+ private $count = 0;
+ private $lastSend = 0;
+
+ public function __construct($eventSource) {
+ $this->eventSource = $eventSource;
+ }
+
+ public function upgradePath($path) {
+ $this->count++;
+ if ($this->count > ($this->lastSend + 5)) {
+ $this->lastSend = $this->count;
+ $this->eventSource->send('count', $this->count);
+ }
+ }
+}
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index a4dcd80a2e2..5b697777e47 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -8,56 +8,78 @@ OCP\JSON::setContentTypeHeader('text/plain');
OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
+$l = OC_L10N::get('files');
+
+
+$dir = $_POST['dir'];
+// get array with current storage stats (e.g. max file size)
+$storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir);
if (!isset($_FILES['files'])) {
- OCP\JSON::error(array("data" => array( "message" => "No file was uploaded. Unknown error" )));
+ OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('No file was uploaded. Unknown error')), $storageStats)));
exit();
}
+
foreach ($_FILES['files']['error'] as $error) {
if ($error != 0) {
- $l=OC_L10N::get('files');
$errors = array(
- UPLOAD_ERR_OK=>$l->t("There is no error, the file uploaded with success"),
- UPLOAD_ERR_INI_SIZE=>$l->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
- UPLOAD_ERR_FORM_SIZE=>$l->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
- UPLOAD_ERR_PARTIAL=>$l->t("The uploaded file was only partially uploaded"),
- UPLOAD_ERR_NO_FILE=>$l->t("No file was uploaded"),
- UPLOAD_ERR_NO_TMP_DIR=>$l->t("Missing a temporary folder"),
- UPLOAD_ERR_CANT_WRITE=>$l->t('Failed to write to disk'),
+ UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'),
+ UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ')
+ . ini_get('upload_max_filesize'),
+ UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
+ UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'),
+ UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'),
+ UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'),
+ UPLOAD_ERR_CANT_WRITE => $l->t('Failed to write to disk'),
);
- OCP\JSON::error(array("data" => array( "message" => $errors[$error] )));
+ OCP\JSON::error(array('data' => array_merge(array('message' => $errors[$error]), $storageStats)));
exit();
}
}
-$files=$_FILES['files'];
+$files = $_FILES['files'];
-$dir = $_POST['dir'];
-$error='';
+$error = '';
+
+$maxUploadFilesize = OCP\Util::maxUploadFilesize($dir);
+$maxHumanFilesize = OCP\Util::humanFileSize($maxUploadFilesize);
-$totalSize=0;
-foreach($files['size'] as $size) {
- $totalSize+=$size;
+$totalSize = 0;
+foreach ($files['size'] as $size) {
+ $totalSize += $size;
}
-if($totalSize>OC_Filesystem::free_space('/')) {
- OCP\JSON::error(array("data" => array( "message" => "Not enough space available" )));
+if ($totalSize > $maxUploadFilesize) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Not enough storage available'),
+ 'uploadMaxFilesize' => $maxUploadFilesize,
+ 'maxHumanFilesize' => $maxHumanFilesize)));
exit();
}
-$result=array();
-if(strpos($dir, '..') === false) {
- $fileCount=count($files['name']);
- for($i=0;$i<$fileCount;$i++) {
- $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]);
- if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) {
- $meta = OC_FileCache::get($target);
- $id = OC_FileCache::getId($target);
- $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'], 'id'=>$id, 'name'=>basename($target));
+$result = array();
+if (strpos($dir, '..') === false) {
+ $fileCount = count($files['name']);
+ for ($i = 0; $i < $fileCount; $i++) {
+ $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]);
+ // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder
+ $target = \OC\Files\Filesystem::normalizePath($target);
+ if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($target);
+ // updated max file size after upload
+ $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir);
+
+ $result[] = array('status' => 'success',
+ 'mime' => $meta['mimetype'],
+ 'size' => $meta['size'],
+ 'id' => $meta['fileid'],
+ 'name' => basename($target),
+ 'uploadMaxFilesize' => $maxUploadFilesize,
+ 'maxHumanFilesize' => $maxHumanFilesize
+ );
}
}
OCP\JSON::encodedPrint($result);
exit();
} else {
- $error='invalid dir';
+ $error = $l->t('Invalid directory.');
}
-OCP\JSON::error(array('data' => array('error' => $error, "file" => $fileName)));
+OCP\JSON::error(array('data' => array_merge(array('message' => $error), $storageStats)));
diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php
index db3b213ab8d..6535a9b7baa 100644
--- a/apps/files/appinfo/app.php
+++ b/apps/files/appinfo/app.php
@@ -1,8 +1,14 @@
<?php
-$l=OC_L10N::get('files');
+OC::$CLASSPATH['OCA\Files\Capabilities'] = 'apps/files/lib/capabilities.php';
-OCP\App::registerAdmin('files','admin');
+$l = OC_L10N::get('files');
-OCP\App::addNavigationEntry( array( "id" => "files_index", "order" => 0, "href" => OCP\Util::linkTo( "files", "index.php" ), "icon" => OCP\Util::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") ));
+OCP\App::registerAdmin('files', 'admin');
+
+OCP\App::addNavigationEntry( array( "id" => "files_index",
+ "order" => 0,
+ "href" => OCP\Util::linkTo( "files", "index.php" ),
+ "icon" => OCP\Util::imagePath( "core", "places/files.svg" ),
+ "name" => $l->t("Files") ));
OC_Search::registerProvider('OC_Search_Provider_File');
diff --git a/apps/files/appinfo/filesync.php b/apps/files/appinfo/filesync.php
index c1fe444cec7..47884a4f15e 100644
--- a/apps/files/appinfo/filesync.php
+++ b/apps/files/appinfo/filesync.php
@@ -20,30 +20,30 @@
* The final URL will look like http://.../remote.php/filesync/oc_chunked/path/to/file
*/
-// only need filesystem apps
-$RUNTIME_APPTYPES=array('filesystem','authentication');
+// load needed apps
+$RUNTIME_APPTYPES=array('filesystem', 'authentication', 'logging');
OC_App::loadApps($RUNTIME_APPTYPES);
if(!OC_User::isLoggedIn()) {
- if(!isset($_SERVER['PHP_AUTH_USER'])) {
- header('WWW-Authenticate: Basic realm="ownCloud Server"');
- header('HTTP/1.0 401 Unauthorized');
- echo 'Valid credentials must be supplied';
- exit();
- } else {
- if(!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) {
- exit();
- }
- }
+ if(!isset($_SERVER['PHP_AUTH_USER'])) {
+ header('WWW-Authenticate: Basic realm="ownCloud Server"');
+ header('HTTP/1.0 401 Unauthorized');
+ echo 'Valid credentials must be supplied';
+ exit();
+ } else {
+ if(!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) {
+ exit();
+ }
+ }
}
-list($type,$file) = explode('/', substr($path_info,1+strlen($service)+1), 2);
+list($type, $file) = explode('/', substr($path_info, 1+strlen($service)+1), 2);
if ($type != 'oc_chunked') {
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
die;
}
-if (!OC_Filesystem::is_file($file)) {
+if (!\OC\Files\Filesystem::is_file($file)) {
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
die;
}
@@ -51,7 +51,7 @@ if (!OC_Filesystem::is_file($file)) {
switch($_SERVER['REQUEST_METHOD']) {
case 'PUT':
$input = fopen("php://input", "r");
- $org_file = OC_Filesystem::fopen($file, 'rb');
+ $org_file = \OC\Files\Filesystem::fopen($file, 'rb');
$info = array(
'name' => basename($file),
);
diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml
index 0a1b196b06f..34800378537 100644
--- a/apps/files/appinfo/info.xml
+++ b/apps/files/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>File Management</description>
<licence>AGPL</licence>
<author>Robin Appelman</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
<standalone/>
<default_enable/>
diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php
index a84216b61b7..6c92cc80b69 100644
--- a/apps/files/appinfo/remote.php
+++ b/apps/files/appinfo/remote.php
@@ -22,25 +22,32 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
-// only need filesystem apps
-$RUNTIME_APPTYPES=array('filesystem','authentication');
+// load needed apps
+$RUNTIME_APPTYPES=array('filesystem', 'authentication', 'logging');
+
OC_App::loadApps($RUNTIME_APPTYPES);
+OC_Util::obEnd();
+
// Backends
$authBackend = new OC_Connector_Sabre_Auth();
$lockBackend = new OC_Connector_Sabre_Locks();
+$requestBackend = new OC_Connector_Sabre_Request();
// Create ownCloud Dir
$publicDir = new OC_Connector_Sabre_Directory('');
// Fire up server
$server = new Sabre_DAV_Server($publicDir);
+$server->httpRequest = $requestBackend;
$server->setBaseUri($baseuri);
// Load plugins
-$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud'));
$server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend));
$server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
+$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin());
+$server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin());
// And off we go!
$server->exec();
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
new file mode 100644
index 00000000000..fcd5f4b2608
--- /dev/null
+++ b/apps/files/appinfo/routes.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$this->create('download', 'download{file}')
+ ->requirements(array('file' => '.*'))
+ ->actionInclude('files/download.php');
+
+// Register with the capabilities API
+OC_API::register('get', '/cloud/capabilities', array('OCA\Files\Capabilities', 'getCapabilities'), 'files', OC_API::USER_AUTH); \ No newline at end of file
diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php
index bcbbc6035fa..3503678e7c7 100644
--- a/apps/files/appinfo/update.php
+++ b/apps/files/appinfo/update.php
@@ -3,12 +3,15 @@
// fix webdav properties,add namespace in front of the property, update for OC4.5
$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
if (version_compare($installedVersion, '1.1.6', '<')) {
- $query = OC_DB::prepare( "SELECT `propertyname`, `propertypath`, `userid` FROM `*PREFIX*properties`" );
+ $query = OC_DB::prepare( 'SELECT `propertyname`, `propertypath`, `userid` FROM `*PREFIX*properties`' );
$result = $query->execute();
- while( $row = $result->fetchRow()){
- if ( $row["propertyname"][0] != '{' ) {
- $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyname` = ? WHERE `userid` = ? AND `propertypath` = ?' );
- $query->execute( array( '{DAV:}' + $row["propertyname"], $row["userid"], $row["propertypath"] ));
+ $updateQuery = OC_DB::prepare('UPDATE `*PREFIX*properties`'
+ .' SET `propertyname` = ?'
+ .' WHERE `userid` = ?'
+ .' AND `propertypath` = ?');
+ while( $row = $result->fetchRow()) {
+ if ( $row['propertyname'][0] != '{' ) {
+ $updateQuery->execute(array('{DAV:}' + $row['propertyname'], $row['userid'], $row['propertypath']));
}
}
}
@@ -36,10 +39,11 @@ foreach($filesToRemove as $file) {
if(!file_exists($filepath)) {
continue;
}
- $success = OCP\Files::rmdirr($filepath);
- if($success === false) {
+ $success = OCP\Files::rmdirr($filepath);
+ if($success === false) {
//probably not sufficient privileges, give up and give a message.
- OCP\Util::writeLog('files','Could not clean /files/ directory. Please remove everything except webdav.php from ' . OC::$SERVERROOT . '/files/', OCP\Util::ERROR);
+ OCP\Util::writeLog('files', 'Could not clean /files/ directory.'
+ .' Please remove everything except webdav.php from ' . OC::$SERVERROOT . '/files/', OCP\Util::ERROR);
break;
- }
+ }
}
diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version
index 0664a8fd291..2bf1ca5f549 100644
--- a/apps/files/appinfo/version
+++ b/apps/files/appinfo/version
@@ -1 +1 @@
-1.1.6
+1.1.7
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index f15cd514241..4d2b16e6f1c 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -3,43 +3,59 @@
See the COPYING-README file. */
/* FILE MENU */
-.actions { padding:.3em; float:left; height:2em; }
-.actions input, .actions button, .actions .button { margin:0; }
-#file_menu { right:0; position:absolute; top:0; }
-#file_menu a { display:block; float:left; background-image:none; text-decoration:none; }
-.file_upload_form, #file_newfolder_form { display:inline; float: left; margin-left:0; }
-#fileSelector, #file_upload_submit, #file_newfolder_submit { display:none; }
-.file_upload_wrapper, #file_newfolder_name { background-repeat:no-repeat; background-position:.5em .5em; padding-left:2em; }
-.file_upload_wrapper { font-weight:bold; display:-moz-inline-box; /* fallback for older firefox versions*/ display:block; float:left; padding-left:0; overflow:hidden; position:relative; margin:0;}
-.file_upload_wrapper .file_upload_button_wrapper { position:absolute; top:0; left:0; width:100%; height:100%; cursor:pointer; z-index:1000; }
-#new { background-color:#5bb75b; float:left; border-top-right-radius:0; border-bottom-right-radius:0; margin:0 0 0 1em; border-right:none; z-index:1010; height:1.3em; }
-#new:hover, a.file_upload_button_wrapper:hover + button.file_upload_filename { background-color:#4b964b; }
-#new.active { border-bottom-left-radius:0; border-bottom:none; }
-#new>a { padding:.5em 1.2em .3em; color:#fff; text-shadow:0 1px 0 #51a351; }
-#new>ul { display:none; position:fixed; text-align:left; padding:.5em; background:#f8f8f8; margin-top:0.075em; border:1px solid #ddd; min-width:7em; margin-left:-.5em; z-index:-1; }
-#new>ul>li { margin:.3em; padding-left:2em; background-repeat:no-repeat; cursor:pointer; padding-bottom:0.1em }
-#new>ul>li>p { cursor:pointer; }
-#new>ul>li>input { padding:0.3em; margin:-0.3em; }
-#new, .file_upload_filename { border:1px solid; border-color:#51a351 #419341 #387038; -moz-box-shadow:0 1px 1px #f8f8f8, 1px 1px 1px #ada inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 1px 1px 1px #ada inset; box-shadow:0 1px 1px #f8f8f8, 1px 1px 1px #ada inset; }
-#new .popup { border-top-left-radius:0; }
+.actions { padding:.3em; height:2em; width: 100%; }
+.actions input, .actions button, .actions .button { margin:0; float:left; }
-#file_newfolder_name { background-image:url('%webroot%/core/img/places/folder.svg'); font-weight:normal; width:7em; }
-.file_upload_start, .file_upload_filename { font-size:1em; }
-#file_newfolder_submit, #file_upload_submit { width:3em; }
-.file_upload_target { display:none; }
+#new {
+ height:17px; margin:0 0 0 1em; z-index:1010; float:left;
+}
+#new.active { border-bottom-left-radius:0; border-bottom-right-radius:0; border-bottom:none; }
+#new>a { padding:.5em 1.2em .3em; }
+#new>ul {
+ display:none; position:fixed; min-width:7em; z-index:10;
+ padding:.5em; padding-bottom:0; margin-top:.075em; margin-left:-.5em;
+ text-align:left;
+ background:#f8f8f8; border:1px solid #ddd; border-radius:10px; border-top-left-radius:0;
+ box-shadow:0 2px 7px rgba(170,170,170,.4);
+}
+#new>ul>li { height:20px; margin:.3em; padding-left:2em; padding-bottom:0.1em;
+ background-repeat:no-repeat; cursor:pointer; }
+#new>ul>li>p { cursor:pointer; }
+#new>ul>li>form>input { padding:0.3em; margin:-0.3em; }
-.file_upload_start { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1; position:absolute; left:0; top:0; width:100%; cursor:pointer;}
-.file_upload_filename.active { border-bottom-right-radius:0 }
-.file_upload_filename { background-color:#5bb75b; z-index:100; cursor:pointer; border-top-left-radius:0; border-bottom-left-radius:0; background-image: url('%webroot%/core/img/actions/upload-white.svg'); background-repeat: no-repeat; background-position: center; height: 2.29em; width: 2.5em; }
+#trash { height:17px; margin: 0 1em; z-index:1010; float: right; }
-#upload { position:absolute; right:13.5em; top:0em; }
-#upload #uploadprogressbar { position:relative; display:inline-block; width:10em; height:1.5em; top:.4em; }
+#upload {
+ height:27px; padding:0; margin-left:0.2em; overflow:hidden;
+}
+#upload a {
+ position:relative; display:block; width:100%; height:27px;
+ cursor:pointer; z-index:10;
+ background-image:url('%webroot%/core/img/actions/upload.svg');
+ background-repeat:no-repeat;
+ background-position:7px 6px;
+}
+.file_upload_target { display:none; }
+.file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; }
+#file_upload_start {
+ left:0; top:0; width:28px; height:27px; padding:0;
+ font-size:1em;
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0;
+ z-index:20; position:relative; cursor:pointer; overflow:hidden;
+}
-.file_upload_form, .file_upload_wrapper, .file_upload_start, .file_upload_filename, #file_upload_submit { cursor:pointer; }
+#uploadprogresswrapper { float: right; position: relative; }
+#uploadprogresswrapper #uploadprogressbar { position:relative; display:inline-block; width:10em; height:1.5em; top:.4em; }
/* FILE TABLE */
-#emptyfolder { position:absolute; margin:10em 0 0 10em; font-size:1.5em; font-weight:bold; color:#888; text-shadow:#fff 0 1px 0; }
-table { position:relative; top:37px; width:100%; }
+
+#emptyfolder {
+ position:absolute;
+ margin:10em 0 0 10em;
+ font-size:1.5em; font-weight:bold;
+ color:#888; text-shadow:#fff 0 1px 0;
+}
+#filestable { position: relative; top:37px; width:100%; }
tbody tr { background-color:#fff; height:2.5em; }
tbody tr:hover, tbody tr:active, tbody tr.selected { background-color:#f8f8f8; }
tbody tr.selected { background-color:#eee; }
@@ -50,43 +66,101 @@ tr:hover span.extension { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Op
table tr.mouseOver td { background-color:#eee; }
table th { height:2em; padding:0 .5em; color:#999; }
table th .name { float:left; margin-left:.5em; }
-table th.multiselect { background:#ddd; color:#000; font-weight:bold; }
table th, table td { border-bottom:1px solid #ddd; text-align:left; font-weight:normal; }
table td { border-bottom:1px solid #eee; font-style:normal; background-position:1em .5em; background-repeat:no-repeat; }
-table th#headerSize, table td.filesize { width:3em; padding:0 1em; text-align:right; }
-table th#headerDate, table td.date { width:11em; padding:0 .1em 0 1em; text-align:left; }
+table th#headerName { width:100em; /* not really sure why this works better than 100% … table styling */ }
+table th#headerSize, table td.filesize { min-width:3em; padding:0 1em; text-align:right; }
+table th#headerDate, table td.date { min-width:11em; padding:0 .1em 0 1em; text-align:left; }
+
+/* Multiselect bar */
+#filestable.multiselect { top:63px; }
+table.multiselect thead { position:fixed; top:82px; z-index:1; -moz-box-sizing: border-box; box-sizing: border-box; left: 0; padding-left: 64px; width:100%; }
+table.multiselect thead th { background:rgba(230,230,230,.8); color:#000; font-weight:bold; border-bottom:0; }
+table.multiselect #headerName { width: 100%; }
table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; }
table td.filename a.name { display:block; height:1.5em; vertical-align:middle; margin-left:3em; }
table tr[data-type="dir"] td.filename a.name span.nametext {font-weight:bold; }
-table td.filename a.name input, table td.filename a.name form { width:100%; cursor:text; }
+table td.filename input.filename { width:100%; cursor:text; }
table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:.2em .5em .5em 0; }
table td.filename .nametext, .uploadtext, .modified { float:left; padding:.3em 0; }
-// TODO fix usability bug (accidental file/folder selection)
-table td.filename .nametext { width:40em; overflow:hidden; text-overflow:ellipsis; }
+/* TODO fix usability bug (accidental file/folder selection) */
+table td.filename .nametext { overflow:hidden; text-overflow:ellipsis; }
table td.filename .uploadtext { font-weight:normal; margin-left:.5em; }
-table td.filename form { float:left; font-size:.85em; }
-table thead.fixed tr{ position:fixed; top:6.5em; z-index:49; -moz-box-shadow:0 -3px 7px #ddd; -webkit-box-shadow:0 -3px 7px #ddd; box-shadow:0 -3px 7px #ddd; }
-table thead.fixed { height:2em; }
-#fileList tr td.filename>input[type=checkbox]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 200ms; -moz-transition:opacity 200ms; -o-transition:opacity 200ms; transition:opacity 200ms; }
+table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; }
+
+/* File checkboxes */
+#fileList tr td.filename>input[type="checkbox"]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 200ms; -moz-transition:opacity 200ms; -o-transition:opacity 200ms; transition:opacity 200ms; }
#fileList tr td.filename>input[type="checkbox"]:hover:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; }
+/* Always show checkbox when selected */
#fileList tr td.filename>input[type="checkbox"]:checked:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; }
-#fileList tr td.filename { -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; position:relative; }
+#fileList tr.selected td.filename>input[type="checkbox"]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; }
+
+#fileList tr td.filename {
+ position:relative; width:100%;
+ -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms;
+}
#select_all { float:left; margin:.3em 0.6em 0 .5em; }
#uploadsize-message,#delete-confirm { display:none; }
-.fileactions { position:relative; top:.3em; font-size:.8em; float:right; }
+
+/* File actions */
+.fileactions {
+ position:absolute; top:.6em; right:0;
+ font-size:.8em;
+}
+#fileList .name { position:relative; /* Firefox needs to explicitly have this default set … */ }
+#fileList tr:hover .fileactions { /* background to distinguish when overlaying with file names */
+ background:rgba(248,248,248,.9); box-shadow:-5px 0 7px rgba(248,248,248,.9);
+}
+#fileList tr.selected:hover .fileactions, #fileList tr.mouseOver .fileactions { /* slightly darker color for selected rows */
+ background:rgba(238,238,238,.9); box-shadow:-5px 0 7px rgba(238,238,238,.9);
+}
#fileList .fileactions a.action img { position:relative; top:.2em; }
#fileList a.action { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; }
+#fileList img.move2trash { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; float:right; }
a.action.delete { float:right; }
a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; }
.selectedActions { display:none; float:right; }
.selectedActions a { display:inline; margin:-.5em 0; padding:.5em !important; }
.selectedActions a img { position:relative; top:.3em; }
-/* add breadcrumb divider to the File item in navigation panel */
-#navigation>ul>li:first-child { background:url('%webroot%/core/img/breadcrumb-start.svg') no-repeat 12.5em 0px; width:12.5em; padding-right:1em; position:fixed; }
-#navigation>ul>li:first-child+li { padding-top:2.9em; }
+#fileList a.action {
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: alpha(opacity=0);
+ opacity: 0;
+ display:none;
+}
+#fileList tr:hover a.action, #fileList a.action.permanent {
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=.5)";
+ filter: alpha(opacity=.5);
+ opacity: .5;
+ display:inline;
+}
+#fileList tr:hover a.action:hover {
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=1)";
+ filter: alpha(opacity=1);
+ opacity: 1;
+ display:inline;
+}
#scanning-message{ top:40%; left:40%; position:absolute; display:none; }
-div.crumb a{ padding: 0.9em 0 0.7em 0; }
+div.crumb a{ padding:0.9em 0 0.7em 0; }
+table.dragshadow {
+ width:auto;
+}
+table.dragshadow td.filename {
+ padding-left:36px;
+ padding-right:16px;
+}
+table.dragshadow td.size {
+ padding-right:8px;
+}
+#upgrade {
+ width: 400px;
+ position: absolute;
+ top: 200px;
+ left: 50%;
+ text-align: center;
+ margin-left: -200px;
+}
diff --git a/apps/files/download.php b/apps/files/download.php
index ff6aefbbe0f..e3fe24e45d7 100644
--- a/apps/files/download.php
+++ b/apps/files/download.php
@@ -21,28 +21,30 @@
*
*/
-// Init owncloud
-
-
// Check if we are a user
OCP\User::checkLoggedIn();
$filename = $_GET["file"];
-if(!OC_Filesystem::file_exists($filename)) {
+if(!\OC\Files\Filesystem::file_exists($filename)) {
header("HTTP/1.0 404 Not Found");
$tmpl = new OCP\Template( '', '404', 'guest' );
- $tmpl->assign('file',$filename);
+ $tmpl->assign('file', $filename);
$tmpl->printPage();
exit;
}
-$ftype=OC_Filesystem::getMimeType( $filename );
+$ftype=\OC\Files\Filesystem::getMimeType( $filename );
header('Content-Type:'.$ftype);
-header('Content-Disposition: attachment; filename="'.basename($filename).'"');
+if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
+ header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
+} else {
+ header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
+ . '; filename="' . rawurlencode( basename($filename) ) . '"' );
+}
OCP\Response::disableCaching();
-header('Content-Length: '.OC_Filesystem::filesize($filename));
+header('Content-Length: '.\OC\Files\Filesystem::filesize($filename));
-@ob_end_clean();
-OC_Filesystem::readfile( $filename );
+OC_Util::obEnd();
+\OC\Files\Filesystem::readfile( $filename );
diff --git a/apps/files/index.php b/apps/files/index.php
index 493087d26f1..20fbf7f93be 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -1,113 +1,140 @@
<?php
/**
-* ownCloud - ajax frontend
-*
-* @author Robin Appelman
-* @copyright 2010 Robin Appelman icewind1991@gmail.com
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library. If not, see <http://www.gnu.org/licenses/>.
-*
-*/
+ * ownCloud - ajax frontend
+ *
+ * @author Robin Appelman
+ * @copyright 2010 Robin Appelman icewind1991@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
// Check if we are a user
OCP\User::checkLoggedIn();
// Load the files we need
-OCP\Util::addStyle( 'files', 'files' );
-OCP\Util::addscript( 'files', 'jquery.iframe-transport' );
-OCP\Util::addscript( 'files', 'jquery.fileupload' );
-OCP\Util::addscript( 'files', 'files' );
-OCP\Util::addscript( 'files', 'filelist' );
-OCP\Util::addscript( 'files', 'fileactions' );
-if(!isset($_SESSION['timezone'])) {
- OCP\Util::addscript( 'files', 'timezone' );
-}
-OCP\App::setActiveNavigationEntry( 'files_index' );
+OCP\Util::addStyle('files', 'files');
+OCP\Util::addscript('files', 'jquery.iframe-transport');
+OCP\Util::addscript('files', 'jquery.fileupload');
+OCP\Util::addscript('files', 'jquery-visibility');
+OCP\Util::addscript('files', 'filelist');
+
+OCP\App::setActiveNavigationEntry('files_index');
// Load the files
-$dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : '';
+$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : '';
// Redirect if directory does not exist
-if(!OC_Filesystem::is_dir($dir.'/')) {
- header('Location: '.$_SERVER['SCRIPT_NAME'].'');
+if (!\OC\Files\Filesystem::is_dir($dir . '/')) {
+ header('Location: ' . OCP\Util::getScriptName() . '');
exit();
}
+function fileCmp($a, $b) {
+ if ($a['type'] == 'dir' and $b['type'] != 'dir') {
+ return -1;
+ } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') {
+ return 1;
+ } else {
+ return strnatcasecmp($a['name'], $b['name']);
+ }
+}
+
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir ) as $i ) {
- $i['date'] = OCP\Util::formatDate($i['mtime'] );
- if($i['type']=='file') {
- $fileinfo=pathinfo($i['name']);
- $i['basename']=$fileinfo['filename'];
+$user = OC_User::getUser();
+if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache
+ $content = array();
+ $needUpgrade = true;
+ $freeSpace = 0;
+} else {
+ $content = \OC\Files\Filesystem::getDirectoryContent($dir);
+ $freeSpace = \OC\Files\Filesystem::free_space($dir);
+ $needUpgrade = false;
+}
+foreach ($content as $i) {
+ $i['date'] = OCP\Util::formatDate($i['mtime']);
+ if ($i['type'] == 'file') {
+ $fileinfo = pathinfo($i['name']);
+ $i['basename'] = $fileinfo['filename'];
if (!empty($fileinfo['extension'])) {
- $i['extension']='.' . $fileinfo['extension'];
- }
- else {
- $i['extension']='';
+ $i['extension'] = '.' . $fileinfo['extension'];
+ } else {
+ $i['extension'] = '';
}
}
- if($i['directory']=='/') {
- $i['directory']='';
- }
+ $i['directory'] = $dir;
$files[] = $i;
}
+usort($files, "fileCmp");
+
// Make breadcrumb
$breadcrumb = array();
$pathtohere = '';
-foreach( explode( '/', $dir ) as $i ) {
- if( $i != '' ) {
- $pathtohere .= '/'.str_replace('+','%20', urlencode($i));
- $breadcrumb[] = array( 'dir' => $pathtohere, 'name' => $i );
+foreach (explode('/', $dir) as $i) {
+ if ($i != '') {
+ $pathtohere .= '/' . $i;
+ $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i);
}
}
// make breadcrumb und filelist markup
-$list = new OCP\Template( 'files', 'part.list', '' );
-$list->assign( 'files', $files, false );
-$list->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false);
-$list->assign( 'downloadURL', OCP\Util::linkTo('files', 'download.php').'?file=', false);
-$breadcrumbNav = new OCP\Template( 'files', 'part.breadcrumb', '' );
-$breadcrumbNav->assign( 'breadcrumb', $breadcrumb, false );
-$breadcrumbNav->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false);
-
-$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
-$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
-$maxUploadFilesize = min($upload_max_filesize, $post_max_size);
+$list = new OCP\Template('files', 'part.list', '');
+$list->assign('files', $files);
+$list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=');
+$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/')));
+$list->assign('disableSharing', false);
+$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '');
+$breadcrumbNav->assign('breadcrumb', $breadcrumb);
+$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=');
-$freeSpace=OC_Filesystem::free_space('/');
-$freeSpace=max($freeSpace,0);
-$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace);
-
-$permissions = OCP\Share::PERMISSION_READ;
-if (OC_Filesystem::isUpdatable($dir.'/')) {
- $permissions |= OCP\Share::PERMISSION_UPDATE;
+$permissions = OCP\PERMISSION_READ;
+if (\OC\Files\Filesystem::isCreatable($dir . '/')) {
+ $permissions |= OCP\PERMISSION_CREATE;
+}
+if (\OC\Files\Filesystem::isUpdatable($dir . '/')) {
+ $permissions |= OCP\PERMISSION_UPDATE;
}
-if (OC_Filesystem::isDeletable($dir.'/')) {
- $permissions |= OCP\Share::PERMISSION_DELETE;
+if (\OC\Files\Filesystem::isDeletable($dir . '/')) {
+ $permissions |= OCP\PERMISSION_DELETE;
}
-if (OC_Filesystem::isSharable($dir.'/')) {
- $permissions |= OCP\Share::PERMISSION_SHARE;
+if (\OC\Files\Filesystem::isSharable($dir . '/')) {
+ $permissions |= OCP\PERMISSION_SHARE;
}
-$tmpl = new OCP\Template( 'files', 'index', 'user' );
-$tmpl->assign( 'fileList', $list->fetchPage(), false );
-$tmpl->assign( 'breadcrumb', $breadcrumbNav->fetchPage(), false );
-$tmpl->assign( 'dir', OC_Filesystem::normalizePath($dir));
-$tmpl->assign( 'isCreatable', OC_Filesystem::isCreatable($dir.'/'));
-$tmpl->assign('permissions', $permissions);
-$tmpl->assign( 'files', $files );
-$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
-$tmpl->assign( 'uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
-$tmpl->assign( 'allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
-$tmpl->printPage();
+if ($needUpgrade) {
+ OCP\Util::addscript('files', 'upgrade');
+ $tmpl = new OCP\Template('files', 'upgrade', 'user');
+ $tmpl->printPage();
+} else {
+ // information about storage capacities
+ $storageInfo=OC_Helper::getStorageInfo();
+ $maxUploadFilesize=OCP\Util::maxUploadFilesize($dir);
+
+ OCP\Util::addscript('files', 'fileactions');
+ OCP\Util::addscript('files', 'files');
+ OCP\Util::addscript('files', 'keyboardshortcuts');
+ $tmpl = new OCP\Template('files', 'index', 'user');
+ $tmpl->assign('fileList', $list->fetchPage());
+ $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage());
+ $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir));
+ $tmpl->assign('isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/'));
+ $tmpl->assign('permissions', $permissions);
+ $tmpl->assign('files', $files);
+ $tmpl->assign('trash', \OCP\App::isEnabled('files_trashbin'));
+ $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
+ $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
+ $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
+ $tmpl->assign('usedSpacePercent', (int)$storageInfo['relative']);
+ $tmpl->printPage();
+} \ No newline at end of file
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index e184cbfa915..f3264da5a12 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -1,191 +1,205 @@
-var FileActions={
- actions:{},
- defaults:{},
- icons:{},
- currentFile:null,
- register:function(mime,name,permissions,icon,action){
- if(!FileActions.actions[mime]){
- FileActions.actions[mime]={};
+var FileActions = {
+ actions: {},
+ defaults: {},
+ icons: {},
+ currentFile: null,
+ register: function (mime, name, permissions, icon, action) {
+ if (!FileActions.actions[mime]) {
+ FileActions.actions[mime] = {};
}
if (!FileActions.actions[mime][name]) {
FileActions.actions[mime][name] = {};
}
FileActions.actions[mime][name]['action'] = action;
FileActions.actions[mime][name]['permissions'] = permissions;
- FileActions.icons[name]=icon;
+ FileActions.icons[name] = icon;
},
- setDefault:function(mime,name){
- FileActions.defaults[mime]=name;
+ setDefault: function (mime, name) {
+ FileActions.defaults[mime] = name;
},
- get:function(mime,type,permissions){
- var actions={};
- if(FileActions.actions.all){
- actions=$.extend( actions, FileActions.actions.all );
+ get: function (mime, type, permissions) {
+ var actions = {};
+ if (FileActions.actions.all) {
+ actions = $.extend(actions, FileActions.actions.all);
}
- if(mime){
- if(FileActions.actions[mime]){
- actions=$.extend( actions, FileActions.actions[mime] );
+ if (mime) {
+ if (FileActions.actions[mime]) {
+ actions = $.extend(actions, FileActions.actions[mime]);
}
- var mimePart=mime.substr(0,mime.indexOf('/'));
- if(FileActions.actions[mimePart]){
- actions=$.extend( actions, FileActions.actions[mimePart] );
+ var mimePart = mime.substr(0, mime.indexOf('/'));
+ if (FileActions.actions[mimePart]) {
+ actions = $.extend(actions, FileActions.actions[mimePart]);
}
}
- if(type){//type is 'dir' or 'file'
- if(FileActions.actions[type]){
- actions=$.extend( actions, FileActions.actions[type] );
+ if (type) {//type is 'dir' or 'file'
+ if (FileActions.actions[type]) {
+ actions = $.extend(actions, FileActions.actions[type]);
}
}
var filteredActions = {};
- $.each(actions, function(name, action) {
+ $.each(actions, function (name, action) {
if (action.permissions & permissions) {
filteredActions[name] = action.action;
}
});
return filteredActions;
},
- getDefault:function(mime,type,permissions){
- if(mime){
- var mimePart=mime.substr(0,mime.indexOf('/'));
+ getDefault: function (mime, type, permissions) {
+ if (mime) {
+ var mimePart = mime.substr(0, mime.indexOf('/'));
}
- var name=false;
- if(mime && FileActions.defaults[mime]){
- name=FileActions.defaults[mime];
- }else if(mime && FileActions.defaults[mimePart]){
- name=FileActions.defaults[mimePart];
- }else if(type && FileActions.defaults[type]){
- name=FileActions.defaults[type];
- }else{
- name=FileActions.defaults.all;
+ var name = false;
+ if (mime && FileActions.defaults[mime]) {
+ name = FileActions.defaults[mime];
+ } else if (mime && FileActions.defaults[mimePart]) {
+ name = FileActions.defaults[mimePart];
+ } else if (type && FileActions.defaults[type]) {
+ name = FileActions.defaults[type];
+ } else {
+ name = FileActions.defaults.all;
}
- var actions=this.get(mime,type,permissions);
+ var actions = this.get(mime, type, permissions);
return actions[name];
},
- display:function(parent){
- FileActions.currentFile=parent;
- $('#fileList span.fileactions, #fileList td.date a.action').remove();
- var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions());
- var file=FileActions.getCurrentFile();
- if($('tr').filterAttr('data-file',file).data('renaming')){
+ display: function (parent) {
+ FileActions.currentFile = parent;
+ var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
+ var file = FileActions.getCurrentFile();
+ if ($('tr').filterAttr('data-file', file).data('renaming')) {
return;
}
parent.children('a.name').append('<span class="fileactions" />');
- var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions());
- for(name in actions){
+ var defaultAction = FileActions.getDefault(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
+
+ var actionHandler = function (event) {
+ event.stopPropagation();
+ event.preventDefault();
+
+ FileActions.currentFile = event.data.elem;
+ var file = FileActions.getCurrentFile();
+
+ event.data.actionFunc(file);
+ };
+
+ var addAction = function (name, action) {
// NOTE: Temporary fix to prevent rename action in root of Shared directory
- if (name == 'Rename' && $('#dir').val() == '/Shared') {
- continue;
+ if (name === 'Rename' && $('#dir').val() === '/Shared') {
+ return true;
}
- if((name=='Download' || actions[name]!=defaultAction) && name!='Delete'){
- var img=FileActions.icons[name];
- if(img.call){
- img=img(file);
+
+ if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') {
+ var img = FileActions.icons[name];
+ if (img.call) {
+ img = img(file);
+ }
+ var html = '<a href="#" class="action" data-action="' + name + '">';
+ if (img) {
+ html += '<img class ="svg" src="' + img + '" /> ';
}
- var html='<a href="#" class="action" style="display:none">';
- if(img) { html+='<img src="'+img+'"/> '; }
- html += t('files', name) +'</a>';
- var element=$(html);
- element.data('action',name);
- element.click(function(event){
- event.stopPropagation();
- event.preventDefault();
- var action=actions[$(this).data('action')];
- var currentFile=FileActions.getCurrentFile();
- FileActions.hide();
- action(currentFile);
- });
- element.hide();
+ html += t('files', name) + '</a>';
+
+ var element = $(html);
+ element.data('action', name);
+ //alert(element);
+ element.on('click', {a: null, elem: parent, actionFunc: actions[name]}, actionHandler);
parent.find('a.name>span.fileactions').append(element);
}
+
+ };
+
+ $.each(actions, function (name, action) {
+ if (name !== 'Share') {
+ addAction(name, action);
+ }
+ });
+ if(actions.Share && !($('#dir').val() === '/' && file === 'Shared')){
+ addAction('Share', actions.Share);
}
- if(actions['Delete']){
- var img=FileActions.icons['Delete'];
- if(img.call){
- img=img(file);
+
+ if (actions['Delete']) {
+ var img = FileActions.icons['Delete'];
+ if (img.call) {
+ img = img(file);
}
- // NOTE: Temporary fix to allow unsharing of files in root of Shared folder
- if ($('#dir').val() == '/Shared') {
- var html = '<a href="#" original-title="' + t('files', 'Unshare') + '" class="action delete" style="display:none" />';
+ if (typeof trashBinApp !== 'undefined' && trashBinApp) {
+ var html = '<a href="#" original-title="' + t('files', 'Delete permanently') + '" class="action delete" />';
} else {
- var html='<a href="#" original-title="' + t('files', 'Delete') + '" class="action delete" style="display:none" />';
+ var html = '<a href="#" original-title="' + t('files', 'Delete') + '" class="action delete" />';
}
- var element=$(html);
- if(img){
- element.append($('<img src="'+img+'"/>'));
+ var element = $(html);
+ if (img) {
+ element.append($('<img class ="svg" src="' + img + '"/>'));
}
- element.data('action','Delete');
- element.click(function(event){
- event.stopPropagation();
- event.preventDefault();
- var action=actions[$(this).data('action')];
- var currentFile=FileActions.getCurrentFile();
- FileActions.hide();
- action(currentFile);
- });
- element.hide();
+ element.data('action', actions['Delete']);
+ element.on('click', {a: null, elem: parent, actionFunc: actions['Delete']}, actionHandler);
parent.parent().children().last().append(element);
}
- $('#fileList .action').css('-o-transition-property','none');//temporarly disable
- $('#fileList .action').fadeIn(200,function(){
- $('#fileList .action').css('-o-transition-property','opacity');
- });
- return false;
- },
- hide:function(){
- $('#fileList span.fileactions, #fileList td.date a.action').fadeOut(200,function(){
- $(this).remove();
- });
},
- getCurrentFile:function(){
+ getCurrentFile: function () {
return FileActions.currentFile.parent().attr('data-file');
},
- getCurrentMimeType:function(){
+ getCurrentMimeType: function () {
return FileActions.currentFile.parent().attr('data-mime');
},
- getCurrentType:function(){
+ getCurrentType: function () {
return FileActions.currentFile.parent().attr('data-type');
},
- getCurrentPermissions:function() {
+ getCurrentPermissions: function () {
return FileActions.currentFile.parent().data('permissions');
}
};
-$(document).ready(function(){
- if($('#allowZipDownload').val() == 1){
+$(document).ready(function () {
+ if ($('#allowZipDownload').val() == 1) {
var downloadScope = 'all';
} else {
var downloadScope = 'file';
}
- FileActions.register(downloadScope,'Download', OC.PERMISSION_READ, function(){return OC.imagePath('core','actions/download');},function(filename){
- window.location=OC.filePath('files', 'ajax', 'download.php') + '&files='+encodeURIComponent(filename)+'&dir='+encodeURIComponent($('#dir').val());
+
+ if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) {
+ FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
+ return OC.imagePath('core', 'actions/download');
+ }, function (filename) {
+ window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val());
+ });
+ }
+
+ $('#fileList tr').each(function () {
+ FileActions.display($(this).children('td.filename'));
});
+
});
-FileActions.register('all','Delete', OC.PERMISSION_DELETE, function(){return OC.imagePath('core','actions/delete');},function(filename){
- if(Files.cancelUpload(filename)) {
- if(filename.substr){
- filename=[filename];
+FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () {
+ return OC.imagePath('core', 'actions/delete');
+}, function (filename) {
+ if (Files.cancelUpload(filename)) {
+ if (filename.substr) {
+ filename = [filename];
}
- $.each(filename,function(index,file){
- var filename = $('tr').filterAttr('data-file',file);
+ $.each(filename, function (index, file) {
+ var filename = $('tr').filterAttr('data-file', file);
filename.hide();
filename.find('input[type="checkbox"]').removeAttr('checked');
filename.removeClass('selected');
});
procesSelection();
- }else{
+ } else {
FileList.do_delete(filename);
}
$('.tipsy').remove();
});
// t('files', 'Rename')
-FileActions.register('all','Rename', OC.PERMISSION_UPDATE, function(){return OC.imagePath('core','actions/rename');},function(filename){
+FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () {
+ return OC.imagePath('core', 'actions/rename');
+}, function (filename) {
FileList.rename(filename);
});
-FileActions.register('dir','Open', OC.PERMISSION_READ, '', function(filename){
- window.location=OC.linkTo('files', 'index.php') + '&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
+
+FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
+ window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent($('#dir').val()).replace(/%2F/g, '/') + '/' + encodeURIComponent(filename);
});
-FileActions.setDefault('dir','Open');
+FileActions.setDefault('dir', 'Open');
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 100a2368722..1db54f45bb8 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -3,74 +3,123 @@ var FileList={
update:function(fileListHtml) {
$('#fileList').empty().html(fileListHtml);
},
- addFile:function(name,size,lastModified,loading,hidden){
- var basename, extension, simpleSize, sizeColor, lastModifiedTime, modifiedColor,
- img=(loading)?OC.imagePath('core', 'loading.gif'):OC.imagePath('core', 'filetypes/file.png'),
- html='<tr data-type="file" data-size="'+size+'" data-permissions="'+$('#permissions').val()+'">';
- if(name.indexOf('.')!=-1){
+ createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions){
+ var td, simpleSize, basename, extension;
+ //containing tr
+ var tr = $('<tr></tr>').attr({
+ "data-type": type,
+ "data-size": size,
+ "data-file": name,
+ "data-permissions": permissions
+ });
+ // filename td
+ td = $('<td></td>').attr({
+ "class": "filename",
+ "style": 'background-image:url('+iconurl+')'
+ });
+ td.append('<input type="checkbox" />');
+ var link_elem = $('<a></a>').attr({
+ "class": "name",
+ "href": linktarget
+ });
+ //split extension from filename for non dirs
+ if (type != 'dir' && name.indexOf('.')!=-1) {
basename=name.substr(0,name.lastIndexOf('.'));
extension=name.substr(name.lastIndexOf('.'));
- }else{
+ } else {
basename=name;
extension=false;
}
- html+='<td class="filename" style="background-image:url('+img+')"><input type="checkbox" />';
- html+='<a class="name" href="download.php?file='+$('#dir').val().replace(/</, '&lt;').replace(/>/, '&gt;')+'/'+escapeHTML(name)+'"><span class="nametext">'+escapeHTML(basename);
+ var name_span=$('<span></span>').addClass('nametext').text(basename);
+ link_elem.append(name_span);
if(extension){
- html+='<span class="extension">'+escapeHTML(extension)+'</span>';
+ name_span.append($('<span></span>').addClass('extension').text(extension));
+ }
+ //dirs can show the number of uploaded files
+ if (type == 'dir') {
+ link_elem.append($('<span></span>').attr({
+ 'class': 'uploadtext',
+ 'currentUploads': 0
+ }));
}
- html+='</span></a></td>';
- if(size!='Pending'){
+ td.append(link_elem);
+ tr.append(td);
+
+ //size column
+ if(size!=t('files', 'Pending')){
simpleSize=simpleFileSize(size);
}else{
- simpleSize='Pending';
+ simpleSize=t('files', 'Pending');
+ }
+ var sizeColor = Math.round(200-Math.pow((size/(1024*1024)),2));
+ var lastModifiedTime = Math.round(lastModified.getTime() / 1000);
+ td = $('<td></td>').attr({
+ "class": "filesize",
+ "title": humanFileSize(size),
+ "style": 'color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')'
+ }).text(simpleSize);
+ tr.append(td);
+
+ // date column
+ var modifiedColor = Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*5);
+ td = $('<td></td>').attr({ "class": "date" });
+ td.append($('<span></span>').attr({
+ "class": "modified",
+ "title": formatDate(lastModified),
+ "style": 'color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')'
+ }).text( relative_modified_date(lastModified.getTime() / 1000) ));
+ tr.append(td);
+ return tr;
+ },
+ addFile:function(name,size,lastModified,loading,hidden){
+ var imgurl;
+ if (loading) {
+ imgurl = OC.imagePath('core', 'loading.gif');
+ } else {
+ imgurl = OC.imagePath('core', 'filetypes/file.png');
}
- sizeColor = Math.round(200-size/(1024*1024)*2);
- lastModifiedTime=Math.round(lastModified.getTime() / 1000);
- modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*14);
- html+='<td class="filesize" title="'+humanFileSize(size)+'" style="color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')">'+simpleSize+'</td>';
- html+='<td class="date"><span class="modified" title="'+formatDate(lastModified)+'" style="color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')">'+relative_modified_date(lastModified.getTime() / 1000)+'</span></td>';
- html+='</tr>';
- FileList.insertElement(name,'file',$(html).attr('data-file',name));
+ var tr = this.createRow(
+ 'file',
+ name,
+ imgurl,
+ OC.Router.generate('download', { file: $('#dir').val()+'/'+name }),
+ size,
+ lastModified,
+ $('#permissions').val()
+ );
+
+ FileList.insertElement(name, 'file', tr.attr('data-file',name));
+ var row = $('tr').filterAttr('data-file',name);
if(loading){
- $('tr').filterAttr('data-file',name).data('loading',true);
+ row.data('loading',true);
}else{
- $('tr').filterAttr('data-file',name).find('td.filename').draggable(dragOptions);
+ row.find('td.filename').draggable(dragOptions);
}
if (hidden) {
- $('tr').filterAttr('data-file', name).hide();
+ row.hide();
}
+ FileActions.display(row.find('td.filename'));
},
addDir:function(name,size,lastModified,hidden){
- var html, td, link_elem, sizeColor, lastModifiedTime, modifiedColor;
- html = $('<tr></tr>').attr({ "data-type": "dir", "data-size": size, "data-file": name, "data-permissions": $('#permissions').val()});
- td = $('<td></td>').attr({"class": "filename", "style": 'background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')' });
- td.append('<input type="checkbox" />');
- link_elem = $('<a></a>').attr({ "class": "name", "href": OC.linkTo('files', 'index.php')+"&dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/') });
- link_elem.append($('<span></span>').addClass('nametext').text(name));
- link_elem.append($('<span></span>').attr({'class': 'uploadtext', 'currentUploads': 0}));
- td.append(link_elem);
- html.append(td);
- if(size!='Pending'){
- simpleSize=simpleFileSize(size);
- }else{
- simpleSize='Pending';
- }
- sizeColor = Math.round(200-Math.pow((size/(1024*1024)),2));
- lastModifiedTime=Math.round(lastModified.getTime() / 1000);
- modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*5);
- td = $('<td></td>').attr({ "class": "filesize", "title": humanFileSize(size), "style": 'color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')'}).text(simpleSize);
- html.append(td);
- td = $('<td></td>').attr({ "class": "date" });
- td.append($('<span></span>').attr({ "class": "modified", "title": formatDate(lastModified), "style": 'color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')' }).text( relative_modified_date(lastModified.getTime() / 1000) ));
- html.append(td);
- FileList.insertElement(name,'dir',html);
- $('tr').filterAttr('data-file',name).find('td.filename').draggable(dragOptions);
- $('tr').filterAttr('data-file',name).find('td.filename').droppable(folderDropOptions);
+ var tr = this.createRow(
+ 'dir',
+ name,
+ OC.imagePath('core', 'filetypes/folder.png'),
+ OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/'),
+ size,
+ lastModified,
+ $('#permissions').val()
+ );
+
+ FileList.insertElement(name,'dir',tr);
+ var row = $('tr').filterAttr('data-file',name);
+ row.find('td.filename').draggable(dragOptions);
+ row.find('td.filename').droppable(folderDropOptions);
if (hidden) {
- $('tr').filterAttr('data-file', name).hide();
+ row.hide();
}
+ FileActions.display(row.find('td.filename'));
},
refresh:function(data) {
var result = jQuery.parseJSON(data.responseText);
@@ -85,7 +134,6 @@ var FileList={
$('tr').filterAttr('data-file',name).remove();
if($('tr[data-file]').length==0){
$('#emptyfolder').show();
- $('.file_upload_filename').addClass('highlight');
}
},
insertElement:function(name,type,element){
@@ -114,7 +162,6 @@ var FileList={
$('#fileList').append(element);
}
$('#emptyfolder').hide();
- $('.file_upload_filename').removeClass('highlight');
},
loadingDone:function(name, id){
var mime, tr=$('tr').filterAttr('data-file',name);
@@ -137,30 +184,32 @@ var FileList={
tr=$('tr').filterAttr('data-file',name);
tr.data('renaming',true);
td=tr.children('td.filename');
- input=$('<input class="filename"></input>').val(name);
+ input=$('<input class="filename"/>').val(name);
form=$('<form></form>');
form.append(input);
- td.children('a.name').text('');
- td.children('a.name').append(form);
+ td.children('a.name').hide();
+ td.append(form);
input.focus();
form.submit(function(event){
event.stopPropagation();
event.preventDefault();
var newname=input.val();
- if (newname != name) {
+ if (!Files.isFileNameValid(newname)) {
+ return false;
+ } else if (newname != name) {
if (FileList.checkName(name, newname, false)) {
newname = name;
- } else {
+ } else {
$.get(OC.filePath('files','ajax','rename.php'), { dir : $('#dir').val(), newname: newname, file: name },function(result) {
if (!result || result.status == 'error') {
OC.dialogs.alert(result.data.message, 'Error moving file');
newname = name;
}
- tr.data('renaming',false);
});
-
+
}
}
+ tr.data('renaming',false);
tr.attr('data-file', newname);
var path = td.children('a.name').attr('href');
td.children('a.name').attr('href', path.replace(encodeURIComponent(name), encodeURIComponent(newname)));
@@ -169,15 +218,24 @@ var FileList={
} else {
var basename=newname;
}
- td.children('a.name').empty();
- var span=$('<span class="nametext"></span>');
- span.text(basename);
- td.children('a.name').append(span);
+ td.find('a.name span.nametext').text(basename);
if (newname.indexOf('.') > 0 && tr.data('type') != 'dir') {
- span.append($('<span class="extension">'+newname.substr(newname.lastIndexOf('.'))+'</span>'));
+ if (td.find('a.name span.extension').length == 0 ) {
+ td.find('a.name span.nametext').append('<span class="extension"></span>');
+ }
+ td.find('a.name span.extension').text(newname.substr(newname.lastIndexOf('.')));
}
+ form.remove();
+ td.children('a.name').show();
return false;
});
+ input.keyup(function(event){
+ if (event.keyCode == 27) {
+ tr.data('renaming',false);
+ form.remove();
+ td.children('a.name').show();
+ }
+ });
input.click(function(event){
event.stopPropagation();
event.preventDefault();
@@ -188,15 +246,17 @@ var FileList={
},
checkName:function(oldName, newName, isNewFile) {
if (isNewFile || $('tr').filterAttr('data-file', newName).length > 0) {
- if (isNewFile) {
- $('#notification').html(escapeHTML(newName)+' '+t('files', 'already exists')+'<span class="replace">'+t('files', 'replace')+'</span><span class="suggest">'+t('files', 'suggest name')+'</span><span class="cancel">'+t('files', 'cancel')+'</span>');
- } else {
- $('#notification').html(escapeHTML(newName)+' '+t('files', 'already exists')+'<span class="replace">'+t('files', 'replace')+'</span><span class="cancel">'+t('files', 'cancel')+'</span>');
+ var html;
+ if(isNewFile){
+ html = t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+'<span class="replace">'+t('files', 'replace')+'</span><span class="suggest">'+t('files', 'suggest name')+'</span>&nbsp;<span class="cancel">'+t('files', 'cancel')+'</span>';
+ }else{
+ html = t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+'<span class="replace">'+t('files', 'replace')+'</span><span class="cancel">'+t('files', 'cancel')+'</span>';
}
- $('#notification').data('oldName', oldName);
- $('#notification').data('newName', newName);
- $('#notification').data('isNewFile', isNewFile);
- $('#notification').fadeIn();
+ html = $('<span>' + html + '</span>');
+ html.attr('data-oldName', oldName);
+ html.attr('data-newName', newName);
+ html.attr('data-isNewFile', isNewFile);
+ OC.Notification.showHtml(html);
return true;
} else {
return false;
@@ -204,9 +264,6 @@ var FileList={
},
replace:function(oldName, newName, isNewFile) {
// Finish any existing actions
- if (FileList.lastAction || !FileList.useUndo) {
- FileList.lastAction();
- }
$('tr').filterAttr('data-file', oldName).hide();
$('tr').filterAttr('data-file', newName).hide();
var tr = $('tr').filterAttr('data-file', oldName).clone();
@@ -237,12 +294,9 @@ var FileList={
FileList.lastAction = function() {
FileList.finishReplace();
};
- if (isNewFile) {
- $('#notification').html(t('files', 'replaced')+' '+newName+'<span class="undo">'+t('files', 'undo')+'</span>');
- } else {
- $('#notification').html(t('files', 'replaced')+' '+newName+' '+t('files', 'with')+' '+oldName+'<span class="undo">'+t('files', 'undo')+'</span>');
+ if (!isNewFile) {
+ OC.Notification.showHtml(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+'<span class="undo">'+t('files', 'undo')+'</span>');
}
- $('#notification').fadeIn();
},
finishReplace:function() {
if (!FileList.replaceCanceled && FileList.replaceOldName && FileList.replaceNewName) {
@@ -260,72 +314,45 @@ var FileList={
}
},
do_delete:function(files){
+ if(files.substr){
+ files=[files];
+ }
+ for (var i=0; i<files.length; i++) {
+ var deleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date").children(".action.delete");
+ var oldHTML = deleteAction[0].outerHTML;
+ var newHTML = '<img class="move2trash" data-action="Delete" title="'+t('files', 'perform delete operation')+'" src="'+ OC.imagePath('core', 'loading.gif') +'"></a>';
+ deleteAction[0].outerHTML = newHTML;
+ }
// Finish any existing actions
if (FileList.lastAction) {
FileList.lastAction();
}
-
- FileList.prepareDeletion(files);
-
- if (!FileList.useUndo) {
- FileList.lastAction();
- } else {
- // NOTE: Temporary fix to change the text to unshared for files in root of Shared folder
- if ($('#dir').val() == '/Shared') {
- $('#notification').html(t('files', 'unshared')+' '+ escapeHTML(files) +'<span class="undo">'+t('files', 'undo')+'</span>');
- } else {
- $('#notification').html(t('files', 'deleted')+' '+ escapeHTML(files)+'<span class="undo">'+t('files', 'undo')+'</span>');
- }
- $('#notification').fadeIn();
- }
- },
- finishDelete:function(ready,sync){
- if(!FileList.deleteCanceled && FileList.deleteFiles){
- var fileNames=FileList.deleteFiles.join(';');
- $.ajax({
- url: OC.filePath('files', 'ajax', 'delete.php'),
- async:!sync,
- type:'post',
- data: {dir:$('#dir').val(),files:fileNames},
- complete: function(data){
- boolOperationFinished(data, function(){
- $('#notification').fadeOut('400');
- $.each(FileList.deleteFiles,function(index,file){
- FileList.remove(file);
+
+ var fileNames = JSON.stringify(files);
+ $.post(OC.filePath('files', 'ajax', 'delete.php'),
+ {dir:$('#dir').val(),files:fileNames},
+ function(result){
+ if (result.status == 'success') {
+ $.each(files,function(index,file){
+ var files = $('tr').filterAttr('data-file',file);
+ files.remove();
+ files.find('input[type="checkbox"]').removeAttr('checked');
+ files.removeClass('selected');
});
- FileList.deleteCanceled=true;
- FileList.deleteFiles=null;
- FileList.lastAction = null;
- if(ready){
- ready();
- }
- });
- }
- });
- }
- },
- prepareDeletion:function(files){
- if(files.substr){
- files=[files];
- }
- $.each(files,function(index,file){
- var files = $('tr').filterAttr('data-file',file);
- files.hide();
- files.find('input[type="checkbox"]').removeAttr('checked');
- files.removeClass('selected');
- });
- procesSelection();
- FileList.deleteCanceled=false;
- FileList.deleteFiles=files;
- FileList.lastAction = function() {
- FileList.finishDelete(null, true);
- };
+ procesSelection();
+ } else {
+ $.each(files,function(index,file) {
+ var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash");
+ deleteAction[0].outerHTML = oldHTML;
+ });
+ }
+ });
}
};
$(document).ready(function(){
$('#notification').hide();
- $('#notification .undo').live('click', function(){
+ $('#notification').on('click', '.undo', function(){
if (FileList.deleteFiles) {
$.each(FileList.deleteFiles,function(index,file){
$('tr').filterAttr('data-file',file).show();
@@ -337,7 +364,6 @@ $(document).ready(function(){
// Delete the new uploaded file
FileList.deleteCanceled = false;
FileList.deleteFiles = [FileList.replaceOldName];
- FileList.finishDelete(null, true);
} else {
$('tr').filterAttr('data-file', FileList.replaceOldName).show();
}
@@ -349,22 +375,21 @@ $(document).ready(function(){
FileList.replaceIsNewFile = null;
}
FileList.lastAction = null;
- $('#notification').fadeOut('400');
+ OC.Notification.hide();
});
- $('#notification .replace').live('click', function() {
- $('#notification').fadeOut('400', function() {
- FileList.replace($('#notification').data('oldName'), $('#notification').data('newName'), $('#notification').data('isNewFile'));
- });
+ $('#notification:first-child').on('click', '.replace', function() {
+ OC.Notification.hide(function() {
+ FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile'));
+ });
});
- $('#notification .suggest').live('click', function() {
- $('tr').filterAttr('data-file', $('#notification').data('oldName')).show();
- $('#notification').fadeOut('400');
+ $('#notification:first-child').on('click', '.suggest', function() {
+ $('tr').filterAttr('data-file', $('#notification > span').attr('data-oldName')).show();
+ OC.Notification.hide();
});
- $('#notification .cancel').live('click', function() {
- if ($('#notification').data('isNewFile')) {
+ $('#notification:first-child').on('click', '.cancel', function() {
+ if ($('#notification > span').attr('data-isNewFile')) {
FileList.deleteCanceled = false;
- FileList.deleteFiles = [$('#notification').data('oldName')];
- FileList.finishDelete(null, true);
+ FileList.deleteFiles = [$('#notification > span').attr('data-oldName')];
}
});
FileList.useUndo=(window.onbeforeunload)?true:false;
@@ -373,4 +398,7 @@ $(document).ready(function(){
FileList.lastAction();
}
});
+ $(window).unload(function (){
+ $(window).trigger('beforeunload');
+ });
});
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index c5333f2fafb..a4ef41c2803 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -25,18 +25,71 @@ Files={
delete uploadingFiles[index];
});
procesSelection();
+ },
+ updateMaxUploadFilesize:function(response) {
+ if(response == undefined) {
+ return;
+ }
+ if(response.data !== undefined && response.data.uploadMaxFilesize !== undefined) {
+ $('#max_upload').val(response.data.uploadMaxFilesize);
+ $('#upload.button').attr('original-title', response.data.maxHumanFilesize);
+ $('#usedSpacePercent').val(response.data.usedSpacePercent);
+ Files.displayStorageWarnings();
+ }
+ if(response[0] == undefined) {
+ return;
+ }
+ if(response[0].uploadMaxFilesize !== undefined) {
+ $('#max_upload').val(response[0].uploadMaxFilesize);
+ $('#upload.button').attr('original-title', response[0].maxHumanFilesize);
+ $('#usedSpacePercent').val(response[0].usedSpacePercent);
+ Files.displayStorageWarnings();
+ }
+
+ },
+ isFileNameValid:function (name) {
+ if (name === '.') {
+ OC.Notification.show(t('files', '\'.\' is an invalid file name.'));
+ return false;
+ }
+ if (name.length == 0) {
+ OC.Notification.show(t('files', 'File name cannot be empty.'));
+ return false;
+ }
+
+ // check for invalid characters
+ var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*'];
+ for (var i = 0; i < invalid_characters.length; i++) {
+ if (name.indexOf(invalid_characters[i]) != -1) {
+ OC.Notification.show(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed."));
+ return false;
+ }
+ }
+ OC.Notification.hide();
+ return true;
+ },
+ displayStorageWarnings: function() {
+ if (!OC.Notification.isHidden()) {
+ return;
+ }
+
+ var usedSpacePercent = $('#usedSpacePercent').val();
+ if (usedSpacePercent > 98) {
+ OC.Notification.show(t('files', 'Your storage is full, files can not be updated or synced anymore!'));
+ return;
+ }
+ if (usedSpacePercent > 90) {
+ OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)', {usedSpacePercent: usedSpacePercent}));
+ }
}
};
$(document).ready(function() {
+ Files.bindKeyboardShortcuts(document, jQuery);
$('#fileList tr').each(function(){
//little hack to set unescape filenames in attribute
$(this).attr('data-file',decodeURIComponent($(this).attr('data-file')));
});
- if($('tr[data-file]').length==0){
- $('.file_upload_filename').addClass('highlight');
- }
-
$('#file_action_panel').attr('activeAction', false);
//drag/drop of files
@@ -57,25 +110,22 @@ $(document).ready(function() {
}
// Triggers invisible file input
- $('.file_upload_button_wrapper').live('click', function() {
- $(this).parent().children('.file_upload_start').trigger('click');
+ $('#upload a').on('click', function() {
+ $(this).parent().children('#file_upload_start').trigger('click');
return false;
});
- // Sets the file-action buttons behaviour :
- $('tr').live('mouseenter',function(event) {
- FileActions.display($(this).children('td.filename'));
- });
- $('tr').live('mouseleave',function(event) {
- FileActions.hide();
+ // Show trash bin
+ $('#trash a').live('click', function() {
+ window.location=OC.filePath('files_trashbin', '', 'index.php');
});
var lastChecked;
// Sets the file link behaviour :
- $('td.filename a').live('click',function(event) {
- event.preventDefault();
+ $('#fileList').on('click','td.filename a',function(event) {
if (event.ctrlKey || event.shiftKey) {
+ event.preventDefault();
if (event.shiftKey) {
var last = $(lastChecked).parent().parent().prevAll().length;
var first = $(this).parent().parent().prevAll().length;
@@ -112,11 +162,13 @@ $(document).ready(function() {
var tr=$('tr').filterAttr('data-file',filename);
var renaming=tr.data('renaming');
if(!renaming && !FileList.isLoading(filename)){
- var mime=$(this).parent().parent().data('mime');
- var type=$(this).parent().parent().data('type');
- var permissions = $(this).parent().parent().data('permissions');
+ FileActions.currentFile = $(this).parent();
+ var mime=FileActions.getCurrentMimeType();
+ var type=FileActions.getCurrentType();
+ var permissions = FileActions.getCurrentPermissions();
var action=FileActions.getDefault(mime,type, permissions);
if(action){
+ event.preventDefault();
action(filename);
}
}
@@ -138,7 +190,7 @@ $(document).ready(function() {
procesSelection();
});
- $('td.filename input:checkbox').live('change',function(event) {
+ $('#fileList').on('change', 'td.filename input:checkbox',function(event) {
if (event.shiftKey) {
var last = $(lastChecked).parent().parent().prevAll().length;
var first = $(this).parent().parent().prevAll().length;
@@ -167,22 +219,16 @@ $(document).ready(function() {
procesSelection();
});
- $('#file_newfolder_name').click(function(){
- if($('#file_newfolder_name').val() == 'New Folder'){
- $('#file_newfolder_name').val('');
- }
- });
-
$('.download').click('click',function(event) {
- var files=getSelectedFiles('name').join(';');
+ var files=getSelectedFiles('name');
+ var fileslist = JSON.stringify(files);
var dir=$('#dir').val()||'/';
- $('#notification').text(t('files','generating ZIP-file, it may take some time.'));
- $('#notification').fadeIn();
+ OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
// use special download URL if provided, e.g. for public shared files
if ( (downloadURL = document.getElementById("downloadURL")) ) {
window.location=downloadURL.value+"&download&files="+files;
} else {
- window.location=OC.filePath('files', 'ajax', 'download.php') + '?'+ $.param({ dir: dir, files: files });
+ window.location=OC.filePath('files', 'ajax', 'download.php') + '?'+ $.param({ dir: dir, files: fileslist });
}
return false;
});
@@ -199,40 +245,45 @@ $(document).ready(function() {
$(document).bind('drop dragover', function (e) {
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
});
-
- if ( document.getElementById("data-upload-form") ) {
+
+ if ( document.getElementById('data-upload-form') ) {
$(function() {
- $('.file_upload_start').fileupload({
+ $('#file_upload_start').fileupload({
dropZone: $('#content'), // restrict dropZone to content div
add: function(e, data) {
var files = data.files;
var totalSize=0;
if(files){
+ if (FileList.lastAction) {
+ FileList.lastAction();
+ }
for(var i=0;i<files.length;i++){
- if(files[i].size ==0 || files[i].type== '')
+ if(files[i].size ==0 && files[i].type== '')
{
OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error'));
return;
}
totalSize+=files[i].size;
- if(FileList.deleteFiles && FileList.deleteFiles.indexOf(files[i].name)!=-1){//finish delete if we are uploading a deleted file
- FileList.finishDelete(function(){
- $('.file_upload_start').change();
- });
- return;
- }
}
}
if(totalSize>$('#max_upload').val()){
$( '#uploadsize-message' ).dialog({
modal: true,
buttons: {
- Close: function() {
- $( this ).dialog( 'close' );
+ Close: {
+ text:t('files', 'Close'),
+ click:function() {
+ $( this ).dialog( 'close' );
+ }
}
}
});
}else{
+ var dropTarget = $(e.originalEvent.target).closest('tr');
+ if(dropTarget && dropTarget.attr('data-type') === 'dir') { // drag&drop upload to folder
+ var dirName = dropTarget.attr('data-file')
+ }
+
var date=new Date();
if(files){
for(var i=0;i<files.length;i++){
@@ -262,7 +313,7 @@ $(document).ready(function() {
uploadtext.text(t('files', '1 file uploading'));
uploadtext.show();
} else {
- uploadtext.text(currentUploads + ' ' + t('files', 'files uploading'));
+ uploadtext.text(t('files', '{count} files uploading', {count: currentUploads}));
}
}
}
@@ -282,21 +333,31 @@ $(document).ready(function() {
var fileName = files[i].name
var dropTarget = $(e.originalEvent.target).closest('tr');
if(dropTarget && dropTarget.attr('data-type') === 'dir') { // drag&drop upload to folder
- var dirName = dropTarget.attr('data-file');
- var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i],
+ var dirName = dropTarget.attr('data-file')
+ var jqXHR = $('#file_upload_start').fileupload('send', {files: files[i],
formData: function(form) {
var formArray = form.serializeArray();
- formArray[1]['value'] = dirName;
+ // array index 0 contains the max files size
+ // array index 1 contains the request token
+ // array index 2 contains the directory
+ formArray[2]['value'] = dirName;
return formArray;
}}).success(function(result, textStatus, jqXHR) {
var response;
response=jQuery.parseJSON(result);
if(response[0] == undefined || response[0].status != 'success') {
- $('#notification').text(t('files', response.data.message));
- $('#notification').fadeIn();
+ OC.Notification.show(t('files', response.data.message));
}
+ Files.updateMaxUploadFilesize(response);
var file=response[0];
+ // TODO: this doesn't work if the file name has been changed server side
delete uploadingFiles[dirName][file.name];
+ if ($.assocArraySize(uploadingFiles[dirName]) == 0) {
+ delete uploadingFiles[dirName];
+ }
+ //TODO update file upload size limit
+
+ var uploadtext = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName).find('.uploadtext')
var currentUploads = parseInt(uploadtext.attr('currentUploads'));
currentUploads -= 1;
uploadtext.attr('currentUploads', currentUploads);
@@ -307,7 +368,7 @@ $(document).ready(function() {
uploadtext.text('');
uploadtext.hide();
} else {
- uploadtext.text(currentUploads + ' ' + t('files', 'files uploading'));
+ uploadtext.text(t('files', '{count} files uploading', {count: currentUploads}));
}
})
.error(function(jqXHR, textStatus, errorThrown) {
@@ -322,11 +383,10 @@ $(document).ready(function() {
uploadtext.text('');
uploadtext.hide();
} else {
- uploadtext.text(currentUploads + ' ' + t('files', 'files uploading'));
+ uploadtext.text(t('files', '{count} files uploading', {count: currentUploads}));
}
- $('#notification').hide();
- $('#notification').text(t('files', 'Upload cancelled.'));
- $('#notification').fadeIn();
+ delete uploadingFiles[dirName][fileName];
+ OC.Notification.show(t('files', 'Upload cancelled.'));
}
});
//TODO test with filenames containing slashes
@@ -335,10 +395,12 @@ $(document).ready(function() {
}
uploadingFiles[dirName][fileName] = jqXHR;
} else {
- var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i]})
+ var jqXHR = $('#file_upload_start').fileupload('send', {files: files[i]})
.success(function(result, textStatus, jqXHR) {
var response;
response=jQuery.parseJSON(result);
+ Files.updateMaxUploadFilesize(response);
+
if(response[0] != undefined && response[0].status == 'success') {
var file=response[0];
delete uploadingFiles[file.name];
@@ -347,21 +409,21 @@ $(document).ready(function() {
if(size==t('files','Pending')){
$('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size);
}
+ //TODO update file upload size limit
FileList.loadingDone(file.name, file.id);
} else {
- $('#notification').text(t('files', response.data.message));
- $('#notification').fadeIn();
+ Files.cancelUpload(this.files[0].name);
+ OC.Notification.show(t('files', response.data.message));
$('#fileList > tr').not('[data-mime]').fadeOut();
$('#fileList > tr').not('[data-mime]').remove();
}
- })
- .error(function(jqXHR, textStatus, errorThrown) {
- if(errorThrown === 'abort') {
- $('#notification').hide();
- $('#notification').text(t('files', 'Upload cancelled.'));
- $('#notification').fadeIn();
- }
- });
+ })
+ .error(function(jqXHR, textStatus, errorThrown) {
+ if(errorThrown === 'abort') {
+ Files.cancelUpload(this.files[0].name);
+ OC.Notification.show(t('files', 'Upload cancelled.'));
+ }
+ });
uploadingFiles[uniqueName] = jqXHR;
}
}
@@ -369,6 +431,7 @@ $(document).ready(function() {
data.submit().success(function(data, status) {
// in safari data is a string
response = jQuery.parseJSON(typeof data === 'string' ? data : data[0].body.innerText);
+ Files.updateMaxUploadFilesize(response);
if(response[0] != undefined && response[0].status == 'success') {
var file=response[0];
delete uploadingFiles[file.name];
@@ -377,10 +440,11 @@ $(document).ready(function() {
if(size==t('files','Pending')){
$('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size);
}
+ //TODO update file upload size limit
FileList.loadingDone(file.name, file.id);
} else {
- $('#notification').text(t('files', response.data.message));
- $('#notification').fadeIn();
+ //TODO Files.cancelUpload(/*where do we get the filename*/);
+ OC.Notification.show(t('files', response.data.message));
$('#fileList > tr').not('[data-mime]').fadeOut();
$('#fileList > tr').not('[data-mime]').remove();
}
@@ -399,6 +463,10 @@ $(document).ready(function() {
$('#uploadprogressbar').progressbar('value',progress);
},
start: function(e, data) {
+ //IE < 10 does not fire the necessary events for the progress bar.
+ if($.browser.msie && parseInt($.browser.version) < 10) {
+ return;
+ }
$('#uploadprogressbar').progressbar({value:0});
$('#uploadprogressbar').fadeIn();
if(data.dataType != 'iframe ') {
@@ -419,7 +487,7 @@ $(document).ready(function() {
// http://stackoverflow.com/a/6700/11236
var size = 0, key;
for (key in obj) {
- if (obj.hasOwnProperty(key)) size++;
+ if (obj.hasOwnProperty(key)) size++;
}
return size;
};
@@ -432,7 +500,7 @@ $(document).ready(function() {
//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
if(navigator.userAgent.search(/konqueror/i)==-1){
- $('.file_upload_start').attr('multiple','multiple')
+ $('#file_upload_start').attr('multiple','multiple')
}
//if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder
@@ -457,13 +525,12 @@ $(document).ready(function() {
crumb.text(text);
}
- $(window).click(function(){
+ $(document).click(function(){
$('#new>ul').hide();
$('#new').removeClass('active');
- $('button.file_upload_filename').removeClass('active');
$('#new li').each(function(i,element){
if($(element).children('p').length==0){
- $(element).children('input').remove();
+ $(element).children('form').remove();
$(element).append('<p>'+$(element).data('text')+'</p>');
}
});
@@ -474,7 +541,6 @@ $(document).ready(function() {
$('#new>a').click(function(){
$('#new>ul').toggle();
$('#new').toggleClass('active');
- $('button.file_upload_filename').toggleClass('active');
});
$('#new li').click(function(){
if($(this).children('p').length==0){
@@ -483,7 +549,7 @@ $(document).ready(function() {
$('#new li').each(function(i,element){
if($(element).children('p').length==0){
- $(element).children('input').remove();
+ $(element).children('form').remove();
$(element).append('<p>'+$(element).data('text')+'</p>');
}
});
@@ -492,18 +558,30 @@ $(document).ready(function() {
var text=$(this).children('p').text();
$(this).data('text',text);
$(this).children('p').remove();
+ var form=$('<form></form>');
var input=$('<input>');
- $(this).append(input);
+ form.append(input);
+ $(this).append(form);
input.focus();
- input.change(function(){
- if(type != 'web' && $(this).val().indexOf('/')!=-1){
- $('#notification').text(t('files','Invalid name, \'/\' is not allowed.'));
- $('#notification').fadeIn();
- return;
+ form.submit(function(event){
+ event.stopPropagation();
+ event.preventDefault();
+ var newname=input.val();
+ if(type == 'web' && newname.length == 0) {
+ OC.Notification.show(t('files', 'URL cannot be empty.'));
+ return false;
+ } else if (type != 'web' && !Files.isFileNameValid(newname)) {
+ return false;
+ } else if( type == 'folder' && $('#dir').val() == '/' && newname == 'Shared') {
+ OC.Notification.show(t('files','Invalid folder name. Usage of \'Shared\' is reserved by Owncloud'));
+ return false;
}
- var name = getUniqueName($(this).val());
- if (name != $(this).val()) {
- FileList.checkName(name, $(this).val(), true);
+ if (FileList.lastAction) {
+ FileList.lastAction();
+ }
+ var name = getUniqueName(newname);
+ if (newname != name) {
+ FileList.checkName(name, newname, true);
var hidden = true;
} else {
var hidden = false;
@@ -518,7 +596,7 @@ $(document).ready(function() {
var date=new Date();
FileList.addFile(name,0,date,false,hidden);
var tr=$('tr').filterAttr('data-file',name);
- tr.data('mime','text/plain').data('id',result.data.id);
+ tr.attr('data-mime','text/plain');
tr.attr('data-id', result.data.id);
getMimeIcon('text/plain',function(path){
tr.find('td.filename').attr('style','background-image:url('+path+')');
@@ -547,7 +625,7 @@ $(document).ready(function() {
break;
case 'web':
if(name.substr(0,8)!='https://' && name.substr(0,7)!='http://'){
- name='http://'.name;
+ name='http://'+name;
}
var localName=name;
if(localName.substr(localName.length-1,1)=='/'){//strip /
@@ -586,19 +664,15 @@ $(document).ready(function() {
});
break;
}
- var li=$(this).parent();
- $(this).remove();
+ var li=form.parent();
+ form.remove();
li.append('<p>'+li.data('text')+'</p>');
$('#new>a').click();
});
});
- //check if we need to scan the filesystem
- $.get(OC.filePath('files','ajax','scan.php'),{checkonly:'true'}, function(response) {
- if(response.data.done){
- scanFiles();
- }
- }, "json");
+ //do a background scan if needed
+ scanFiles();
var lastWidth = 0;
var breadcrumbs = [];
@@ -613,9 +687,10 @@ $(document).ready(function() {
breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth;
});
- if ($('#controls .actions').length > 0) {
- breadcrumbsWidth += $('#controls .actions').get(0).offsetWidth;
- }
+
+ $.each($('#controls .actions>div'), function(index, action) {
+ breadcrumbsWidth += $(action).get(0).offsetWidth;
+ });
function resizeBreadcrumbs(firstRun) {
var width = $(this).width();
@@ -665,35 +740,66 @@ $(document).ready(function() {
});
resizeBreadcrumbs(true);
+
+ // display storage warnings
+ setTimeout ( "Files.displayStorageWarnings()", 100 );
+ OC.Notification.setDefault(Files.displayStorageWarnings);
+
+ // file space size sync
+ function update_storage_statistics() {
+ $.getJSON(OC.filePath('files','ajax','getstoragestats.php'),function(response) {
+ Files.updateMaxUploadFilesize(response);
+ });
+ }
+
+ // start on load - we ask the server every 5 minutes
+ var update_storage_statistics_interval = 5*60*1000;
+ var update_storage_statistics_interval_id = setInterval(update_storage_statistics, update_storage_statistics_interval);
+
+ // Use jquery-visibility to de-/re-activate file stats sync
+ if ($.support.pageVisibility) {
+ $(document).on({
+ 'show.visibility': function() {
+ if (!update_storage_statistics_interval_id) {
+ update_storage_statistics_interval_id = setInterval(update_storage_statistics, update_storage_statistics_interval);
+ }
+ },
+ 'hide.visibility': function() {
+ clearInterval(update_storage_statistics_interval_id);
+ update_storage_statistics_interval_id = 0;
+ }
+ });
+ }
});
-function scanFiles(force,dir){
+function scanFiles(force, dir){
+ if (!OC.currentUser) {
+ return;
+ }
+
if(!dir){
- dir='';
+ dir = '';
}
- force=!!force; //cast to bool
- scanFiles.scanning=true;
- $('#scanning-message').show();
- $('#fileList').remove();
- var scannerEventSource=new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir});
- scanFiles.cancel=scannerEventSource.close.bind(scannerEventSource);
- scannerEventSource.listen('scanning',function(data){
- $('#scan-count').text(data.count + ' ' + t('files', 'files scanned'));
- $('#scan-current').text(data.file+'/');
+ force = !!force; //cast to bool
+ scanFiles.scanning = true;
+ var scannerEventSource = new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir});
+ scanFiles.cancel = scannerEventSource.close.bind(scannerEventSource);
+ scannerEventSource.listen('count',function(count){
+ console.log(count + 'files scanned')
+ });
+ scannerEventSource.listen('folder',function(path){
+ console.log('now scanning ' + path)
});
- scannerEventSource.listen('success',function(success){
+ scannerEventSource.listen('done',function(count){
scanFiles.scanning=false;
- if(success){
- window.location.reload();
- }else{
- alert(t('files', 'error while scanning'));
- }
+ console.log('done after ' + count + 'files');
});
}
scanFiles.scanning=false;
function boolOperationFinished(data, callback) {
result = jQuery.parseJSON(data.responseText);
+ Files.updateMaxUploadFilesize(result);
if(result.status == 'success'){
callback.call();
} else {
@@ -705,32 +811,105 @@ function updateBreadcrumb(breadcrumbHtml) {
$('p.nav').empty().html(breadcrumbHtml);
}
-//options for file drag/dropp
+var createDragShadow = function(event){
+ //select dragged file
+ var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
+ if (!isDragSelected) {
+ //select dragged file
+ $(event.target).parents('tr').find('td input:first').prop('checked',true);
+ }
+
+ var selectedFiles = getSelectedFiles();
+
+ if (!isDragSelected && selectedFiles.length == 1) {
+ //revert the selection
+ $(event.target).parents('tr').find('td input:first').prop('checked',false);
+ }
+
+ //also update class when we dragged more than one file
+ if (selectedFiles.length > 1) {
+ $(event.target).parents('tr').addClass('selected');
+ }
+
+ // build dragshadow
+ var dragshadow = $('<table class="dragshadow"></table>');
+ var tbody = $('<tbody></tbody>');
+ dragshadow.append(tbody);
+
+ var dir=$('#dir').val();
+
+ $(selectedFiles).each(function(i,elem){
+ var newtr = $('<tr data-dir="'+dir+'" data-filename="'+elem.name+'">'
+ +'<td class="filename">'+elem.name+'</td><td class="size">'+humanFileSize(elem.size)+'</td>'
+ +'</tr>');
+ tbody.append(newtr);
+ if (elem.type === 'dir') {
+ newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')');
+ } else {
+ getMimeIcon(elem.mime,function(path){
+ newtr.find('td.filename').attr('style','background-image:url('+path+')');
+ });
+ }
+ });
+
+ return dragshadow;
+}
+
+//options for file drag/drop
var dragOptions={
- distance: 20, revert: 'invalid', opacity: 0.7,
+ revert: 'invalid', revertDuration: 300,
+ opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: -5, top: -5 },
+ helper: createDragShadow, cursor: 'move',
stop: function(event, ui) {
$('#fileList tr td.filename').addClass('ui-draggable');
}
-};
+}
+// sane browsers support using the distance option
+if ( ! $.browser.msie) {
+ dragOptions['distance'] = 20;
+}
+
var folderDropOptions={
drop: function( event, ui ) {
- var file=ui.draggable.parent().data('file');
- var target=$(this).find('.nametext').text().trim();
- var dir=$('#dir').val();
- $.ajax({
- url: OC.filePath('files', 'ajax', 'move.php'),
- data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(dir)+'/'+encodeURIComponent(target),
- complete: function(data){boolOperationFinished(data, function(){
- var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename');
- el.draggable('destroy');
- FileList.remove(file);
- });}
+ //don't allow moving a file into a selected folder
+ if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) {
+ return false;
+ }
+
+ var target=$.trim($(this).find('.nametext').text());
+
+ var files = ui.helper.find('tr');
+ $(files).each(function(i,row){
+ var dir = $(row).data('dir');
+ var file = $(row).data('filename');
+ $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) {
+ if (result) {
+ if (result.status === 'success') {
+ //recalculate folder size
+ var oldSize = $('#fileList tr').filterAttr('data-file',target).data('size');
+ var newSize = oldSize + $('#fileList tr').filterAttr('data-file',file).data('size');
+ $('#fileList tr').filterAttr('data-file',target).data('size', newSize);
+ $('#fileList tr').filterAttr('data-file',target).find('td.filesize').text(humanFileSize(newSize));
+
+ FileList.remove(file);
+ procesSelection();
+ $('#notification').hide();
+ } else {
+ $('#notification').hide();
+ $('#notification').text(result.data.message);
+ $('#notification').fadeIn();
+ }
+ } else {
+ OC.dialogs.alert(t('Error moving file'));
+ }
+ });
});
- }
+ },
+ tolerance: 'pointer'
}
+
var crumbDropOptions={
drop: function( event, ui ) {
- var file=ui.draggable.text().trim();
var target=$(this).data('dir');
var dir=$('#dir').val();
while(dir.substr(0,1)=='/'){//remove extra leading /'s
@@ -743,12 +922,25 @@ var crumbDropOptions={
if(target==dir || target+'/'==dir){
return;
}
- $.ajax({
- url: OC.filePath('files', 'ajax', 'move.php'),
- data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(target),
- complete: function(data){boolOperationFinished(data, function(){
- FileList.remove(file);
- });}
+ var files = ui.helper.find('tr');
+ $(files).each(function(i,row){
+ var dir = $(row).data('dir');
+ var file = $(row).data('filename');
+ $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) {
+ if (result) {
+ if (result.status === 'success') {
+ FileList.remove(file);
+ procesSelection();
+ $('#notification').hide();
+ } else {
+ $('#notification').hide();
+ $('#notification').text(result.data.message);
+ $('#notification').fadeIn();
+ }
+ } else {
+ OC.dialogs.alert(t('Error moving file'));
+ }
+ });
});
},
tolerance: 'pointer'
@@ -758,22 +950,14 @@ function procesSelection(){
var selected=getSelectedFiles();
var selectedFiles=selected.filter(function(el){return el.type=='file'});
var selectedFolders=selected.filter(function(el){return el.type=='dir'});
- if(selectedFiles.length==0 && selectedFolders.length==0){
+ if(selectedFiles.length==0 && selectedFolders.length==0) {
$('#headerName>span.name').text(t('files','Name'));
$('#headerSize').text(t('files','Size'));
$('#modified').text(t('files','Modified'));
- $('th').removeClass('multiselect');
+ $('table').removeClass('multiselect');
$('.selectedActions').hide();
- $('thead').removeClass('fixed');
- $('#headerName').css('width','auto');
- $('#headerSize').css('width','auto');
- $('#headerDate').css('width','auto');
- $('table').css('padding-top','0');
- }else{
- var width={name:$('#headerName').css('width'),size:$('#headerSize').css('width'),date:$('#headerDate').css('width')};
- $('#headerName').css('width',width.name);
- $('#headerSize').css('width',width.size);
- $('#headerDate').css('width',width.date);
+ }
+ else {
$('.selectedActions').show();
var totalSize=0;
for(var i=0;i<selectedFiles.length;i++){
@@ -788,9 +972,9 @@ function procesSelection(){
var selection='';
if(selectedFolders.length>0){
if(selectedFolders.length==1){
- selection+='1 '+t('files','folder');
+ selection+=t('files','1 folder');
}else{
- selection+=selectedFolders.length+' '+t('files','folders');
+ selection+=t('files','{count} folders',{count: selectedFolders.length});
}
if(selectedFiles.length>0){
selection+=' & ';
@@ -798,14 +982,14 @@ function procesSelection(){
}
if(selectedFiles.length>0){
if(selectedFiles.length==1){
- selection+='1 '+t('files','file');
+ selection+=t('files','1 file');
}else{
- selection+=selectedFiles.length+' '+t('files','files');
+ selection+=t('files','{count} files',{count: selectedFiles.length});
}
}
$('#headerName>span.name').text(selection);
$('#modified').text('');
- $('th').addClass('multiselect');
+ $('table').addClass('multiselect');
}
}
@@ -826,7 +1010,7 @@ function getSelectedFiles(property){
name:$(element).attr('data-file'),
mime:$(element).data('mime'),
type:$(element).data('type'),
- size:$(element).data('size'),
+ size:$(element).data('size')
};
if(property){
files.push(file[property]);
@@ -837,33 +1021,11 @@ function getSelectedFiles(property){
return files;
}
-function relative_modified_date(timestamp) {
- var timediff = Math.round((new Date()).getTime() / 1000) - timestamp;
- var diffminutes = Math.round(timediff/60);
- var diffhours = Math.round(diffminutes/60);
- var diffdays = Math.round(diffhours/24);
- var diffmonths = Math.round(diffdays/31);
- var diffyears = Math.round(diffdays/365);
- if(timediff < 60) { return t('files','seconds ago'); }
- else if(timediff < 120) { return '1 '+t('files','minute ago'); }
- else if(timediff < 3600) { return diffminutes+' '+t('files','minutes ago'); }
- //else if($timediff < 7200) { return '1 hour ago'; }
- //else if($timediff < 86400) { return $diffhours.' hours ago'; }
- else if(timediff < 86400) { return t('files','today'); }
- else if(timediff < 172800) { return t('files','yesterday'); }
- else if(timediff < 2678400) { return diffdays+' '+t('files','days ago'); }
- else if(timediff < 5184000) { return t('files','last month'); }
- //else if($timediff < 31556926) { return $diffmonths.' months ago'; }
- else if(timediff < 31556926) { return t('files','months ago'); }
- else if(timediff < 63113852) { return t('files','last year'); }
- else { return diffyears+' '+t('files','years ago'); }
-}
-
function getMimeIcon(mime, ready){
if(getMimeIcon.cache[mime]){
ready(getMimeIcon.cache[mime]);
}else{
- $.get( OC.filePath('files','ajax','mimeicon.php')+'?mime='+mime, function(path){
+ $.get( OC.filePath('files','ajax','mimeicon.php'), {mime: mime}, function(path){
getMimeIcon.cache[mime]=path;
ready(getMimeIcon.cache[mime]);
});
@@ -885,7 +1047,7 @@ function getUniqueName(name){
num=parseInt(numMatch[numMatch.length-1])+1;
base=base.split('(')
base.pop();
- base=base.join('(').trim();
+ base=$.trim(base.join('('));
}
name=base+' ('+num+')';
if (extension) {
diff --git a/apps/files/js/jquery-visibility.js b/apps/files/js/jquery-visibility.js
new file mode 100644
index 00000000000..18f57d1f2bd
--- /dev/null
+++ b/apps/files/js/jquery-visibility.js
@@ -0,0 +1,31 @@
+/*! http://mths.be/visibility v1.0.5 by @mathias */
+(function (window, document, $, undefined) {
+
+ var prefix,
+ property,
+ // In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior
+ eventName = 'onfocusin' in document && 'hasFocus' in document ? 'focusin focusout' : 'focus blur',
+ prefixes = ['', 'moz', 'ms', 'o', 'webkit'],
+ $support = $.support,
+ $event = $.event;
+
+ while ((property = prefix = prefixes.pop()) != undefined) {
+ property = (prefix ? prefix + 'H' : 'h') + 'idden';
+ if ($support.pageVisibility = typeof document[property] == 'boolean') {
+ eventName = prefix + 'visibilitychange';
+ break;
+ }
+ }
+
+ $(/blur$/.test(eventName) ? window : document).on(eventName, function (event) {
+ var type = event.type,
+ originalEvent = event.originalEvent;
+ // If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement` should both be `null` or `undefined`;
+ // else, the page visibility hasn’t changed, but the user just clicked somewhere in the doc.
+ // In IE9, we need to check the `relatedTarget` property instead.
+ if (!/^focus./.test(type) || originalEvent == undefined || (originalEvent.toElement == undefined && originalEvent.fromElement == undefined && originalEvent.relatedTarget == undefined)) {
+ $event.trigger((property && document[property] || /^(?:blur|focusout)$/.test(type) ? 'hide' : 'show') + '.visibility');
+ }
+ });
+
+}(this, document, jQuery));
diff --git a/apps/files/js/keyboardshortcuts.js b/apps/files/js/keyboardshortcuts.js
new file mode 100644
index 00000000000..cc2b5d42139
--- /dev/null
+++ b/apps/files/js/keyboardshortcuts.js
@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2012 Erik Sargent <esthepiking at gmail dot com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ */
+/*****************************
+ * Keyboard shortcuts for Files app
+ * ctrl/cmd+n: new folder
+ * ctrl/cmd+shift+n: new file
+ * esc (while new file context menu is open): close menu
+ * up/down: select file/folder
+ * enter: open file/folder
+ * delete/backspace: delete file/folder
+ *****************************/
+var Files = Files || {};
+(function(Files) {
+ var keys = [];
+ var keyCodes = {
+ shift: 16,
+ n: 78,
+ cmdFirefox: 224,
+ cmdOpera: 17,
+ leftCmdWebKit: 91,
+ rightCmdWebKit: 93,
+ ctrl: 17,
+ esc: 27,
+ downArrow: 40,
+ upArrow: 38,
+ enter: 13,
+ del: 46
+ };
+
+ function removeA(arr) {
+ var what, a = arguments,
+ L = a.length,
+ ax;
+ while (L > 1 && arr.length) {
+ what = a[--L];
+ while ((ax = arr.indexOf(what)) !== -1) {
+ arr.splice(ax, 1);
+ }
+ }
+ return arr;
+ }
+
+ function newFile() {
+ $("#new").addClass("active");
+ $(".popup.popupTop").toggle(true);
+ $('#new li[data-type="file"]').trigger('click');
+ removeA(keys, keyCodes.n);
+ }
+
+ function newFolder() {
+ $("#new").addClass("active");
+ $(".popup.popupTop").toggle(true);
+ $('#new li[data-type="folder"]').trigger('click');
+ removeA(keys, keyCodes.n);
+ }
+
+ function esc() {
+ $("#controls").trigger('click');
+ }
+
+ function down() {
+ var select = -1;
+ $("#fileList tr").each(function(index) {
+ if ($(this).hasClass("mouseOver")) {
+ select = index + 1;
+ $(this).removeClass("mouseOver");
+ }
+ });
+ if (select === -1) {
+ $("#fileList tr:first").addClass("mouseOver");
+ } else {
+ $("#fileList tr").each(function(index) {
+ if (index === select) {
+ $(this).addClass("mouseOver");
+ }
+ });
+ }
+ }
+
+ function up() {
+ var select = -1;
+ $("#fileList tr").each(function(index) {
+ if ($(this).hasClass("mouseOver")) {
+ select = index - 1;
+ $(this).removeClass("mouseOver");
+ }
+ });
+ if (select === -1) {
+ $("#fileList tr:last").addClass("mouseOver");
+ } else {
+ $("#fileList tr").each(function(index) {
+ if (index === select) {
+ $(this).addClass("mouseOver");
+ }
+ });
+ }
+ }
+
+ function enter() {
+ $("#fileList tr").each(function(index) {
+ if ($(this).hasClass("mouseOver")) {
+ $(this).removeClass("mouseOver");
+ $(this).find("span.nametext").trigger('click');
+ }
+ });
+ }
+
+ function del() {
+ $("#fileList tr").each(function(index) {
+ if ($(this).hasClass("mouseOver")) {
+ $(this).removeClass("mouseOver");
+ $(this).find("a.action.delete").trigger('click');
+ }
+ });
+ }
+
+ function rename() {
+ $("#fileList tr").each(function(index) {
+ if ($(this).hasClass("mouseOver")) {
+ $(this).removeClass("mouseOver");
+ $(this).find("a[data-action='Rename']").trigger('click');
+ }
+ });
+ }
+ Files.bindKeyboardShortcuts = function(document, $) {
+ $(document).keydown(function(event) { //check for modifier keys
+ if(!$(event.target).is('body')) {
+ return;
+ }
+ var preventDefault = false;
+ if ($.inArray(event.keyCode, keys) === -1) keys.push(event.keyCode);
+ if (
+ $.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
+ preventDefault = true; //new file/folder prevent browser from responding
+ }
+ if (preventDefault) {
+ event.preventDefault(); //Prevent web browser from responding
+ event.stopPropagation();
+ return false;
+ }
+ });
+ $(document).keyup(function(event) {
+ // do your event.keyCode checks in here
+ if (
+ $.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
+ if ($.inArray(keyCodes.shift, keys) !== -1) { //16=shift, New File
+ newFile();
+ } else { //New Folder
+ newFolder();
+ }
+ } else if ($("#new").hasClass("active") && $.inArray(keyCodes.esc, keys) !== -1) { //close new window
+ esc();
+ } else if ($.inArray(keyCodes.downArrow, keys) !== -1) { //select file
+ down();
+ } else if ($.inArray(keyCodes.upArrow, keys) !== -1) { //select file
+ up();
+ } else if (!$("#new").hasClass("active") && $.inArray(keyCodes.enter, keys) !== -1) { //open file
+ enter();
+ } else if (!$("#new").hasClass("active") && $.inArray(keyCodes.del, keys) !== -1) { //delete file
+ del();
+ }
+ removeA(keys, event.keyCode);
+ });
+ };
+})(Files); \ No newline at end of file
diff --git a/apps/files/js/timezone.js b/apps/files/js/timezone.js
deleted file mode 100644
index 4749417199d..00000000000
--- a/apps/files/js/timezone.js
+++ /dev/null
@@ -1,12 +0,0 @@
-//send the clients time zone to the server
-$(document).ready(function() {
- var visitortimezone = (-new Date().getTimezoneOffset()/60);
- $.ajax({
- type: "GET",
- url: OC.filePath('files', 'ajax', 'timezone.php'),
- data: 'time='+ visitortimezone,
- success: function(){
- location.reload();
- }
- });
-}); \ No newline at end of file
diff --git a/apps/files/js/upgrade.js b/apps/files/js/upgrade.js
new file mode 100644
index 00000000000..02d57fc9e6c
--- /dev/null
+++ b/apps/files/js/upgrade.js
@@ -0,0 +1,17 @@
+$(document).ready(function () {
+ var eventSource, total, bar = $('#progressbar');
+ console.log('start');
+ bar.progressbar({value: 0});
+ eventSource = new OC.EventSource(OC.filePath('files', 'ajax', 'upgrade.php'));
+ eventSource.listen('total', function (count) {
+ total = count;
+ console.log(count + ' files needed to be migrated');
+ });
+ eventSource.listen('count', function (count) {
+ bar.progressbar({value: (count / total) * 100});
+ console.log(count);
+ });
+ eventSource.listen('done', function () {
+ document.location.reload();
+ });
+});
diff --git a/apps/files/js/upload.js b/apps/files/js/upload.js
new file mode 100644
index 00000000000..9d9f61f600e
--- /dev/null
+++ b/apps/files/js/upload.js
@@ -0,0 +1,8 @@
+function Upload(fileSelector) {
+ if ($.support.xhrFileUpload) {
+ return new XHRUpload(fileSelector.target.files);
+ } else {
+ return new FormUpload(fileSelector);
+ }
+}
+Upload.target = OC.filePath('files', 'ajax', 'upload.php');
diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php
index a5530851d2e..ce8a34acedb 100644
--- a/apps/files/l10n/ar.php
+++ b/apps/files/l10n/ar.php
@@ -1,22 +1,24 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "تم ترفيع الملفات بنجاح.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "حجم الملف الذي تريد ترفيعه أعلى مما upload_max_filesize يسمح به في ملف php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حجم الملف الذي تريد ترفيعه أعلى مما MAX_FILE_SIZE يسمح به في واجهة ال HTML.",
"The uploaded file was only partially uploaded" => "تم ترفيع جزء من الملفات الذي تريد ترفيعها فقط",
"No file was uploaded" => "لم يتم ترفيع أي من الملفات",
"Missing a temporary folder" => "المجلد المؤقت غير موجود",
"Files" => "الملفات",
"Delete" => "محذوف",
+"Close" => "إغلق",
"Name" => "الاسم",
"Size" => "حجم",
"Modified" => "معدل",
+"Upload" => "إرفع",
"Maximum upload size" => "الحد الأقصى لحجم الملفات التي يمكن رفعها",
+"Save" => "حفظ",
"New" => "جديد",
"Text file" => "ملف",
"Folder" => "مجلد",
-"Upload" => "إرفع",
"Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرفع بعض الملفات!",
"Download" => "تحميل",
+"Unshare" => "إلغاء مشاركة",
"Upload too large" => "حجم الترفيع أعلى من المسموح",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "حجم الملفات التي تريد ترفيعها أعلى من المسموح على الخادم."
);
diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php
index 86f26a1798c..a4d23e5afb5 100644
--- a/apps/files/l10n/bg_BG.php
+++ b/apps/files/l10n/bg_BG.php
@@ -1,34 +1,34 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Файлът е качен успешно",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Файлът който се опитвате да качите, надвишава зададените стойности в upload_max_filesize в PHP.INI",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Файлът който се опитвате да качите надвишава стойностите в MAX_FILE_SIZE в HTML формата.",
-"The uploaded file was only partially uploaded" => "Файлът е качен частично",
-"No file was uploaded" => "Фахлът не бе качен",
-"Missing a temporary folder" => "Липсва временната папка",
-"Failed to write to disk" => "Грешка при запис на диска",
+"Missing a temporary folder" => "Липсва временна папка",
+"Failed to write to disk" => "Възникна проблем при запис в диска",
+"Invalid directory." => "Невалидна директория.",
"Files" => "Файлове",
+"Delete permanently" => "Изтриване завинаги",
"Delete" => "Изтриване",
-"Upload Error" => "Грешка при качване",
-"Upload cancelled." => "Качването е отменено.",
-"Invalid name, '/' is not allowed." => "Неправилно име – \"/\" не е позволено.",
+"Rename" => "Преименуване",
+"Pending" => "Чакащо",
+"replace" => "препокриване",
+"cancel" => "отказ",
+"undo" => "възтановяване",
+"Upload Error" => "Възникна грешка при качването",
+"Close" => "Затвори",
+"Upload cancelled." => "Качването е спряно.",
"Name" => "Име",
"Size" => "Размер",
"Modified" => "Променено",
-"folder" => "папка",
-"folders" => "папки",
-"file" => "файл",
-"Maximum upload size" => "Макс. размер за качване",
-"0 is unlimited" => "0 означава без ограничение",
-"New" => "Нов",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папки",
+"1 file" => "1 файл",
+"{count} files" => "{count} файла",
+"Upload" => "Качване",
+"Maximum upload size" => "Максимален размер за качване",
+"0 is unlimited" => "Ползвайте 0 за без ограничения",
+"Save" => "Запис",
+"New" => "Ново",
"Text file" => "Текстов файл",
"Folder" => "Папка",
-"From url" => "От url-адрес",
-"Upload" => "Качване",
-"Cancel upload" => "Отказване на качването",
-"Nothing in here. Upload something!" => "Няма нищо, качете нещо!",
-"Share" => "Споделяне",
+"Cancel upload" => "Спри качването",
+"Nothing in here. Upload something!" => "Няма нищо тук. Качете нещо.",
"Download" => "Изтегляне",
-"Upload too large" => "Файлът е прекалено голям",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файловете които се опитвате да качите са по-големи от позволеното за сървъра.",
-"Files are being scanned, please wait." => "Файловете се претърсват, изчакайте."
+"Upload too large" => "Файлът който сте избрали за качване е прекалено голям"
);
diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php
new file mode 100644
index 00000000000..aec5d7f9d92
--- /dev/null
+++ b/apps/files/l10n/bn_BD.php
@@ -0,0 +1,64 @@
+<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s কে স্থানান্তর করা সম্ভব হলো না - এই নামের ফাইল বিদ্যমান",
+"Could not move %s" => "%s কে স্থানান্তর করা সম্ভব হলো না",
+"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না",
+"No file was uploaded. Unknown error" => "কোন ফাইল আপলোড করা হয় নি। সমস্যা অজ্ঞাত।",
+"There is no error, the file uploaded with success" => "কোন সমস্যা নেই, ফাইল আপলোড সুসম্পন্ন হয়েছে",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "আপলোড করা ফাইলটি php.ini তে বর্ণিত upload_max_filesize নির্দেশিত আয়তন অতিক্রম করছেঃ",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "আপলোড করা ফাইলটি HTML ফর্মে নির্ধারিত MAX_FILE_SIZE নির্দেশিত সর্বোচ্চ আকার অতিক্রম করেছে ",
+"The uploaded file was only partially uploaded" => "আপলোড করা ফাইলটি আংশিক আপলোড করা হয়েছে",
+"No file was uploaded" => "কোন ফাইল আপলোড করা হয় নি",
+"Missing a temporary folder" => "অস্থায়ী ফোল্ডার খোয়া গিয়েছে",
+"Failed to write to disk" => "ডিস্কে লিখতে ব্যর্থ",
+"Invalid directory." => "ভুল ডিরেক্টরি",
+"Files" => "ফাইল",
+"Delete" => "মুছে ফেল",
+"Rename" => "পূনঃনামকরণ",
+"Pending" => "মুলতুবি",
+"{new_name} already exists" => "{new_name} টি বিদ্যমান",
+"replace" => "প্রতিস্থাপন",
+"suggest name" => "নাম সুপারিশ করুন",
+"cancel" => "বাতিল",
+"replaced {new_name} with {old_name}" => "{new_name} কে {old_name} নামে প্রতিস্থাপন করা হয়েছে",
+"undo" => "ক্রিয়া প্রত্যাহার",
+"'.' is an invalid file name." => "টি একটি অননুমোদিত নাম।",
+"File name cannot be empty." => "ফাইলের নামটি ফাঁকা রাখা যাবে না।",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "নামটি সঠিক নয়, '\\', '/', '<', '>', ':', '\"', '|', '?' এবং '*' অনুমোদিত নয়।",
+"Unable to upload your file as it is a directory or has 0 bytes" => "আপনার ফাইলটি আপলোড করা সম্ভব হলো না, কেননা এটি হয় একটি ফোল্ডার কিংবা এর আকার ০ বাইট",
+"Upload Error" => "আপলোড করতে সমস্যা ",
+"Close" => "বন্ধ",
+"1 file uploading" => "১টি ফাইল আপলোড করা হচ্ছে",
+"{count} files uploading" => "{count} টি ফাইল আপলোড করা হচ্ছে",
+"Upload cancelled." => "আপলোড বাতিল করা হয়েছে।",
+"File upload is in progress. Leaving the page now will cancel the upload." => "ফাইল আপলোড চলমান। এই পৃষ্ঠা পরিত্যাগ করলে আপলোড বাতিল করা হবে।",
+"URL cannot be empty." => "URL ফাঁকা রাখা যাবে না।",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "ফোল্ডারের নামটি সঠিক নয়। 'ভাগাভাগি করা' শুধুমাত্র Owncloud এর জন্য সংরক্ষিত।",
+"Name" => "নাম",
+"Size" => "আকার",
+"Modified" => "পরিবর্তিত",
+"1 folder" => "১টি ফোল্ডার",
+"{count} folders" => "{count} টি ফোল্ডার",
+"1 file" => "১টি ফাইল",
+"{count} files" => "{count} টি ফাইল",
+"Upload" => "আপলোড",
+"File handling" => "ফাইল হ্যার্ডলিং",
+"Maximum upload size" => "আপলোডের সর্বোচ্চ আকার",
+"max. possible: " => "অনুমোদিত সর্বোচ্চ আকার",
+"Needed for multi-file and folder downloads." => "একাধিক ফাইল এবং ফোল্ডার ডাউনলোড করার জন্য আবশ্যক।",
+"Enable ZIP-download" => "ZIP ডাউনলোড সক্রিয় কর",
+"0 is unlimited" => "০ এর অর্থ অসীম",
+"Maximum input size for ZIP files" => "ZIP ফাইলের ইনপুটের সর্বোচ্চ আকার",
+"Save" => "সংরক্ষন কর",
+"New" => "নতুন",
+"Text file" => "টেক্সট ফাইল",
+"Folder" => "ফোল্ডার",
+"From link" => " লিংক থেকে",
+"Cancel upload" => "আপলোড বাতিল কর",
+"Nothing in here. Upload something!" => "এখানে কিছুই নেই। কিছু আপলোড করুন !",
+"Download" => "ডাউনলোড",
+"Unshare" => "ভাগাভাগি বাতিল ",
+"Upload too large" => "আপলোডের আকারটি অনেক বড়",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "আপনি এই সার্ভারে আপলোড করার জন্য অনুমোদিত ফাইলের সর্বোচ্চ আকারের চেয়ে বৃহদাকার ফাইল আপলোড করার চেষ্টা করছেন ",
+"Files are being scanned, please wait." => "ফাইলগুলো স্ক্যান করা হচ্ছে, দয়া করে অপেক্ষা করুন।",
+"Current scanning" => "বর্তমান স্ক্যানিং"
+);
diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php
index 17432f72a1e..43aaea31e8a 100644
--- a/apps/files/l10n/ca.php
+++ b/apps/files/l10n/ca.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "No s'ha pogut moure %s - Ja hi ha un fitxer amb aquest nom",
+"Could not move %s" => " No s'ha pogut moure %s",
+"Unable to rename file" => "No es pot canviar el nom del fitxer",
+"No file was uploaded. Unknown error" => "No s'ha carregat cap fitxer. Error desconegut",
"There is no error, the file uploaded with success" => "El fitxer s'ha pujat correctament",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "El fitxer de pujada excedeix la directiva upload_max_filesize establerta a php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "L’arxiu que voleu carregar supera el màxim definit en la directiva upload_max_filesize del php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El fitxer de pujada excedeix la directiva MAX_FILE_SIZE especificada al formulari HTML",
"The uploaded file was only partially uploaded" => "El fitxer només s'ha pujat parcialment",
"No file was uploaded" => "El fitxer no s'ha pujat",
"Missing a temporary folder" => "S'ha perdut un fitxer temporal",
"Failed to write to disk" => "Ha fallat en escriure al disc",
+"Not enough storage available" => "No hi ha prou espai disponible",
+"Invalid directory." => "Directori no vàlid.",
"Files" => "Fitxers",
-"Unshare" => "Deixa de compartir",
+"Delete permanently" => "Esborra permanentment",
"Delete" => "Suprimeix",
"Rename" => "Reanomena",
-"already exists" => "ja existeix",
+"Pending" => "Pendents",
+"{new_name} already exists" => "{new_name} ja existeix",
"replace" => "substitueix",
"suggest name" => "sugereix un nom",
"cancel" => "cancel·la",
-"replaced" => "substituït",
+"replaced {new_name} with {old_name}" => "s'ha substituït {old_name} per {new_name}",
"undo" => "desfés",
-"with" => "per",
-"unshared" => "No compartits",
-"deleted" => "esborrat",
-"generating ZIP-file, it may take some time." => "s'estan generant fitxers ZIP, pot trigar una estona.",
+"perform delete operation" => "executa d'operació d'esborrar",
+"'.' is an invalid file name." => "'.' és un nom no vàlid per un fitxer.",
+"File name cannot be empty." => "El nom del fitxer no pot ser buit.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "El nóm no és vàlid, '\\', '/', '<', '>', ':', '\"', '|', '?' i '*' no estan permesos.",
+"Your storage is full, files can not be updated or synced anymore!" => "El vostre espai d'emmagatzemament és ple, els fitxers ja no es poden actualitzar o sincronitzar!",
+"Your storage is almost full ({usedSpacePercent}%)" => "El vostre espai d'emmagatzemament és gairebé ple ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "S'està preparant la baixada. Pot trigar una estona si els fitxers són grans.",
"Unable to upload your file as it is a directory or has 0 bytes" => "No es pot pujar el fitxer perquè és una carpeta o té 0 bytes",
"Upload Error" => "Error en la pujada",
-"Pending" => "Pendents",
+"Close" => "Tanca",
"1 file uploading" => "1 fitxer pujant",
-"files uploading" => "fitxers pujant",
+"{count} files uploading" => "{count} fitxers en pujada",
"Upload cancelled." => "La pujada s'ha cancel·lat.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Hi ha una pujada en curs. Si abandoneu la pàgina la pujada es cancel·larà.",
-"Invalid name, '/' is not allowed." => "El nom no és vàlid, no es permet '/'.",
-"files scanned" => "arxius escanejats",
-"error while scanning" => "error durant l'escaneig",
+"URL cannot be empty." => "La URL no pot ser buida",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nom de carpeta no vàlid. L'ús de 'Shared' està reservat per Owncloud",
"Name" => "Nom",
"Size" => "Mida",
"Modified" => "Modificat",
-"folder" => "carpeta",
-"folders" => "carpetes",
-"file" => "fitxer",
-"files" => "fitxers",
-"seconds ago" => "segons enrere",
-"minute ago" => "minut enrere",
-"minutes ago" => "minuts enrere",
-"today" => "avui",
-"yesterday" => "ahir",
-"days ago" => "dies enrere",
-"last month" => "el mes passat",
-"months ago" => "mesos enrere",
-"last year" => "l'any passat",
-"years ago" => "anys enrere",
+"1 folder" => "1 carpeta",
+"{count} folders" => "{count} carpetes",
+"1 file" => "1 fitxer",
+"{count} files" => "{count} fitxers",
+"Upload" => "Puja",
"File handling" => "Gestió de fitxers",
"Maximum upload size" => "Mida màxima de pujada",
"max. possible: " => "màxim possible:",
@@ -58,14 +58,16 @@
"New" => "Nou",
"Text file" => "Fitxer de text",
"Folder" => "Carpeta",
-"From url" => "Des de la url",
-"Upload" => "Puja",
+"From link" => "Des d'enllaç",
+"Deleted files" => "Fitxers esborrats",
"Cancel upload" => "Cancel·la la pujada",
+"You don’t have write permissions here." => "No teniu permisos d'escriptura aquí.",
"Nothing in here. Upload something!" => "Res per aquí. Pugeu alguna cosa!",
-"Share" => "Comparteix",
"Download" => "Baixa",
+"Unshare" => "Deixa de compartir",
"Upload too large" => "La pujada és massa gran",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada del servidor",
"Files are being scanned, please wait." => "S'estan escanejant els fitxers, espereu",
-"Current scanning" => "Actualment escanejant"
+"Current scanning" => "Actualment escanejant",
+"Upgrading filesystem cache..." => "Actualitzant la memòria de cau del sistema de fitxers..."
);
diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php
index 04db7316f90..48b60bfb711 100644
--- a/apps/files/l10n/cs_CZ.php
+++ b/apps/files/l10n/cs_CZ.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Nelze přesunout %s - existuje soubor se stejným názvem",
+"Could not move %s" => "Nelze přesunout %s",
+"Unable to rename file" => "Nelze přejmenovat soubor",
+"No file was uploaded. Unknown error" => "Soubor nebyl odeslán. Neznámá chyba",
"There is no error, the file uploaded with success" => "Soubor byl odeslán úspěšně",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Odeslaný soubor přesáhl svou velikostí parametr upload_max_filesize v php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Odesílaný soubor přesahuje velikost upload_max_filesize povolenou v php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Odeslaný soubor přesáhl svou velikostí parametr MAX_FILE_SIZE specifikovaný v formuláři HTML",
"The uploaded file was only partially uploaded" => "Soubor byl odeslán pouze částečně",
"No file was uploaded" => "Žádný soubor nebyl odeslán",
"Missing a temporary folder" => "Chybí adresář pro dočasné soubory",
"Failed to write to disk" => "Zápis na disk selhal",
+"Not enough storage available" => "Nedostatek dostupného úložného prostoru",
+"Invalid directory." => "Neplatný adresář",
"Files" => "Soubory",
-"Unshare" => "Zrušit sdílení",
+"Delete permanently" => "Trvale odstranit",
"Delete" => "Smazat",
"Rename" => "Přejmenovat",
-"already exists" => "již existuje",
+"Pending" => "Čekající",
+"{new_name} already exists" => "{new_name} již existuje",
"replace" => "nahradit",
"suggest name" => "navrhnout název",
"cancel" => "zrušit",
-"replaced" => "nahrazeno",
+"replaced {new_name} with {old_name}" => "nahrazeno {new_name} s {old_name}",
"undo" => "zpět",
-"with" => "s",
-"unshared" => "sdílení zrušeno",
-"deleted" => "smazáno",
-"generating ZIP-file, it may take some time." => "generuji ZIP soubor, může to nějakou dobu trvat.",
+"perform delete operation" => "provést smazání",
+"'.' is an invalid file name." => "'.' je neplatným názvem souboru.",
+"File name cannot be empty." => "Název souboru nemůže být prázdný řetězec.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Neplatný název, znaky '\\', '/', '<', '>', ':', '\"', '|', '?' a '*' nejsou povoleny.",
+"Your storage is full, files can not be updated or synced anymore!" => "Vaše úložiště je plné, nelze aktualizovat ani synchronizovat soubory.",
+"Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložiště je téměř plné ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Vaše soubory ke stažení se připravují. Pokud jsou velké může to chvíli trvat.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nelze odeslat Váš soubor, protože je to adresář nebo má velikost 0 bajtů",
"Upload Error" => "Chyba odesílání",
-"Pending" => "Čekající",
+"Close" => "Zavřít",
"1 file uploading" => "odesílá se 1 soubor",
-"files uploading" => "souborů se odesílá",
+"{count} files uploading" => "odesílám {count} souborů",
"Upload cancelled." => "Odesílání zrušeno.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Probíhá odesílání souboru. Opuštění stránky vyústí ve zrušení nahrávání.",
-"Invalid name, '/' is not allowed." => "Neplatný název, znak '/' není povolen",
-"files scanned" => "soubory prohledány",
-"error while scanning" => "chyba při prohledávání",
+"URL cannot be empty." => "URL nemůže být prázdná",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatný název složky. Použití 'Shared' je rezervováno pro vnitřní potřeby Owncloud",
"Name" => "Název",
"Size" => "Velikost",
"Modified" => "Změněno",
-"folder" => "složka",
-"folders" => "složky",
-"file" => "soubor",
-"files" => "soubory",
-"seconds ago" => "před pár sekundami",
-"minute ago" => "před minutou",
-"minutes ago" => "před pár minutami",
-"today" => "dnes",
-"yesterday" => "včera",
-"days ago" => "před pár dny",
-"last month" => "minulý měsíc",
-"months ago" => "před pár měsíci",
-"last year" => "minulý rok",
-"years ago" => "před pár lety",
+"1 folder" => "1 složka",
+"{count} folders" => "{count} složky",
+"1 file" => "1 soubor",
+"{count} files" => "{count} soubory",
+"Upload" => "Odeslat",
"File handling" => "Zacházení se soubory",
"Maximum upload size" => "Maximální velikost pro odesílání",
"max. possible: " => "největší možná: ",
@@ -58,14 +58,16 @@
"New" => "Nový",
"Text file" => "Textový soubor",
"Folder" => "Složka",
-"From url" => "Z url",
-"Upload" => "Odeslat",
+"From link" => "Z odkazu",
+"Deleted files" => "Odstraněné soubory",
"Cancel upload" => "Zrušit odesílání",
+"You don’t have write permissions here." => "Nemáte zde práva zápisu.",
"Nothing in here. Upload something!" => "Žádný obsah. Nahrajte něco.",
-"Share" => "Sdílet",
"Download" => "Stáhnout",
+"Unshare" => "Zrušit sdílení",
"Upload too large" => "Odeslaný soubor je příliš velký",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažíte odeslat, překračují limit velikosti odesílání na tomto serveru.",
"Files are being scanned, please wait." => "Soubory se prohledávají, prosím čekejte.",
-"Current scanning" => "Aktuální prohledávání"
+"Current scanning" => "Aktuální prohledávání",
+"Upgrading filesystem cache..." => "Aktualizuji mezipaměť souborového systému..."
);
diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php
index 5e428c9b2d9..c147c939f84 100644
--- a/apps/files/l10n/da.php
+++ b/apps/files/l10n/da.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Kunne ikke flytte %s - der findes allerede en fil med dette navn",
+"Could not move %s" => "Kunne ikke flytte %s",
+"Unable to rename file" => "Kunne ikke omdøbe fil",
+"No file was uploaded. Unknown error" => "Ingen fil blev uploadet. Ukendt fejl.",
"There is no error, the file uploaded with success" => "Der er ingen fejl, filen blev uploadet med success",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den uploadede fil overskrider upload_max_filesize direktivet i php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uploadede fil overstiger upload_max_filesize direktivet i php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uploadede fil overskrider MAX_FILE_SIZE -direktivet som er specificeret i HTML-formularen",
"The uploaded file was only partially uploaded" => "Den uploadede file blev kun delvist uploadet",
"No file was uploaded" => "Ingen fil blev uploadet",
"Missing a temporary folder" => "Mangler en midlertidig mappe",
"Failed to write to disk" => "Fejl ved skrivning til disk.",
+"Not enough storage available" => "Der er ikke nok plads til rådlighed",
+"Invalid directory." => "Ugyldig mappe.",
"Files" => "Filer",
-"Unshare" => "Fjern deling",
+"Delete permanently" => "Slet permanent",
"Delete" => "Slet",
"Rename" => "Omdøb",
-"already exists" => "findes allerede",
+"Pending" => "Afventer",
+"{new_name} already exists" => "{new_name} eksisterer allerede",
"replace" => "erstat",
"suggest name" => "foreslå navn",
"cancel" => "fortryd",
-"replaced" => "erstattet",
+"replaced {new_name} with {old_name}" => "erstattede {new_name} med {old_name}",
"undo" => "fortryd",
-"with" => "med",
-"unshared" => "udelt",
-"deleted" => "Slettet",
-"generating ZIP-file, it may take some time." => "genererer ZIP-fil, det kan tage lidt tid.",
+"perform delete operation" => "udfør slet operation",
+"'.' is an invalid file name." => "'.' er et ugyldigt filnavn.",
+"File name cannot be empty." => "Filnavnet kan ikke stå tomt.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldigt navn, '\\', '/', '<', '>', ':' | '?', '\"', '', og '*' er ikke tilladt.",
+"Your storage is full, files can not be updated or synced anymore!" => "Din opbevaringsplads er fyldt op, filer kan ikke opdateres eller synkroniseres længere!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Din opbevaringsplads er næsten fyldt op ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Dit download forberedes. Dette kan tage lidt tid ved større filer.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kunne ikke uploade din fil, da det enten er en mappe eller er tom",
"Upload Error" => "Fejl ved upload",
-"Pending" => "Afventer",
+"Close" => "Luk",
"1 file uploading" => "1 fil uploades",
-"files uploading" => "filer uploades",
+"{count} files uploading" => "{count} filer uploades",
"Upload cancelled." => "Upload afbrudt.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.",
-"Invalid name, '/' is not allowed." => "Ugyldigt navn, '/' er ikke tilladt.",
-"files scanned" => "filer scannet",
-"error while scanning" => "fejl under scanning",
+"URL cannot be empty." => "URLen kan ikke være tom.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ugyldigt mappenavn. Brug af \"Shared\" er forbeholdt Owncloud",
"Name" => "Navn",
"Size" => "Størrelse",
"Modified" => "Ændret",
-"folder" => "mappe",
-"folders" => "mapper",
-"file" => "fil",
-"files" => "filer",
-"seconds ago" => "sekunder siden",
-"minute ago" => "minut siden",
-"minutes ago" => "minutter",
-"today" => "i dag",
-"yesterday" => "i går",
-"days ago" => "dage siden",
-"last month" => "sidste måned",
-"months ago" => "måneder siden",
-"last year" => "sidste år",
-"years ago" => "år siden",
+"1 folder" => "1 mappe",
+"{count} folders" => "{count} mapper",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Upload" => "Upload",
"File handling" => "Filhåndtering",
"Maximum upload size" => "Maksimal upload-størrelse",
"max. possible: " => "max. mulige: ",
@@ -58,14 +58,16 @@
"New" => "Ny",
"Text file" => "Tekstfil",
"Folder" => "Mappe",
-"From url" => "Fra URL",
-"Upload" => "Upload",
+"From link" => "Fra link",
+"Deleted files" => "Slettede filer",
"Cancel upload" => "Fortryd upload",
+"You don’t have write permissions here." => "Du har ikke skriverettigheder her.",
"Nothing in here. Upload something!" => "Her er tomt. Upload noget!",
-"Share" => "Del",
"Download" => "Download",
+"Unshare" => "Fjern deling",
"Upload too large" => "Upload for stor",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne, du prøver at uploade, er større end den maksimale størrelse for fil-upload på denne server.",
"Files are being scanned, please wait." => "Filerne bliver indlæst, vent venligst.",
-"Current scanning" => "Indlæser"
+"Current scanning" => "Indlæser",
+"Upgrading filesystem cache..." => "Opgraderer filsystems cachen..."
);
diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php
index fc07c9b911e..53427503f4c 100644
--- a/apps/files/l10n/de.php
+++ b/apps/files/l10n/de.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s konnte nicht verschoben werden - eine Datei mit diesem Namen existiert bereits.",
+"Could not move %s" => "%s konnte nicht verschoben werden",
+"Unable to rename file" => "Die Datei konnte nicht umbenannt werden",
+"No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler",
"There is no error, the file uploaded with success" => "Datei fehlerfrei hochgeladen.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Die Größe der hochzuladenden Datei überschreitet die upload_max_filesize-Richtlinie in php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Größe der hochzuladenden Datei überschreitet die MAX_FILE_SIZE-Richtlinie, die im HTML-Formular angegeben wurde",
"The uploaded file was only partially uploaded" => "Die Datei wurde nur teilweise hochgeladen.",
"No file was uploaded" => "Es wurde keine Datei hochgeladen.",
"Missing a temporary folder" => "Temporärer Ordner fehlt.",
"Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte",
+"Not enough storage available" => "Nicht genug Speicherplatz verfügbar",
+"Invalid directory." => "Ungültiges Verzeichnis.",
"Files" => "Dateien",
-"Unshare" => "Nicht mehr freigeben",
+"Delete permanently" => "Permanent löschen",
"Delete" => "Löschen",
"Rename" => "Umbenennen",
-"already exists" => "ist bereits vorhanden",
+"Pending" => "Ausstehend",
+"{new_name} already exists" => "{new_name} existiert bereits",
"replace" => "ersetzen",
"suggest name" => "Name vorschlagen",
"cancel" => "abbrechen",
-"replaced" => "ersetzt",
+"replaced {new_name} with {old_name}" => "{old_name} ersetzt durch {new_name}",
"undo" => "rückgängig machen",
-"with" => "mit",
-"unshared" => "Nicht mehr freigegeben",
-"deleted" => "gelöscht",
-"generating ZIP-file, it may take some time." => "Erstelle ZIP-Datei. Dies kann eine Weile dauern.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.",
+"perform delete operation" => "Löschvorgang ausführen",
+"'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.",
+"File name cannot be empty." => "Der Dateiname darf nicht leer sein.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicherplatz ist voll, Dateien können nicht mehr aktualisiert oder synchronisiert werden!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicherplatz ist fast aufgebraucht ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Dein Download wird vorbereitet. Dies kann bei größeren Dateien etwas dauern.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Deine Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.",
"Upload Error" => "Fehler beim Upload",
-"Pending" => "Ausstehend",
+"Close" => "Schließen",
"1 file uploading" => "Eine Datei wird hoch geladen",
-"files uploading" => "Dateien werden hoch geladen",
+"{count} files uploading" => "{count} Dateien werden hochgeladen",
"Upload cancelled." => "Upload abgebrochen.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.",
-"Invalid name, '/' is not allowed." => "Ungültiger Name: \"/\" ist nicht erlaubt.",
-"files scanned" => "Dateien gescannt",
-"error while scanning" => "Fehler beim Scannen",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Du die Seite jetzt verlässt, wird der Upload abgebrochen.",
+"URL cannot be empty." => "Die URL darf nicht leer sein.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ungültiger Verzeichnisname. Die Nutzung von \"Shared\" ist ownCloud vorbehalten.",
"Name" => "Name",
"Size" => "Größe",
"Modified" => "Bearbeitet",
-"folder" => "Ordner",
-"folders" => "Ordner",
-"file" => "Datei",
-"files" => "Dateien",
-"seconds ago" => "Sekunden her",
-"minute ago" => "Minute her",
-"minutes ago" => "Minuten her",
-"today" => "Heute",
-"yesterday" => "Gestern",
-"days ago" => "Tage her",
-"last month" => "Letzten Monat",
-"months ago" => "Monate her",
-"last year" => "Letztes Jahr",
-"years ago" => "Jahre her",
+"1 folder" => "1 Ordner",
+"{count} folders" => "{count} Ordner",
+"1 file" => "1 Datei",
+"{count} files" => "{count} Dateien",
+"Upload" => "Hochladen",
"File handling" => "Dateibehandlung",
"Maximum upload size" => "Maximale Upload-Größe",
"max. possible: " => "maximal möglich:",
@@ -58,14 +58,16 @@
"New" => "Neu",
"Text file" => "Textdatei",
"Folder" => "Ordner",
-"From url" => "Von einer URL",
-"Upload" => "Hochladen",
+"From link" => "Von einem Link",
+"Deleted files" => "Gelöschte Dateien",
"Cancel upload" => "Upload abbrechen",
+"You don’t have write permissions here." => "Du besitzt hier keine Schreib-Berechtigung.",
"Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!",
-"Share" => "Teilen",
"Download" => "Herunterladen",
+"Unshare" => "Nicht mehr freigeben",
"Upload too large" => "Upload zu groß",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.",
"Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.",
-"Current scanning" => "Scanne"
+"Current scanning" => "Scanne",
+"Upgrading filesystem cache..." => "Dateisystem-Cache wird aktualisiert ..."
);
diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php
new file mode 100644
index 00000000000..538c1b63652
--- /dev/null
+++ b/apps/files/l10n/de_DE.php
@@ -0,0 +1,73 @@
+<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Konnte %s nicht verschieben - Datei mit diesem Namen existiert bereits",
+"Could not move %s" => "Konnte %s nicht verschieben",
+"Unable to rename file" => "Konnte Datei nicht umbenennen",
+"No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler",
+"There is no error, the file uploaded with success" => "Es sind keine Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Größe der hochzuladenden Datei überschreitet die MAX_FILE_SIZE-Richtlinie, die im HTML-Formular angegeben wurde",
+"The uploaded file was only partially uploaded" => "Die Datei wurde nur teilweise hochgeladen.",
+"No file was uploaded" => "Es wurde keine Datei hochgeladen.",
+"Missing a temporary folder" => "Der temporäre Ordner fehlt.",
+"Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte",
+"Not enough storage available" => "Nicht genug Speicher vorhanden.",
+"Invalid directory." => "Ungültiges Verzeichnis.",
+"Files" => "Dateien",
+"Delete permanently" => "Entgültig löschen",
+"Delete" => "Löschen",
+"Rename" => "Umbenennen",
+"Pending" => "Ausstehend",
+"{new_name} already exists" => "{new_name} existiert bereits",
+"replace" => "ersetzen",
+"suggest name" => "Name vorschlagen",
+"cancel" => "abbrechen",
+"replaced {new_name} with {old_name}" => "{old_name} wurde ersetzt durch {new_name}",
+"undo" => "rückgängig machen",
+"perform delete operation" => "führe das Löschen aus",
+"'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.",
+"File name cannot be empty." => "Der Dateiname darf nicht leer sein.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicher ist voll. Daher können keine Dateien mehr aktualisiert oder synchronisiert werden!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicher ist fast voll ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Ihr Download wird vorbereitet. Dies kann bei größeren Dateien einen Moment dauern.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.",
+"Upload Error" => "Fehler beim Upload",
+"Close" => "Schließen",
+"1 file uploading" => "1 Datei wird hochgeladen",
+"{count} files uploading" => "{count} Dateien wurden hochgeladen",
+"Upload cancelled." => "Upload abgebrochen.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Der Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.",
+"URL cannot be empty." => "Die URL darf nicht leer sein.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ungültiger Verzeichnisname. Die Nutzung von \"Shared\" ist ownCloud vorbehalten",
+"Name" => "Name",
+"Size" => "Größe",
+"Modified" => "Bearbeitet",
+"1 folder" => "1 Ordner",
+"{count} folders" => "{count} Ordner",
+"1 file" => "1 Datei",
+"{count} files" => "{count} Dateien",
+"Upload" => "Hochladen",
+"File handling" => "Dateibehandlung",
+"Maximum upload size" => "Maximale Upload-Größe",
+"max. possible: " => "maximal möglich:",
+"Needed for multi-file and folder downloads." => "Für Mehrfachdatei- und Ordnerdownloads benötigt:",
+"Enable ZIP-download" => "ZIP-Download aktivieren",
+"0 is unlimited" => "0 bedeutet unbegrenzt",
+"Maximum input size for ZIP files" => "Maximale Größe für ZIP-Dateien",
+"Save" => "Speichern",
+"New" => "Neu",
+"Text file" => "Textdatei",
+"Folder" => "Ordner",
+"From link" => "Von einem Link",
+"Deleted files" => "Gelöschte Dateien",
+"Cancel upload" => "Upload abbrechen",
+"You don’t have write permissions here." => "Sie haben hier keine Schreib-Berechtigungen.",
+"Nothing in here. Upload something!" => "Alles leer. Bitte laden Sie etwas hoch!",
+"Download" => "Herunterladen",
+"Unshare" => "Nicht mehr freigeben",
+"Upload too large" => "Der Upload ist zu groß",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.",
+"Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.",
+"Current scanning" => "Scanne",
+"Upgrading filesystem cache..." => "Aktualisiere den Dateisystem-Cache..."
+);
diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php
index ef2e0bd2839..63759f1201e 100644
--- a/apps/files/l10n/el.php
+++ b/apps/files/l10n/el.php
@@ -1,54 +1,54 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Δεν υπάρχει λάθος, το αρχείο μεταφορτώθηκε επιτυχώς",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Το αρχείο που μεταφορτώθηκε υπερβαίνει την οδηγία μέγιστου επιτρεπτού μεγέθους \"upload_max_filesize\" του php.ini",
+"Could not move %s - File with this name already exists" => "Αδυναμία μετακίνησης του %s - υπάρχει ήδη αρχείο με αυτό το όνομα",
+"Could not move %s" => "Αδυναμία μετακίνησης του %s",
+"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου",
+"No file was uploaded. Unknown error" => "Δεν ανέβηκε κάποιο αρχείο. Άγνωστο σφάλμα",
+"There is no error, the file uploaded with success" => "Δεν υπάρχει σφάλμα, το αρχείο εστάλει επιτυχώς",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Το απεσταλμένο αρχείο ξεπερνά την οδηγία upload_max_filesize στο php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Το αρχείο υπερβαίνει την οδηγία μέγιστου επιτρεπτού μεγέθους \"MAX_FILE_SIZE\" που έχει οριστεί στην HTML φόρμα",
-"The uploaded file was only partially uploaded" => "Το αρχείο μεταφορώθηκε μόνο εν μέρει",
-"No file was uploaded" => "Κανένα αρχείο δεν μεταφορτώθηκε",
+"The uploaded file was only partially uploaded" => "Το αρχείο εστάλει μόνο εν μέρει",
+"No file was uploaded" => "Κανένα αρχείο δεν στάλθηκε",
"Missing a temporary folder" => "Λείπει ο προσωρινός φάκελος",
"Failed to write to disk" => "Αποτυχία εγγραφής στο δίσκο",
+"Not enough storage available" => "Μη επαρκής διαθέσιμος αποθηκευτικός χώρος",
+"Invalid directory." => "Μη έγκυρος φάκελος.",
"Files" => "Αρχεία",
-"Unshare" => "Διακοπή κοινής χρήσης",
+"Delete permanently" => "Μόνιμη διαγραφή",
"Delete" => "Διαγραφή",
"Rename" => "Μετονομασία",
-"already exists" => "υπάρχει ήδη",
+"Pending" => "Εκκρεμεί",
+"{new_name} already exists" => "{new_name} υπάρχει ήδη",
"replace" => "αντικατέστησε",
"suggest name" => "συνιστώμενο όνομα",
"cancel" => "ακύρωση",
-"replaced" => "αντικαταστάθηκε",
+"replaced {new_name} with {old_name}" => "αντικαταστάθηκε το {new_name} με {old_name}",
"undo" => "αναίρεση",
-"with" => "με",
-"unshared" => "Διακόπηκε ο διαμοιρασμός",
-"deleted" => "διαγράφηκε",
-"generating ZIP-file, it may take some time." => "παραγωγή αρχείου ZIP, ίσως διαρκέσει αρκετά.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Αδυναμία στην μεταφόρτωση του αρχείου σας αφού είναι φάκελος ή έχει 0 bytes",
-"Upload Error" => "Σφάλμα Μεταφόρτωσης",
-"Pending" => "Εκκρεμεί",
+"perform delete operation" => "εκτέλεση διαδικασία διαγραφής",
+"'.' is an invalid file name." => "'.' είναι μη έγκυρο όνομα αρχείου.",
+"File name cannot be empty." => "Το όνομα αρχείου δεν πρέπει να είναι κενό.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Μη έγκυρο όνομα, '\\', '/', '<', '>', ':', '\"', '|', '?' και '*' δεν επιτρέπονται.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ο αποθηκευτικός σας χώρος είναι γεμάτος, τα αρχεία δεν μπορούν να ενημερωθούν ή να συγχρονιστούν πια!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ο αποθηκευτικός χώρος είναι σχεδόν γεμάτος ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Η λήψη προετοιμάζεται. Αυτό μπορεί να πάρει ώρα εάν τα αρχεία έχουν μεγάλο μέγεθος.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Αδυναμία στην αποστολή του αρχείου σας αφού είναι φάκελος ή έχει 0 bytes",
+"Upload Error" => "Σφάλμα Αποστολής",
+"Close" => "Κλείσιμο",
"1 file uploading" => "1 αρχείο ανεβαίνει",
-"files uploading" => "αρχεία ανεβαίνουν",
-"Upload cancelled." => "Η μεταφόρτωση ακυρώθηκε.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "Η μεταφόρτωση του αρχείου βρίσκεται σε εξέλιξη. Έξοδος από την σελίδα τώρα θα ακυρώσει την μεταφόρτωση.",
-"Invalid name, '/' is not allowed." => "Μη έγκυρο όνομα, το '/' δεν επιτρέπεται.",
-"files scanned" => "αρχεία σαρώθηκαν",
-"error while scanning" => "σφάλμα κατά την ανίχνευση",
+"{count} files uploading" => "{count} αρχεία ανεβαίνουν",
+"Upload cancelled." => "Η αποστολή ακυρώθηκε.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Η αποστολή του αρχείου βρίσκεται σε εξέλιξη. Το κλείσιμο της σελίδας θα ακυρώσει την αποστολή.",
+"URL cannot be empty." => "Η URL δεν πρέπει να είναι κενή.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Μη έγκυρο όνομα φακέλου. Η χρήση του 'Κοινόχρηστος' χρησιμοποιείται από ο Owncloud",
"Name" => "Όνομα",
"Size" => "Μέγεθος",
"Modified" => "Τροποποιήθηκε",
-"folder" => "φάκελος",
-"folders" => "φάκελοι",
-"file" => "αρχείο",
-"files" => "αρχεία",
-"seconds ago" => "δευτερόλεπτα πριν",
-"minute ago" => "λεπτό πριν",
-"minutes ago" => "λεπτά πριν",
-"today" => "σήμερα",
-"yesterday" => "χτες",
-"days ago" => "μέρες πριν",
-"last month" => "τελευταίο μήνα",
-"months ago" => "μήνες πριν",
-"last year" => "τελευταίο χρόνο",
-"years ago" => "χρόνια πριν",
+"1 folder" => "1 φάκελος",
+"{count} folders" => "{count} φάκελοι",
+"1 file" => "1 αρχείο",
+"{count} files" => "{count} αρχεία",
+"Upload" => "Αποστολή",
"File handling" => "Διαχείριση αρχείων",
-"Maximum upload size" => "Μέγιστο μέγεθος μεταφόρτωσης",
+"Maximum upload size" => "Μέγιστο μέγεθος αποστολής",
"max. possible: " => "μέγιστο δυνατό:",
"Needed for multi-file and folder downloads." => "Απαραίτητο για κατέβασμα πολλαπλών αρχείων και φακέλων",
"Enable ZIP-download" => "Ενεργοποίηση κατεβάσματος ZIP",
@@ -58,14 +58,15 @@
"New" => "Νέο",
"Text file" => "Αρχείο κειμένου",
"Folder" => "Φάκελος",
-"From url" => "Από την διεύθυνση",
-"Upload" => "Μεταφόρτωση",
-"Cancel upload" => "Ακύρωση μεταφόρτωσης",
-"Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανέβασε κάτι!",
-"Share" => "Διαμοιρασμός",
+"From link" => "Από σύνδεσμο",
+"Deleted files" => "Διαγραμμένα αρχεία",
+"Cancel upload" => "Ακύρωση αποστολής",
+"Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Μεταφορτώστε κάτι!",
"Download" => "Λήψη",
-"Upload too large" => "Πολύ μεγάλο αρχείο προς μεταφόρτωση",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να μεταφορτώσετε υπερβαίνουν το μέγιστο μέγεθος μεταφόρτωσης αρχείων σε αυτόν το διακομιστή.",
+"Unshare" => "Διακοπή κοινής χρήσης",
+"Upload too large" => "Πολύ μεγάλο αρχείο προς αποστολή",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος αποστολής αρχείων σε αυτόν τον διακομιστή.",
"Files are being scanned, please wait." => "Τα αρχεία σαρώνονται, παρακαλώ περιμένετε",
-"Current scanning" => "Τρέχουσα αναζήτηση "
+"Current scanning" => "Τρέχουσα αναζήτηση ",
+"Upgrading filesystem cache..." => "Αναβάθμιση μνήμης cache του συστήματος αρχείων..."
);
diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php
index e9d8eb9e9af..225408f9a76 100644
--- a/apps/files/l10n/eo.php
+++ b/apps/files/l10n/eo.php
@@ -1,52 +1,47 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Ne eblis movi %s: dosiero kun ĉi tiu nomo jam ekzistas",
+"Could not move %s" => "Ne eblis movi %s",
+"Unable to rename file" => "Ne eblis alinomigi dosieron",
+"No file was uploaded. Unknown error" => "Neniu dosiero alŝutiĝis. Nekonata eraro.",
"There is no error, the file uploaded with success" => "Ne estas eraro, la dosiero alŝutiĝis sukcese",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "La dosiero alŝutita superas la regulon upload_max_filesize el php.ini",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "La dosiero alŝutita superas laregulon MAX_FILE_SIZE, kiu estas difinita en la HTML-formularo",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "La dosiero alŝutita superas la regulon upload_max_filesize el php.ini: ",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "La dosiero alŝutita superas la regulon MAX_FILE_SIZE, kiu estas difinita en la HTML-formularo",
"The uploaded file was only partially uploaded" => "La alŝutita dosiero nur parte alŝutiĝis",
"No file was uploaded" => "Neniu dosiero estas alŝutita",
"Missing a temporary folder" => "Mankas tempa dosierujo",
"Failed to write to disk" => "Malsukcesis skribo al disko",
+"Invalid directory." => "Nevalida dosierujo.",
"Files" => "Dosieroj",
-"Unshare" => "Malkunhavigi",
"Delete" => "Forigi",
"Rename" => "Alinomigi",
-"already exists" => "jam ekzistas",
+"Pending" => "Traktotaj",
+"{new_name} already exists" => "{new_name} jam ekzistas",
"replace" => "anstataŭigi",
"suggest name" => "sugesti nomon",
"cancel" => "nuligi",
-"replaced" => "anstataŭigita",
+"replaced {new_name} with {old_name}" => "anstataŭiĝis {new_name} per {old_name}",
"undo" => "malfari",
-"with" => "kun",
-"unshared" => "malkunhavigita",
-"deleted" => "forigita",
-"generating ZIP-file, it may take some time." => "generanta ZIP-dosiero, ĝi povas daŭri iom da tempo",
+"'.' is an invalid file name." => "'.' ne estas valida dosiernomo.",
+"File name cannot be empty." => "Dosiernomo devas ne malpleni.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nevalida nomo: “\\”, “/”, “<”, “>”, “:”, “\"”, “|”, “?” kaj “*” ne permesatas.",
+"Your download is being prepared. This might take some time if the files are big." => "Via elŝuto pretiĝatas. Ĉi tio povas daŭri iom da tempo se la dosieroj grandas.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Ne eblis alŝuti vian dosieron ĉar ĝi estas dosierujo aŭ havas 0 duumokojn",
"Upload Error" => "Alŝuta eraro",
-"Pending" => "Traktotaj",
+"Close" => "Fermi",
"1 file uploading" => "1 dosiero estas alŝutata",
-"files uploading" => "dosieroj estas alŝutataj",
+"{count} files uploading" => "{count} dosieroj alŝutatas",
"Upload cancelled." => "La alŝuto nuliĝis.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Dosieralŝuto plenumiĝas. Lasi la paĝon nun nuligus la alŝuton.",
-"Invalid name, '/' is not allowed." => "Nevalida nomo, “/” ne estas permesata.",
-"files scanned" => "dosieroj skanitaj",
-"error while scanning" => "eraro dum skano",
+"URL cannot be empty." => "URL ne povas esti malplena.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nevalida dosierujnomo. Uzo de “Shared” rezervatas de Owncloud.",
"Name" => "Nomo",
"Size" => "Grando",
"Modified" => "Modifita",
-"folder" => "dosierujo",
-"folders" => "dosierujoj",
-"file" => "dosiero",
-"files" => "dosieroj",
-"seconds ago" => "sekundoj antaŭe",
-"minute ago" => "minuto antaŭe",
-"minutes ago" => "minutoj antaŭe",
-"today" => "hodiaŭ",
-"yesterday" => "hieraŭ",
-"days ago" => "tagoj antaŭe",
-"last month" => "lastamonate",
-"months ago" => "monatoj antaŭe",
-"last year" => "lastajare",
-"years ago" => "jaroj antaŭe",
+"1 folder" => "1 dosierujo",
+"{count} folders" => "{count} dosierujoj",
+"1 file" => "1 dosiero",
+"{count} files" => "{count} dosierujoj",
+"Upload" => "Alŝuti",
"File handling" => "Dosieradministro",
"Maximum upload size" => "Maksimuma alŝutogrando",
"max. possible: " => "maks. ebla: ",
@@ -58,12 +53,11 @@
"New" => "Nova",
"Text file" => "Tekstodosiero",
"Folder" => "Dosierujo",
-"From url" => "El URL",
-"Upload" => "Alŝuti",
+"From link" => "El ligilo",
"Cancel upload" => "Nuligi alŝuton",
"Nothing in here. Upload something!" => "Nenio estas ĉi tie. Alŝutu ion!",
-"Share" => "Kunhavigi",
"Download" => "Elŝuti",
+"Unshare" => "Malkunhavigi",
"Upload too large" => "Elŝuto tro larĝa",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "La dosieroj, kiujn vi provas alŝuti, transpasas la maksimuman grandon por dosieralŝutoj en ĉi tiu servilo.",
"Files are being scanned, please wait." => "Dosieroj estas skanataj, bonvolu atendi.",
diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php
index 3837cf49f99..f702a5b513d 100644
--- a/apps/files/l10n/es.php
+++ b/apps/files/l10n/es.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "No se puede mover %s - Ya existe un archivo con ese nombre",
+"Could not move %s" => "No se puede mover %s",
+"Unable to rename file" => "No se puede renombrar el archivo",
+"No file was uploaded. Unknown error" => "Fallo no se subió el fichero",
"There is no error, the file uploaded with success" => "No se ha producido ningún error, el archivo se ha subido con éxito",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "El archivo que intentas subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentas subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo que intentas subir sobrepasa el tamaño definido por la variable MAX_FILE_SIZE especificada en el formulario HTML",
"The uploaded file was only partially uploaded" => "El archivo que intentas subir solo se subió parcialmente",
"No file was uploaded" => "No se ha subido ningún archivo",
"Missing a temporary folder" => "Falta un directorio temporal",
"Failed to write to disk" => "La escritura en disco ha fallado",
+"Not enough storage available" => "No hay suficiente espacio disponible",
+"Invalid directory." => "Directorio invalido.",
"Files" => "Archivos",
-"Unshare" => "Dejar de compartir",
+"Delete permanently" => "Eliminar permanentemente",
"Delete" => "Eliminar",
"Rename" => "Renombrar",
-"already exists" => "ya existe",
+"Pending" => "Pendiente",
+"{new_name} already exists" => "{new_name} ya existe",
"replace" => "reemplazar",
"suggest name" => "sugerir nombre",
"cancel" => "cancelar",
-"replaced" => "reemplazado",
+"replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}",
"undo" => "deshacer",
-"with" => "con",
-"unshared" => "no compartido",
-"deleted" => "borrado",
-"generating ZIP-file, it may take some time." => "generando un fichero ZIP, puede llevar un tiempo.",
+"perform delete operation" => "Eliminar",
+"'.' is an invalid file name." => "'.' es un nombre de archivo inválido.",
+"File name cannot be empty." => "El nombre de archivo no puede estar vacío.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre Invalido, \"\\\", \"/\", \"<\", \">\", \":\", \"\", \"|\" \"?\" y \"*\" no están permitidos ",
+"Your storage is full, files can not be updated or synced anymore!" => "Su almacenamiento esta lleno, los archivos no pueden ser mas actualizados o sincronizados!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Su almacenamiento esta lleno en un ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Tu descarga esta siendo preparada. Esto puede tardar algun tiempo si los archivos son muy grandes.",
"Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes",
"Upload Error" => "Error al subir el archivo",
-"Pending" => "Pendiente",
+"Close" => "cerrrar",
"1 file uploading" => "subiendo 1 archivo",
-"files uploading" => "archivos subiendo",
+"{count} files uploading" => "Subiendo {count} archivos",
"Upload cancelled." => "Subida cancelada.",
"File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Salir de la página ahora cancelará la subida.",
-"Invalid name, '/' is not allowed." => "Nombre no válido, '/' no está permitido.",
-"files scanned" => "archivos escaneados",
-"error while scanning" => "error escaneando",
+"URL cannot be empty." => "La URL no puede estar vacía.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nombre de carpeta invalido. El uso de \"Shared\" esta reservado para Owncloud",
"Name" => "Nombre",
"Size" => "Tamaño",
"Modified" => "Modificado",
-"folder" => "carpeta",
-"folders" => "carpetas",
-"file" => "archivo",
-"files" => "archivos",
-"seconds ago" => "hace segundos",
-"minute ago" => "minuto",
-"minutes ago" => "hace minutos",
-"today" => "hoy",
-"yesterday" => "ayer",
-"days ago" => "días",
-"last month" => "mes pasado",
-"months ago" => "hace meses",
-"last year" => "año pasado",
-"years ago" => "hace años",
+"1 folder" => "1 carpeta",
+"{count} folders" => "{count} carpetas",
+"1 file" => "1 archivo",
+"{count} files" => "{count} archivos",
+"Upload" => "Subir",
"File handling" => "Tratamiento de archivos",
"Maximum upload size" => "Tamaño máximo de subida",
"max. possible: " => "máx. posible:",
@@ -58,14 +58,16 @@
"New" => "Nuevo",
"Text file" => "Archivo de texto",
"Folder" => "Carpeta",
-"From url" => "Desde la URL",
-"Upload" => "Subir",
+"From link" => "Desde el enlace",
+"Deleted files" => "Archivos eliminados",
"Cancel upload" => "Cancelar subida",
+"You don’t have write permissions here." => "No tienes permisos para escribir aquí.",
"Nothing in here. Upload something!" => "Aquí no hay nada. ¡Sube algo!",
-"Share" => "Compartir",
"Download" => "Descargar",
+"Unshare" => "Dejar de compartir",
"Upload too large" => "El archivo es demasiado grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.",
"Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.",
-"Current scanning" => "Ahora escaneando"
+"Current scanning" => "Ahora escaneando",
+"Upgrading filesystem cache..." => "Actualizando cache de archivos de sistema"
);
diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php
index e94a904327c..6cd7c026922 100644
--- a/apps/files/l10n/es_AR.php
+++ b/apps/files/l10n/es_AR.php
@@ -1,56 +1,56 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "No se pudo mover %s - Un archivo con este nombre ya existe",
+"Could not move %s" => "No se pudo mover %s ",
+"Unable to rename file" => "No fue posible cambiar el nombre al archivo",
+"No file was uploaded. Unknown error" => "El archivo no fue subido. Error desconocido",
"There is no error, the file uploaded with success" => "No se han producido errores, el archivo se ha subido con éxito",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "El archivo que intentás subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentás subir excede el tamaño definido por upload_max_filesize en el php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo que intentás subir sobrepasa el tamaño definido por la variable MAX_FILE_SIZE especificada en el formulario HTML",
"The uploaded file was only partially uploaded" => "El archivo que intentás subir solo se subió parcialmente",
"No file was uploaded" => "El archivo no fue subido",
"Missing a temporary folder" => "Falta un directorio temporal",
-"Failed to write to disk" => "La escritura en disco falló",
+"Failed to write to disk" => "Error al escribir en el disco",
+"Not enough storage available" => "No hay suficiente capacidad de almacenamiento",
+"Invalid directory." => "Directorio invalido.",
"Files" => "Archivos",
-"Unshare" => "Dejar de compartir",
+"Delete permanently" => "Borrar de manera permanente",
"Delete" => "Borrar",
-"Rename" => "cambiar nombre",
-"already exists" => "ya existe",
+"Rename" => "Cambiar nombre",
+"Pending" => "Pendiente",
+"{new_name} already exists" => "{new_name} ya existe",
"replace" => "reemplazar",
"suggest name" => "sugerir nombre",
"cancel" => "cancelar",
-"replaced" => "reemplazado",
+"replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}",
"undo" => "deshacer",
-"with" => "con",
-"unshared" => "no compartido",
-"deleted" => "borrado",
-"generating ZIP-file, it may take some time." => "generando un archivo ZIP, puede llevar un tiempo.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "No fue posible subir tu archivo porque es un directorio o su tamaño es 0 bytes",
+"perform delete operation" => "Eliminar",
+"'.' is an invalid file name." => "'.' es un nombre de archivo inválido.",
+"File name cannot be empty." => "El nombre del archivo no puede quedar vacío.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre invalido, '\\', '/', '<', '>', ':', '\"', '|', '?' y '*' no están permitidos.",
+"Your storage is full, files can not be updated or synced anymore!" => "El almacenamiento está lleno, los archivos no se pueden seguir actualizando ni sincronizando",
+"Your storage is almost full ({usedSpacePercent}%)" => "El almacenamiento está casi lleno ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Tu descarga esta siendo preparada. Esto puede tardar algun tiempo si los archivos son muy grandes.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "No fue posible subir el archivo porque es un directorio o porque su tamaño es 0 bytes",
"Upload Error" => "Error al subir el archivo",
-"Pending" => "Pendiente",
+"Close" => "Cerrar",
"1 file uploading" => "Subiendo 1 archivo",
-"files uploading" => "Subiendo archivos",
+"{count} files uploading" => "Subiendo {count} archivos",
"Upload cancelled." => "La subida fue cancelada",
"File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si salís de la página ahora, la subida se cancelará.",
-"Invalid name, '/' is not allowed." => "Nombre no válido, '/' no está permitido.",
-"files scanned" => "archivos escaneados",
-"error while scanning" => "error mientras se escaneaba",
+"URL cannot be empty." => "La URL no puede estar vacía",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nombre de carpeta inválido. El uso de 'Shared' está reservado por ownCloud",
"Name" => "Nombre",
"Size" => "Tamaño",
"Modified" => "Modificado",
-"folder" => "carpeta",
-"folders" => "carpetas",
-"file" => "archivo",
-"files" => "archivos",
-"seconds ago" => "segundos atrás",
-"minute ago" => "hace un minuto",
-"minutes ago" => "minutos atrás",
-"today" => "hoy",
-"yesterday" => "ayer",
-"days ago" => "días atrás",
-"last month" => "el mes pasado",
-"months ago" => "meses atrás",
-"last year" => "el año pasado",
-"years ago" => "años atrás",
+"1 folder" => "1 directorio",
+"{count} folders" => "{count} directorios",
+"1 file" => "1 archivo",
+"{count} files" => "{count} archivos",
+"Upload" => "Subir",
"File handling" => "Tratamiento de archivos",
"Maximum upload size" => "Tamaño máximo de subida",
"max. possible: " => "máx. posible:",
-"Needed for multi-file and folder downloads." => "Se necesita para descargas multi-archivo y de carpetas",
+"Needed for multi-file and folder downloads." => "Es necesario para descargas multi-archivo y de carpetas",
"Enable ZIP-download" => "Habilitar descarga en formato ZIP",
"0 is unlimited" => "0 significa ilimitado",
"Maximum input size for ZIP files" => "Tamaño máximo para archivos ZIP de entrada",
@@ -58,14 +58,16 @@
"New" => "Nuevo",
"Text file" => "Archivo de texto",
"Folder" => "Carpeta",
-"From url" => "Desde la URL",
-"Upload" => "Subir",
+"From link" => "Desde enlace",
+"Deleted files" => "Archivos Borrados",
"Cancel upload" => "Cancelar subida",
-"Nothing in here. Upload something!" => "Aquí no hay nada. ¡Subí contenido!",
-"Share" => "Compartir",
+"You don’t have write permissions here." => "No tenés permisos de escritura acá.",
+"Nothing in here. Upload something!" => "No hay nada. ¡Subí contenido!",
"Download" => "Descargar",
+"Unshare" => "Dejar de compartir",
"Upload too large" => "El archivo es demasiado grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que intentás subir sobrepasan el tamaño máximo ",
-"Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.",
-"Current scanning" => "Escaneo actual"
+"Files are being scanned, please wait." => "Se están escaneando los archivos, por favor esperá.",
+"Current scanning" => "Escaneo actual",
+"Upgrading filesystem cache..." => "Actualizando el cache del sistema de archivos"
);
diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php
index f2721b194e2..f1c94e93aab 100644
--- a/apps/files/l10n/et_EE.php
+++ b/apps/files/l10n/et_EE.php
@@ -1,37 +1,45 @@
<?php $TRANSLATIONS = array(
+"Could not move %s" => "%s liigutamine ebaõnnestus",
+"Unable to rename file" => "Faili ümbernimetamine ebaõnnestus",
+"No file was uploaded. Unknown error" => "Ühtegi faili ei laetud üles. Tundmatu viga",
"There is no error, the file uploaded with success" => "Ühtegi viga pole, fail on üles laetud",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Üles laetud faili suurus ületab php.ini määratud upload_max_filesize suuruse",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Üles laetud faili suurus ületab HTML vormis määratud upload_max_filesize suuruse",
"The uploaded file was only partially uploaded" => "Fail laeti üles ainult osaliselt",
"No file was uploaded" => "Ühtegi faili ei laetud üles",
"Missing a temporary folder" => "Ajutiste failide kaust puudub",
"Failed to write to disk" => "Kettale kirjutamine ebaõnnestus",
+"Not enough storage available" => "Saadaval pole piisavalt ruumi",
+"Invalid directory." => "Vigane kaust.",
"Files" => "Failid",
-"Unshare" => "Lõpeta jagamine",
+"Delete permanently" => "Kustuta jäädavalt",
"Delete" => "Kustuta",
-"already exists" => "on juba olemas",
+"Rename" => "ümber",
+"Pending" => "Ootel",
+"{new_name} already exists" => "{new_name} on juba olemas",
"replace" => "asenda",
"suggest name" => "soovita nime",
"cancel" => "loobu",
-"replaced" => "asendatud",
+"replaced {new_name} with {old_name}" => "asendas nime {old_name} nimega {new_name}",
"undo" => "tagasi",
-"with" => "millega",
-"unshared" => "jagamata",
-"deleted" => "kustutatud",
-"generating ZIP-file, it may take some time." => "ZIP-faili loomine, see võib veidi aega võtta.",
+"'.' is an invalid file name." => "'.' on vigane failinimi.",
+"File name cannot be empty." => "Faili nimi ei saa olla tühi.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Vigane nimi, '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' pole lubatud.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Sinu faili üleslaadimine ebaõnnestus, kuna see on kaust või selle suurus on 0 baiti",
"Upload Error" => "Üleslaadimise viga",
-"Pending" => "Ootel",
+"Close" => "Sulge",
+"1 file uploading" => "1 faili üleslaadimisel",
+"{count} files uploading" => "{count} faili üleslaadimist",
"Upload cancelled." => "Üleslaadimine tühistati.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Faili üleslaadimine on töös. Lehelt lahkumine katkestab selle üleslaadimise.",
-"Invalid name, '/' is not allowed." => "Vigane nimi, '/' pole lubatud.",
+"URL cannot be empty." => "URL ei saa olla tühi.",
"Name" => "Nimi",
"Size" => "Suurus",
"Modified" => "Muudetud",
-"folder" => "kaust",
-"folders" => "kausta",
-"file" => "fail",
-"files" => "faili",
+"1 folder" => "1 kaust",
+"{count} folders" => "{count} kausta",
+"1 file" => "1 fail",
+"{count} files" => "{count} faili",
+"Upload" => "Lae üles",
"File handling" => "Failide käsitlemine",
"Maximum upload size" => "Maksimaalne üleslaadimise suurus",
"max. possible: " => "maks. võimalik: ",
@@ -43,12 +51,11 @@
"New" => "Uus",
"Text file" => "Tekstifail",
"Folder" => "Kaust",
-"From url" => "URL-ilt",
-"Upload" => "Lae üles",
+"From link" => "Allikast",
"Cancel upload" => "Tühista üleslaadimine",
"Nothing in here. Upload something!" => "Siin pole midagi. Lae midagi üles!",
-"Share" => "Jaga",
"Download" => "Lae alla",
+"Unshare" => "Lõpeta jagamine",
"Upload too large" => "Üleslaadimine on liiga suur",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Failid, mida sa proovid üles laadida, ületab serveri poolt üleslaetavatele failidele määratud maksimaalse suuruse.",
"Files are being scanned, please wait." => "Faile skannitakse, palun oota",
diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php
index 94212afe8c2..63c62ce9a55 100644
--- a/apps/files/l10n/eu.php
+++ b/apps/files/l10n/eu.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Ezin da %s mugitu - Izen hau duen fitxategia dagoeneko existitzen da",
+"Could not move %s" => "Ezin dira fitxategiak mugitu %s",
+"Unable to rename file" => "Ezin izan da fitxategia berrizendatu",
+"No file was uploaded. Unknown error" => "Ez da fitxategirik igo. Errore ezezaguna",
"There is no error, the file uploaded with success" => "Ez da arazorik izan, fitxategia ongi igo da",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Igotako fitxategiaren tamaina php.ini-ko upload_max_filesize direktiban adierazitakoa baino handiagoa da",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Igotako fitxategiak php.ini fitxategian ezarritako upload_max_filesize muga gainditu du:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Igotako fitxategiaren tamaina HTML inprimakiko MAX_FILESIZE direktiban adierazitakoa baino handiagoa da",
"The uploaded file was only partially uploaded" => "Igotako fitxategiaren zati bat baino gehiago ez da igo",
"No file was uploaded" => "Ez da fitxategirik igo",
"Missing a temporary folder" => "Aldi baterako karpeta falta da",
"Failed to write to disk" => "Errore bat izan da diskoan idazterakoan",
+"Not enough storage available" => "Ez dago behar aina leku erabilgarri,",
+"Invalid directory." => "Baliogabeko karpeta.",
"Files" => "Fitxategiak",
-"Unshare" => "Ez partekatu",
+"Delete permanently" => "Ezabatu betirako",
"Delete" => "Ezabatu",
"Rename" => "Berrizendatu",
-"already exists" => "dagoeneko existitzen da",
+"Pending" => "Zain",
+"{new_name} already exists" => "{new_name} dagoeneko existitzen da",
"replace" => "ordeztu",
"suggest name" => "aholkatu izena",
"cancel" => "ezeztatu",
-"replaced" => "ordeztua",
+"replaced {new_name} with {old_name}" => " {new_name}-k {old_name} ordezkatu du",
"undo" => "desegin",
-"with" => "honekin",
-"unshared" => "Ez partekatuta",
-"deleted" => "ezabatuta",
-"generating ZIP-file, it may take some time." => "ZIP-fitxategia sortzen ari da, denbora har dezake",
+"perform delete operation" => "Ezabatu",
+"'.' is an invalid file name." => "'.' ez da fitxategi izen baliogarria.",
+"File name cannot be empty." => "Fitxategi izena ezin da hutsa izan.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "IZen aliogabea, '\\', '/', '<', '>', ':', '\"', '|', '?' eta '*' ez daude baimenduta.",
+"Your storage is full, files can not be updated or synced anymore!" => "Zure biltegiratzea beterik dago, ezingo duzu aurrerantzean fitxategirik igo edo sinkronizatu!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Zure biltegiratzea nahiko beterik dago (%{usedSpacePercent})",
+"Your download is being prepared. This might take some time if the files are big." => "Zure deskarga prestatu egin behar da. Denbora bat har lezake fitxategiak handiak badira. ",
"Unable to upload your file as it is a directory or has 0 bytes" => "Ezin da zure fitxategia igo, karpeta bat da edo 0 byt ditu",
"Upload Error" => "Igotzean errore bat suertatu da",
-"Pending" => "Zain",
+"Close" => "Itxi",
"1 file uploading" => "fitxategi 1 igotzen",
-"files uploading" => "fitxategiak igotzen",
+"{count} files uploading" => "{count} fitxategi igotzen",
"Upload cancelled." => "Igoera ezeztatuta",
"File upload is in progress. Leaving the page now will cancel the upload." => "Fitxategien igoera martxan da. Orria orain uzteak igoera ezeztatutko du.",
-"Invalid name, '/' is not allowed." => "Baliogabeko izena, '/' ezin da erabili. ",
-"files scanned" => "fitxategiak eskaneatuta",
-"error while scanning" => "errore bat egon da eskaneatzen zen bitartean",
+"URL cannot be empty." => "URLa ezin da hutsik egon.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Baliogabeako karpeta izena. 'Shared' izena Owncloudek erreserbatzen du",
"Name" => "Izena",
"Size" => "Tamaina",
"Modified" => "Aldatuta",
-"folder" => "karpeta",
-"folders" => "Karpetak",
-"file" => "fitxategia",
-"files" => "fitxategiak",
-"seconds ago" => "segundu",
-"minute ago" => "minutu",
-"minutes ago" => "minutu",
-"today" => "gaur",
-"yesterday" => "atzo",
-"days ago" => "egun",
-"last month" => "joan den hilabetean",
-"months ago" => "hilabete",
-"last year" => "joan den urtean",
-"years ago" => "urte",
+"1 folder" => "karpeta bat",
+"{count} folders" => "{count} karpeta",
+"1 file" => "fitxategi bat",
+"{count} files" => "{count} fitxategi",
+"Upload" => "Igo",
"File handling" => "Fitxategien kudeaketa",
"Maximum upload size" => "Igo daitekeen gehienezko tamaina",
"max. possible: " => "max, posiblea:",
@@ -58,14 +58,16 @@
"New" => "Berria",
"Text file" => "Testu fitxategia",
"Folder" => "Karpeta",
-"From url" => "URLtik",
-"Upload" => "Igo",
+"From link" => "Estekatik",
+"Deleted files" => "Ezabatutako fitxategiak",
"Cancel upload" => "Ezeztatu igoera",
+"You don’t have write permissions here." => "Ez duzu hemen idazteko baimenik.",
"Nothing in here. Upload something!" => "Ez dago ezer. Igo zerbait!",
-"Share" => "Elkarbanatu",
"Download" => "Deskargatu",
+"Unshare" => "Ez elkarbanatu",
"Upload too large" => "Igotakoa handiegia da",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Igotzen saiatzen ari zaren fitxategiak zerbitzari honek igotzeko onartzen duena baino handiagoak dira.",
"Files are being scanned, please wait." => "Fitxategiak eskaneatzen ari da, itxoin mezedez.",
-"Current scanning" => "Orain eskaneatzen ari da"
+"Current scanning" => "Orain eskaneatzen ari da",
+"Upgrading filesystem cache..." => "Fitxategi sistemaren katxea eguneratzen..."
);
diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php
index 979e58c5578..b9a88b57912 100644
--- a/apps/files/l10n/fa.php
+++ b/apps/files/l10n/fa.php
@@ -1,33 +1,48 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s نمی تواند حرکت کند - در حال حاضر پرونده با این نام وجود دارد. ",
+"Could not move %s" => "%s نمی تواند حرکت کند ",
+"Unable to rename file" => "قادر به تغییر نام پرونده نیست.",
+"No file was uploaded. Unknown error" => "هیچ فایلی آپلود نشد.خطای ناشناس",
"There is no error, the file uploaded with success" => "هیچ خطایی وجود ندارد فایل با موفقیت بار گذاری شد",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "حداکثر حجم تعیین شده برای بارگذاری در php.ini قابل ویرایش است",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "پرونده آپلود شده بیش ازدستور ماکزیمم_حجم فایل_برای آپلود در php.ini استفاده کرده است.",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حداکثر حجم مجاز برای بارگذاری از طریق HTML \nMAX_FILE_SIZE",
"The uploaded file was only partially uploaded" => "مقدار کمی از فایل بارگذاری شده",
"No file was uploaded" => "هیچ فایلی بارگذاری نشده",
"Missing a temporary folder" => "یک پوشه موقت گم شده است",
"Failed to write to disk" => "نوشتن بر روی دیسک سخت ناموفق بود",
+"Invalid directory." => "فهرست راهنما نامعتبر می باشد.",
"Files" => "فایل ها",
+"Delete permanently" => "حذف قطعی",
"Delete" => "پاک کردن",
-"already exists" => "وجود دارد",
+"Rename" => "تغییرنام",
+"Pending" => "در انتظار",
+"{new_name} already exists" => "{نام _جدید} در حال حاضر وجود دارد.",
"replace" => "جایگزین",
+"suggest name" => "پیشنهاد نام",
"cancel" => "لغو",
-"replaced" => "جایگزین‌شده",
+"replaced {new_name} with {old_name}" => "{نام_جدید} با { نام_قدیمی} جایگزین شد.",
"undo" => "بازگشت",
-"with" => "همراه",
-"deleted" => "حذف شده",
-"generating ZIP-file, it may take some time." => "در حال ساخت فایل فشرده ممکن است زمان زیادی به طول بیانجامد",
+"'.' is an invalid file name." => "'.' یک نام پرونده نامعتبر است.",
+"File name cannot be empty." => "نام پرونده نمی تواند خالی باشد.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "نام نامعتبر ، '\\', '/', '<', '>', ':', '\"', '|', '?' و '*' مجاز نمی باشند.",
+"Your download is being prepared. This might take some time if the files are big." => "دانلود شما در حال آماده شدن است. در صورتیکه پرونده ها بزرگ باشند ممکن است مدتی طول بکشد.",
"Unable to upload your file as it is a directory or has 0 bytes" => "ناتوان در بارگذاری یا فایل یک پوشه است یا 0بایت دارد",
"Upload Error" => "خطا در بار گذاری",
-"Pending" => "در انتظار",
+"Close" => "بستن",
+"1 file uploading" => "1 پرونده آپلود شد.",
+"{count} files uploading" => "{ شمار } فایل های در حال آپلود",
"Upload cancelled." => "بار گذاری لغو شد",
-"Invalid name, '/' is not allowed." => "نام نامناسب '/' غیرفعال است",
+"File upload is in progress. Leaving the page now will cancel the upload." => "آپلودکردن پرونده در حال پیشرفت است. در صورت خروج از صفحه آپلود لغو میگردد. ",
+"URL cannot be empty." => "URL نمی تواند خالی باشد.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "نام پوشه نامعتبر است. استفاده از \" به اشتراک گذاشته شده \" متعلق به سایت Owncloud است.",
"Name" => "نام",
"Size" => "اندازه",
"Modified" => "تغییر یافته",
-"folder" => "پوشه",
-"folders" => "پوشه ها",
-"file" => "پرونده",
-"files" => "پرونده ها",
+"1 folder" => "1 پوشه",
+"{count} folders" => "{ شمار} پوشه ها",
+"1 file" => "1 پرونده",
+"{count} files" => "{ شمار } فایل ها",
+"Upload" => "بارگذاری",
"File handling" => "اداره پرونده ها",
"Maximum upload size" => "حداکثر اندازه بارگزاری",
"max. possible: " => "حداکثرمقدارممکن:",
@@ -35,15 +50,16 @@
"Enable ZIP-download" => "فعال سازی بارگیری پرونده های فشرده",
"0 is unlimited" => "0 نامحدود است",
"Maximum input size for ZIP files" => "حداکثرمقدار برای بار گزاری پرونده های فشرده",
+"Save" => "ذخیره",
"New" => "جدید",
"Text file" => "فایل متنی",
"Folder" => "پوشه",
-"From url" => "از نشانی",
-"Upload" => "بارگذاری",
+"From link" => "از پیوند",
+"Deleted files" => "فایل های حذف شده",
"Cancel upload" => "متوقف کردن بار گذاری",
"Nothing in here. Upload something!" => "اینجا هیچ چیز نیست.",
-"Share" => "به اشتراک گذاری",
"Download" => "بارگیری",
+"Unshare" => "لغو اشتراک",
"Upload too large" => "حجم بارگذاری بسیار زیاد است",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "فایلها بیش از حد تعیین شده در این سرور هستند\nمترجم:با تغییر فایل php,ini میتوان این محدودیت را برطرف کرد",
"Files are being scanned, please wait." => "پرونده ها در حال بازرسی هستند لطفا صبر کنید",
diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php
index 8e874ad4d95..6eb891d29d3 100644
--- a/apps/files/l10n/fi_FI.php
+++ b/apps/files/l10n/fi_FI.php
@@ -1,46 +1,47 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Kohteen %s siirto ei onnistunut - Tiedosto samalla nimellä on jo olemassa",
+"Could not move %s" => "Kohteen %s siirto ei onnistunut",
+"Unable to rename file" => "Tiedoston nimeäminen uudelleen ei onnistunut",
+"No file was uploaded. Unknown error" => "Tiedostoa ei lähetetty. Tuntematon virhe",
"There is no error, the file uploaded with success" => "Ei virheitä, tiedosto lähetettiin onnistuneesti",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Lähetetty tiedosto ylittää upload_max_filesize-arvon rajan php.ini-tiedostossa",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lähetetty tiedosto ylittää HTML-lomakkeessa määritetyn MAX_FILE_SIZE-arvon ylärajan",
"The uploaded file was only partially uploaded" => "Tiedoston lähetys onnistui vain osittain",
"No file was uploaded" => "Yhtäkään tiedostoa ei lähetetty",
"Missing a temporary folder" => "Väliaikaiskansiota ei ole olemassa",
"Failed to write to disk" => "Levylle kirjoitus epäonnistui",
+"Not enough storage available" => "Tallennustilaa ei ole riittävästi käytettävissä",
+"Invalid directory." => "Virheellinen kansio.",
"Files" => "Tiedostot",
+"Delete permanently" => "Poista pysyvästi",
"Delete" => "Poista",
"Rename" => "Nimeä uudelleen",
-"already exists" => "on jo olemassa",
+"Pending" => "Odottaa",
+"{new_name} already exists" => "{new_name} on jo olemassa",
"replace" => "korvaa",
"suggest name" => "ehdota nimeä",
"cancel" => "peru",
-"replaced" => "korvattu",
"undo" => "kumoa",
-"with" => "käyttäen",
-"deleted" => "poistettu",
-"generating ZIP-file, it may take some time." => "luodaan ZIP-tiedostoa, tämä saattaa kestää hetken.",
+"perform delete operation" => "suorita poistotoiminto",
+"'.' is an invalid file name." => "'.' on virheellinen nimi tiedostolle.",
+"File name cannot be empty." => "Tiedoston nimi ei voi olla tyhjä.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Virheellinen nimi, merkit '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' eivät ole sallittuja.",
+"Your storage is full, files can not be updated or synced anymore!" => "Tallennustila on loppu, tiedostoja ei voi enää päivittää tai synkronoida!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Tallennustila on melkein loppu ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Lataustasi valmistellaan. Tämä saattaa kestää hetken, jos tiedostot ovat suuria kooltaan.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio",
"Upload Error" => "Lähetysvirhe.",
-"Pending" => "Odottaa",
+"Close" => "Sulje",
"Upload cancelled." => "Lähetys peruttu.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Tiedoston lähetys on meneillään. Sivulta poistuminen nyt peruu tiedoston lähetyksen.",
-"Invalid name, '/' is not allowed." => "Virheellinen nimi, merkki '/' ei ole sallittu.",
+"URL cannot be empty." => "Verkko-osoite ei voi olla tyhjä",
"Name" => "Nimi",
"Size" => "Koko",
"Modified" => "Muutettu",
-"folder" => "kansio",
-"folders" => "kansiota",
-"file" => "tiedosto",
-"files" => "tiedostoa",
-"seconds ago" => "sekuntia sitten",
-"minute ago" => "minuutti sitten",
-"minutes ago" => "minuuttia sitten",
-"today" => "tänään",
-"yesterday" => "eilen",
-"days ago" => "päivää sitten",
-"last month" => "viime kuussa",
-"months ago" => "kuukautta sitten",
-"last year" => "viime vuonna",
-"years ago" => "vuotta sitten",
+"1 folder" => "1 kansio",
+"{count} folders" => "{count} kansiota",
+"1 file" => "1 tiedosto",
+"{count} files" => "{count} tiedostoa",
+"Upload" => "Lähetä",
"File handling" => "Tiedostonhallinta",
"Maximum upload size" => "Lähetettävän tiedoston suurin sallittu koko",
"max. possible: " => "suurin mahdollinen:",
@@ -52,14 +53,16 @@
"New" => "Uusi",
"Text file" => "Tekstitiedosto",
"Folder" => "Kansio",
-"From url" => "Verkko-osoitteesta",
-"Upload" => "Lähetä",
+"From link" => "Linkistä",
+"Deleted files" => "Poistetut tiedostot",
"Cancel upload" => "Peru lähetys",
+"You don’t have write permissions here." => "Tunnuksellasi ei ole kirjoitusoikeuksia tänne.",
"Nothing in here. Upload something!" => "Täällä ei ole mitään. Lähetä tänne jotakin!",
-"Share" => "Jaa",
"Download" => "Lataa",
+"Unshare" => "Peru jakaminen",
"Upload too large" => "Lähetettävä tiedosto on liian suuri",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Lähetettäväksi valitsemasi tiedostot ylittävät palvelimen salliman tiedostokoon rajan.",
"Files are being scanned, please wait." => "Tiedostoja tarkistetaan, odota hetki.",
-"Current scanning" => "Tämänhetkinen tutkinta"
+"Current scanning" => "Tämänhetkinen tutkinta",
+"Upgrading filesystem cache..." => "Päivitetään tiedostojärjestelmän välimuistia..."
);
diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php
index f58bfa5cc5e..5e53f5ab024 100644
--- a/apps/files/l10n/fr.php
+++ b/apps/files/l10n/fr.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Impossible de déplacer %s - Un fichier possédant ce nom existe déjà",
+"Could not move %s" => "Impossible de déplacer %s",
+"Unable to rename file" => "Impossible de renommer le fichier",
+"No file was uploaded. Unknown error" => "Aucun fichier n'a été chargé. Erreur inconnue",
"There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été téléversé avec succès",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Le fichier téléversé excède la valeur de upload_max_filesize spécifiée dans php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse la valeur upload_max_filesize située dans le fichier php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Le fichier téléversé excède la valeur de MAX_FILE_SIZE spécifiée dans le formulaire HTML",
"The uploaded file was only partially uploaded" => "Le fichier n'a été que partiellement téléversé",
"No file was uploaded" => "Aucun fichier n'a été téléversé",
"Missing a temporary folder" => "Il manque un répertoire temporaire",
"Failed to write to disk" => "Erreur d'écriture sur le disque",
+"Not enough storage available" => "Plus assez d'espace de stockage disponible",
+"Invalid directory." => "Dossier invalide.",
"Files" => "Fichiers",
-"Unshare" => "Ne plus partager",
+"Delete permanently" => "Supprimer de façon définitive",
"Delete" => "Supprimer",
"Rename" => "Renommer",
-"already exists" => "existe déjà",
+"Pending" => "En cours",
+"{new_name} already exists" => "{new_name} existe déjà",
"replace" => "remplacer",
"suggest name" => "Suggérer un nom",
"cancel" => "annuler",
-"replaced" => "remplacé",
+"replaced {new_name} with {old_name}" => "{new_name} a été remplacé par {old_name}",
"undo" => "annuler",
-"with" => "avec",
-"unshared" => "non partagée",
-"deleted" => "supprimé",
-"generating ZIP-file, it may take some time." => "Fichier ZIP en cours d'assemblage ; cela peut prendre du temps.",
+"perform delete operation" => "effectuer l'opération de suppression",
+"'.' is an invalid file name." => "'.' n'est pas un nom de fichier valide.",
+"File name cannot be empty." => "Le nom de fichier ne peut être vide.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nom invalide, les caractères '\\', '/', '<', '>', ':', '\"', '|', '?' et '*' ne sont pas autorisés.",
+"Your storage is full, files can not be updated or synced anymore!" => "Votre espage de stockage est plein, les fichiers ne peuvent plus être téléversés ou synchronisés !",
+"Your storage is almost full ({usedSpacePercent}%)" => "Votre espace de stockage est presque plein ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Votre téléchargement est cours de préparation. Ceci peut nécessiter un certain temps si les fichiers sont volumineux.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible de charger vos fichiers car il s'agit d'un dossier ou le fichier fait 0 octet.",
"Upload Error" => "Erreur de chargement",
-"Pending" => "En cours",
+"Close" => "Fermer",
"1 file uploading" => "1 fichier en cours de téléchargement",
-"files uploading" => "fichiers en cours de téléchargement",
+"{count} files uploading" => "{count} fichiers téléversés",
"Upload cancelled." => "Chargement annulé.",
"File upload is in progress. Leaving the page now will cancel the upload." => "L'envoi du fichier est en cours. Quitter cette page maintenant annulera l'envoi du fichier.",
-"Invalid name, '/' is not allowed." => "Nom invalide, '/' n'est pas autorisé.",
-"files scanned" => "fichiers indexés",
-"error while scanning" => "erreur lors de l'indexation",
+"URL cannot be empty." => "L'URL ne peut-être vide",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée à Owncloud",
"Name" => "Nom",
"Size" => "Taille",
"Modified" => "Modifié",
-"folder" => "dossier",
-"folders" => "dossiers",
-"file" => "fichier",
-"files" => "fichiers",
-"seconds ago" => "secondes passées",
-"minute ago" => "minute passée",
-"minutes ago" => "minutes passées",
-"today" => "aujourd'hui",
-"yesterday" => "hier",
-"days ago" => "jours passés",
-"last month" => "mois dernier",
-"months ago" => "mois passés",
-"last year" => "année dernière",
-"years ago" => "années passées",
+"1 folder" => "1 dossier",
+"{count} folders" => "{count} dossiers",
+"1 file" => "1 fichier",
+"{count} files" => "{count} fichiers",
+"Upload" => "Envoyer",
"File handling" => "Gestion des fichiers",
"Maximum upload size" => "Taille max. d'envoi",
"max. possible: " => "Max. possible :",
@@ -58,14 +58,16 @@
"New" => "Nouveau",
"Text file" => "Fichier texte",
"Folder" => "Dossier",
-"From url" => "Depuis URL",
-"Upload" => "Envoyer",
+"From link" => "Depuis le lien",
+"Deleted files" => "Fichiers supprimés",
"Cancel upload" => "Annuler l'envoi",
+"You don’t have write permissions here." => "Vous n'avez pas le droit d'écriture ici.",
"Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)",
-"Share" => "Partager",
-"Download" => "Téléchargement",
+"Download" => "Télécharger",
+"Unshare" => "Ne plus partager",
"Upload too large" => "Fichier trop volumineux",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.",
"Files are being scanned, please wait." => "Les fichiers sont en cours d'analyse, veuillez patienter.",
-"Current scanning" => "Analyse en cours"
+"Current scanning" => "Analyse en cours",
+"Upgrading filesystem cache..." => "Mise à niveau du cache du système de fichier"
);
diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php
index 77e87ee282c..d48839d0b60 100644
--- a/apps/files/l10n/gl.php
+++ b/apps/files/l10n/gl.php
@@ -1,58 +1,73 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Non hai erros, o ficheiro enviouse correctamente",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O ficheiro enviado supera a directiva upload_max_filesize no php.ini",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado supera a directiva MAX_FILE_SIZE que foi indicada no formulario HTML",
+"Could not move %s - File with this name already exists" => "Non se moveu %s - Xa existe un ficheiro con ese nome.",
+"Could not move %s" => "Non foi posíbel mover %s",
+"Unable to rename file" => "Non é posíbel renomear o ficheiro",
+"No file was uploaded. Unknown error" => "Non foi enviado ningún ficheiro. Produciuse un erro descoñecido.",
+"There is no error, the file uploaded with success" => "Non se produciu ningún erro. O ficheiro enviouse correctamente",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede a directiva indicada por upload_max_filesize de php.ini:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede a directiva MAX_FILE_SIZE que foi indicada no formulario HTML",
"The uploaded file was only partially uploaded" => "O ficheiro enviado foi só parcialmente enviado",
"No file was uploaded" => "Non se enviou ningún ficheiro",
"Missing a temporary folder" => "Falta un cartafol temporal",
-"Failed to write to disk" => "Erro ao escribir no disco",
+"Failed to write to disk" => "Produciuse un erro ao escribir no disco",
+"Not enough storage available" => "Non hai espazo de almacenamento abondo",
+"Invalid directory." => "O directorio é incorrecto.",
"Files" => "Ficheiros",
-"Unshare" => "Deixar de compartir",
+"Delete permanently" => "Eliminar permanentemente",
"Delete" => "Eliminar",
-"already exists" => "xa existe",
+"Rename" => "Renomear",
+"Pending" => "Pendentes",
+"{new_name} already exists" => "Xa existe un {new_name}",
"replace" => "substituír",
-"suggest name" => "suxira nome",
+"suggest name" => "suxerir nome",
"cancel" => "cancelar",
-"replaced" => "substituído",
+"replaced {new_name} with {old_name}" => "substituír {new_name} por {old_name}",
"undo" => "desfacer",
-"with" => "con",
-"unshared" => "non compartido",
-"deleted" => "eliminado",
-"generating ZIP-file, it may take some time." => "xerando ficheiro ZIP, pode levar un anaco.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Non se puido subir o ficheiro pois ou é un directorio ou ten 0 bytes",
-"Upload Error" => "Erro na subida",
-"Pending" => "Pendentes",
-"Upload cancelled." => "Subida cancelada.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "A subida do ficheiro está en curso. Saír agora da páxina cancelará a subida.",
-"Invalid name, '/' is not allowed." => "Nome non válido, '/' non está permitido.",
-"files scanned" => "ficheiros analizados",
-"error while scanning" => "erro mentras analizaba",
+"perform delete operation" => "realizar a operación de eliminación",
+"'.' is an invalid file name." => "«.» é un nome de ficheiro incorrecto",
+"File name cannot be empty." => "O nome de ficheiro non pode estar baleiro",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome incorrecto, non se permite «\\», «/», «<», «>», «:», «\"», «|», «?» e «*».",
+"Your storage is full, files can not be updated or synced anymore!" => "O seu espazo de almacenamento está cheo, non é posíbel actualizar ou sincronizar máis os ficheiros!",
+"Your storage is almost full ({usedSpacePercent}%)" => "O seu espazo de almacenamento está case cheo ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Está a prepararse a súa descarga. Isto pode levar bastante tempo se os ficheiros son grandes.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Non foi posíbel enviar o ficheiro pois ou é un directorio ou ten 0 bytes",
+"Upload Error" => "Produciuse un erro no envío",
+"Close" => "Pechar",
+"1 file uploading" => "Enviándose 1 ficheiro",
+"{count} files uploading" => "Enviandose {count} ficheiros",
+"Upload cancelled." => "Envío cancelado.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "O envío do ficheiro está en proceso. Saír agora da páxina cancelará o envío.",
+"URL cannot be empty." => "O URL non pode quedar baleiro.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de cartafol incorrecto. O uso de «Shared» está reservado por Owncloud",
"Name" => "Nome",
"Size" => "Tamaño",
"Modified" => "Modificado",
-"folder" => "cartafol",
-"folders" => "cartafoles",
-"file" => "ficheiro",
-"files" => "ficheiros",
+"1 folder" => "1 cartafol",
+"{count} folders" => "{count} cartafoles",
+"1 file" => "1 ficheiro",
+"{count} files" => "{count} ficheiros",
+"Upload" => "Enviar",
"File handling" => "Manexo de ficheiro",
-"Maximum upload size" => "Tamaño máximo de envío",
-"max. possible: " => "máx. posible: ",
-"Needed for multi-file and folder downloads." => "Preciso para descarga de varios ficheiros e cartafoles.",
+"Maximum upload size" => "Tamaño máximo do envío",
+"max. possible: " => "máx. posíbel: ",
+"Needed for multi-file and folder downloads." => "Precísase para a descarga de varios ficheiros e cartafoles.",
"Enable ZIP-download" => "Habilitar a descarga-ZIP",
"0 is unlimited" => "0 significa ilimitado",
-"Maximum input size for ZIP files" => "Tamaño máximo de descarga para os ZIP",
+"Maximum input size for ZIP files" => "Tamaño máximo de descarga para os ficheiros ZIP",
"Save" => "Gardar",
"New" => "Novo",
"Text file" => "Ficheiro de texto",
"Folder" => "Cartafol",
-"From url" => "Desde url",
-"Upload" => "Enviar",
-"Cancel upload" => "Cancelar subida",
-"Nothing in here. Upload something!" => "Nada por aquí. Envíe algo.",
-"Share" => "Compartir",
+"From link" => "Desde a ligazón",
+"Deleted files" => "Ficheiros eliminados",
+"Cancel upload" => "Cancelar o envío",
+"You don’t have write permissions here." => "Non ten permisos para escribir aquí.",
+"Nothing in here. Upload something!" => "Aquí non hai nada. Envíe algo.",
"Download" => "Descargar",
+"Unshare" => "Deixar de compartir",
"Upload too large" => "Envío demasiado grande",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que trata de subir superan o tamaño máximo permitido neste servidor",
-"Files are being scanned, please wait." => "Estanse analizando os ficheiros, espere por favor.",
-"Current scanning" => "Análise actual."
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que tenta enviar exceden do tamaño máximo permitido neste servidor",
+"Files are being scanned, please wait." => "Estanse analizando os ficheiros. Agarde.",
+"Current scanning" => "Análise actual",
+"Upgrading filesystem cache..." => "Anovando a caché do sistema de ficheiros..."
);
diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php
index 84c669cba31..9d6b411c415 100644
--- a/apps/files/l10n/he.php
+++ b/apps/files/l10n/he.php
@@ -1,27 +1,40 @@
<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "לא הועלה קובץ. טעות בלתי מזוהה.",
"There is no error, the file uploaded with success" => "לא אירעה תקלה, הקבצים הועלו בהצלחה",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "הקובץ שהועלה חרג מההנחיה upload_max_filesize בקובץ php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "הקבצים שנשלחו חורגים מהגודל שצוין בהגדרה upload_max_filesize שבקובץ php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "הקובץ שהועלה חרג מההנחיה MAX_FILE_SIZE שצוינה בטופס ה־HTML",
"The uploaded file was only partially uploaded" => "הקובץ שהועלה הועלה בצורה חלקית",
"No file was uploaded" => "לא הועלו קבצים",
"Missing a temporary folder" => "תיקייה זמנית חסרה",
"Failed to write to disk" => "הכתיבה לכונן נכשלה",
"Files" => "קבצים",
+"Delete permanently" => "מחק לצמיתות",
"Delete" => "מחיקה",
-"already exists" => "כבר קיים",
-"generating ZIP-file, it may take some time." => "יוצר קובץ ZIP, אנא המתן.",
+"Rename" => "שינוי שם",
+"Pending" => "ממתין",
+"{new_name} already exists" => "{new_name} כבר קיים",
+"replace" => "החלפה",
+"suggest name" => "הצעת שם",
+"cancel" => "ביטול",
+"replaced {new_name} with {old_name}" => "{new_name} הוחלף ב־{old_name}",
+"undo" => "ביטול",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "השם שגוי, אסור להשתמש בתווים '\\', '/', '<', '>', ':', '\"', '|', '?' ו־'*'.",
"Unable to upload your file as it is a directory or has 0 bytes" => "לא יכול להעלות את הקובץ מכיוון שזו תקיה או שמשקל הקובץ 0 בתים",
"Upload Error" => "שגיאת העלאה",
-"Pending" => "ממתין",
+"Close" => "סגירה",
+"1 file uploading" => "קובץ אחד נשלח",
+"{count} files uploading" => "{count} קבצים נשלחים",
"Upload cancelled." => "ההעלאה בוטלה.",
-"Invalid name, '/' is not allowed." => "שם לא חוקי, '/' אסור לשימוש.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "מתבצעת כעת העלאת קבצים. עזיבה של העמוד תבטל את ההעלאה.",
+"URL cannot be empty." => "קישור אינו יכול להיות ריק.",
"Name" => "שם",
"Size" => "גודל",
"Modified" => "זמן שינוי",
-"folder" => "תקיה",
-"folders" => "תקיות",
-"file" => "קובץ",
-"files" => "קבצים",
+"1 folder" => "תיקייה אחת",
+"{count} folders" => "{count} תיקיות",
+"1 file" => "קובץ אחד",
+"{count} files" => "{count} קבצים",
+"Upload" => "העלאה",
"File handling" => "טיפול בקבצים",
"Maximum upload size" => "גודל העלאה מקסימלי",
"max. possible: " => "המרבי האפשרי: ",
@@ -29,15 +42,15 @@
"Enable ZIP-download" => "הפעלת הורדת ZIP",
"0 is unlimited" => "0 - ללא הגבלה",
"Maximum input size for ZIP files" => "גודל הקלט המרבי לקובצי ZIP",
+"Save" => "שמירה",
"New" => "חדש",
"Text file" => "קובץ טקסט",
"Folder" => "תיקייה",
-"From url" => "מכתובת",
-"Upload" => "העלאה",
+"From link" => "מקישור",
"Cancel upload" => "ביטול ההעלאה",
"Nothing in here. Upload something!" => "אין כאן שום דבר. אולי ברצונך להעלות משהו?",
-"Share" => "שיתוף",
"Download" => "הורדה",
+"Unshare" => "הסר שיתוף",
"Upload too large" => "העלאה גדולה מידי",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "הקבצים שניסית להעלות חרגו מהגודל המקסימלי להעלאת קבצים על שרת זה.",
"Files are being scanned, please wait." => "הקבצים נסרקים, נא להמתין.",
diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php
index 412c97630d4..3516ab8c1e6 100644
--- a/apps/files/l10n/hr.php
+++ b/apps/files/l10n/hr.php
@@ -1,52 +1,28 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Datoteka je poslana uspješno i bez pogrešaka",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Poslana datoteka izlazi iz okvira upload_max_size direktive postavljene u php.ini konfiguracijskoj datoteci",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka izlazi iz okvira MAX_FILE_SIZE direktive postavljene u HTML obrascu",
"The uploaded file was only partially uploaded" => "Datoteka je poslana samo djelomično",
"No file was uploaded" => "Ni jedna datoteka nije poslana",
"Missing a temporary folder" => "Nedostaje privremena mapa",
"Failed to write to disk" => "Neuspjelo pisanje na disk",
"Files" => "Datoteke",
-"Unshare" => "Prekini djeljenje",
"Delete" => "Briši",
"Rename" => "Promjeni ime",
-"already exists" => "već postoji",
+"Pending" => "U tijeku",
"replace" => "zamjeni",
"suggest name" => "predloži ime",
"cancel" => "odustani",
-"replaced" => "zamjenjeno",
"undo" => "vrati",
-"with" => "sa",
-"unshared" => "maknuto djeljenje",
-"deleted" => "izbrisano",
-"generating ZIP-file, it may take some time." => "generiranje ZIP datoteke, ovo može potrajati.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij",
"Upload Error" => "Pogreška pri slanju",
-"Pending" => "U tijeku",
+"Close" => "Zatvori",
"1 file uploading" => "1 datoteka se učitava",
-"files uploading" => "datoteke se učitavaju",
"Upload cancelled." => "Slanje poništeno.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Učitavanje datoteke. Napuštanjem stranice će prekinuti učitavanje.",
-"Invalid name, '/' is not allowed." => "Neispravan naziv, znak '/' nije dozvoljen.",
-"files scanned" => "datoteka skenirana",
-"error while scanning" => "grečka prilikom skeniranja",
"Name" => "Naziv",
"Size" => "Veličina",
"Modified" => "Zadnja promjena",
-"folder" => "mapa",
-"folders" => "mape",
-"file" => "datoteka",
-"files" => "datoteke",
-"seconds ago" => "sekundi prije",
-"minute ago" => "minutu",
-"minutes ago" => "minuta",
-"today" => "danas",
-"yesterday" => "jučer",
-"days ago" => "dana",
-"last month" => "prošli mjesec",
-"months ago" => "mjeseci",
-"last year" => "prošlu godinu",
-"years ago" => "godina",
+"Upload" => "Pošalji",
"File handling" => "datoteka za rukovanje",
"Maximum upload size" => "Maksimalna veličina prijenosa",
"max. possible: " => "maksimalna moguća: ",
@@ -58,12 +34,10 @@
"New" => "novo",
"Text file" => "tekstualna datoteka",
"Folder" => "mapa",
-"From url" => "od URL-a",
-"Upload" => "Pošalji",
"Cancel upload" => "Prekini upload",
"Nothing in here. Upload something!" => "Nema ničega u ovoj mapi. Pošalji nešto!",
-"Share" => "podjeli",
"Download" => "Preuzmi",
+"Unshare" => "Prekini djeljenje",
"Upload too large" => "Prijenos je preobiman",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke koje pokušavate prenijeti prelaze maksimalnu veličinu za prijenos datoteka na ovom poslužitelju.",
"Files are being scanned, please wait." => "Datoteke se skeniraju, molimo pričekajte.",
diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php
index 0d44e6e157a..54d5033f907 100644
--- a/apps/files/l10n/hu_HU.php
+++ b/apps/files/l10n/hu_HU.php
@@ -1,51 +1,73 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Nincs hiba, a fájl sikeresen feltöltve.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "A feltöltött file meghaladja az upload_max_filesize direktívát a php.ini-ben.",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "A feltöltött fájl meghaladja a MAX_FILE_SIZE direktívát ami meghatározott a HTML form-ban.",
-"The uploaded file was only partially uploaded" => "Az eredeti fájl csak részlegesen van feltöltve.",
-"No file was uploaded" => "Nem lett fájl feltöltve.",
-"Missing a temporary folder" => "Hiányzik az ideiglenes könyvtár",
-"Failed to write to disk" => "Nem írható lemezre",
+"Could not move %s - File with this name already exists" => "%s áthelyezése nem sikerült - már létezik másik fájl ezzel a névvel",
+"Could not move %s" => "Nem sikerült %s áthelyezése",
+"Unable to rename file" => "Nem lehet átnevezni a fájlt",
+"No file was uploaded. Unknown error" => "Nem történt feltöltés. Ismeretlen hiba",
+"There is no error, the file uploaded with success" => "A fájlt sikerült feltölteni",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "A feltöltött fájl mérete meghaladja a php.ini állományban megadott upload_max_filesize paraméter értékét.",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "A feltöltött fájl mérete meghaladja a MAX_FILE_SIZE paramétert, ami a HTML formban került megadásra.",
+"The uploaded file was only partially uploaded" => "Az eredeti fájlt csak részben sikerült feltölteni.",
+"No file was uploaded" => "Nem töltődött fel semmi",
+"Missing a temporary folder" => "Hiányzik egy ideiglenes mappa",
+"Failed to write to disk" => "Nem sikerült a lemezre történő írás",
+"Not enough storage available" => "Nincs elég szabad hely.",
+"Invalid directory." => "Érvénytelen mappa.",
"Files" => "Fájlok",
+"Delete permanently" => "Végleges törlés",
"Delete" => "Törlés",
-"already exists" => "már létezik",
-"replace" => "cserél",
+"Rename" => "Átnevezés",
+"Pending" => "Folyamatban",
+"{new_name} already exists" => "{new_name} már létezik",
+"replace" => "írjuk fölül",
+"suggest name" => "legyen más neve",
"cancel" => "mégse",
-"replaced" => "kicserélve",
-"undo" => "visszavon",
-"with" => "-val/-vel",
-"deleted" => "törölve",
-"generating ZIP-file, it may take some time." => "ZIP-fájl generálása, ez eltarthat egy ideig.",
+"replaced {new_name} with {old_name}" => "{new_name} fájlt kicseréltük ezzel: {old_name}",
+"undo" => "visszavonás",
+"perform delete operation" => "a törlés végrehajtása",
+"'.' is an invalid file name." => "'.' fájlnév érvénytelen.",
+"File name cannot be empty." => "A fájlnév nem lehet semmi.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Érvénytelen elnevezés. Ezek a karakterek nem használhatók: '\\', '/', '<', '>', ':', '\"', '|', '?' és '*'",
+"Your storage is full, files can not be updated or synced anymore!" => "A tároló tele van, a fájlok nem frissíthetőek vagy szinkronizálhatóak a jövőben.",
+"Your storage is almost full ({usedSpacePercent}%)" => "A tároló majdnem tele van ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Készül a letöltendő állomány. Ez eltarthat egy ideig, ha nagyok a fájlok.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nem tölthető fel, mert mappa volt, vagy 0 byte méretű",
"Upload Error" => "Feltöltési hiba",
-"Pending" => "Folyamatban",
-"Upload cancelled." => "Feltöltés megszakítva",
-"Invalid name, '/' is not allowed." => "Érvénytelen név, a '/' nem megengedett",
+"Close" => "Bezárás",
+"1 file uploading" => "1 fájl töltődik föl",
+"{count} files uploading" => "{count} fájl töltődik föl",
+"Upload cancelled." => "A feltöltést megszakítottuk.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Fájlfeltöltés van folyamatban. Az oldal elhagyása megszakítja a feltöltést.",
+"URL cannot be empty." => "Az URL nem lehet semmi.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Érvénytelen mappanév. A név használata csak a Owncloud számára lehetséges.",
"Name" => "Név",
"Size" => "Méret",
"Modified" => "Módosítva",
-"folder" => "mappa",
-"folders" => "mappák",
-"file" => "fájl",
-"files" => "fájlok",
+"1 folder" => "1 mappa",
+"{count} folders" => "{count} mappa",
+"1 file" => "1 fájl",
+"{count} files" => "{count} fájl",
+"Upload" => "Feltöltés",
"File handling" => "Fájlkezelés",
"Maximum upload size" => "Maximális feltölthető fájlméret",
-"max. possible: " => "max. lehetséges",
-"Needed for multi-file and folder downloads." => "Kötegelt file- vagy mappaletöltéshez szükséges",
-"Enable ZIP-download" => "ZIP-letöltés engedélyezése",
+"max. possible: " => "max. lehetséges: ",
+"Needed for multi-file and folder downloads." => "Kötegelt fájl- vagy mappaletöltéshez szükséges",
+"Enable ZIP-download" => "A ZIP-letöltés engedélyezése",
"0 is unlimited" => "0 = korlátlan",
-"Maximum input size for ZIP files" => "ZIP file-ok maximum mérete",
+"Maximum input size for ZIP files" => "ZIP-fájlok maximális kiindulási mérete",
+"Save" => "Mentés",
"New" => "Új",
"Text file" => "Szövegfájl",
"Folder" => "Mappa",
-"From url" => "URL-ből",
-"Upload" => "Feltöltés",
-"Cancel upload" => "Feltöltés megszakítása",
-"Nothing in here. Upload something!" => "Töltsön fel egy fájlt.",
-"Share" => "Megosztás",
+"From link" => "Feltöltés linkről",
+"Deleted files" => "Törölt fájlok",
+"Cancel upload" => "A feltöltés megszakítása",
+"You don’t have write permissions here." => "Itt nincs írásjoga.",
+"Nothing in here. Upload something!" => "Itt nincs semmi. Töltsön fel valamit!",
"Download" => "Letöltés",
-"Upload too large" => "Feltöltés túl nagy",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "A fájlokat amit próbálsz feltölteni meghaladta a legnagyobb fájlméretet ezen a szerveren.",
-"Files are being scanned, please wait." => "File-ok vizsgálata, kis türelmet",
-"Current scanning" => "Aktuális vizsgálat"
+"Unshare" => "Megosztás visszavonása",
+"Upload too large" => "A feltöltés túl nagy",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "A feltöltendő állományok mérete meghaladja a kiszolgálón megengedett maximális méretet.",
+"Files are being scanned, please wait." => "A fájllista ellenőrzése zajlik, kis türelmet!",
+"Current scanning" => "Ellenőrzés alatt",
+"Upgrading filesystem cache..." => "A fájlrendszer gyorsítótárának frissítése zajlik..."
);
diff --git a/apps/files/l10n/hy.php b/apps/files/l10n/hy.php
new file mode 100644
index 00000000000..29c0cd8b8d0
--- /dev/null
+++ b/apps/files/l10n/hy.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"Delete" => "Ջնջել",
+"Close" => "Փակել",
+"Save" => "Պահպանել",
+"Download" => "Բեռնել"
+);
diff --git a/apps/files/l10n/ia.php b/apps/files/l10n/ia.php
index 21a0bb52374..ae614c1bf5d 100644
--- a/apps/files/l10n/ia.php
+++ b/apps/files/l10n/ia.php
@@ -1,16 +1,19 @@
<?php $TRANSLATIONS = array(
"The uploaded file was only partially uploaded" => "Le file incargate solmente esseva incargate partialmente",
"No file was uploaded" => "Nulle file esseva incargate",
+"Missing a temporary folder" => "Manca un dossier temporari",
"Files" => "Files",
"Delete" => "Deler",
+"Close" => "Clauder",
"Name" => "Nomine",
"Size" => "Dimension",
"Modified" => "Modificate",
+"Upload" => "Incargar",
"Maximum upload size" => "Dimension maxime de incargamento",
+"Save" => "Salveguardar",
"New" => "Nove",
"Text file" => "File de texto",
"Folder" => "Dossier",
-"Upload" => "Incargar",
"Nothing in here. Upload something!" => "Nihil hic. Incarga alcun cosa!",
"Download" => "Discargar",
"Upload too large" => "Incargamento troppo longe"
diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php
index 07bccdc597d..aff1933e569 100644
--- a/apps/files/l10n/id.php
+++ b/apps/files/l10n/id.php
@@ -1,6 +1,5 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Tidak ada galat, berkas sukses diunggah",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "File yang diunggah melampaui directive upload_max_filesize di php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "File yang diunggah melampaui directive MAX_FILE_SIZE yang disebutan dalam form HTML.",
"The uploaded file was only partially uploaded" => "Berkas hanya diunggah sebagian",
"No file was uploaded" => "Tidak ada berkas yang diunggah",
@@ -8,26 +7,23 @@
"Failed to write to disk" => "Gagal menulis ke disk",
"Files" => "Berkas",
"Delete" => "Hapus",
-"already exists" => "sudah ada",
+"Pending" => "Menunggu",
"replace" => "mengganti",
"cancel" => "batalkan",
-"replaced" => "diganti",
"undo" => "batal dikerjakan",
-"with" => "dengan",
-"deleted" => "dihapus",
-"generating ZIP-file, it may take some time." => "membuat berkas ZIP, ini mungkin memakan waktu.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas anda karena berupa direktori atau mempunyai ukuran 0 byte",
"Upload Error" => "Terjadi Galat Pengunggahan",
-"Pending" => "Menunggu",
+"Close" => "tutup",
"Upload cancelled." => "Pengunggahan dibatalkan.",
-"Invalid name, '/' is not allowed." => "Kesalahan nama, '/' tidak diijinkan.",
+"URL cannot be empty." => "tautan tidak boleh kosong",
"Name" => "Nama",
"Size" => "Ukuran",
"Modified" => "Dimodifikasi",
-"folder" => "folder",
-"folders" => "folder-folder",
-"file" => "berkas",
-"files" => "berkas-berkas",
+"1 folder" => "1 map",
+"{count} folders" => "{count} map",
+"1 file" => "1 berkas",
+"{count} files" => "{count} berkas",
+"Upload" => "Unggah",
"File handling" => "Penanganan berkas",
"Maximum upload size" => "Ukuran unggah maksimum",
"max. possible: " => "Kemungkinan maks:",
@@ -35,15 +31,14 @@
"Enable ZIP-download" => "Aktifkan unduhan ZIP",
"0 is unlimited" => "0 adalah tidak terbatas",
"Maximum input size for ZIP files" => "Ukuran masukan maksimal untuk berkas ZIP",
+"Save" => "simpan",
"New" => "Baru",
"Text file" => "Berkas teks",
"Folder" => "Folder",
-"From url" => "Dari url",
-"Upload" => "Unggah",
"Cancel upload" => "Batal mengunggah",
"Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!",
-"Share" => "Bagikan",
"Download" => "Unduh",
+"Unshare" => "batalkan berbagi",
"Upload too large" => "Unggahan terlalu besar",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan berkas di server ini.",
"Files are being scanned, please wait." => "Berkas sedang dipindai, silahkan tunggu.",
diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php
new file mode 100644
index 00000000000..9d735c3c541
--- /dev/null
+++ b/apps/files/l10n/is.php
@@ -0,0 +1,64 @@
+<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Gat ekki fært %s - Skrá með þessu nafni er þegar til",
+"Could not move %s" => "Gat ekki fært %s",
+"Unable to rename file" => "Gat ekki endurskýrt skrá",
+"No file was uploaded. Unknown error" => "Engin skrá var send inn. Óþekkt villa.",
+"There is no error, the file uploaded with success" => "Engin villa, innsending heppnaðist",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Innsend skrá er stærri en upload_max stillingin í php.ini:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Innsenda skráin er stærri en MAX_FILE_SIZE sem skilgreint er í HTML sniðinu.",
+"The uploaded file was only partially uploaded" => "Einungis hluti af innsendri skrá skilaði sér",
+"No file was uploaded" => "Engin skrá skilaði sér",
+"Missing a temporary folder" => "Vantar bráðabirgðamöppu",
+"Failed to write to disk" => "Tókst ekki að skrifa á disk",
+"Invalid directory." => "Ógild mappa.",
+"Files" => "Skrár",
+"Delete" => "Eyða",
+"Rename" => "Endurskýra",
+"Pending" => "Bíður",
+"{new_name} already exists" => "{new_name} er þegar til",
+"replace" => "yfirskrifa",
+"suggest name" => "stinga upp á nafni",
+"cancel" => "hætta við",
+"replaced {new_name} with {old_name}" => "yfirskrifaði {new_name} með {old_name}",
+"undo" => "afturkalla",
+"'.' is an invalid file name." => "'.' er ekki leyfilegt nafn.",
+"File name cannot be empty." => "Nafn skráar má ekki vera tómt",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ógilt nafn, táknin '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' eru ekki leyfð.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Innsending á skrá mistókst, hugsanlega sendir þú möppu eða skráin er 0 bæti.",
+"Upload Error" => "Villa við innsendingu",
+"Close" => "Loka",
+"1 file uploading" => "1 skrá innsend",
+"{count} files uploading" => "{count} skrár innsendar",
+"Upload cancelled." => "Hætt við innsendingu.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Innsending í gangi. Ef þú ferð af þessari síðu mun innsending misheppnast.",
+"URL cannot be empty." => "Vefslóð má ekki vera tóm.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Óleyfilegt nafn á möppu. Nafnið 'Shared' er frátekið fyrir Owncloud",
+"Name" => "Nafn",
+"Size" => "Stærð",
+"Modified" => "Breytt",
+"1 folder" => "1 mappa",
+"{count} folders" => "{count} möppur",
+"1 file" => "1 skrá",
+"{count} files" => "{count} skrár",
+"Upload" => "Senda inn",
+"File handling" => "Meðhöndlun skrár",
+"Maximum upload size" => "Hámarks stærð innsendingar",
+"max. possible: " => "hámark mögulegt: ",
+"Needed for multi-file and folder downloads." => "Nauðsynlegt til að sækja margar skrár og möppur í einu.",
+"Enable ZIP-download" => "Virkja ZIP niðurhal.",
+"0 is unlimited" => "0 er ótakmarkað",
+"Maximum input size for ZIP files" => "Hámarks inntaksstærð fyrir ZIP skrár",
+"Save" => "Vista",
+"New" => "Nýtt",
+"Text file" => "Texta skrá",
+"Folder" => "Mappa",
+"From link" => "Af tengli",
+"Cancel upload" => "Hætta við innsendingu",
+"Nothing in here. Upload something!" => "Ekkert hér. Settu eitthvað inn!",
+"Download" => "Niðurhal",
+"Unshare" => "Hætta deilingu",
+"Upload too large" => "Innsend skrá er of stór",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Skrárnar sem þú ert að senda inn eru stærri en hámarks innsendingarstærð á þessum netþjóni.",
+"Files are being scanned, please wait." => "Verið er að skima skrár, vinsamlegast hinkraðu.",
+"Current scanning" => "Er að skima"
+);
diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php
index 86f2ff84fcf..2aac4ef20f5 100644
--- a/apps/files/l10n/it.php
+++ b/apps/files/l10n/it.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Impossibile spostare %s - un file con questo nome esiste già",
+"Could not move %s" => "Impossibile spostare %s",
+"Unable to rename file" => "Impossibile rinominare il file",
+"No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto",
"There is no error, the file uploaded with success" => "Non ci sono errori, file caricato con successo",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Il file caricato supera il valore upload_max_filesize in php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Il file caricato supera la direttiva upload_max_filesize in php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Il file caricato supera il valore MAX_FILE_SIZE definito nel form HTML",
"The uploaded file was only partially uploaded" => "Il file è stato parzialmente caricato",
"No file was uploaded" => "Nessun file è stato caricato",
"Missing a temporary folder" => "Cartella temporanea mancante",
"Failed to write to disk" => "Scrittura su disco non riuscita",
+"Not enough storage available" => "Spazio di archiviazione insufficiente",
+"Invalid directory." => "Cartella non valida.",
"Files" => "File",
-"Unshare" => "Rimuovi condivisione",
+"Delete permanently" => "Elimina definitivamente",
"Delete" => "Elimina",
"Rename" => "Rinomina",
-"already exists" => "esiste già",
+"Pending" => "In corso",
+"{new_name} already exists" => "{new_name} esiste già",
"replace" => "sostituisci",
"suggest name" => "suggerisci nome",
"cancel" => "annulla",
-"replaced" => "sostituito",
+"replaced {new_name} with {old_name}" => "sostituito {new_name} con {old_name}",
"undo" => "annulla",
-"with" => "con",
-"unshared" => "condivisione rimossa",
-"deleted" => "eliminati",
-"generating ZIP-file, it may take some time." => "creazione file ZIP, potrebbe richiedere del tempo.",
+"perform delete operation" => "esegui l'operazione di eliminazione",
+"'.' is an invalid file name." => "'.' non è un nome file valido.",
+"File name cannot be empty." => "Il nome del file non può essere vuoto.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome non valido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' non sono consentiti.",
+"Your storage is full, files can not be updated or synced anymore!" => "Lo spazio di archiviazione è pieno, i file non possono essere più aggiornati o sincronizzati!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Lo spazio di archiviazione è quasi pieno ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Il tuo scaricamento è in fase di preparazione. Ciò potrebbe richiedere del tempo se i file sono grandi.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte",
"Upload Error" => "Errore di invio",
-"Pending" => "In corso",
+"Close" => "Chiudi",
"1 file uploading" => "1 file in fase di caricamento",
-"files uploading" => "file in fase di caricamento",
+"{count} files uploading" => "{count} file in fase di caricamentoe",
"Upload cancelled." => "Invio annullato",
"File upload is in progress. Leaving the page now will cancel the upload." => "Caricamento del file in corso. La chiusura della pagina annullerà il caricamento.",
-"Invalid name, '/' is not allowed." => "Nome non valido",
-"files scanned" => "file analizzati",
-"error while scanning" => "errore durante la scansione",
+"URL cannot be empty." => "L'URL non può essere vuoto.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome della cartella non valido. L'uso di 'Shared' è riservato da ownCloud",
"Name" => "Nome",
"Size" => "Dimensione",
"Modified" => "Modificato",
-"folder" => "cartella",
-"folders" => "cartelle",
-"file" => "file",
-"files" => "file",
-"seconds ago" => "secondi fa",
-"minute ago" => "minuto fa",
-"minutes ago" => "minuti fa",
-"today" => "oggi",
-"yesterday" => "ieri",
-"days ago" => "giorni fa",
-"last month" => "mese scorso",
-"months ago" => "mesi fa",
-"last year" => "anno scorso",
-"years ago" => "anni fa",
+"1 folder" => "1 cartella",
+"{count} folders" => "{count} cartelle",
+"1 file" => "1 file",
+"{count} files" => "{count} file",
+"Upload" => "Carica",
"File handling" => "Gestione file",
"Maximum upload size" => "Dimensione massima upload",
"max. possible: " => "numero mass.: ",
@@ -58,14 +58,16 @@
"New" => "Nuovo",
"Text file" => "File di testo",
"Folder" => "Cartella",
-"From url" => "Da URL",
-"Upload" => "Carica",
+"From link" => "Da collegamento",
+"Deleted files" => "File eliminati",
"Cancel upload" => "Annulla invio",
+"You don’t have write permissions here." => "Qui non hai i permessi di scrittura.",
"Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!",
-"Share" => "Condividi",
"Download" => "Scarica",
+"Unshare" => "Rimuovi condivisione",
"Upload too large" => "Il file caricato è troppo grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.",
"Files are being scanned, please wait." => "Scansione dei file in corso, attendi",
-"Current scanning" => "Scansione corrente"
+"Current scanning" => "Scansione corrente",
+"Upgrading filesystem cache..." => "Aggiornamento della cache del filesystem in corso..."
);
diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php
index 7aa246f9644..88349d1ba40 100644
--- a/apps/files/l10n/ja_JP.php
+++ b/apps/files/l10n/ja_JP.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s を移動できませんでした ― この名前のファイルはすでに存在します",
+"Could not move %s" => "%s を移動できませんでした",
+"Unable to rename file" => "ファイル名の変更ができません",
+"No file was uploaded. Unknown error" => "ファイルは何もアップロードされていません。不明なエラー",
"There is no error, the file uploaded with success" => "エラーはありません。ファイルのアップロードは成功しました",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "アップロードされたファイルはphp.iniのupload_max_filesizeに設定されたサイズを超えています",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "アップロードされたファイルはphp.ini の upload_max_filesize に設定されたサイズを超えています:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "アップロードされたファイルはHTMLのフォームに設定されたMAX_FILE_SIZEに設定されたサイズを超えています",
"The uploaded file was only partially uploaded" => "ファイルは一部分しかアップロードされませんでした",
"No file was uploaded" => "ファイルはアップロードされませんでした",
"Missing a temporary folder" => "テンポラリフォルダが見つかりません",
"Failed to write to disk" => "ディスクへの書き込みに失敗しました",
+"Not enough storage available" => "ストレージに十分な空き容量がありません",
+"Invalid directory." => "無効なディレクトリです。",
"Files" => "ファイル",
-"Unshare" => "共有しない",
+"Delete permanently" => "完全に削除する",
"Delete" => "削除",
"Rename" => "名前の変更",
-"already exists" => "既に存在します",
+"Pending" => "保留",
+"{new_name} already exists" => "{new_name} はすでに存在しています",
"replace" => "置き換え",
"suggest name" => "推奨名称",
"cancel" => "キャンセル",
-"replaced" => "置換:",
+"replaced {new_name} with {old_name}" => "{old_name} を {new_name} に置換",
"undo" => "元に戻す",
-"with" => "←",
-"unshared" => "未共有",
-"deleted" => "削除",
-"generating ZIP-file, it may take some time." => "ZIPファイルを生成中です、しばらくお待ちください。",
-"Unable to upload your file as it is a directory or has 0 bytes" => "アップロード使用としているファイルがディレクトリ、もしくはサイズが0バイトのため、アップロードできません。",
+"perform delete operation" => "削除を実行",
+"'.' is an invalid file name." => "'.' は無効なファイル名です。",
+"File name cannot be empty." => "ファイル名を空にすることはできません。",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "無効な名前、'\\', '/', '<', '>', ':', '\"', '|', '?', '*' は使用できません。",
+"Your storage is full, files can not be updated or synced anymore!" => "あなたのストレージは一杯です。ファイルの更新と同期はもうできません!",
+"Your storage is almost full ({usedSpacePercent}%)" => "あなたのストレージはほぼ一杯です({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "ダウンロードの準備中です。ファイルサイズが大きい場合は少し時間がかかるかもしれません。",
+"Unable to upload your file as it is a directory or has 0 bytes" => "ディレクトリもしくは0バイトのファイルはアップロードできません",
"Upload Error" => "アップロードエラー",
-"Pending" => "保留",
+"Close" => "閉じる",
"1 file uploading" => "ファイルを1つアップロード中",
-"files uploading" => "ファイルをアップロード中",
+"{count} files uploading" => "{count} ファイルをアップロード中",
"Upload cancelled." => "アップロードはキャンセルされました。",
"File upload is in progress. Leaving the page now will cancel the upload." => "ファイル転送を実行中です。今このページから移動するとアップロードが中止されます。",
-"Invalid name, '/' is not allowed." => "無効な名前、'/' は使用できません。",
-"files scanned" => "ファイルをスキャンしました",
-"error while scanning" => "スキャン中のエラー",
+"URL cannot be empty." => "URLは空にできません。",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無効なフォルダ名です。'Shared' の利用は ownCloud が予約済みです。",
"Name" => "名前",
"Size" => "サイズ",
"Modified" => "更新日時",
-"folder" => "フォルダ",
-"folders" => "フォルダ",
-"file" => "ファイル",
-"files" => "ファイル",
-"seconds ago" => "秒前",
-"minute ago" => "分前",
-"minutes ago" => "分前",
-"today" => "今日",
-"yesterday" => "昨日",
-"days ago" => "日前",
-"last month" => "一月前",
-"months ago" => "月前",
-"last year" => "一年前",
-"years ago" => "年前",
+"1 folder" => "1 フォルダ",
+"{count} folders" => "{count} フォルダ",
+"1 file" => "1 ファイル",
+"{count} files" => "{count} ファイル",
+"Upload" => "アップロード",
"File handling" => "ファイル操作",
"Maximum upload size" => "最大アップロードサイズ",
"max. possible: " => "最大容量: ",
@@ -58,14 +58,16 @@
"New" => "新規",
"Text file" => "テキストファイル",
"Folder" => "フォルダ",
-"From url" => "URL",
-"Upload" => "アップロード",
+"From link" => "リンク",
+"Deleted files" => "削除ファイル",
"Cancel upload" => "アップロードをキャンセル",
+"You don’t have write permissions here." => "あなたには書き込み権限がありません。",
"Nothing in here. Upload something!" => "ここには何もありません。何かアップロードしてください。",
-"Share" => "共有",
"Download" => "ダウンロード",
+"Unshare" => "共有しない",
"Upload too large" => "ファイルサイズが大きすぎます",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップロードしようとしているファイルは、サーバで規定された最大サイズを超えています。",
"Files are being scanned, please wait." => "ファイルをスキャンしています、しばらくお待ちください。",
-"Current scanning" => "スキャン中"
+"Current scanning" => "スキャン中",
+"Upgrading filesystem cache..." => "ファイルシステムキャッシュを更新中..."
);
diff --git a/apps/files/l10n/ka.php b/apps/files/l10n/ka.php
new file mode 100644
index 00000000000..148e688547a
--- /dev/null
+++ b/apps/files/l10n/ka.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Files" => "ფაილები",
+"Download" => "გადმოწერა"
+);
diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php
new file mode 100644
index 00000000000..421c720a330
--- /dev/null
+++ b/apps/files/l10n/ka_GE.php
@@ -0,0 +1,52 @@
+<?php $TRANSLATIONS = array(
+"There is no error, the file uploaded with success" => "ჭოცდომა არ დაფიქსირდა, ფაილი წარმატებით აიტვირთა",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ატვირთული ფაილი აჭარბებს MAX_FILE_SIZE დირექტივას, რომელიც მითითებულია HTML ფორმაში",
+"The uploaded file was only partially uploaded" => "ატვირთული ფაილი მხოლოდ ნაწილობრივ აიტვირთა",
+"No file was uploaded" => "ფაილი არ აიტვირთა",
+"Missing a temporary folder" => "დროებითი საქაღალდე არ არსებობს",
+"Failed to write to disk" => "შეცდომა დისკზე ჩაწერისას",
+"Files" => "ფაილები",
+"Delete" => "წაშლა",
+"Rename" => "გადარქმევა",
+"Pending" => "მოცდის რეჟიმში",
+"{new_name} already exists" => "{new_name} უკვე არსებობს",
+"replace" => "შეცვლა",
+"suggest name" => "სახელის შემოთავაზება",
+"cancel" => "უარყოფა",
+"replaced {new_name} with {old_name}" => "{new_name} შეცვლილია {old_name}–ით",
+"undo" => "დაბრუნება",
+"Unable to upload your file as it is a directory or has 0 bytes" => "თქვენი ფაილის ატვირთვა ვერ მოხერხდა. ის არის საქაღალდე და შეიცავს 0 ბაიტს",
+"Upload Error" => "შეცდომა ატვირთვისას",
+"Close" => "დახურვა",
+"1 file uploading" => "1 ფაილის ატვირთვა",
+"{count} files uploading" => "{count} ფაილი იტვირთება",
+"Upload cancelled." => "ატვირთვა შეჩერებულ იქნა.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "მიმდინარეობს ფაილის ატვირთვა. სხვა გვერდზე გადასვლა გამოიწვევს ატვირთვის შეჩერებას",
+"Name" => "სახელი",
+"Size" => "ზომა",
+"Modified" => "შეცვლილია",
+"1 folder" => "1 საქაღალდე",
+"{count} folders" => "{count} საქაღალდე",
+"1 file" => "1 ფაილი",
+"{count} files" => "{count} ფაილი",
+"Upload" => "ატვირთვა",
+"File handling" => "ფაილის დამუშავება",
+"Maximum upload size" => "მაქსიმუმ ატვირთის ზომა",
+"max. possible: " => "მაქს. შესაძლებელი:",
+"Needed for multi-file and folder downloads." => "საჭიროა მულტი ფაილ ან საქაღალდის ჩამოტვირთვა.",
+"Enable ZIP-download" => "ZIP-Download–ის ჩართვა",
+"0 is unlimited" => "0 is unlimited",
+"Maximum input size for ZIP files" => "ZIP ფაილების მაქსიმუმ დასაშვები ზომა",
+"Save" => "შენახვა",
+"New" => "ახალი",
+"Text file" => "ტექსტური ფაილი",
+"Folder" => "საქაღალდე",
+"Cancel upload" => "ატვირთვის გაუქმება",
+"Nothing in here. Upload something!" => "აქ არაფერი არ არის. ატვირთე რამე!",
+"Download" => "ჩამოტვირთვა",
+"Unshare" => "გაზიარების მოხსნა",
+"Upload too large" => "ასატვირთი ფაილი ძალიან დიდია",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "ფაილის ზომა რომლის ატვირთვასაც თქვენ აპირებთ, აჭარბებს სერვერზე დაშვებულ მაქსიმუმს.",
+"Files are being scanned, please wait." => "მიმდინარეობს ფაილების სკანირება, გთხოვთ დაელოდოთ.",
+"Current scanning" => "მიმდინარე სკანირება"
+);
diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php
index e54d696c34e..ba45bdaa5d7 100644
--- a/apps/files/l10n/ko.php
+++ b/apps/files/l10n/ko.php
@@ -1,51 +1,68 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s 항목을 이동시키지 못하였음 - 파일 이름이 이미 존재함",
+"Could not move %s" => "%s 항목을 이딩시키지 못하였음",
+"Unable to rename file" => "파일 이름바꾸기 할 수 없음",
+"No file was uploaded. Unknown error" => "파일이 업로드되지 않았습니다. 알 수 없는 오류입니다",
"There is no error, the file uploaded with success" => "업로드에 성공하였습니다.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "업로드한 파일이 php.ini에서 지정한 upload_max_filesize보다 더 큼",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "업로드한 파일이 php.ini의 upload_max_filesize보다 큽니다:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "업로드한 파일이 HTML 문서에 지정한 MAX_FILE_SIZE보다 더 큼",
"The uploaded file was only partially uploaded" => "파일이 부분적으로 업로드됨",
"No file was uploaded" => "업로드된 파일 없음",
"Missing a temporary folder" => "임시 폴더가 사라짐",
"Failed to write to disk" => "디스크에 쓰지 못했습니다",
+"Invalid directory." => "올바르지 않은 디렉터리입니다.",
"Files" => "파일",
"Delete" => "삭제",
-"already exists" => "이미 존재 합니다",
-"replace" => "대체",
-"cancel" => "취소",
-"replaced" => "대체됨",
-"undo" => "복구",
-"with" => "와",
-"deleted" => "삭제",
-"generating ZIP-file, it may take some time." => "ZIP파일 생성에 시간이 걸릴 수 있습니다.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉토리이거나 0 바이트이기 때문에 업로드 할 수 없습니다.",
-"Upload Error" => "업로드 에러",
+"Rename" => "이름 바꾸기",
"Pending" => "보류 중",
-"Upload cancelled." => "업로드 취소.",
-"Invalid name, '/' is not allowed." => "잘못된 이름, '/' 은 허용이 되지 않습니다.",
+"{new_name} already exists" => "{new_name}이(가) 이미 존재함",
+"replace" => "바꾸기",
+"suggest name" => "이름 제안",
+"cancel" => "취소",
+"replaced {new_name} with {old_name}" => "{old_name}이(가) {new_name}(으)로 대체됨",
+"undo" => "실행 취소",
+"'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름 입니다.",
+"File name cannot be empty." => "파일 이름이 비어 있을 수 없습니다.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "폴더 이름이 올바르지 않습니다. 이름에 문자 '\\', '/', '<', '>', ':', '\"', '|', '? ', '*'는 사용할 수 없습니다.",
+"Your storage is full, files can not be updated or synced anymore!" => "저장 공간이 가득 찼습니다. 파일을 업데이트하거나 동기화할 수 없습니다!",
+"Your storage is almost full ({usedSpacePercent}%)" => "저장 공간이 거의 가득 찼습니다 ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "다운로드가 준비 중입니다. 파일 크기가 크다면 시간이 오래 걸릴 수도 있습니다.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉터리이거나 비어 있기 때문에 업로드할 수 없습니다",
+"Upload Error" => "업로드 오류",
+"Close" => "닫기",
+"1 file uploading" => "파일 1개 업로드 중",
+"{count} files uploading" => "파일 {count}개 업로드 중",
+"Upload cancelled." => "업로드가 취소되었습니다.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "파일 업로드가 진행 중입니다. 이 페이지를 벗어나면 업로드가 취소됩니다.",
+"URL cannot be empty." => "URL을 입력해야 합니다.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "폴더 이름이 유효하지 않습니다. ",
"Name" => "이름",
"Size" => "크기",
"Modified" => "수정됨",
-"folder" => "폴더",
-"folders" => "폴더",
-"file" => "파일",
-"files" => "파일",
+"1 folder" => "폴더 1개",
+"{count} folders" => "폴더 {count}개",
+"1 file" => "파일 1개",
+"{count} files" => "파일 {count}개",
+"Upload" => "업로드",
"File handling" => "파일 처리",
"Maximum upload size" => "최대 업로드 크기",
-"max. possible: " => "최대. 가능한:",
-"Needed for multi-file and folder downloads." => "멀티 파일 및 폴더 다운로드에 필요.",
-"Enable ZIP-download" => "ZIP- 다운로드 허용",
-"0 is unlimited" => "0은 무제한 입니다",
-"Maximum input size for ZIP files" => "ZIP 파일에 대한 최대 입력 크기",
+"max. possible: " => "최대 가능:",
+"Needed for multi-file and folder downloads." => "다중 파일 및 폴더 다운로드에 필요합니다.",
+"Enable ZIP-download" => "ZIP 다운로드 허용",
+"0 is unlimited" => "0은 무제한입니다",
+"Maximum input size for ZIP files" => "ZIP 파일 최대 크기",
+"Save" => "저장",
"New" => "새로 만들기",
"Text file" => "텍스트 파일",
"Folder" => "폴더",
-"From url" => "URL 에서",
-"Upload" => "업로드",
+"From link" => "링크에서",
"Cancel upload" => "업로드 취소",
"Nothing in here. Upload something!" => "내용이 없습니다. 업로드할 수 있습니다!",
-"Share" => "공유",
"Download" => "다운로드",
+"Unshare" => "공유 해제",
"Upload too large" => "업로드 용량 초과",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "이 파일이 서버에서 허용하는 최대 업로드 가능 용량보다 큽니다.",
-"Files are being scanned, please wait." => "파일을 검색중입니다, 기다려 주십시오.",
-"Current scanning" => "커런트 스캐닝"
+"Files are being scanned, please wait." => "파일을 검색하고 있습니다. 기다려 주십시오.",
+"Current scanning" => "현재 검색",
+"Upgrading filesystem cache..." => "파일 시스템 캐시 업그레이드 중..."
);
diff --git a/apps/files/l10n/ku_IQ.php b/apps/files/l10n/ku_IQ.php
new file mode 100644
index 00000000000..5c5a3d6bd8f
--- /dev/null
+++ b/apps/files/l10n/ku_IQ.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Close" => "داخستن",
+"URL cannot be empty." => "ناونیشانی به‌سته‌ر نابێت به‌تاڵ بێت.",
+"Name" => "ناو",
+"Upload" => "بارکردن",
+"Save" => "پاشکه‌وتکردن",
+"Folder" => "بوخچه",
+"Download" => "داگرتن"
+);
diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php
index a51980348c7..b052da3a027 100644
--- a/apps/files/l10n/lb.php
+++ b/apps/files/l10n/lb.php
@@ -1,6 +1,5 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Keen Feeler, Datei ass komplett ropgelueden ginn",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Déi ropgelueden Datei ass méi grouss wei d'upload_max_filesize Eegenschaft an der php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Déi ropgelueden Datei ass méi grouss wei d'MAX_FILE_SIZE Eegenschaft déi an der HTML form uginn ass",
"The uploaded file was only partially uploaded" => "Déi ropgelueden Datei ass nëmmen hallef ropgelueden ginn",
"No file was uploaded" => "Et ass keng Datei ropgelueden ginn",
@@ -8,26 +7,18 @@
"Failed to write to disk" => "Konnt net op den Disk schreiwen",
"Files" => "Dateien",
"Delete" => "Läschen",
-"already exists" => "existéiert schonn",
"replace" => "ersetzen",
"cancel" => "ofbriechen",
-"replaced" => "ersat",
"undo" => "réckgängeg man",
-"with" => "mat",
-"deleted" => "geläscht",
-"generating ZIP-file, it may take some time." => "Et gëtt eng ZIP-File generéiert, dëst ka bëssen daueren.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kann deng Datei net eroplueden well et en Dossier ass oder 0 byte grouss ass.",
"Upload Error" => "Fehler beim eroplueden",
+"Close" => "Zoumaachen",
"Upload cancelled." => "Upload ofgebrach.",
"File upload is in progress. Leaving the page now will cancel the upload." => "File Upload am gaang. Wann's de des Säit verléiss gëtt den Upload ofgebrach.",
-"Invalid name, '/' is not allowed." => "Ongültege Numm, '/' net erlaabt.",
"Name" => "Numm",
"Size" => "Gréisst",
"Modified" => "Geännert",
-"folder" => "Dossier",
-"folders" => "Dossieren",
-"file" => "Datei",
-"files" => "Dateien",
+"Upload" => "Eroplueden",
"File handling" => "Fichier handling",
"Maximum upload size" => "Maximum Upload Gréisst ",
"max. possible: " => "max. méiglech:",
@@ -35,15 +26,14 @@
"Enable ZIP-download" => "ZIP-download erlaben",
"0 is unlimited" => "0 ass onlimitéiert",
"Maximum input size for ZIP files" => "Maximal Gréisst fir ZIP Fichieren",
+"Save" => "Späicheren",
"New" => "Nei",
"Text file" => "Text Fichier",
"Folder" => "Dossier",
-"From url" => "From URL",
-"Upload" => "Eroplueden",
"Cancel upload" => "Upload ofbriechen",
"Nothing in here. Upload something!" => "Hei ass näischt. Lued eppes rop!",
-"Share" => "Share",
"Download" => "Eroflueden",
+"Unshare" => "Net méi deelen",
"Upload too large" => "Upload ze grouss",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Déi Dateien déi Dir probéiert erop ze lueden sinn méi grouss wei déi Maximal Gréisst déi op dësem Server erlaabt ass.",
"Files are being scanned, please wait." => "Fichieren gi gescannt, war weg.",
diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php
index 47a79c4a5a0..2f16fc22420 100644
--- a/apps/files/l10n/lt_LT.php
+++ b/apps/files/l10n/lt_LT.php
@@ -1,6 +1,5 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Klaidų nėra, failas įkeltas sėkmingai",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Įkeliamo failo dydis viršija upload_max_filesize parametrą php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Įkeliamo failo dydis viršija MAX_FILE_SIZE parametrą, kuris yra nustatytas HTML formoje",
"The uploaded file was only partially uploaded" => "Failas buvo įkeltas tik dalinai",
"No file was uploaded" => "Nebuvo įkeltas nė vienas failas",
@@ -8,34 +7,44 @@
"Failed to write to disk" => "Nepavyko įrašyti į diską",
"Files" => "Failai",
"Delete" => "Ištrinti",
+"Rename" => "Pervadinti",
+"Pending" => "Laukiantis",
+"{new_name} already exists" => "{new_name} jau egzistuoja",
+"replace" => "pakeisti",
+"suggest name" => "pasiūlyti pavadinimą",
"cancel" => "atšaukti",
-"generating ZIP-file, it may take some time." => "kuriamas ZIP archyvas, tai gali užtrukti šiek tiek laiko.",
+"replaced {new_name} with {old_name}" => "pakeiskite {new_name} į {old_name}",
+"undo" => "anuliuoti",
"Unable to upload your file as it is a directory or has 0 bytes" => "Neįmanoma įkelti failo - jo dydis gali būti 0 bitų arba tai katalogas",
"Upload Error" => "Įkėlimo klaida",
-"Pending" => "Laukiantis",
+"Close" => "Užverti",
+"1 file uploading" => "įkeliamas 1 failas",
+"{count} files uploading" => "{count} įkeliami failai",
"Upload cancelled." => "Įkėlimas atšauktas.",
-"Invalid name, '/' is not allowed." => "Pavadinime negali būti naudojamas ženklas \"/\".",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Failo įkėlimas pradėtas. Jei paliksite šį puslapį, įkėlimas nutrūks.",
"Name" => "Pavadinimas",
"Size" => "Dydis",
"Modified" => "Pakeista",
-"folder" => "katalogas",
-"folders" => "katalogai",
-"file" => "failas",
-"files" => "failai",
+"1 folder" => "1 aplankalas",
+"{count} folders" => "{count} aplankalai",
+"1 file" => "1 failas",
+"{count} files" => "{count} failai",
+"Upload" => "Įkelti",
"File handling" => "Failų tvarkymas",
"Maximum upload size" => "Maksimalus įkeliamo failo dydis",
+"max. possible: " => "maks. galima:",
+"Needed for multi-file and folder downloads." => "Reikalinga daugybinui failų ir aplankalų atsisiuntimui.",
"Enable ZIP-download" => "Įjungti atsisiuntimą ZIP archyvu",
"0 is unlimited" => "0 yra neribotas",
"Maximum input size for ZIP files" => "Maksimalus ZIP archyvo failo dydis",
+"Save" => "Išsaugoti",
"New" => "Naujas",
"Text file" => "Teksto failas",
"Folder" => "Katalogas",
-"From url" => "Iš adreso",
-"Upload" => "Įkelti",
"Cancel upload" => "Atšaukti siuntimą",
"Nothing in here. Upload something!" => "Čia tuščia. Įkelkite ką nors!",
-"Share" => "Dalintis",
"Download" => "Atsisiųsti",
+"Unshare" => "Nebesidalinti",
"Upload too large" => "Įkėlimui failas per didelis",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis viršija maksimalų leidžiamą šiame serveryje",
"Files are being scanned, please wait." => "Skenuojami failai, prašome palaukti.",
diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php
index 813d693f94a..a7a9284c651 100644
--- a/apps/files/l10n/lv.php
+++ b/apps/files/l10n/lv.php
@@ -1,42 +1,73 @@
<?php $TRANSLATIONS = array(
-"No file was uploaded" => "Neviens fails netika augšuplādēts",
-"Failed to write to disk" => "Nav iespējams saglabāt",
-"Files" => "Faili",
-"Delete" => "Izdzēst",
-"already exists" => "tāds fails jau eksistē",
+"Could not move %s - File with this name already exists" => "Nevarēja pārvietot %s — jau eksistē datne ar tādu nosaukumu",
+"Could not move %s" => "Nevarēja pārvietot %s",
+"Unable to rename file" => "Nevarēja pārsaukt datni",
+"No file was uploaded. Unknown error" => "Netika augšupielādēta neviena datne. Nezināma kļūda",
+"There is no error, the file uploaded with success" => "Augšupielāde pabeigta bez kļūdām",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Augšupielādētā datne pārsniedz upload_max_filesize norādījumu php.ini datnē:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Augšupielādētā datne pārsniedz MAX_FILE_SIZE norādi, kas ir norādīta HTML formā",
+"The uploaded file was only partially uploaded" => "Augšupielādētā datne ir tikai daļēji augšupielādēta",
+"No file was uploaded" => "Neviena datne netika augšupielādēta",
+"Missing a temporary folder" => "Trūkst pagaidu mapes",
+"Failed to write to disk" => "Neizdevās saglabāt diskā",
+"Not enough storage available" => "Nav pietiekami daudz vietas",
+"Invalid directory." => "Nederīga direktorija.",
+"Files" => "Datnes",
+"Delete permanently" => "Dzēst pavisam",
+"Delete" => "Dzēst",
+"Rename" => "Pārsaukt",
+"Pending" => "Gaida savu kārtu",
+"{new_name} already exists" => "{new_name} jau eksistē",
"replace" => "aizvietot",
+"suggest name" => "ieteiktais nosaukums",
"cancel" => "atcelt",
-"replaced" => "aizvietots",
-"undo" => "vienu soli atpakaļ",
-"with" => "ar",
-"deleted" => "izdzests",
-"generating ZIP-file, it may take some time." => "lai uzģenerētu ZIP failu, kāds brīdis ir jāpagaida",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Nav iespējams augšuplādēt jūsu failu, jo tāds jau eksistē vai arī failam nav izmēra (0 baiti)",
-"Upload Error" => "Augšuplādēšanas laikā radās kļūda",
-"Pending" => "Gaida savu kārtu",
-"Upload cancelled." => "Augšuplāde ir atcelta",
-"Invalid name, '/' is not allowed." => "Šis simbols '/', nav atļauts.",
+"replaced {new_name} with {old_name}" => "aizvietoja {new_name} ar {old_name}",
+"undo" => "atsaukt",
+"perform delete operation" => "veikt dzēšanas darbību",
+"'.' is an invalid file name." => "'.' ir nederīgs datnes nosaukums.",
+"File name cannot be empty." => "Datnes nosaukums nevar būt tukšs.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nederīgs nosaukums, nav atļauti '\\', '/', '<', '>', ':', '\"', '|', '?' un '*'.",
+"Your storage is full, files can not be updated or synced anymore!" => "Jūsu krātuve ir pilna, datnes vairs nevar augšupielādēt vai sinhronizēt!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Jūsu krātuve ir gandrīz pilna ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Tiek sagatavota lejupielāde. Tas var aizņemt kādu laiciņu, ja datnes ir lielas.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Nevar augšupielādēt jūsu datni, jo tā ir direktorija vai arī tās izmērs ir 0 baiti",
+"Upload Error" => "Kļūda augšupielādējot",
+"Close" => "Aizvērt",
+"1 file uploading" => "Augšupielādē 1 datni",
+"{count} files uploading" => "augšupielādē {count} datnes",
+"Upload cancelled." => "Augšupielāde ir atcelta.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Notiek augšupielāde. Pametot lapu tagad, tiks atcelta augšupielāde.",
+"URL cannot be empty." => "URL nevar būt tukšs.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nederīgs mapes nosaukums. “Koplietots” izmantojums ir rezervēts ownCloud servisam.",
"Name" => "Nosaukums",
"Size" => "Izmērs",
-"Modified" => "Izmainīts",
-"folder" => "mape",
-"folders" => "mapes",
-"file" => "fails",
-"files" => "faili",
-"Maximum upload size" => "Maksimālais failu augšuplādes apjoms",
-"max. possible: " => "maksīmālais iespējamais:",
-"Enable ZIP-download" => "Iespējot ZIP lejuplādi",
+"Modified" => "Mainīts",
+"1 folder" => "1 mape",
+"{count} folders" => "{count} mapes",
+"1 file" => "1 datne",
+"{count} files" => "{count} datnes",
+"Upload" => "Augšupielādēt",
+"File handling" => "Datņu pārvaldība",
+"Maximum upload size" => "Maksimālais datņu augšupielādes apjoms",
+"max. possible: " => "maksimālais iespējamais:",
+"Needed for multi-file and folder downloads." => "Vajadzīgs vairāku datņu un mapju lejupielādēšanai.",
+"Enable ZIP-download" => "Aktivēt ZIP lejupielādi",
"0 is unlimited" => "0 ir neierobežots",
-"New" => "Jauns",
-"Text file" => "Teksta fails",
+"Maximum input size for ZIP files" => "Maksimālais ievades izmērs ZIP datnēm",
+"Save" => "Saglabāt",
+"New" => "Jauna",
+"Text file" => "Teksta datne",
"Folder" => "Mape",
-"From url" => "No URL saites",
-"Upload" => "Augšuplādet",
-"Cancel upload" => "Atcelt augšuplādi",
-"Nothing in here. Upload something!" => "Te vēl nekas nav. Rīkojies, sāc augšuplādēt",
-"Share" => "Līdzdalīt",
-"Download" => "Lejuplādēt",
-"Upload too large" => "Fails ir par lielu lai to augšuplādetu",
-"Files are being scanned, please wait." => "Faili šobrīd tiek caurskatīti, nedaudz jāpagaida.",
-"Current scanning" => "Šobrīd tiek pārbaudīti"
+"From link" => "No saites",
+"Deleted files" => "Dzēstās datnes",
+"Cancel upload" => "Atcelt augšupielādi",
+"You don’t have write permissions here." => "Jums nav tiesību šeit rakstīt.",
+"Nothing in here. Upload something!" => "Te vēl nekas nav. Rīkojies, sāc augšupielādēt!",
+"Download" => "Lejupielādēt",
+"Unshare" => "Pārtraukt dalīšanos",
+"Upload too large" => "Datne ir par lielu, lai to augšupielādētu",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Augšupielādējamās datnes pārsniedz servera pieļaujamo datņu augšupielādes apjomu",
+"Files are being scanned, please wait." => "Datnes šobrīd tiek caurskatītas, lūdzu, uzgaidiet.",
+"Current scanning" => "Šobrīd tiek caurskatīts",
+"Upgrading filesystem cache..." => "Uzlabo datņu sistēmas kešatmiņu..."
);
diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php
index f4aee5ba178..cf9ad8abafc 100644
--- a/apps/files/l10n/mk.php
+++ b/apps/files/l10n/mk.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "Ниту еден фајл не се вчита. Непозната грешка",
"There is no error, the file uploaded with success" => "Нема грешка, датотеката беше подигната успешно",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Подигнатата датотека ја надминува upload_max_filesize директивата во php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Подигнатата датотека ја надминува upload_max_filesize директивата во php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Подигнатата датотеката ја надминува MAX_FILE_SIZE директивата која беше поставена во HTML формата",
"The uploaded file was only partially uploaded" => "Датотеката беше само делумно подигната.",
"No file was uploaded" => "Не беше подигната датотека",
@@ -8,19 +9,31 @@
"Failed to write to disk" => "Неуспеав да запишам на диск",
"Files" => "Датотеки",
"Delete" => "Избриши",
-"generating ZIP-file, it may take some time." => "Се генерира ZIP фајлот, ќе треба извесно време.",
+"Rename" => "Преименувај",
+"Pending" => "Чека",
+"{new_name} already exists" => "{new_name} веќе постои",
+"replace" => "замени",
+"suggest name" => "предложи име",
+"cancel" => "откажи",
+"replaced {new_name} with {old_name}" => "заменета {new_name} со {old_name}",
+"undo" => "врати",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправилно име. , '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не се дозволени.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Не може да се преземе вашата датотека бидејќи фолдерот во кој се наоѓа фајлот има големина од 0 бајти",
"Upload Error" => "Грешка при преземање",
-"Pending" => "Чека",
+"Close" => "Затвои",
+"1 file uploading" => "1 датотека се подига",
+"{count} files uploading" => "{count} датотеки се подигаат",
"Upload cancelled." => "Преземањето е прекинато.",
-"Invalid name, '/' is not allowed." => "неисправно име, '/' не е дозволено.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Подигање на датотека е во тек. Напуштење на страницата ќе го прекине.",
+"URL cannot be empty." => "Адресата неможе да биде празна.",
"Name" => "Име",
"Size" => "Големина",
"Modified" => "Променето",
-"folder" => "фолдер",
-"folders" => "фолдери",
-"file" => "датотека",
-"files" => "датотеки",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папки",
+"1 file" => "1 датотека",
+"{count} files" => "{count} датотеки",
+"Upload" => "Подигни",
"File handling" => "Ракување со датотеки",
"Maximum upload size" => "Максимална големина за подигање",
"max. possible: " => "макс. можно:",
@@ -28,15 +41,15 @@
"Enable ZIP-download" => "Овозможи ZIP симнување ",
"0 is unlimited" => "0 е неограничено",
"Maximum input size for ZIP files" => "Максимална големина за внес на ZIP датотеки",
+"Save" => "Сними",
"New" => "Ново",
"Text file" => "Текстуална датотека",
"Folder" => "Папка",
-"From url" => "Од адреса",
-"Upload" => "Подигни",
+"From link" => "Од врска",
"Cancel upload" => "Откажи прикачување",
"Nothing in here. Upload something!" => "Тука нема ништо. Снимете нешто!",
-"Share" => "Сподели",
"Download" => "Преземи",
+"Unshare" => "Не споделувај",
"Upload too large" => "Датотеката е премногу голема",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.",
"Files are being scanned, please wait." => "Се скенираат датотеки, ве молам почекајте.",
diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php
index bfa47969b3c..b15a9111e70 100644
--- a/apps/files/l10n/ms_MY.php
+++ b/apps/files/l10n/ms_MY.php
@@ -1,6 +1,6 @@
<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "Tiada fail dimuatnaik. Ralat tidak diketahui.",
"There is no error, the file uploaded with success" => "Tiada ralat, fail berjaya dimuat naik.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Fail yang dimuat naik melebihi penyata upload_max_filesize dalam php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Fail yang dimuat naik melebihi MAX_FILE_SIZE yang dinyatakan dalam form HTML ",
"The uploaded file was only partially uploaded" => "Sebahagian daripada fail telah dimuat naik. ",
"No file was uploaded" => "Tiada fail yang dimuat naik",
@@ -8,25 +8,17 @@
"Failed to write to disk" => "Gagal untuk disimpan",
"Files" => "fail",
"Delete" => "Padam",
-"already exists" => "Sudah wujud",
+"Pending" => "Dalam proses",
"replace" => "ganti",
"cancel" => "Batal",
-"replaced" => "diganti",
-"with" => "dengan",
-"deleted" => "dihapus",
-"generating ZIP-file, it may take some time." => "sedang menghasilkan fail ZIP, mungkin mengambil sedikit masa.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes",
"Upload Error" => "Muat naik ralat",
-"Pending" => "Dalam proses",
+"Close" => "Tutup",
"Upload cancelled." => "Muatnaik dibatalkan.",
-"Invalid name, '/' is not allowed." => "penggunaa nama tidak sah, '/' tidak dibenarkan.",
"Name" => "Nama ",
"Size" => "Saiz",
"Modified" => "Dimodifikasi",
-"folder" => "direktori",
-"folders" => "direktori",
-"file" => "fail",
-"files" => "fail",
+"Upload" => "Muat naik",
"File handling" => "Pengendalian fail",
"Maximum upload size" => "Saiz maksimum muat naik",
"max. possible: " => "maksimum:",
@@ -34,14 +26,12 @@
"Enable ZIP-download" => "Aktifkan muatturun ZIP",
"0 is unlimited" => "0 adalah tanpa had",
"Maximum input size for ZIP files" => "Saiz maksimum input untuk fail ZIP",
+"Save" => "Simpan",
"New" => "Baru",
"Text file" => "Fail teks",
"Folder" => "Folder",
-"From url" => "Dari url",
-"Upload" => "Muat naik",
"Cancel upload" => "Batal muat naik",
"Nothing in here. Upload something!" => "Tiada apa-apa di sini. Muat naik sesuatu!",
-"Share" => "Kongsi",
"Download" => "Muat turun",
"Upload too large" => "Muat naik terlalu besar",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang cuba dimuat naik melebihi saiz maksimum fail upload server",
diff --git a/apps/files/l10n/my_MM.php b/apps/files/l10n/my_MM.php
new file mode 100644
index 00000000000..b791a134ccc
--- /dev/null
+++ b/apps/files/l10n/my_MM.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Files" => "ဖိုင်များ",
+"Download" => "ဒေါင်းလုတ်"
+);
diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php
index cda1d51ed2f..d9972feb6a5 100644
--- a/apps/files/l10n/nb_NO.php
+++ b/apps/files/l10n/nb_NO.php
@@ -1,33 +1,39 @@
<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "Ingen filer ble lastet opp. Ukjent feil.",
"There is no error, the file uploaded with success" => "Det er ingen feil. Filen ble lastet opp.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Filstørrelsen overskrider maksgrensedirektivet upload_max_filesize i php.ini-konfigurasjonen.",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Filstørrelsen overskrider maksgrensen på MAX_FILE_SIZE som ble oppgitt i HTML-skjemaet",
"The uploaded file was only partially uploaded" => "Filopplastningen ble bare delvis gjennomført",
"No file was uploaded" => "Ingen fil ble lastet opp",
"Missing a temporary folder" => "Mangler en midlertidig mappe",
"Failed to write to disk" => "Klarte ikke å skrive til disk",
"Files" => "Filer",
+"Delete permanently" => "Slett permanent",
"Delete" => "Slett",
-"already exists" => "eksisterer allerede",
+"Rename" => "Omdøp",
+"Pending" => "Ventende",
+"{new_name} already exists" => "{new_name} finnes allerede",
"replace" => "erstatt",
+"suggest name" => "foreslå navn",
"cancel" => "avbryt",
-"replaced" => "erstattet",
+"replaced {new_name} with {old_name}" => "erstatt {new_name} med {old_name}",
"undo" => "angre",
-"with" => "med",
-"deleted" => "slettet",
-"generating ZIP-file, it may take some time." => "opprettet ZIP-fil, dette kan ta litt tid",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldig navn, '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' er ikke tillatt.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes",
"Upload Error" => "Opplasting feilet",
-"Pending" => "Ventende",
+"Close" => "Lukk",
+"1 file uploading" => "1 fil lastes opp",
+"{count} files uploading" => "{count} filer laster opp",
"Upload cancelled." => "Opplasting avbrutt.",
-"Invalid name, '/' is not allowed." => "Ugyldig navn, '/' er ikke tillatt. ",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Filopplasting pågår. Forlater du siden nå avbrytes opplastingen.",
+"URL cannot be empty." => "URL-en kan ikke være tom.",
"Name" => "Navn",
"Size" => "Størrelse",
"Modified" => "Endret",
-"folder" => "mappe",
-"folders" => "mapper",
-"file" => "fil",
-"files" => "filer",
+"1 folder" => "1 mappe",
+"{count} folders" => "{count} mapper",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Upload" => "Last opp",
"File handling" => "Filhåndtering",
"Maximum upload size" => "Maksimum opplastingsstørrelse",
"max. possible: " => "max. mulige:",
@@ -39,12 +45,11 @@
"New" => "Ny",
"Text file" => "Tekstfil",
"Folder" => "Mappe",
-"From url" => "Fra url",
-"Upload" => "Last opp",
+"From link" => "Fra link",
"Cancel upload" => "Avbryt opplasting",
"Nothing in here. Upload something!" => "Ingenting her. Last opp noe!",
-"Share" => "Del",
"Download" => "Last ned",
+"Unshare" => "Avslutt deling",
"Upload too large" => "Opplasting for stor",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er for store for å laste opp til denne serveren.",
"Files are being scanned, please wait." => "Skanner etter filer, vennligst vent.",
diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php
index 5f7f03a3d81..6af7edf2501 100644
--- a/apps/files/l10n/nl.php
+++ b/apps/files/l10n/nl.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Kon %s niet verplaatsen - Er bestaat al een bestand met deze naam",
+"Could not move %s" => "Kon %s niet verplaatsen",
+"Unable to rename file" => "Kan bestand niet hernoemen",
+"No file was uploaded. Unknown error" => "Er was geen bestand geladen. Onbekende fout",
"There is no error, the file uploaded with success" => "Geen fout opgetreden, bestand successvol geupload.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Het geüploade bestand is groter dan de upload_max_filesize instelling in php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Het geüploade bestand overscheidt de upload_max_filesize optie in php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Het geüploade bestand is groter dan de MAX_FILE_SIZE richtlijn die is opgegeven in de HTML-formulier",
"The uploaded file was only partially uploaded" => "Het bestand is slechts gedeeltelijk geupload",
"No file was uploaded" => "Geen bestand geüpload",
"Missing a temporary folder" => "Een tijdelijke map mist",
"Failed to write to disk" => "Schrijven naar schijf mislukt",
+"Not enough storage available" => "Niet genoeg opslagruimte beschikbaar",
+"Invalid directory." => "Ongeldige directory.",
"Files" => "Bestanden",
-"Unshare" => "Stop delen",
+"Delete permanently" => "Verwijder definitief",
"Delete" => "Verwijder",
"Rename" => "Hernoem",
-"already exists" => "bestaat al",
+"Pending" => "Wachten",
+"{new_name} already exists" => "{new_name} bestaat al",
"replace" => "vervang",
"suggest name" => "Stel een naam voor",
"cancel" => "annuleren",
-"replaced" => "vervangen",
+"replaced {new_name} with {old_name}" => "verving {new_name} met {old_name}",
"undo" => "ongedaan maken",
-"with" => "door",
-"unshared" => "niet gedeeld",
-"deleted" => "verwijderd",
-"generating ZIP-file, it may take some time." => "aanmaken ZIP-file, dit kan enige tijd duren.",
+"perform delete operation" => "uitvoeren verwijderactie",
+"'.' is an invalid file name." => "'.' is een ongeldige bestandsnaam.",
+"File name cannot be empty." => "Bestandsnaam kan niet leeg zijn.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Onjuiste naam; '\\', '/', '<', '>', ':', '\"', '|', '?' en '*' zijn niet toegestaan.",
+"Your storage is full, files can not be updated or synced anymore!" => "Uw opslagruimte zit vol, Bestanden kunnen niet meer worden ge-upload of gesynchroniseerd!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Uw opslagruimte zit bijna vol ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Uw download wordt voorbereid. Dit kan enige tijd duren bij grote bestanden.",
"Unable to upload your file as it is a directory or has 0 bytes" => "uploaden van de file mislukt, het is of een directory of de bestandsgrootte is 0 bytes",
"Upload Error" => "Upload Fout",
-"Pending" => "Wachten",
+"Close" => "Sluit",
"1 file uploading" => "1 bestand wordt ge-upload",
-"files uploading" => "Bestanden aan het uploaden",
+"{count} files uploading" => "{count} bestanden aan het uploaden",
"Upload cancelled." => "Uploaden geannuleerd.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "Bestands upload is bezig. Wanneer de pagina nu verlaten wordt, stopt de upload.",
-"Invalid name, '/' is not allowed." => "Ongeldige naam, '/' is niet toegestaan.",
-"files scanned" => "Gescande bestanden",
-"error while scanning" => "Fout tijdens het scannen",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Bestandsupload is bezig. Wanneer de pagina nu verlaten wordt, stopt de upload.",
+"URL cannot be empty." => "URL kan niet leeg zijn.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ongeldige mapnaam. Gebruik van'Gedeeld' is voorbehouden aan Owncloud",
"Name" => "Naam",
"Size" => "Bestandsgrootte",
"Modified" => "Laatst aangepast",
-"folder" => "map",
-"folders" => "mappen",
-"file" => "bestand",
-"files" => "bestanden",
-"seconds ago" => "seconden geleden",
-"minute ago" => "minuut geleden",
-"minutes ago" => "minuten geleden",
-"today" => "vandaag",
-"yesterday" => "gisteren",
-"days ago" => "dagen geleden",
-"last month" => "vorige maand",
-"months ago" => "maanden geleden",
-"last year" => "vorig jaar",
-"years ago" => "jaar geleden",
+"1 folder" => "1 map",
+"{count} folders" => "{count} mappen",
+"1 file" => "1 bestand",
+"{count} files" => "{count} bestanden",
+"Upload" => "Upload",
"File handling" => "Bestand",
"Maximum upload size" => "Maximale bestandsgrootte voor uploads",
"max. possible: " => "max. mogelijk: ",
@@ -58,14 +58,16 @@
"New" => "Nieuw",
"Text file" => "Tekstbestand",
"Folder" => "Map",
-"From url" => "Van hyperlink",
-"Upload" => "Upload",
+"From link" => "Vanaf link",
+"Deleted files" => "Verwijderde bestanden",
"Cancel upload" => "Upload afbreken",
+"You don’t have write permissions here." => "U hebt hier geen schrijfpermissies.",
"Nothing in here. Upload something!" => "Er bevindt zich hier niets. Upload een bestand!",
-"Share" => "Delen",
"Download" => "Download",
+"Unshare" => "Stop delen",
"Upload too large" => "Bestanden te groot",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.",
"Files are being scanned, please wait." => "Bestanden worden gescand, even wachten.",
-"Current scanning" => "Er wordt gescand"
+"Current scanning" => "Er wordt gescand",
+"Upgrading filesystem cache..." => "Upgraden bestandssysteem cache..."
);
diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php
index 7af37057ce0..8a4ab91ea7e 100644
--- a/apps/files/l10n/nn_NO.php
+++ b/apps/files/l10n/nn_NO.php
@@ -1,20 +1,21 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Ingen feil, fila vart lasta opp",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den opplasta fila er større enn variabelen upload_max_filesize i php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den opplasta fila er større enn variabelen MAX_FILE_SIZE i HTML-skjemaet",
"The uploaded file was only partially uploaded" => "Fila vart berre delvis lasta opp",
"No file was uploaded" => "Ingen filer vart lasta opp",
"Missing a temporary folder" => "Manglar ei mellombels mappe",
"Files" => "Filer",
"Delete" => "Slett",
+"Close" => "Lukk",
"Name" => "Namn",
"Size" => "Storleik",
"Modified" => "Endra",
+"Upload" => "Last opp",
"Maximum upload size" => "Maksimal opplastingsstorleik",
+"Save" => "Lagre",
"New" => "Ny",
"Text file" => "Tekst fil",
"Folder" => "Mappe",
-"Upload" => "Last opp",
"Nothing in here. Upload something!" => "Ingenting her. Last noko opp!",
"Download" => "Last ned",
"Upload too large" => "For stor opplasting",
diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php
index 3f70761e903..7a39e9399f5 100644
--- a/apps/files/l10n/oc.php
+++ b/apps/files/l10n/oc.php
@@ -1,52 +1,27 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Amontcargament capitat, pas d'errors",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Lo fichièr amontcargat es tròp bèl per la directiva «upload_max_filesize » del php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lo fichièr amontcargat es mai gròs que la directiva «MAX_FILE_SIZE» especifiada dins lo formulari HTML",
"The uploaded file was only partially uploaded" => "Lo fichièr foguèt pas completament amontcargat",
"No file was uploaded" => "Cap de fichièrs son estats amontcargats",
"Missing a temporary folder" => "Un dorsièr temporari manca",
"Failed to write to disk" => "L'escriptura sul disc a fracassat",
"Files" => "Fichièrs",
-"Unshare" => "Non parteja",
"Delete" => "Escafa",
"Rename" => "Torna nomenar",
-"already exists" => "existís jà",
+"Pending" => "Al esperar",
"replace" => "remplaça",
"suggest name" => "nom prepausat",
"cancel" => "anulla",
-"replaced" => "remplaçat",
"undo" => "defar",
-"with" => "amb",
-"unshared" => "Non partejat",
-"deleted" => "escafat",
-"generating ZIP-file, it may take some time." => "Fichièr ZIP a se far, aquò pòt trigar un briu.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'amontcargar lo teu fichièr qu'es un repertòri o que ten pas que 0 octet.",
"Upload Error" => "Error d'amontcargar",
-"Pending" => "Al esperar",
"1 file uploading" => "1 fichièr al amontcargar",
-"files uploading" => "fichièrs al amontcargar",
"Upload cancelled." => "Amontcargar anullat.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Un amontcargar es a se far. Daissar aquesta pagina ara tamparà lo cargament. ",
-"Invalid name, '/' is not allowed." => "Nom invalid, '/' es pas permis.",
-"files scanned" => "Fichièr explorat",
-"error while scanning" => "error pendant l'exploracion",
"Name" => "Nom",
"Size" => "Talha",
"Modified" => "Modificat",
-"folder" => "Dorsièr",
-"folders" => "Dorsièrs",
-"file" => "fichièr",
-"files" => "fichièrs",
-"seconds ago" => "secondas",
-"minute ago" => "minuta",
-"minutes ago" => "minutas",
-"today" => "uèi",
-"yesterday" => "ièr",
-"days ago" => "jorns",
-"last month" => "mes passat",
-"months ago" => "meses",
-"last year" => "an passat",
-"years ago" => "ans",
+"Upload" => "Amontcarga",
"File handling" => "Manejament de fichièr",
"Maximum upload size" => "Talha maximum d'amontcargament",
"max. possible: " => "max. possible: ",
@@ -58,12 +33,10 @@
"New" => "Nòu",
"Text file" => "Fichièr de tèxte",
"Folder" => "Dorsièr",
-"From url" => "Dempuèi l'URL",
-"Upload" => "Amontcarga",
"Cancel upload" => " Anulla l'amontcargar",
"Nothing in here. Upload something!" => "Pas res dedins. Amontcarga qualquaren",
-"Share" => "Parteja",
"Download" => "Avalcarga",
+"Unshare" => "Non parteja",
"Upload too large" => "Amontcargament tròp gròs",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los fichièrs que sias a amontcargar son tròp pesucs per la talha maxi pel servidor.",
"Files are being scanned, please wait." => "Los fiichièrs son a èsser explorats, ",
diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php
index 425c6a5a18d..89eb3421291 100644
--- a/apps/files/l10n/pl.php
+++ b/apps/files/l10n/pl.php
@@ -1,71 +1,73 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Nie można było przenieść %s - Plik o takiej nazwie już istnieje",
+"Could not move %s" => "Nie można było przenieść %s",
+"Unable to rename file" => "Nie można zmienić nazwy pliku",
+"No file was uploaded. Unknown error" => "Żaden plik nie został załadowany. Nieznany błąd",
"There is no error, the file uploaded with success" => "Przesłano plik",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Rozmiar przesłanego pliku przekracza maksymalną wartość dyrektywy upload_max_filesize, zawartą w pliku php.ini",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Rozmiar przesłanego pliku przekracza maksymalną wartość dyrektywy upload_max_filesize, zawartą formularzu HTML",
-"The uploaded file was only partially uploaded" => "Plik przesłano tylko częściowo",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Wgrany plik przekracza wartość upload_max_filesize zdefiniowaną w php.ini: ",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Wysłany plik przekracza wielkość dyrektywy MAX_FILE_SIZE określonej w formularzu HTML",
+"The uploaded file was only partially uploaded" => "Załadowany plik został wysłany tylko częściowo.",
"No file was uploaded" => "Nie przesłano żadnego pliku",
"Missing a temporary folder" => "Brak katalogu tymczasowego",
"Failed to write to disk" => "Błąd zapisu na dysk",
+"Not enough storage available" => "Za mało dostępnego miejsca",
+"Invalid directory." => "Zła ścieżka.",
"Files" => "Pliki",
-"Unshare" => "Nie udostępniaj",
-"Delete" => "Usuwa element",
+"Delete permanently" => "Trwale usuń",
+"Delete" => "Usuń",
"Rename" => "Zmień nazwę",
-"already exists" => "Już istnieje",
-"replace" => "zastap",
+"Pending" => "Oczekujące",
+"{new_name} already exists" => "{new_name} już istnieje",
+"replace" => "zastąp",
"suggest name" => "zasugeruj nazwę",
"cancel" => "anuluj",
-"replaced" => "zastąpione",
-"undo" => "wróć",
-"with" => "z",
-"unshared" => "Nie udostępnione",
-"deleted" => "skasuj",
-"generating ZIP-file, it may take some time." => "Generowanie pliku ZIP, może potrwać pewien czas.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku jeśli jest katalogiem lub ma 0 bajtów",
+"replaced {new_name} with {old_name}" => "zastąpiono {new_name} przez {old_name}",
+"undo" => "cofnij",
+"perform delete operation" => "wykonaj operację usunięcia",
+"'.' is an invalid file name." => "„.” jest nieprawidłową nazwą pliku.",
+"File name cannot be empty." => "Nazwa pliku nie może być pusta.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nieprawidłowa nazwa. Znaki '\\', '/', '<', '>', ':', '\"', '|', '?' oraz '*' są niedozwolone.",
+"Your storage is full, files can not be updated or synced anymore!" => "Magazyn jest pełny. Pliki nie mogą zostać zaktualizowane lub zsynchronizowane!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Twój magazyn jest prawie pełny ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Pobieranie jest przygotowywane. Może to zająć trochę czasu jeśli pliki są duże.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku, ponieważ jest on katalogiem lub ma 0 bajtów",
"Upload Error" => "Błąd wczytywania",
-"Pending" => "Oczekujące",
-"1 file uploading" => "1 plik wczytany",
-"files uploading" => "pliki wczytane",
+"Close" => "Zamknij",
+"1 file uploading" => "1 plik wczytywany",
+"{count} files uploading" => "Ilość przesyłanych plików: {count}",
"Upload cancelled." => "Wczytywanie anulowane.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Teraz opuszczając stronę wysyłanie zostanie anulowane.",
-"Invalid name, '/' is not allowed." => "Nieprawidłowa nazwa '/' jest niedozwolone.",
-"files scanned" => "Pliki skanowane",
-"error while scanning" => "Wystąpił błąd podczas skanowania",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Jeśli opuścisz tę stronę, wysyłanie zostanie przerwane.",
+"URL cannot be empty." => "URL nie może być pusty.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nieprawidłowa nazwa folderu. Korzystanie z nazwy „Shared” jest zarezerwowane dla ownCloud",
"Name" => "Nazwa",
"Size" => "Rozmiar",
-"Modified" => "Czas modyfikacji",
-"folder" => "folder",
-"folders" => "foldery",
-"file" => "plik",
-"files" => "pliki",
-"seconds ago" => "sekund temu",
-"minute ago" => "minutę temu",
-"minutes ago" => "minut temu",
-"today" => "dziś",
-"yesterday" => "wczoraj",
-"days ago" => "dni temu",
-"last month" => "ostani miesiąc",
-"months ago" => "miesięcy temu",
-"last year" => "ostatni rok",
-"years ago" => "lat temu",
+"Modified" => "Modyfikacja",
+"1 folder" => "1 folder",
+"{count} folders" => "Ilość folderów: {count}",
+"1 file" => "1 plik",
+"{count} files" => "Ilość plików: {count}",
+"Upload" => "Prześlij",
"File handling" => "Zarządzanie plikami",
"Maximum upload size" => "Maksymalny rozmiar wysyłanego pliku",
-"max. possible: " => "max. możliwych",
+"max. possible: " => "maks. możliwy:",
"Needed for multi-file and folder downloads." => "Wymagany do pobierania wielu plików i folderów",
"Enable ZIP-download" => "Włącz pobieranie ZIP-paczki",
-"0 is unlimited" => "0 jest nielimitowane",
+"0 is unlimited" => "0 - bez limitów",
"Maximum input size for ZIP files" => "Maksymalna wielkość pliku wejściowego ZIP ",
"Save" => "Zapisz",
"New" => "Nowy",
"Text file" => "Plik tekstowy",
"Folder" => "Katalog",
-"From url" => "Z adresu",
-"Upload" => "Prześlij",
-"Cancel upload" => "Przestań wysyłać",
-"Nothing in here. Upload something!" => "Brak zawartości. Proszę wysłać pliki!",
-"Share" => "Współdziel",
-"Download" => "Pobiera element",
+"From link" => "Z odnośnika",
+"Deleted files" => "Pliki usunięte",
+"Cancel upload" => "Anuluj wysyłanie",
+"You don’t have write permissions here." => "Nie masz uprawnień do zapisu w tym miejscu.",
+"Nothing in here. Upload something!" => "Pusto. Wyślij coś!",
+"Download" => "Pobierz",
+"Unshare" => "Nie udostępniaj",
"Upload too large" => "Wysyłany plik ma za duży rozmiar",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki które próbujesz przesłać, przekraczają maksymalną, dopuszczalną wielkość.",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki, które próbujesz przesłać, przekraczają maksymalną dopuszczalną wielkość.",
"Files are being scanned, please wait." => "Skanowanie plików, proszę czekać.",
-"Current scanning" => "Aktualnie skanowane"
+"Current scanning" => "Aktualnie skanowane",
+"Upgrading filesystem cache..." => "Uaktualnianie plików pamięci podręcznej..."
);
diff --git a/apps/files/l10n/pl_PL.php b/apps/files/l10n/pl_PL.php
new file mode 100644
index 00000000000..157d9a41e4d
--- /dev/null
+++ b/apps/files/l10n/pl_PL.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Save" => "Zapisz"
+);
diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php
index 59ec8a8a018..3bebb682271 100644
--- a/apps/files/l10n/pt_BR.php
+++ b/apps/files/l10n/pt_BR.php
@@ -1,56 +1,56 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Impossível mover %s - Um arquivo com este nome já existe",
+"Could not move %s" => "Impossível mover %s",
+"Unable to rename file" => "Impossível renomear arquivo",
+"No file was uploaded. Unknown error" => "Nenhum arquivo foi enviado. Erro desconhecido",
"There is no error, the file uploaded with success" => "Não houve nenhum erro, o arquivo foi transferido com sucesso",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O tamanho do arquivo excede o limed especifiicado em upload_max_filesize no php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O arquivo enviado excede a diretiva upload_max_filesize no php.ini: ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O arquivo carregado excede o MAX_FILE_SIZE que foi especificado no formulário HTML",
"The uploaded file was only partially uploaded" => "O arquivo foi transferido parcialmente",
"No file was uploaded" => "Nenhum arquivo foi transferido",
"Missing a temporary folder" => "Pasta temporária não encontrada",
"Failed to write to disk" => "Falha ao escrever no disco",
+"Not enough storage available" => "Espaço de armazenamento insuficiente",
+"Invalid directory." => "Diretório inválido.",
"Files" => "Arquivos",
-"Unshare" => "Descompartilhar",
+"Delete permanently" => "Excluir permanentemente",
"Delete" => "Excluir",
"Rename" => "Renomear",
-"already exists" => "já existe",
+"Pending" => "Pendente",
+"{new_name} already exists" => "{new_name} já existe",
"replace" => "substituir",
"suggest name" => "sugerir nome",
"cancel" => "cancelar",
-"replaced" => "substituido ",
+"replaced {new_name} with {old_name}" => "Substituído {old_name} por {new_name} ",
"undo" => "desfazer",
-"with" => "com",
-"unshared" => "descompartilhado",
-"deleted" => "deletado",
-"generating ZIP-file, it may take some time." => "gerando arquivo ZIP, isso pode levar um tempo.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Impossível enviar seus arquivo como diretório ou ele tem 0 bytes.",
+"perform delete operation" => "realizar operação de exclusão",
+"'.' is an invalid file name." => "'.' é um nome de arquivo inválido.",
+"File name cannot be empty." => "O nome do arquivo não pode estar vazio.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome inválido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.",
+"Your storage is full, files can not be updated or synced anymore!" => "Seu armazenamento está cheio, arquivos não podem mais ser atualizados ou sincronizados!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Seu armazenamento está quase cheio ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Seu download está sendo preparado. Isto pode levar algum tempo se os arquivos forem grandes.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Impossível enviar seus arquivo por ele ser um diretório ou ter 0 bytes.",
"Upload Error" => "Erro de envio",
-"Pending" => "Pendente",
+"Close" => "Fechar",
"1 file uploading" => "enviando 1 arquivo",
-"files uploading" => "enviando arquivos",
+"{count} files uploading" => "Enviando {count} arquivos",
"Upload cancelled." => "Envio cancelado.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Upload em andamento. Sair da página agora resultará no cancelamento do envio.",
-"Invalid name, '/' is not allowed." => "Nome inválido, '/' não é permitido.",
-"files scanned" => "arquivos verificados",
-"error while scanning" => "erro durante verificação",
+"URL cannot be empty." => "URL não pode ficar em branco",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de pasta inválido. O uso de 'Shared' é reservado para o Owncloud",
"Name" => "Nome",
"Size" => "Tamanho",
"Modified" => "Modificado",
-"folder" => "pasta",
-"folders" => "pastas",
-"file" => "arquivo",
-"files" => "arquivos",
-"seconds ago" => "segundos atrás",
-"minute ago" => "minuto atrás",
-"minutes ago" => "minutos atrás",
-"today" => "hoje",
-"yesterday" => "ontem",
-"days ago" => "dias atrás",
-"last month" => "último mês",
-"months ago" => "meses atrás",
-"last year" => "último ano",
-"years ago" => "anos atrás",
+"1 folder" => "1 pasta",
+"{count} folders" => "{count} pastas",
+"1 file" => "1 arquivo",
+"{count} files" => "{count} arquivos",
+"Upload" => "Carregar",
"File handling" => "Tratamento de Arquivo",
"Maximum upload size" => "Tamanho máximo para carregar",
"max. possible: " => "max. possível:",
-"Needed for multi-file and folder downloads." => "Necessário para multiplos arquivos e diretório de downloads.",
+"Needed for multi-file and folder downloads." => "Necessário para download de múltiplos arquivos e diretórios.",
"Enable ZIP-download" => "Habilitar ZIP-download",
"0 is unlimited" => "0 para ilimitado",
"Maximum input size for ZIP files" => "Tamanho máximo para arquivo ZIP",
@@ -58,14 +58,16 @@
"New" => "Novo",
"Text file" => "Arquivo texto",
"Folder" => "Pasta",
-"From url" => "URL de origem",
-"Upload" => "Carregar",
+"From link" => "Do link",
+"Deleted files" => "Arquivos apagados",
"Cancel upload" => "Cancelar upload",
+"You don’t have write permissions here." => "Você não possui permissão de escrita aqui.",
"Nothing in here. Upload something!" => "Nada aqui.Carrege alguma coisa!",
-"Share" => "Compartilhar",
"Download" => "Baixar",
+"Unshare" => "Descompartilhar",
"Upload too large" => "Arquivo muito grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor.",
"Files are being scanned, please wait." => "Arquivos sendo escaneados, por favor aguarde.",
-"Current scanning" => "Scanning atual"
+"Current scanning" => "Scanning atual",
+"Upgrading filesystem cache..." => "Atualizando cache do sistema de arquivos..."
);
diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php
index 5968769f2a1..7162517e816 100644
--- a/apps/files/l10n/pt_PT.php
+++ b/apps/files/l10n/pt_PT.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Não foi possível mover o ficheiro %s - Já existe um ficheiro com esse nome",
+"Could not move %s" => "Não foi possível move o ficheiro %s",
+"Unable to rename file" => "Não foi possível renomear o ficheiro",
+"No file was uploaded. Unknown error" => "Nenhum ficheiro foi carregado. Erro desconhecido",
"There is no error, the file uploaded with success" => "Sem erro, ficheiro enviado com sucesso",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O ficheiro enviado excede a directiva upload_max_filesize no php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede o limite permitido na directiva do php.ini upload_max_filesize",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede o diretivo MAX_FILE_SIZE especificado no formulário HTML",
"The uploaded file was only partially uploaded" => "O ficheiro enviado só foi enviado parcialmente",
"No file was uploaded" => "Não foi enviado nenhum ficheiro",
"Missing a temporary folder" => "Falta uma pasta temporária",
"Failed to write to disk" => "Falhou a escrita no disco",
+"Not enough storage available" => "Não há espaço suficiente em disco",
+"Invalid directory." => "Directório Inválido",
"Files" => "Ficheiros",
-"Unshare" => "Deixar de partilhar",
+"Delete permanently" => "Eliminar permanentemente",
"Delete" => "Apagar",
"Rename" => "Renomear",
-"already exists" => "já existe",
+"Pending" => "Pendente",
+"{new_name} already exists" => "O nome {new_name} já existe",
"replace" => "substituir",
-"suggest name" => "Sugira um nome",
+"suggest name" => "sugira um nome",
"cancel" => "cancelar",
-"replaced" => "substituído",
+"replaced {new_name} with {old_name}" => "substituido {new_name} por {old_name}",
"undo" => "desfazer",
-"with" => "com",
-"unshared" => "não partilhado",
-"deleted" => "apagado",
-"generating ZIP-file, it may take some time." => "a gerar o ficheiro ZIP, poderá demorar algum tempo.",
+"perform delete operation" => "Executar a tarefa de apagar",
+"'.' is an invalid file name." => "'.' não é um nome de ficheiro válido!",
+"File name cannot be empty." => "O nome do ficheiro não pode estar vazio.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome Inválido, os caracteres '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.",
+"Your storage is full, files can not be updated or synced anymore!" => "O seu armazenamento está cheio, os ficheiros não podem ser sincronizados.",
+"Your storage is almost full ({usedSpacePercent}%)" => "O seu espaço de armazenamento está quase cheiro ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "O seu download está a ser preparado. Este processo pode demorar algum tempo se os ficheiros forem grandes.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Não é possível fazer o envio do ficheiro devido a ser uma pasta ou ter 0 bytes",
"Upload Error" => "Erro no envio",
-"Pending" => "Pendente",
+"Close" => "Fechar",
"1 file uploading" => "A enviar 1 ficheiro",
-"files uploading" => "ficheiros a serem enviados",
-"Upload cancelled." => "O envio foi cancelado.",
+"{count} files uploading" => "A carregar {count} ficheiros",
+"Upload cancelled." => "Envio cancelado.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Envio de ficheiro em progresso. Irá cancelar o envio se sair da página agora.",
-"Invalid name, '/' is not allowed." => "Nome inválido, '/' não permitido.",
-"files scanned" => "ficheiros analisados",
-"error while scanning" => "erro ao analisar",
+"URL cannot be empty." => "O URL não pode estar vazio.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de pasta inválido. O Uso de 'shared' é reservado para o ownCloud",
"Name" => "Nome",
"Size" => "Tamanho",
"Modified" => "Modificado",
-"folder" => "pasta",
-"folders" => "pastas",
-"file" => "ficheiro",
-"files" => "ficheiros",
-"seconds ago" => "há segundos",
-"minute ago" => "há um minuto",
-"minutes ago" => "há minutos",
-"today" => "hoje",
-"yesterday" => "ontem",
-"days ago" => "há dias",
-"last month" => "mês passado",
-"months ago" => "há meses",
-"last year" => "ano passado",
-"years ago" => "há anos",
+"1 folder" => "1 pasta",
+"{count} folders" => "{count} pastas",
+"1 file" => "1 ficheiro",
+"{count} files" => "{count} ficheiros",
+"Upload" => "Enviar",
"File handling" => "Manuseamento de ficheiros",
"Maximum upload size" => "Tamanho máximo de envio",
"max. possible: " => "max. possivel: ",
@@ -58,14 +58,16 @@
"New" => "Novo",
"Text file" => "Ficheiro de texto",
"Folder" => "Pasta",
-"From url" => "Do endereço",
-"Upload" => "Enviar",
+"From link" => "Da ligação",
+"Deleted files" => "Ficheiros eliminados",
"Cancel upload" => "Cancelar envio",
+"You don’t have write permissions here." => "Não tem permissões de escrita aqui.",
"Nothing in here. Upload something!" => "Vazio. Envie alguma coisa!",
-"Share" => "Partilhar",
"Download" => "Transferir",
+"Unshare" => "Deixar de partilhar",
"Upload too large" => "Envio muito grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que está a tentar enviar excedem o tamanho máximo de envio permitido neste servidor.",
"Files are being scanned, please wait." => "Os ficheiros estão a ser analisados, por favor aguarde.",
-"Current scanning" => "Análise actual"
+"Current scanning" => "Análise actual",
+"Upgrading filesystem cache..." => "Atualizar cache do sistema de ficheiros..."
);
diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php
index 8d651e23827..153caba2291 100644
--- a/apps/files/l10n/ro.php
+++ b/apps/files/l10n/ro.php
@@ -1,52 +1,47 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Nu se poate de mutat %s - Fișier cu acest nume deja există",
+"Could not move %s" => "Nu s-a putut muta %s",
+"Unable to rename file" => "Nu s-a putut redenumi fișierul",
+"No file was uploaded. Unknown error" => "Nici un fișier nu a fost încărcat. Eroare necunoscută",
"There is no error, the file uploaded with success" => "Nicio eroare, fișierul a fost încărcat cu succes",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Fișierul are o dimensiune mai mare decât cea specificată în variabila upload_max_filesize din php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Fisierul incarcat depaseste upload_max_filesize permisi in php.ini: ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Fișierul are o dimensiune mai mare decât variabile MAX_FILE_SIZE specificată în formularul HTML",
"The uploaded file was only partially uploaded" => "Fișierul a fost încărcat doar parțial",
"No file was uploaded" => "Niciun fișier încărcat",
"Missing a temporary folder" => "Lipsește un dosar temporar",
"Failed to write to disk" => "Eroare la scriere pe disc",
+"Invalid directory." => "Director invalid.",
"Files" => "Fișiere",
-"Unshare" => "Anulează partajarea",
"Delete" => "Șterge",
"Rename" => "Redenumire",
-"already exists" => "deja există",
+"Pending" => "În așteptare",
+"{new_name} already exists" => "{new_name} deja exista",
"replace" => "înlocuire",
"suggest name" => "sugerează nume",
"cancel" => "anulare",
-"replaced" => "înlocuit",
+"replaced {new_name} with {old_name}" => "{new_name} inlocuit cu {old_name}",
"undo" => "Anulează ultima acțiune",
-"with" => "cu",
-"unshared" => "nepartajat",
-"deleted" => "șters",
-"generating ZIP-file, it may take some time." => "se generază fișierul ZIP, va dura ceva timp.",
+"'.' is an invalid file name." => "'.' este un nume invalid de fișier.",
+"File name cannot be empty." => "Numele fișierului nu poate rămâne gol.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nume invalid, '\\', '/', '<', '>', ':', '\"', '|', '?' si '*' nu sunt permise.",
+"Your download is being prepared. This might take some time if the files are big." => "Se pregătește descărcarea. Aceasta poate să dureze ceva timp dacă fișierele sunt mari.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nu s-a putut încărca fișierul tău deoarece pare să fie un director sau are 0 bytes.",
"Upload Error" => "Eroare la încărcare",
-"Pending" => "În așteptare",
+"Close" => "Închide",
"1 file uploading" => "un fișier se încarcă",
-"files uploading" => "fișiere se încarcă",
+"{count} files uploading" => "{count} fisiere incarcate",
"Upload cancelled." => "Încărcare anulată.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.",
-"Invalid name, '/' is not allowed." => "Nume invalid, '/' nu este permis.",
-"files scanned" => "fișiere scanate",
-"error while scanning" => "eroare la scanarea",
+"URL cannot be empty." => "Adresa URL nu poate fi goală.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Invalid folder name. Usage of 'Shared' is reserved by Ownclou",
"Name" => "Nume",
"Size" => "Dimensiune",
"Modified" => "Modificat",
-"folder" => "director",
-"folders" => "directoare",
-"file" => "fișier",
-"files" => "fișiere",
-"seconds ago" => "secunde în urmă",
-"minute ago" => "minut în urmă",
-"minutes ago" => "minute în urmă",
-"today" => "astăzi",
-"yesterday" => "ieri",
-"days ago" => "zile în urmă",
-"last month" => "ultima lună",
-"months ago" => "luni în urmă",
-"last year" => "ultimul an",
-"years ago" => "ani în urmă",
+"1 folder" => "1 folder",
+"{count} folders" => "{count} foldare",
+"1 file" => "1 fisier",
+"{count} files" => "{count} fisiere",
+"Upload" => "Încarcă",
"File handling" => "Manipulare fișiere",
"Maximum upload size" => "Dimensiune maximă admisă la încărcare",
"max. possible: " => "max. posibil:",
@@ -58,12 +53,11 @@
"New" => "Nou",
"Text file" => "Fișier text",
"Folder" => "Dosar",
-"From url" => "De la URL",
-"Upload" => "Încarcă",
+"From link" => "de la adresa",
"Cancel upload" => "Anulează încărcarea",
"Nothing in here. Upload something!" => "Nimic aici. Încarcă ceva!",
-"Share" => "Partajează",
"Download" => "Descarcă",
+"Unshare" => "Anulează partajarea",
"Upload too large" => "Fișierul încărcat este prea mare",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fișierul care l-ai încărcat a depășită limita maximă admisă la încărcare pe acest server.",
"Files are being scanned, please wait." => "Fișierele sunt scanate, te rog așteptă.",
diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php
index d21d6e3a6d8..cf8ee7c6c75 100644
--- a/apps/files/l10n/ru.php
+++ b/apps/files/l10n/ru.php
@@ -1,37 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Невозможно переместить %s - файл с таким именем уже существует",
+"Could not move %s" => "Невозможно переместить %s",
+"Unable to rename file" => "Невозможно переименовать файл",
+"No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка",
"There is no error, the file uploaded with success" => "Файл успешно загружен",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Файл превышает допустимые размеры (описаны как upload_max_filesize в php.ini)",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Файл превышает размер установленный upload_max_filesize в php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Файл превышает размер MAX_FILE_SIZE, указаный в HTML-форме",
"The uploaded file was only partially uploaded" => "Файл был загружен не полностью",
"No file was uploaded" => "Файл не был загружен",
"Missing a temporary folder" => "Невозможно найти временную папку",
"Failed to write to disk" => "Ошибка записи на диск",
+"Not enough storage available" => "Недостаточно доступного места в хранилище",
+"Invalid directory." => "Неправильный каталог.",
"Files" => "Файлы",
-"Unshare" => "Отменить публикацию",
+"Delete permanently" => "Удалено навсегда",
"Delete" => "Удалить",
-"already exists" => "уже существует",
+"Rename" => "Переименовать",
+"Pending" => "Ожидание",
+"{new_name} already exists" => "{new_name} уже существует",
"replace" => "заменить",
"suggest name" => "предложить название",
"cancel" => "отмена",
-"replaced" => "заменён",
+"replaced {new_name} with {old_name}" => "заменено {new_name} на {old_name}",
"undo" => "отмена",
-"with" => "с",
-"unshared" => "публикация отменена",
-"deleted" => "удален",
-"generating ZIP-file, it may take some time." => "создание ZIP-файла, это может занять некоторое время.",
+"perform delete operation" => "выполняется операция удаления",
+"'.' is an invalid file name." => "'.' - неправильное имя файла.",
+"File name cannot be empty." => "Имя файла не может быть пустым.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправильное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' недопустимы.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ваше дисковое пространство полностью заполнено, произведите очистку перед загрузкой новых файлов.",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ваше хранилище почти заполнено ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Загрузка началась. Это может потребовать много времени, если файл большого размера.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Не удается загрузить файл размером 0 байт в каталог",
"Upload Error" => "Ошибка загрузки",
-"Pending" => "Ожидание",
+"Close" => "Закрыть",
+"1 file uploading" => "загружается 1 файл",
+"{count} files uploading" => "{count} файлов загружается",
"Upload cancelled." => "Загрузка отменена.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Файл в процессе загрузки. Покинув страницу вы прервёте загрузку.",
-"Invalid name, '/' is not allowed." => "Неверное имя, '/' не допускается.",
+"URL cannot be empty." => "Ссылка не может быть пустой.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неправильное имя каталога. Имя 'Shared' зарезервировано.",
"Name" => "Название",
"Size" => "Размер",
"Modified" => "Изменён",
-"folder" => "папка",
-"folders" => "папки",
-"file" => "файл",
-"files" => "файлы",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папок",
+"1 file" => "1 файл",
+"{count} files" => "{count} файлов",
+"Upload" => "Загрузить",
"File handling" => "Управление файлами",
"Maximum upload size" => "Максимальный размер загружаемого файла",
"max. possible: " => "макс. возможно: ",
@@ -43,14 +58,16 @@
"New" => "Новый",
"Text file" => "Текстовый файл",
"Folder" => "Папка",
-"From url" => "С url",
-"Upload" => "Загрузить",
+"From link" => "Из ссылки",
+"Deleted files" => "Удалённые файлы",
"Cancel upload" => "Отмена загрузки",
+"You don’t have write permissions here." => "У вас нет разрешений на запись здесь.",
"Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!",
-"Share" => "Опубликовать",
"Download" => "Скачать",
+"Unshare" => "Отменить публикацию",
"Upload too large" => "Файл слишком большой",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файлы, которые Вы пытаетесь загрузить, превышают лимит для файлов на этом сервере.",
"Files are being scanned, please wait." => "Подождите, файлы сканируются.",
-"Current scanning" => "Текущее сканирование"
+"Current scanning" => "Текущее сканирование",
+"Upgrading filesystem cache..." => "Обновление кеша файловой системы..."
);
diff --git a/apps/files/l10n/ru_RU.php b/apps/files/l10n/ru_RU.php
index 34e9d19ca33..054ed8094db 100644
--- a/apps/files/l10n/ru_RU.php
+++ b/apps/files/l10n/ru_RU.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Неполучается перенести %s - Файл с таким именем уже существует",
+"Could not move %s" => "Неполучается перенести %s ",
+"Unable to rename file" => "Невозможно переименовать файл",
+"No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка",
"There is no error, the file uploaded with success" => "Ошибка отсутствует, файл загружен успешно.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Размер загруженного файла превышает заданный в директиве upload_max_filesize в php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Размер загружаемого файла превышает upload_max_filesize директиву в php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Размер загруженного",
"The uploaded file was only partially uploaded" => "Загружаемый файл был загружен частично",
"No file was uploaded" => "Файл не был загружен",
"Missing a temporary folder" => "Отсутствует временная папка",
"Failed to write to disk" => "Не удалось записать на диск",
+"Not enough storage available" => "Недостаточно места в хранилище",
+"Invalid directory." => "Неверный каталог.",
"Files" => "Файлы",
-"Unshare" => "Скрыть",
+"Delete permanently" => "Удалить навсегда",
"Delete" => "Удалить",
"Rename" => "Переименовать",
-"already exists" => "уже существует",
+"Pending" => "Ожидающий решения",
+"{new_name} already exists" => "{новое_имя} уже существует",
"replace" => "отмена",
"suggest name" => "подобрать название",
"cancel" => "отменить",
-"replaced" => "заменено",
+"replaced {new_name} with {old_name}" => "заменено {новое_имя} с {старое_имя}",
"undo" => "отменить действие",
-"with" => "с",
-"unshared" => "скрытый",
-"deleted" => "удалено",
-"generating ZIP-file, it may take some time." => "Создание ZIP-файла, это может занять некоторое время.",
+"perform delete operation" => "выполняется процесс удаления",
+"'.' is an invalid file name." => "'.' является неверным именем файла.",
+"File name cannot be empty." => "Имя файла не может быть пустым.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Некорректное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не допустимы.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ваше хранилище переполнено, фалы больше не могут быть обновлены или синхронизированы!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ваше хранилище почти полно ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Идёт подготовка к скачке Вашего файла. Это может занять некоторое время, если фалы большие.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Невозможно загрузить файл,\n так как он имеет нулевой размер или является директорией",
"Upload Error" => "Ошибка загрузки",
-"Pending" => "Ожидающий решения",
+"Close" => "Закрыть",
"1 file uploading" => "загрузка 1 файла",
-"files uploading" => "загрузка файлов",
+"{count} files uploading" => "{количество} загружено файлов",
"Upload cancelled." => "Загрузка отменена",
"File upload is in progress. Leaving the page now will cancel the upload." => "Процесс загрузки файла. Если покинуть страницу сейчас, загрузка будет отменена.",
-"Invalid name, '/' is not allowed." => "Неправильное имя, '/' не допускается.",
-"files scanned" => "файлы отсканированы",
-"error while scanning" => "ошибка при сканировании",
+"URL cannot be empty." => "URL не должен быть пустым.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неверное имя папки. Использование наименования 'Опубликовано' зарезервировано Owncloud",
"Name" => "Имя",
"Size" => "Размер",
"Modified" => "Изменен",
-"folder" => "папка",
-"folders" => "папки",
-"file" => "файл",
-"files" => "файлы",
-"seconds ago" => "секунд назад",
-"minute ago" => "минуту назад",
-"minutes ago" => "минут назад",
-"today" => "сегодня",
-"yesterday" => "вчера",
-"days ago" => "дней назад",
-"last month" => "в прошлом месяце",
-"months ago" => "месяцев назад",
-"last year" => "в прошлом году",
-"years ago" => "лет назад",
+"1 folder" => "1 папка",
+"{count} folders" => "{количество} папок",
+"1 file" => "1 файл",
+"{count} files" => "{количество} файлов",
+"Upload" => "Загрузить ",
"File handling" => "Работа с файлами",
"Maximum upload size" => "Максимальный размер загружаемого файла",
"max. possible: " => "Максимально возможный",
@@ -58,14 +58,14 @@
"New" => "Новый",
"Text file" => "Текстовый файл",
"Folder" => "Папка",
-"From url" => "Из url",
-"Upload" => "Загрузить ",
+"From link" => "По ссылке",
"Cancel upload" => "Отмена загрузки",
"Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!",
-"Share" => "Сделать общим",
"Download" => "Загрузить",
+"Unshare" => "Скрыть",
"Upload too large" => "Загрузка слишком велика",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Размер файлов, которые Вы пытаетесь загрузить, превышает максимально допустимый размер для загрузки на данный сервер.",
"Files are being scanned, please wait." => "Файлы сканируются, пожалуйста, подождите.",
-"Current scanning" => "Текущее сканирование"
+"Current scanning" => "Текущее сканирование",
+"Upgrading filesystem cache..." => "Обновление кэша файловой системы... "
);
diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php
new file mode 100644
index 00000000000..de2b8906845
--- /dev/null
+++ b/apps/files/l10n/si_LK.php
@@ -0,0 +1,48 @@
+<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "ගොනුවක් උඩුගත නොවුනි. නොහැඳිනු දෝෂයක්",
+"There is no error, the file uploaded with success" => "නිවැරදි ව ගොනුව උඩුගත කෙරිනි",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "උඩුගත කළ ගොනුවේ විශාලත්වය HTML පෝරමයේ නියම කළ ඇති MAX_FILE_SIZE විශාලත්වයට වඩා වැඩිය",
+"The uploaded file was only partially uploaded" => "උඩුගත කළ ගොනුවේ කොටසක් පමණක් උඩුගත විය",
+"No file was uploaded" => "කිසිදු ගොනවක් උඩුගත නොවිනි",
+"Missing a temporary folder" => "තාවකාලික ෆොල්ඩරයක් සොයාගත නොහැක",
+"Failed to write to disk" => "තැටිගත කිරීම අසාර්ථකයි",
+"Files" => "ගොනු",
+"Delete" => "මකන්න",
+"Rename" => "නැවත නම් කරන්න",
+"replace" => "ප්‍රතිස්ථාපනය කරන්න",
+"suggest name" => "නමක් යෝජනා කරන්න",
+"cancel" => "අත් හරින්න",
+"undo" => "නිෂ්ප්‍රභ කරන්න",
+"Upload Error" => "උඩුගත කිරීමේ දෝශයක්",
+"Close" => "වසන්න",
+"1 file uploading" => "1 ගොනුවක් උඩගත කෙරේ",
+"Upload cancelled." => "උඩුගත කිරීම අත් හරින්න ලදී",
+"File upload is in progress. Leaving the page now will cancel the upload." => "උඩුගතකිරීමක් සිදුවේ. පිටුව හැර යාමෙන් එය නැවතෙනු ඇත",
+"URL cannot be empty." => "යොමුව හිස් විය නොහැක",
+"Name" => "නම",
+"Size" => "ප්‍රමාණය",
+"Modified" => "වෙනස් කළ",
+"1 folder" => "1 ෆොල්ඩරයක්",
+"1 file" => "1 ගොනුවක්",
+"Upload" => "උඩුගත කිරීම",
+"File handling" => "ගොනු පරිහරණය",
+"Maximum upload size" => "උඩුගත කිරීමක උපරිම ප්‍රමාණය",
+"max. possible: " => "හැකි උපරිමය:",
+"Needed for multi-file and folder downloads." => "බහු-ගොනු හා ෆොල්ඩර බාගත කිරීමට අවශ්‍යයි",
+"Enable ZIP-download" => "ZIP-බාගත කිරීම් සක්‍රිය කරන්න",
+"0 is unlimited" => "0 යනු සීමාවක් නැති බවය",
+"Maximum input size for ZIP files" => "ZIP ගොනු සඳහා දැමිය හැකි උපරිම විශාලතවය",
+"Save" => "සුරකින්න",
+"New" => "නව",
+"Text file" => "පෙළ ගොනුව",
+"Folder" => "ෆෝල්ඩරය",
+"From link" => "යොමුවෙන්",
+"Cancel upload" => "උඩුගත කිරීම අත් හරින්න",
+"Nothing in here. Upload something!" => "මෙහි කිසිවක් නොමැත. යමක් උඩුගත කරන්න",
+"Download" => "බාගත කිරීම",
+"Unshare" => "නොබෙදු",
+"Upload too large" => "උඩුගත කිරීම විශාල වැඩිය",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "ඔබ උඩුගත කිරීමට තැත් කරන ගොනු මෙම සේවාදායකයා උඩුගත කිරීමට ඉඩදී ඇති උපරිම ගොනු විශාලත්වයට වඩා වැඩිය",
+"Files are being scanned, please wait." => "ගොනු පරික්ෂා කෙරේ. මඳක් රැඳී සිටින්න",
+"Current scanning" => "වර්තමාන පරික්ෂාව"
+);
diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php
index 7f8c35650dd..a6cb2909111 100644
--- a/apps/files/l10n/sk_SK.php
+++ b/apps/files/l10n/sk_SK.php
@@ -1,56 +1,56 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Nie je možné presunúť %s - súbor s týmto menom už existuje",
+"Could not move %s" => "Nie je možné presunúť %s",
+"Unable to rename file" => "Nemožno premenovať súbor",
+"No file was uploaded. Unknown error" => "Žiaden súbor nebol odoslaný. Neznáma chyba",
"There is no error, the file uploaded with success" => "Nenastala žiadna chyba, súbor bol úspešne nahraný",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Nahraný súbor presiahol direktívu upload_max_filesize v php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Nahraný súbor predčil konfiguračnú direktívu upload_max_filesize v súbore php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Nahrávaný súbor presiahol MAX_FILE_SIZE direktívu, ktorá bola špecifikovaná v HTML formulári",
"The uploaded file was only partially uploaded" => "Nahrávaný súbor bol iba čiastočne nahraný",
"No file was uploaded" => "Žiaden súbor nebol nahraný",
"Missing a temporary folder" => "Chýbajúci dočasný priečinok",
"Failed to write to disk" => "Zápis na disk sa nepodaril",
+"Not enough storage available" => "Nedostatok dostupného úložného priestoru",
+"Invalid directory." => "Neplatný priečinok",
"Files" => "Súbory",
-"Unshare" => "Nezdielať",
+"Delete permanently" => "Zmazať trvalo",
"Delete" => "Odstrániť",
"Rename" => "Premenovať",
-"already exists" => "už existuje",
+"Pending" => "Čaká sa",
+"{new_name} already exists" => "{new_name} už existuje",
"replace" => "nahradiť",
"suggest name" => "pomôcť s menom",
"cancel" => "zrušiť",
-"replaced" => "zmenené",
+"replaced {new_name} with {old_name}" => "prepísaný {new_name} súborom {old_name}",
"undo" => "vrátiť",
-"with" => "s",
-"unshared" => "zdielané",
-"deleted" => "zmazané",
-"generating ZIP-file, it may take some time." => "generujem ZIP-súbor, môže to chvíľu trvať.",
+"perform delete operation" => "vykonať zmazanie",
+"'.' is an invalid file name." => "'.' je neplatné meno súboru.",
+"File name cannot be empty." => "Meno súboru nemôže byť prázdne",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nesprávne meno, '\\', '/', '<', '>', ':', '\"', '|', '?' a '*' nie sú povolené hodnoty.",
+"Your storage is full, files can not be updated or synced anymore!" => "Vaše úložisko je plné. Súbory nemožno aktualizovať ani synchronizovať!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložisko je takmer plné ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Vaše sťahovanie sa pripravuje. Ak sú sťahované súbory veľké, môže to chvíľu trvať.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nemôžem nahrať súbor lebo je to priečinok alebo má 0 bajtov.",
-"Upload Error" => "Chyba nahrávania",
-"Pending" => "Čaká sa",
+"Upload Error" => "Chyba odosielania",
+"Close" => "Zavrieť",
"1 file uploading" => "1 súbor sa posiela ",
-"files uploading" => "súbory sa posielajú",
-"Upload cancelled." => "Nahrávanie zrušené",
+"{count} files uploading" => "{count} súborov odosielaných",
+"Upload cancelled." => "Odosielanie zrušené",
"File upload is in progress. Leaving the page now will cancel the upload." => "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.",
-"Invalid name, '/' is not allowed." => "Chybný názov, \"/\" nie je povolené",
-"files scanned" => "skontrolovaných súborov",
-"error while scanning" => "chyba počas kontroly",
+"URL cannot be empty." => "URL nemôže byť prázdne",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatné meno priečinka. Používanie mena 'Shared' je vyhradené len pre Owncloud",
"Name" => "Meno",
"Size" => "Veľkosť",
"Modified" => "Upravené",
-"folder" => "priečinok",
-"folders" => "priečinky",
-"file" => "súbor",
-"files" => "súbory",
-"seconds ago" => "pred sekundami",
-"minute ago" => "pred minútou",
-"minutes ago" => "pred minútami",
-"today" => "dnes",
-"yesterday" => "včera",
-"days ago" => "pred pár dňami",
-"last month" => "minulý mesiac",
-"months ago" => "pred mesiacmi",
-"last year" => "minulý rok",
-"years ago" => "pred rokmi",
-"File handling" => "Nastavenie správanie k súborom",
+"1 folder" => "1 priečinok",
+"{count} folders" => "{count} priečinkov",
+"1 file" => "1 súbor",
+"{count} files" => "{count} súborov",
+"Upload" => "Odoslať",
+"File handling" => "Nastavenie správania sa k súborom",
"Maximum upload size" => "Maximálna veľkosť odosielaného súboru",
"max. possible: " => "najväčšie možné:",
-"Needed for multi-file and folder downloads." => "Vyžadované pre sťahovanie viacerých súborov a adresárov.",
+"Needed for multi-file and folder downloads." => "Vyžadované pre sťahovanie viacerých súborov a priečinkov.",
"Enable ZIP-download" => "Povoliť sťahovanie ZIP súborov",
"0 is unlimited" => "0 znamená neobmedzené",
"Maximum input size for ZIP files" => "Najväčšia veľkosť ZIP súborov",
@@ -58,14 +58,16 @@
"New" => "Nový",
"Text file" => "Textový súbor",
"Folder" => "Priečinok",
-"From url" => "Z url",
-"Upload" => "Nahrať",
+"From link" => "Z odkazu",
+"Deleted files" => "Zmazané súbory",
"Cancel upload" => "Zrušiť odosielanie",
+"You don’t have write permissions here." => "Nemáte oprávnenie na zápis.",
"Nothing in here. Upload something!" => "Žiadny súbor. Nahrajte niečo!",
-"Share" => "Zdielať",
"Download" => "Stiahnuť",
+"Unshare" => "Nezdielať",
"Upload too large" => "Odosielaný súbor je príliš veľký",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory ktoré sa snažíte nahrať presahujú maximálnu veľkosť pre nahratie súborov na tento server.",
-"Files are being scanned, please wait." => "Súbory sa práve prehľadávajú, prosím čakajte.",
-"Current scanning" => "Práve prehliadané"
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory, ktoré sa snažíte nahrať, presahujú maximálnu veľkosť pre nahratie súborov na tento server.",
+"Files are being scanned, please wait." => "Čakajte, súbory sú prehľadávané.",
+"Current scanning" => "Práve prezerané",
+"Upgrading filesystem cache..." => "Aktualizujem medzipamäť súborového systému..."
);
diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php
index 2a733d2c26a..01405530ffa 100644
--- a/apps/files/l10n/sl.php
+++ b/apps/files/l10n/sl.php
@@ -1,59 +1,73 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Datoteka je bila uspešno naložena.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Naložena datoteka presega velikost, ki jo določa parameter upload_max_filesize v datoteki php.ini",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Naložena datoteka presega velikost, ki jo določa parameter MAX_FILE_SIZE v HTML obrazcu",
-"The uploaded file was only partially uploaded" => "Datoteka je bila le delno naložena",
+"Could not move %s - File with this name already exists" => "Ni mogoče premakniti %s - datoteka s tem imenom že obstaja",
+"Could not move %s" => "Ni mogoče premakniti %s",
+"Unable to rename file" => "Ni mogoče preimenovati datoteke",
+"No file was uploaded. Unknown error" => "Ni poslane nobene datoteke. Neznana napaka.",
+"There is no error, the file uploaded with success" => "Datoteka je uspešno poslana.",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Poslana datoteka presega dovoljeno velikost, ki je določena z možnostjo upload_max_filesize v datoteki php.ini:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka presega velikost, ki jo določa parameter največje dovoljene velikosti v obrazcu HTML.",
+"The uploaded file was only partially uploaded" => "Datoteka je le delno naložena",
"No file was uploaded" => "Nobena datoteka ni bila naložena",
"Missing a temporary folder" => "Manjka začasna mapa",
"Failed to write to disk" => "Pisanje na disk je spodletelo",
+"Not enough storage available" => "Na voljo ni dovolj prostora",
+"Invalid directory." => "Neveljavna mapa.",
"Files" => "Datoteke",
-"Unshare" => "Odstrani iz souporabe",
+"Delete permanently" => "Izbriši trajno",
"Delete" => "Izbriši",
"Rename" => "Preimenuj",
-"already exists" => "že obstaja",
-"replace" => "nadomesti",
+"Pending" => "V čakanju ...",
+"{new_name} already exists" => "{new_name} že obstaja",
+"replace" => "zamenjaj",
"suggest name" => "predlagaj ime",
-"cancel" => "ekliči",
-"replaced" => "nadomeščen",
+"cancel" => "prekliči",
+"replaced {new_name} with {old_name}" => "preimenovano ime {new_name} z imenom {old_name}",
"undo" => "razveljavi",
-"with" => "z",
-"unshared" => "odstranjeno iz souporabe",
-"deleted" => "izbrisano",
-"generating ZIP-file, it may take some time." => "Ustvarjam ZIP datoteko. To lahko traja nekaj časa.",
-"Unable to upload your file as it is a directory or has 0 bytes" => "Nalaganje ni mogoče, saj gre za mapo, ali pa ima datoteka velikost 0 bajtov.",
-"Upload Error" => "Napaka pri nalaganju",
-"Pending" => "Na čakanju",
-"Upload cancelled." => "Nalaganje je bilo preklicano.",
-"File upload is in progress. Leaving the page now will cancel the upload." => "Nalaganje datoteke je v teku. Če zapustite to stran zdaj, boste nalaganje preklicali.",
-"Invalid name, '/' is not allowed." => "Neveljavno ime. Znak '/' ni dovoljen.",
-"files scanned" => "pregledanih datotek",
-"error while scanning" => "napaka med pregledovanjem datotek",
+"perform delete operation" => "izvedi opravilo brisanja",
+"'.' is an invalid file name." => "'.' je neveljavno ime datoteke.",
+"File name cannot be empty." => "Ime datoteke ne sme biti prazno polje.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Neveljavno ime, znaki '\\', '/', '<', '>', ':', '\"', '|', '?' in '*' niso dovoljeni.",
+"Your storage is full, files can not be updated or synced anymore!" => "Shramba je povsem napolnjena. Datotek ni več mogoče posodabljati in usklajevati!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Mesto za shranjevanje je skoraj polno ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Postopek priprave datoteke za prejem je lahko dolgotrajen, če je datoteka zelo velika.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Pošiljanje ni mogoče, saj gre za mapo, ali pa je datoteka velikosti 0 bajtov.",
+"Upload Error" => "Napaka med pošiljanjem",
+"Close" => "Zapri",
+"1 file uploading" => "Pošiljanje 1 datoteke",
+"{count} files uploading" => "pošiljanje {count} datotek",
+"Upload cancelled." => "Pošiljanje je preklicano.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "V teku je pošiljanje datoteke. Če zapustite to stran zdaj, bo pošiljanje preklicano.",
+"URL cannot be empty." => "Naslov URL ne sme biti prazna vrednost.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neveljavno ime mape. Uporaba oznake \"Souporaba\" je zadržan za sistem ownCloud.",
"Name" => "Ime",
"Size" => "Velikost",
"Modified" => "Spremenjeno",
-"folder" => "mapa",
-"folders" => "mape",
-"file" => "datoteka",
-"files" => "datoteke",
-"File handling" => "Rokovanje z datotekami",
-"Maximum upload size" => "Največja velikost za nalaganje",
+"1 folder" => "1 mapa",
+"{count} folders" => "{count} map",
+"1 file" => "1 datoteka",
+"{count} files" => "{count} datotek",
+"Upload" => "Pošlji",
+"File handling" => "Upravljanje z datotekami",
+"Maximum upload size" => "Največja velikost za pošiljanja",
"max. possible: " => "največ mogoče:",
-"Needed for multi-file and folder downloads." => "Potrebno za prenose večih datotek in map.",
-"Enable ZIP-download" => "Omogoči ZIP-prejemanje",
-"0 is unlimited" => "0 je neskončno",
-"Maximum input size for ZIP files" => "Največja vhodna velikost za ZIP datoteke",
+"Needed for multi-file and folder downloads." => "Uporabljeno za prejem več datotek in map.",
+"Enable ZIP-download" => "Omogoči prejemanje arhivov ZIP",
+"0 is unlimited" => "0 predstavlja neomejeno vrednost",
+"Maximum input size for ZIP files" => "Največja vhodna velikost za datoteke ZIP",
"Save" => "Shrani",
"New" => "Nova",
"Text file" => "Besedilna datoteka",
"Folder" => "Mapa",
-"From url" => "Iz url naslova",
-"Upload" => "Naloži",
-"Cancel upload" => "Prekliči nalaganje",
-"Nothing in here. Upload something!" => "Tukaj ni ničesar. Naložite kaj!",
-"Share" => "Souporaba",
-"Download" => "Prenesi",
-"Upload too large" => "Nalaganje ni mogoče, ker je preveliko",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite naložiti, presegajo največjo dovoljeno velikost na tem strežniku.",
-"Files are being scanned, please wait." => "Preiskujem datoteke, prosimo počakajte.",
-"Current scanning" => "Trenutno preiskujem"
+"From link" => "Iz povezave",
+"Deleted files" => "Izbrisane datoteke",
+"Cancel upload" => "Prekliči pošiljanje",
+"You don’t have write permissions here." => "Za to mesto ni ustreznih dovoljenj za pisanje.",
+"Nothing in here. Upload something!" => "Tukaj še ni ničesar. Najprej je treba kakšno datoteko poslati v oblak!",
+"Download" => "Prejmi",
+"Unshare" => "Odstrani iz souporabe",
+"Upload too large" => "Prekoračenje omejitve velikosti",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite poslati, presegajo največjo dovoljeno velikost na strežniku.",
+"Files are being scanned, please wait." => "Poteka preučevanje datotek, počakajte ...",
+"Current scanning" => "Trenutno poteka preučevanje",
+"Upgrading filesystem cache..." => "Nadgrajevanje predpomnilnika datotečnega sistema ..."
);
diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php
index 99e4b12697c..fe71ee9c90d 100644
--- a/apps/files/l10n/sr.php
+++ b/apps/files/l10n/sr.php
@@ -1,22 +1,55 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Нема грешке, фајл је успешно послат",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Послати фајл превазилази директиву upload_max_filesize из ",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Послати фајл превазилази директиву MAX_FILE_SIZE која је наведена у ХТМЛ форми",
-"The uploaded file was only partially uploaded" => "Послати фајл је само делимично отпремљен!",
-"No file was uploaded" => "Ниједан фајл није послат",
+"There is no error, the file uploaded with success" => "Није дошло до грешке. Датотека је успешно отпремљена.",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Отпремљена датотека прелази смерницу upload_max_filesize у датотеци php.ini:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Отпремљена датотека прелази смерницу MAX_FILE_SIZE која је наведена у HTML обрасцу",
+"The uploaded file was only partially uploaded" => "Датотека је делимично отпремљена",
+"No file was uploaded" => "Датотека није отпремљена",
"Missing a temporary folder" => "Недостаје привремена фасцикла",
-"Files" => "Фајлови",
+"Failed to write to disk" => "Не могу да пишем на диск",
+"Files" => "Датотеке",
"Delete" => "Обриши",
-"Name" => "Име",
+"Rename" => "Преименуј",
+"Pending" => "На чекању",
+"{new_name} already exists" => "{new_name} већ постоји",
+"replace" => "замени",
+"suggest name" => "предложи назив",
+"cancel" => "откажи",
+"replaced {new_name} with {old_name}" => "замењено {new_name} са {old_name}",
+"undo" => "опозови",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неисправан назив. Следећи знакови нису дозвољени: \\, /, <, >, :, \", |, ? и *.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "Не могу да отпремим датотеку као фасциклу или она има 0 бајтова",
+"Upload Error" => "Грешка при отпремању",
+"Close" => "Затвори",
+"1 file uploading" => "Отпремам 1 датотеку",
+"{count} files uploading" => "Отпремам {count} датотеке/а",
+"Upload cancelled." => "Отпремање је прекинуто.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Отпремање датотеке је у току. Ако сада напустите страницу, прекинућете отпремање.",
+"Name" => "Назив",
"Size" => "Величина",
-"Modified" => "Задња измена",
-"Maximum upload size" => "Максимална величина пошиљке",
-"New" => "Нови",
-"Text file" => "текстуални фајл",
+"Modified" => "Измењено",
+"1 folder" => "1 фасцикла",
+"{count} folders" => "{count} фасцикле/и",
+"1 file" => "1 датотека",
+"{count} files" => "{count} датотеке/а",
+"Upload" => "Отпреми",
+"File handling" => "Управљање датотекама",
+"Maximum upload size" => "Највећа величина датотеке",
+"max. possible: " => "највећа величина:",
+"Needed for multi-file and folder downloads." => "Неопходно за преузимање вишеделних датотека и фасцикли.",
+"Enable ZIP-download" => "Омогући преузимање у ZIP-у",
+"0 is unlimited" => "0 је неограничено",
+"Maximum input size for ZIP files" => "Највећа величина ZIP датотека",
+"Save" => "Сачувај",
+"New" => "Нова",
+"Text file" => "текстуална датотека",
"Folder" => "фасцикла",
-"Upload" => "Пошаљи",
-"Nothing in here. Upload something!" => "Овде нема ничег. Пошаљите нешто!",
+"From link" => "Са везе",
+"Cancel upload" => "Прекини отпремање",
+"Nothing in here. Upload something!" => "Овде нема ничег. Отпремите нешто!",
"Download" => "Преузми",
-"Upload too large" => "Пошиљка је превелика",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Фајлови које желите да пошаљете превазилазе ограничење максималне величине пошиљке на овом серверу."
+"Unshare" => "Укини дељење",
+"Upload too large" => "Датотека је превелика",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеке које желите да отпремите прелазе ограничење у величини.",
+"Files are being scanned, please wait." => "Скенирам датотеке…",
+"Current scanning" => "Тренутно скенирање"
);
diff --git a/apps/files/l10n/sr@latin.php b/apps/files/l10n/sr@latin.php
index d8c7ef18989..0fda24532dc 100644
--- a/apps/files/l10n/sr@latin.php
+++ b/apps/files/l10n/sr@latin.php
@@ -1,17 +1,18 @@
<?php $TRANSLATIONS = array(
"There is no error, the file uploaded with success" => "Nema greške, fajl je uspešno poslat",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Poslati fajl prevazilazi direktivu upload_max_filesize iz ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslati fajl prevazilazi direktivu MAX_FILE_SIZE koja je navedena u HTML formi",
"The uploaded file was only partially uploaded" => "Poslati fajl je samo delimično otpremljen!",
"No file was uploaded" => "Nijedan fajl nije poslat",
"Missing a temporary folder" => "Nedostaje privremena fascikla",
"Files" => "Fajlovi",
"Delete" => "Obriši",
+"Close" => "Zatvori",
"Name" => "Ime",
"Size" => "Veličina",
"Modified" => "Zadnja izmena",
-"Maximum upload size" => "Maksimalna veličina pošiljke",
"Upload" => "Pošalji",
+"Maximum upload size" => "Maksimalna veličina pošiljke",
+"Save" => "Snimi",
"Nothing in here. Upload something!" => "Ovde nema ničeg. Pošaljite nešto!",
"Download" => "Preuzmi",
"Upload too large" => "Pošiljka je prevelika",
diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php
index d5dcf1ad2e8..ca9610a33c7 100644
--- a/apps/files/l10n/sv.php
+++ b/apps/files/l10n/sv.php
@@ -1,52 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Kunde inte flytta %s - Det finns redan en fil med detta namn",
+"Could not move %s" => "Kan inte flytta %s",
+"Unable to rename file" => "Kan inte byta namn på filen",
+"No file was uploaded. Unknown error" => "Ingen fil uppladdad. Okänt fel",
"There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den uppladdade filen överskrider upload_max_filesize direktivet i php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uppladdade filen överskrider upload_max_filesize direktivet php.ini:",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uppladdade filen överstiger MAX_FILE_SIZE direktivet som anges i HTML-formulär",
"The uploaded file was only partially uploaded" => "Den uppladdade filen var endast delvis uppladdad",
"No file was uploaded" => "Ingen fil blev uppladdad",
"Missing a temporary folder" => "Saknar en tillfällig mapp",
"Failed to write to disk" => "Misslyckades spara till disk",
+"Not enough storage available" => "Inte tillräckligt med lagringsutrymme tillgängligt",
+"Invalid directory." => "Felaktig mapp.",
"Files" => "Filer",
-"Unshare" => "Sluta dela",
+"Delete permanently" => "Radera permanent",
"Delete" => "Radera",
"Rename" => "Byt namn",
-"already exists" => "finns redan",
+"Pending" => "Väntar",
+"{new_name} already exists" => "{new_name} finns redan",
"replace" => "ersätt",
"suggest name" => "föreslå namn",
"cancel" => "avbryt",
-"replaced" => "ersatt",
+"replaced {new_name} with {old_name}" => "ersatt {new_name} med {old_name}",
"undo" => "ångra",
-"with" => "med",
-"unshared" => "Ej delad",
-"deleted" => "raderad",
-"generating ZIP-file, it may take some time." => "genererar ZIP-fil, det kan ta lite tid.",
+"perform delete operation" => "utför raderingen",
+"'.' is an invalid file name." => "'.' är ett ogiltigt filnamn.",
+"File name cannot be empty." => "Filnamn kan inte vara tomt.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ogiltigt namn, '\\', '/', '<', '>', ':', '\"', '|', '?' och '*' är inte tillåtet.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ditt lagringsutrymme är fullt, filer kan ej längre laddas upp eller synkas!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ditt lagringsutrymme är nästan fullt ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Din nedladdning förbereds. Det kan ta tid om det är stora filer.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kunde inte ladda upp dina filer eftersom det antingen är en mapp eller har 0 bytes.",
"Upload Error" => "Uppladdningsfel",
-"Pending" => "Väntar",
+"Close" => "Stäng",
"1 file uploading" => "1 filuppladdning",
-"files uploading" => "filer laddas upp",
+"{count} files uploading" => "{count} filer laddas upp",
"Upload cancelled." => "Uppladdning avbruten.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Filuppladdning pågår. Lämnar du sidan så avbryts uppladdningen.",
-"Invalid name, '/' is not allowed." => "Ogiltigt namn, '/' är inte tillåten.",
-"files scanned" => "filer skannade",
-"error while scanning" => "fel vid skanning",
+"URL cannot be empty." => "URL kan inte vara tom.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ogiltigt mappnamn. Användande av 'Shared' är reserverat av ownCloud",
"Name" => "Namn",
"Size" => "Storlek",
"Modified" => "Ändrad",
-"folder" => "mapp",
-"folders" => "mappar",
-"file" => "fil",
-"files" => "filer",
-"seconds ago" => "sekunder sedan",
-"minute ago" => "minut sedan",
-"minutes ago" => "minuter sedan",
-"today" => "i dag",
-"yesterday" => "i går",
-"days ago" => "dagar sedan",
-"last month" => "förra månaden",
-"months ago" => "månader sedan",
-"last year" => "förra året",
-"years ago" => "år sedan",
+"1 folder" => "1 mapp",
+"{count} folders" => "{count} mappar",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Upload" => "Ladda upp",
"File handling" => "Filhantering",
"Maximum upload size" => "Maximal storlek att ladda upp",
"max. possible: " => "max. möjligt:",
@@ -58,14 +58,16 @@
"New" => "Ny",
"Text file" => "Textfil",
"Folder" => "Mapp",
-"From url" => "Från webbadress",
-"Upload" => "Ladda upp",
+"From link" => "Från länk",
+"Deleted files" => "Raderade filer",
"Cancel upload" => "Avbryt uppladdning",
+"You don’t have write permissions here." => "Du saknar skrivbehörighet här.",
"Nothing in here. Upload something!" => "Ingenting här. Ladda upp något!",
-"Share" => "Dela",
"Download" => "Ladda ner",
+"Unshare" => "Sluta dela",
"Upload too large" => "För stor uppladdning",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar på servern.",
"Files are being scanned, please wait." => "Filer skannas, var god vänta",
-"Current scanning" => "Aktuell skanning"
+"Current scanning" => "Aktuell skanning",
+"Upgrading filesystem cache..." => "Uppgraderar filsystemets cache..."
);
diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php
new file mode 100644
index 00000000000..304453f1290
--- /dev/null
+++ b/apps/files/l10n/ta_LK.php
@@ -0,0 +1,56 @@
+<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "ஒரு கோப்பும் பதிவேற்றப்படவில்லை. அறியப்படாத வழு",
+"There is no error, the file uploaded with success" => "இங்கு வழு இல்லை, கோப்பு வெற்றிகரமாக பதிவேற்றப்பட்டது",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "பதிவேற்றப்பட்ட கோப்பானது HTML படிவத்தில் குறிப்பிடப்பட்டுள்ள MAX_FILE_SIZE directive ஐ விட கூடியது",
+"The uploaded file was only partially uploaded" => "பதிவேற்றப்பட்ட கோப்பானது பகுதியாக மட்டுமே பதிவேற்றப்பட்டுள்ளது",
+"No file was uploaded" => "எந்த கோப்பும் பதிவேற்றப்படவில்லை",
+"Missing a temporary folder" => "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை",
+"Failed to write to disk" => "வட்டில் எழுத முடியவில்லை",
+"Files" => "கோப்புகள்",
+"Delete" => "அழிக்க",
+"Rename" => "பெயர்மாற்றம்",
+"Pending" => "நிலுவையிலுள்ள",
+"{new_name} already exists" => "{new_name} ஏற்கனவே உள்ளது",
+"replace" => "மாற்றிடுக",
+"suggest name" => "பெயரை பரிந்துரைக்க",
+"cancel" => "இரத்து செய்க",
+"replaced {new_name} with {old_name}" => "{new_name} ஆனது {old_name} இனால் மாற்றப்பட்டது",
+"undo" => "முன் செயல் நீக்கம் ",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "செல்லுபடியற்ற பெயர்,'\\', '/', '<', '>', ':', '\"', '|', '?' மற்றும் '*' ஆகியன அனுமதிக்கப்படமாட்டாது.",
+"Unable to upload your file as it is a directory or has 0 bytes" => "அடைவு அல்லது 0 bytes ஐ கொண்டுள்ளதால் உங்களுடைய கோப்பை பதிவேற்ற முடியவில்லை",
+"Upload Error" => "பதிவேற்றல் வழு",
+"Close" => "மூடுக",
+"1 file uploading" => "1 கோப்பு பதிவேற்றப்படுகிறது",
+"{count} files uploading" => "{எண்ணிக்கை} கோப்புகள் பதிவேற்றப்படுகின்றது",
+"Upload cancelled." => "பதிவேற்றல் இரத்து செய்யப்பட்டுள்ளது",
+"File upload is in progress. Leaving the page now will cancel the upload." => "கோப்பு பதிவேற்றம் செயல்பாட்டில் உள்ளது. இந்தப் பக்கத்திலிருந்து வெறியேறுவதானது பதிவேற்றலை இரத்து செய்யும்.",
+"URL cannot be empty." => "URL வெறுமையாக இருக்கமுடியாது.",
+"Name" => "பெயர்",
+"Size" => "அளவு",
+"Modified" => "மாற்றப்பட்டது",
+"1 folder" => "1 கோப்புறை",
+"{count} folders" => "{எண்ணிக்கை} கோப்புறைகள்",
+"1 file" => "1 கோப்பு",
+"{count} files" => "{எண்ணிக்கை} கோப்புகள்",
+"Upload" => "பதிவேற்றுக",
+"File handling" => "கோப்பு கையாளுதல்",
+"Maximum upload size" => "பதிவேற்றக்கூடிய ஆகக்கூடிய அளவு ",
+"max. possible: " => "ஆகக் கூடியது:",
+"Needed for multi-file and folder downloads." => "பல்வேறுப்பட்ட கோப்பு மற்றும் கோப்புறைகளை பதிவிறக்க தேவையானது.",
+"Enable ZIP-download" => "ZIP பதிவிறக்கலை இயலுமைப்படுத்துக",
+"0 is unlimited" => "0 ஆனது எல்லையற்றது",
+"Maximum input size for ZIP files" => "ZIP கோப்புகளுக்கான ஆகக்கூடிய உள்ளீட்டு அளவு",
+"Save" => "சேமிக்க",
+"New" => "புதிய",
+"Text file" => "கோப்பு உரை",
+"Folder" => "கோப்புறை",
+"From link" => "இணைப்பிலிருந்து",
+"Cancel upload" => "பதிவேற்றலை இரத்து செய்க",
+"Nothing in here. Upload something!" => "இங்கு ஒன்றும் இல்லை. ஏதாவது பதிவேற்றுக!",
+"Download" => "பதிவிறக்குக",
+"Unshare" => "பகிரப்படாதது",
+"Upload too large" => "பதிவேற்றல் மிகப்பெரியது",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "நீங்கள் பதிவேற்ற முயற்சிக்கும் கோப்புகளானது இந்த சேவையகத்தில் கோப்பு பதிவேற்றக்கூடிய ஆகக்கூடிய அளவிலும் கூடியது.",
+"Files are being scanned, please wait." => "கோப்புகள் வருடப்படுகின்றன, தயவுசெய்து காத்திருங்கள்.",
+"Current scanning" => "தற்போது வருடப்படுபவை"
+);
diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php
index dc94ddc7de5..2353501b478 100644
--- a/apps/files/l10n/th_TH.php
+++ b/apps/files/l10n/th_TH.php
@@ -1,52 +1,51 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "ไม่สามารถย้าย %s ได้ - ไฟล์ที่ใช้ชื่อนี้มีอยู่แล้ว",
+"Could not move %s" => "ไม่สามารถย้าย %s ได้",
+"Unable to rename file" => "ไม่สามารถเปลี่ยนชื่อไฟล์ได้",
+"No file was uploaded. Unknown error" => "ยังไม่มีไฟล์ใดที่ถูกอัพโหลด เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ",
"There is no error, the file uploaded with success" => "ไม่มีข้อผิดพลาดใดๆ ไฟล์ถูกอัพโหลดเรียบร้อยแล้ว",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ไฟล์ที่อัพโหลดมีขนาดเกินคำสั่ง upload_max_filesize ที่ระบุเอาไว้ในไฟล์ php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "ขนาดไฟล์ที่อัพโหลดมีขนาดเกิน upload_max_filesize ที่ระบุไว้ใน php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ไฟล์ที่อัพโหลดมีขนาดเกินคำสั่ง MAX_FILE_SIZE ที่ระบุเอาไว้ในรูปแบบคำสั่งในภาษา HTML",
"The uploaded file was only partially uploaded" => "ไฟล์ที่อัพโหลดยังไม่ได้ถูกอัพโหลดอย่างสมบูรณ์",
"No file was uploaded" => "ยังไม่มีไฟล์ที่ถูกอัพโหลด",
"Missing a temporary folder" => "แฟ้มเอกสารชั่วคราวเกิดการสูญหาย",
"Failed to write to disk" => "เขียนข้อมูลลงแผ่นดิสก์ล้มเหลว",
+"Not enough storage available" => "เหลือพื้นที่ไม่เพียงสำหรับใช้งาน",
+"Invalid directory." => "ไดเร็กทอรี่ไม่ถูกต้อง",
"Files" => "ไฟล์",
-"Unshare" => "ยกเลิกการแชร์ข้อมูล",
"Delete" => "ลบ",
"Rename" => "เปลี่ยนชื่อ",
-"already exists" => "มีอยู่แล้ว",
+"Pending" => "อยู่ระหว่างดำเนินการ",
+"{new_name} already exists" => "{new_name} มีอยู่แล้วในระบบ",
"replace" => "แทนที่",
"suggest name" => "แนะนำชื่อ",
"cancel" => "ยกเลิก",
-"replaced" => "แทนที่แล้ว",
+"replaced {new_name} with {old_name}" => "แทนที่ {new_name} ด้วย {old_name} แล้ว",
"undo" => "เลิกทำ",
-"with" => "กับ",
-"unshared" => "ยกเลิกการแชร์ข้อมูลแล้ว",
-"deleted" => "ลบแล้ว",
-"generating ZIP-file, it may take some time." => "กำลังสร้างไฟล์บีบอัด ZIP อาจใช้เวลาสักครู่",
+"perform delete operation" => "ดำเนินการตามคำสั่งลบ",
+"'.' is an invalid file name." => "'.' เป็นชื่อไฟล์ที่ไม่ถูกต้อง",
+"File name cannot be empty." => "ชื่อไฟล์ไม่สามารถเว้นว่างได้",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "ชื่อที่ใช้ไม่ถูกต้อง, '\\', '/', '<', '>', ':', '\"', '|', '?' และ '*' ไม่ได้รับอนุญาตให้ใช้งานได้",
+"Your storage is full, files can not be updated or synced anymore!" => "พื้นที่จัดเก็บข้อมูลของคุณเต็มแล้ว ไม่สามารถอัพเดทหรือผสานไฟล์ต่างๆได้อีกต่อไป",
+"Your storage is almost full ({usedSpacePercent}%)" => "พื้นที่จัดเก็บข้อมูลของคุณใกล้เต็มแล้ว ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "กำลังเตรียมดาวน์โหลดข้อมูล หากไฟล์มีขนาดใหญ่ อาจใช้เวลาสักครู่",
"Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถอัพโหลดไฟล์ของคุณได้ เนื่องจากไฟล์ดังกล่าวเป็นไดเร็กทอรี่หรือมีขนาด 0 ไบต์",
"Upload Error" => "เกิดข้อผิดพลาดในการอัพโหลด",
-"Pending" => "อยู่ระหว่างดำเนินการ",
+"Close" => "ปิด",
"1 file uploading" => "กำลังอัพโหลดไฟล์ 1 ไฟล์",
-"files uploading" => "การอัพโหลดไฟล์",
+"{count} files uploading" => "กำลังอัพโหลด {count} ไฟล์",
"Upload cancelled." => "การอัพโหลดถูกยกเลิก",
"File upload is in progress. Leaving the page now will cancel the upload." => "การอัพโหลดไฟล์กำลังอยู่ในระหว่างดำเนินการ การออกจากหน้าเว็บนี้จะทำให้การอัพโหลดถูกยกเลิก",
-"Invalid name, '/' is not allowed." => "ชื่อที่ใช้ไม่ถูกต้อง '/' ไม่อนุญาตให้ใช้งาน",
-"files scanned" => "ไฟล์ต่างๆได้รับการสแกนแล้ว",
-"error while scanning" => "พบข้อผิดพลาดในระหว่างการสแกนไฟล์",
+"URL cannot be empty." => "URL ไม่สามารถเว้นว่างได้",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "ชื่อโฟลเดอร์ไม่ถูกต้อง การใช้งาน 'แชร์' สงวนไว้สำหรับ Owncloud เท่านั้น",
"Name" => "ชื่อ",
"Size" => "ขนาด",
"Modified" => "ปรับปรุงล่าสุด",
-"folder" => "โฟลเดอร์",
-"folders" => "โฟลเดอร์",
-"file" => "ไฟล์",
-"files" => "ไฟล์",
-"seconds ago" => "วินาที ก่อนหน้านี้",
-"minute ago" => "นาที ที่ผ่านมา",
-"minutes ago" => "นาที ที่ผ่านมา",
-"today" => "วันนี้",
-"yesterday" => "เมื่อวานนี้",
-"days ago" => "วัน ที่ผ่านมา",
-"last month" => "เดือนที่แล้ว",
-"months ago" => "เดือน ที่ผ่านมา",
-"last year" => "ปีที่แล้ว",
-"years ago" => "ปี ที่ผ่านมา",
+"1 folder" => "1 โฟลเดอร์",
+"{count} folders" => "{count} โฟลเดอร์",
+"1 file" => "1 ไฟล์",
+"{count} files" => "{count} ไฟล์",
+"Upload" => "อัพโหลด",
"File handling" => "การจัดกาไฟล์",
"Maximum upload size" => "ขนาดไฟล์สูงสุดที่อัพโหลดได้",
"max. possible: " => "จำนวนสูงสุดที่สามารถทำได้: ",
@@ -58,14 +57,14 @@
"New" => "อัพโหลดไฟล์ใหม่",
"Text file" => "ไฟล์ข้อความ",
"Folder" => "แฟ้มเอกสาร",
-"From url" => "จาก url",
-"Upload" => "อัพโหลด",
+"From link" => "จากลิงก์",
"Cancel upload" => "ยกเลิกการอัพโหลด",
"Nothing in here. Upload something!" => "ยังไม่มีไฟล์ใดๆอยู่ที่นี่ กรุณาอัพโหลดไฟล์!",
-"Share" => "แชร์",
"Download" => "ดาวน์โหลด",
+"Unshare" => "ยกเลิกการแชร์ข้อมูล",
"Upload too large" => "ไฟล์ที่อัพโหลดมีขนาดใหญ่เกินไป",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะอัพโหลดมีขนาดเกินกว่าขนาดสูงสุดที่กำหนดไว้ให้อัพโหลดได้สำหรับเซิร์ฟเวอร์นี้",
"Files are being scanned, please wait." => "ไฟล์กำลังอยู่ระหว่างการสแกน, กรุณารอสักครู่.",
-"Current scanning" => "ไฟล์ที่กำลังสแกนอยู่ขณะนี้"
+"Current scanning" => "ไฟล์ที่กำลังสแกนอยู่ขณะนี้",
+"Upgrading filesystem cache..." => "กำลังอัพเกรดหน่วยความจำแคชของระบบไฟล์..."
);
diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php
index bcbee7c5aca..547b490330a 100644
--- a/apps/files/l10n/tr.php
+++ b/apps/files/l10n/tr.php
@@ -1,34 +1,52 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "%s taşınamadı. Bu isimde dosya zaten var.",
+"Could not move %s" => "%s taşınamadı",
+"Unable to rename file" => "Dosya adı değiştirilemedi",
+"No file was uploaded. Unknown error" => "Dosya yüklenmedi. Bilinmeyen hata",
"There is no error, the file uploaded with success" => "Bir hata yok, dosya başarıyla yüklendi",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Yüklenen dosya php.ini de belirtilen upload_max_filesize sınırını aşıyor",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "php.ini dosyasında upload_max_filesize ile belirtilen dosya yükleme sınırı aşıldı.",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Yüklenen dosya HTML formundaki MAX_FILE_SIZE sınırını aşıyor",
"The uploaded file was only partially uploaded" => "Yüklenen dosyanın sadece bir kısmı yüklendi",
"No file was uploaded" => "Hiç dosya yüklenmedi",
"Missing a temporary folder" => "Geçici bir klasör eksik",
"Failed to write to disk" => "Diske yazılamadı",
+"Not enough storage available" => "Yeterli disk alanı yok",
+"Invalid directory." => "Geçersiz dizin.",
"Files" => "Dosyalar",
+"Delete permanently" => "Kalıcı olarak sil",
"Delete" => "Sil",
-"already exists" => "zaten mevcut",
+"Rename" => "İsim değiştir.",
+"Pending" => "Bekliyor",
+"{new_name} already exists" => "{new_name} zaten mevcut",
"replace" => "değiştir",
+"suggest name" => "Öneri ad",
"cancel" => "iptal",
-"replaced" => "değiştirildi",
+"replaced {new_name} with {old_name}" => "{new_name} ismi {old_name} ile değiştirildi",
"undo" => "geri al",
-"with" => "ile",
-"deleted" => "silindi",
-"generating ZIP-file, it may take some time." => "ZIP dosyası oluşturuluyor, biraz sürebilir.",
+"perform delete operation" => "Silme işlemini gerçekleştir",
+"'.' is an invalid file name." => "'.' geçersiz dosya adı.",
+"File name cannot be empty." => "Dosya adı boş olamaz.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Geçersiz isim, '\\', '/', '<', '>', ':', '\"', '|', '?' ve '*' karakterlerine izin verilmemektedir.",
+"Your storage is full, files can not be updated or synced anymore!" => "Depolama alanınız dolu, artık dosyalar güncellenmeyecek yada senkronizasyon edilmeyecek.",
+"Your storage is almost full ({usedSpacePercent}%)" => "Depolama alanınız neredeyse dolu ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "İndirmeniz hazırlanıyor. Dosya büyük ise biraz zaman alabilir.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Dosyanızın boyutu 0 byte olduğundan veya bir dizin olduğundan yüklenemedi",
"Upload Error" => "Yükleme hatası",
-"Pending" => "Bekliyor",
+"Close" => "Kapat",
+"1 file uploading" => "1 dosya yüklendi",
+"{count} files uploading" => "{count} dosya yükleniyor",
"Upload cancelled." => "Yükleme iptal edildi.",
"File upload is in progress. Leaving the page now will cancel the upload." => "Dosya yükleme işlemi sürüyor. Şimdi sayfadan ayrılırsanız işleminiz iptal olur.",
-"Invalid name, '/' is not allowed." => "Geçersiz isim, '/' işaretine izin verilmiyor.",
+"URL cannot be empty." => "URL boş olamaz.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Geçersiz dizin adı. Shared isminin kullanımı Owncloud tarafından rezerver edilmiştir.",
"Name" => "Ad",
"Size" => "Boyut",
"Modified" => "Değiştirilme",
-"folder" => "dizin",
-"folders" => "dizinler",
-"file" => "dosya",
-"files" => "dosyalar",
+"1 folder" => "1 dizin",
+"{count} folders" => "{count} dizin",
+"1 file" => "1 dosya",
+"{count} files" => "{count} dosya",
+"Upload" => "Yükle",
"File handling" => "Dosya taşıma",
"Maximum upload size" => "Maksimum yükleme boyutu",
"max. possible: " => "mümkün olan en fazla: ",
@@ -36,17 +54,20 @@
"Enable ZIP-download" => "ZIP indirmeyi aktif et",
"0 is unlimited" => "0 limitsiz demektir",
"Maximum input size for ZIP files" => "ZIP dosyaları için en fazla girdi sayısı",
+"Save" => "Kaydet",
"New" => "Yeni",
"Text file" => "Metin dosyası",
"Folder" => "Klasör",
-"From url" => "Url'den",
-"Upload" => "Yükle",
+"From link" => "Bağlantıdan",
+"Deleted files" => "Dosyalar silindi",
"Cancel upload" => "Yüklemeyi iptal et",
+"You don’t have write permissions here." => "Buraya erişim hakkınız yok.",
"Nothing in here. Upload something!" => "Burada hiçbir şey yok. Birşeyler yükleyin!",
-"Share" => "Paylaş",
"Download" => "İndir",
+"Unshare" => "Paylaşılmayan",
"Upload too large" => "Yüklemeniz çok büyük",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosyalar bu sunucudaki maksimum yükleme boyutunu aşıyor.",
"Files are being scanned, please wait." => "Dosyalar taranıyor, lütfen bekleyin.",
-"Current scanning" => "Güncel tarama"
+"Current scanning" => "Güncel tarama",
+"Upgrading filesystem cache..." => "Sistem dosyası önbelleği güncelleniyor"
);
diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php
index 7e72683b8ba..f5e161996c0 100644
--- a/apps/files/l10n/uk.php
+++ b/apps/files/l10n/uk.php
@@ -1,41 +1,73 @@
<?php $TRANSLATIONS = array(
-"There is no error, the file uploaded with success" => "Файл успішно відвантажено без помилок.",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Розмір відвантаженого файлу перевищує директиву upload_max_filesize в php.ini",
+"Could not move %s - File with this name already exists" => "Не вдалося перемістити %s - Файл з таким ім'ям вже існує",
+"Could not move %s" => "Не вдалося перемістити %s",
+"Unable to rename file" => "Не вдалося перейменувати файл",
+"No file was uploaded. Unknown error" => "Не завантажено жодного файлу. Невідома помилка",
+"There is no error, the file uploaded with success" => "Файл успішно вивантажено без помилок.",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Розмір звантаження перевищує upload_max_filesize параметра в php.ini: ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Розмір відвантаженого файлу перевищує директиву MAX_FILE_SIZE вказану в HTML формі",
"The uploaded file was only partially uploaded" => "Файл відвантажено лише частково",
"No file was uploaded" => "Не відвантажено жодного файлу",
"Missing a temporary folder" => "Відсутній тимчасовий каталог",
+"Failed to write to disk" => "Невдалося записати на диск",
+"Not enough storage available" => "Місця більше немає",
+"Invalid directory." => "Невірний каталог.",
"Files" => "Файли",
+"Delete permanently" => "Видалити назавжди",
"Delete" => "Видалити",
+"Rename" => "Перейменувати",
+"Pending" => "Очікування",
+"{new_name} already exists" => "{new_name} вже існує",
+"replace" => "заміна",
+"suggest name" => "запропонуйте назву",
+"cancel" => "відміна",
+"replaced {new_name} with {old_name}" => "замінено {new_name} на {old_name}",
"undo" => "відмінити",
-"deleted" => "видалені",
-"generating ZIP-file, it may take some time." => "Створення ZIP-файлу, це може зайняти певний час.",
+"perform delete operation" => "виконати операцію видалення",
+"'.' is an invalid file name." => "'.' це невірне ім'я файлу.",
+"File name cannot be empty." => " Ім'я файлу не може бути порожнім.",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Невірне ім'я, '\\', '/', '<', '>', ':', '\"', '|', '?' та '*' не дозволені.",
+"Your storage is full, files can not be updated or synced anymore!" => "Ваше сховище переповнене, файли більше не можуть бути оновлені або синхронізовані !",
+"Your storage is almost full ({usedSpacePercent}%)" => "Ваше сховище майже повне ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Ваше завантаження готується. Це може зайняти деякий час, якщо файли завеликі.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Неможливо завантажити ваш файл тому, що він тека або файл розміром 0 байт",
"Upload Error" => "Помилка завантаження",
-"Pending" => "Очікування",
+"Close" => "Закрити",
+"1 file uploading" => "1 файл завантажується",
+"{count} files uploading" => "{count} файлів завантажується",
"Upload cancelled." => "Завантаження перервано.",
-"Invalid name, '/' is not allowed." => "Некоректне ім'я, '/' не дозволено.",
+"File upload is in progress. Leaving the page now will cancel the upload." => "Виконується завантаження файлу. Закриття цієї сторінки приведе до відміни завантаження.",
+"URL cannot be empty." => "URL не може бути пустим.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Невірне ім'я теки. Використання \"Shared\" зарезервовано Owncloud",
"Name" => "Ім'я",
"Size" => "Розмір",
"Modified" => "Змінено",
-"folder" => "тека",
-"folders" => "теки",
-"file" => "файл",
-"files" => "файли",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папок",
+"1 file" => "1 файл",
+"{count} files" => "{count} файлів",
+"Upload" => "Відвантажити",
+"File handling" => "Робота з файлами",
"Maximum upload size" => "Максимальний розмір відвантажень",
"max. possible: " => "макс.можливе:",
+"Needed for multi-file and folder downloads." => "Необхідно для мульти-файлового та каталогового завантаження.",
+"Enable ZIP-download" => "Активувати ZIP-завантаження",
"0 is unlimited" => "0 є безліміт",
+"Maximum input size for ZIP files" => "Максимальний розмір завантажуємого ZIP файлу",
+"Save" => "Зберегти",
"New" => "Створити",
"Text file" => "Текстовий файл",
"Folder" => "Папка",
-"From url" => "З URL",
-"Upload" => "Відвантажити",
+"From link" => "З посилання",
+"Deleted files" => "Видалено файлів",
"Cancel upload" => "Перервати завантаження",
+"You don’t have write permissions here." => "У вас тут немає прав на запис.",
"Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!",
-"Share" => "Поділитися",
"Download" => "Завантажити",
+"Unshare" => "Заборонити доступ",
"Upload too large" => "Файл занадто великий",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файли,що ви намагаєтесь відвантажити перевищують максимальний дозволений розмір файлів на цьому сервері.",
"Files are being scanned, please wait." => "Файли скануються, зачекайте, будь-ласка.",
-"Current scanning" => "Поточне сканування"
+"Current scanning" => "Поточне сканування",
+"Upgrading filesystem cache..." => "Оновлення кеша файлової системи..."
);
diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php
index d6593022d8b..affca6c12f8 100644
--- a/apps/files/l10n/vi.php
+++ b/apps/files/l10n/vi.php
@@ -1,38 +1,55 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "Không thể di chuyển %s - Đã có tên file này trên hệ thống",
+"Could not move %s" => "Không thể di chuyển %s",
+"Unable to rename file" => "Không thể đổi tên file",
+"No file was uploaded. Unknown error" => "Không có tập tin nào được tải lên. Lỗi không xác định",
"There is no error, the file uploaded with success" => "Không có lỗi, các tập tin đã được tải lên thành công",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Những tập tin được tải lên vượt quá upload_max_filesize được chỉ định trong php.ini",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "The uploaded file exceeds the upload_max_filesize directive in php.ini: ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Kích thước những tập tin tải lên vượt quá MAX_FILE_SIZE đã được quy định",
"The uploaded file was only partially uploaded" => "Tập tin tải lên mới chỉ tải lên được một phần",
"No file was uploaded" => "Không có tập tin nào được tải lên",
"Missing a temporary folder" => "Không tìm thấy thư mục tạm",
-"Failed to write to disk" => "Không thể ghi vào đĩa cứng",
+"Failed to write to disk" => "Không thể ghi ",
+"Not enough storage available" => "Không đủ không gian lưu trữ",
+"Invalid directory." => "Thư mục không hợp lệ",
"Files" => "Tập tin",
-"Unshare" => "Không chia sẽ",
+"Delete permanently" => "Xóa vĩnh vễn",
"Delete" => "Xóa",
-"already exists" => "đã tồn tại",
+"Rename" => "Sửa tên",
+"Pending" => "Chờ",
+"{new_name} already exists" => "{new_name} đã tồn tại",
"replace" => "thay thế",
"suggest name" => "tên gợi ý",
"cancel" => "hủy",
-"replaced" => "đã được thay thế",
+"replaced {new_name} with {old_name}" => "đã thay thế {new_name} bằng {old_name}",
"undo" => "lùi lại",
-"with" => "với",
-"deleted" => "đã xóa",
-"generating ZIP-file, it may take some time." => "Tạo tập tinh ZIP, điều này có thể mất một ít thời gian",
+"perform delete operation" => "thực hiện việc xóa",
+"'.' is an invalid file name." => "'.' là một tên file không hợp lệ",
+"File name cannot be empty." => "Tên file không được rỗng",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Tên không hợp lệ, '\\', '/', '<', '>', ':', '\"', '|', '?' và '*' thì không được phép dùng.",
+"Your storage is full, files can not be updated or synced anymore!" => "Your storage is full, files can not be updated or synced anymore!",
+"Your storage is almost full ({usedSpacePercent}%)" => "Your storage is almost full ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "Your download is being prepared. This might take some time if the files are big.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Không thể tải lên tập tin này do nó là một thư mục hoặc kích thước tập tin bằng 0 byte",
"Upload Error" => "Tải lên lỗi",
-"Pending" => "Chờ",
+"Close" => "Đóng",
+"1 file uploading" => "1 tệp tin đang được tải lên",
+"{count} files uploading" => "{count} tập tin đang tải lên",
"Upload cancelled." => "Hủy tải lên",
"File upload is in progress. Leaving the page now will cancel the upload." => "Tập tin tải lên đang được xử lý. Nếu bạn rời khỏi trang bây giờ sẽ hủy quá trình này.",
-"Invalid name, '/' is not allowed." => "Tên không hợp lệ ,không được phép dùng '/'",
+"URL cannot be empty." => "URL không được để trống.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Invalid folder name. Usage of 'Shared' is reserved by Owncloud",
"Name" => "Tên",
"Size" => "Kích cỡ",
"Modified" => "Thay đổi",
-"folder" => "folder",
-"folders" => "folders",
-"file" => "file",
-"files" => "files",
+"1 folder" => "1 thư mục",
+"{count} folders" => "{count} thư mục",
+"1 file" => "1 tập tin",
+"{count} files" => "{count} tập tin",
+"Upload" => "Tải lên",
"File handling" => "Xử lý tập tin",
"Maximum upload size" => "Kích thước tối đa ",
+"max. possible: " => "tối đa cho phép:",
"Needed for multi-file and folder downloads." => "Cần thiết cho tải nhiều tập tin và thư mục.",
"Enable ZIP-download" => "Cho phép ZIP-download",
"0 is unlimited" => "0 là không giới hạn",
@@ -40,15 +57,16 @@
"Save" => "Lưu",
"New" => "Mới",
"Text file" => "Tập tin văn bản",
-"Folder" => "Folder",
-"From url" => "Từ url",
-"Upload" => "Tải lên",
+"Folder" => "Thư mục",
+"From link" => "Từ liên kết",
+"Deleted files" => "File đã bị xóa",
"Cancel upload" => "Hủy upload",
"Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên một cái gì đó !",
-"Share" => "Chia sẻ",
"Download" => "Tải xuống",
-"Upload too large" => "File tải lên quá lớn",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Các tập tin bạn đang cố gắng tải lên vượt quá kích thước tối đa cho phép trên máy chủ này.",
+"Unshare" => "Không chia sẽ",
+"Upload too large" => "Tập tin tải lên quá lớn",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Các tập tin bạn đang tải lên vượt quá kích thước tối đa cho phép trên máy chủ .",
"Files are being scanned, please wait." => "Tập tin đang được quét ,vui lòng chờ.",
-"Current scanning" => "Hiện tại đang quét"
+"Current scanning" => "Hiện tại đang quét",
+"Upgrading filesystem cache..." => "Upgrading filesystem cache..."
);
diff --git a/apps/files/l10n/zh_CN.GB2312.php b/apps/files/l10n/zh_CN.GB2312.php
index 8d4ae972b9b..fa75627f141 100644
--- a/apps/files/l10n/zh_CN.GB2312.php
+++ b/apps/files/l10n/zh_CN.GB2312.php
@@ -1,52 +1,37 @@
<?php $TRANSLATIONS = array(
+"No file was uploaded. Unknown error" => "没有上传文件。未知错误",
"There is no error, the file uploaded with success" => "没有任何错误,文件上传成功了",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "上传的文件超过了php.ini指定的upload_max_filesize",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件超过了HTML表单指定的MAX_FILE_SIZE",
"The uploaded file was only partially uploaded" => "文件只有部分被上传",
"No file was uploaded" => "没有上传完成的文件",
"Missing a temporary folder" => "丢失了一个临时文件夹",
"Failed to write to disk" => "写磁盘失败",
"Files" => "文件",
-"Unshare" => "取消共享",
"Delete" => "删除",
"Rename" => "重命名",
-"already exists" => "已经存在了",
+"Pending" => "Pending",
+"{new_name} already exists" => "{new_name} 已存在",
"replace" => "替换",
"suggest name" => "推荐名称",
"cancel" => "取消",
-"replaced" => "替换过了",
+"replaced {new_name} with {old_name}" => "已用 {old_name} 替换 {new_name}",
"undo" => "撤销",
-"with" => "随着",
-"unshared" => "已取消共享",
-"deleted" => "删除",
-"generating ZIP-file, it may take some time." => "正在生成ZIP文件,这可能需要点时间",
"Unable to upload your file as it is a directory or has 0 bytes" => "不能上传你指定的文件,可能因为它是个文件夹或者大小为0",
"Upload Error" => "上传错误",
-"Pending" => "Pending",
+"Close" => "关闭",
"1 file uploading" => "1 个文件正在上传",
-"files uploading" => "个文件正在上传",
+"{count} files uploading" => "{count} 个文件正在上传",
"Upload cancelled." => "上传取消了",
"File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传。关闭页面会取消上传。",
-"Invalid name, '/' is not allowed." => "非法文件名,\"/\"是不被许可的",
-"files scanned" => "文件已扫描",
-"error while scanning" => "扫描出错",
+"URL cannot be empty." => "网址不能为空。",
"Name" => "名字",
"Size" => "大小",
"Modified" => "修改日期",
-"folder" => "文件夹",
-"folders" => "文件夹",
-"file" => "文件",
-"files" => "文件",
-"seconds ago" => "秒前",
-"minute ago" => "分钟前",
-"minutes ago" => "分钟前",
-"today" => "今天",
-"yesterday" => "昨天",
-"days ago" => "天前",
-"last month" => "上个月",
-"months ago" => "月前",
-"last year" => "去年",
-"years ago" => "年前",
+"1 folder" => "1 个文件夹",
+"{count} folders" => "{count} 个文件夹",
+"1 file" => "1 个文件",
+"{count} files" => "{count} 个文件",
+"Upload" => "上传",
"File handling" => "文件处理中",
"Maximum upload size" => "最大上传大小",
"max. possible: " => "最大可能",
@@ -58,12 +43,11 @@
"New" => "新建",
"Text file" => "文本文档",
"Folder" => "文件夹",
-"From url" => "从URL:",
-"Upload" => "上传",
+"From link" => "来自链接",
"Cancel upload" => "取消上传",
"Nothing in here. Upload something!" => "这里没有东西.上传点什么!",
-"Share" => "分享",
"Download" => "下载",
+"Unshare" => "取消共享",
"Upload too large" => "上传的文件太大了",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "你正在试图上传的文件超过了此服务器支持的最大的文件大小.",
"Files are being scanned, please wait." => "正在扫描文件,请稍候.",
diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php
index f2ea43ae81e..2923126d10f 100644
--- a/apps/files/l10n/zh_CN.php
+++ b/apps/files/l10n/zh_CN.php
@@ -1,55 +1,55 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "无法移动 %s - 同名文件已存在",
+"Could not move %s" => "无法移动 %s",
+"Unable to rename file" => "无法重命名文件",
+"No file was uploaded. Unknown error" => "没有文件被上传。未知错误",
"There is no error, the file uploaded with success" => "没有发生错误,文件上传成功。",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "上传的文件大小超过了php.ini 中指定的upload_max_filesize",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上传文件大小已超过php.ini中upload_max_filesize所规定的值",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件超过了在HTML 表单中指定的MAX_FILE_SIZE",
"The uploaded file was only partially uploaded" => "只上传了文件的一部分",
"No file was uploaded" => "文件没有上传",
"Missing a temporary folder" => "缺少临时目录",
"Failed to write to disk" => "写入磁盘失败",
+"Not enough storage available" => "没有足够的存储空间",
+"Invalid directory." => "无效文件夹。",
"Files" => "文件",
-"Unshare" => "取消分享",
+"Delete permanently" => "永久删除",
"Delete" => "删除",
"Rename" => "重命名",
-"already exists" => "已经存在",
+"Pending" => "操作等待中",
+"{new_name} already exists" => "{new_name} 已存在",
"replace" => "替换",
"suggest name" => "建议名称",
"cancel" => "取消",
-"replaced" => "已经替换",
+"replaced {new_name} with {old_name}" => "已将 {old_name}替换成 {new_name}",
"undo" => "撤销",
-"with" => "随着",
-"unshared" => "已取消分享",
-"deleted" => "已经删除",
-"generating ZIP-file, it may take some time." => "正在生成 ZIP 文件,可能需要一些时间",
+"perform delete operation" => "进行删除操作",
+"'.' is an invalid file name." => "'.' 是一个无效的文件名。",
+"File name cannot be empty." => "文件名不能为空。",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "无效名称,'\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 不被允许使用。",
+"Your storage is full, files can not be updated or synced anymore!" => "您的存储空间已满,文件将无法更新或同步!",
+"Your storage is almost full ({usedSpacePercent}%)" => "您的存储空间即将用完 ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "下载正在准备中。如果文件较大可能会花费一些时间。",
"Unable to upload your file as it is a directory or has 0 bytes" => "无法上传文件,因为它是一个目录或者大小为 0 字节",
"Upload Error" => "上传错误",
-"Pending" => "操作等待中",
+"Close" => "关闭",
"1 file uploading" => "1个文件上传中",
-"files uploading" => "文件上传中",
+"{count} files uploading" => "{count} 个文件上传中",
"Upload cancelled." => "上传已取消",
"File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传中。现在离开此页会导致上传动作被取消。",
-"Invalid name, '/' is not allowed." => "非法的名称,不允许使用‘/’。",
-"files scanned" => "已扫描文件",
-"error while scanning" => "扫描时出错",
+"URL cannot be empty." => "URL不能为空",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "无效文件夹名。'共享' 是 Owncloud 预留的文件夹名。",
"Name" => "名称",
"Size" => "大小",
"Modified" => "修改日期",
-"folder" => "文件夹",
-"folders" => "文件夹",
-"file" => "文件",
-"files" => "文件",
-"seconds ago" => "几秒前",
-"minute ago" => "1分钟前",
-"minutes ago" => "分钟前",
-"today" => "今天",
-"yesterday" => "昨天",
-"days ago" => "%d 天前",
-"last month" => "上月",
-"months ago" => "月前",
-"last year" => "上年",
-"years ago" => "几年前",
+"1 folder" => "1个文件夹",
+"{count} folders" => "{count} 个文件夹",
+"1 file" => "1 个文件",
+"{count} files" => "{count} 个文件",
+"Upload" => "上传",
"File handling" => "文件处理",
"Maximum upload size" => "最大上传大小",
-"max. possible: " => "最大可能: ",
+"max. possible: " => "最大允许: ",
"Needed for multi-file and folder downloads." => "多文件和文件夹下载需要此项。",
"Enable ZIP-download" => "启用 ZIP 下载",
"0 is unlimited" => "0 为无限制",
@@ -58,14 +58,16 @@
"New" => "新建",
"Text file" => "文本文件",
"Folder" => "文件夹",
-"From url" => "来自地址",
-"Upload" => "上传",
+"From link" => "来自链接",
+"Deleted files" => "删除文件",
"Cancel upload" => "取消上传",
+"You don’t have write permissions here." => "您没有写权限",
"Nothing in here. Upload something!" => "这里还什么都没有。上传些东西吧!",
-"Share" => "共享",
"Download" => "下载",
+"Unshare" => "取消分享",
"Upload too large" => "上传文件过大",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您正尝试上传的文件超过了此服务器可以上传的最大大小",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您正尝试上传的文件超过了此服务器可以上传的最大容量限制",
"Files are being scanned, please wait." => "文件正在被扫描,请稍候。",
-"Current scanning" => "当前扫描"
+"Current scanning" => "当前扫描",
+"Upgrading filesystem cache..." => "正在更新文件系统缓存..."
);
diff --git a/apps/files/l10n/zh_HK.php b/apps/files/l10n/zh_HK.php
new file mode 100644
index 00000000000..4eaa908476b
--- /dev/null
+++ b/apps/files/l10n/zh_HK.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Files" => "文件",
+"Delete" => "刪除",
+"Name" => "名稱",
+"Upload" => "上傳",
+"Save" => "儲存",
+"Download" => "下載",
+"Unshare" => "取消分享"
+);
diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php
index 30ab1901a20..793fedac412 100644
--- a/apps/files/l10n/zh_TW.php
+++ b/apps/files/l10n/zh_TW.php
@@ -1,43 +1,71 @@
<?php $TRANSLATIONS = array(
+"Could not move %s - File with this name already exists" => "無法移動 %s - 同名的檔案已經存在",
+"Could not move %s" => "無法移動 %s",
+"Unable to rename file" => "無法重新命名檔案",
+"No file was uploaded. Unknown error" => "沒有檔案被上傳。未知的錯誤。",
"There is no error, the file uploaded with success" => "無錯誤,檔案上傳成功",
-"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "上傳的檔案超過了 php.ini 中的 upload_max_filesize 設定",
-"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上傳黨案的超過 HTML 表單中指定 MAX_FILE_SIZE 限制",
-"The uploaded file was only partially uploaded" => "只有部分檔案被上傳",
+"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上傳的檔案大小超過 php.ini 當中 upload_max_filesize 參數的設定:",
+"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上傳的檔案大小超過 HTML 表單中 MAX_FILE_SIZE 的限制",
+"The uploaded file was only partially uploaded" => "只有檔案的一部分被上傳",
"No file was uploaded" => "無已上傳檔案",
"Missing a temporary folder" => "遺失暫存資料夾",
"Failed to write to disk" => "寫入硬碟失敗",
+"Not enough storage available" => "儲存空間不足",
+"Invalid directory." => "無效的資料夾。",
"Files" => "檔案",
+"Delete permanently" => "永久刪除",
"Delete" => "刪除",
-"already exists" => "已經存在",
+"Rename" => "重新命名",
+"Pending" => "等候中",
+"{new_name} already exists" => "{new_name} 已經存在",
"replace" => "取代",
+"suggest name" => "建議檔名",
"cancel" => "取消",
-"generating ZIP-file, it may take some time." => "產生壓縮檔, 它可能需要一段時間.",
+"replaced {new_name} with {old_name}" => "使用 {new_name} 取代 {old_name}",
+"undo" => "復原",
+"perform delete operation" => "進行刪除動作",
+"'.' is an invalid file name." => "'.' 是不合法的檔名。",
+"File name cannot be empty." => "檔名不能為空。",
+"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "檔名不合法,不允許 '\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 。",
+"Your storage is full, files can not be updated or synced anymore!" => "您的儲存空間已滿,沒有辦法再更新或是同步檔案!",
+"Your storage is almost full ({usedSpacePercent}%)" => "您的儲存空間快要滿了 ({usedSpacePercent}%)",
+"Your download is being prepared. This might take some time if the files are big." => "正在準備您的下載,若您的檔案較大,將會需要更多時間。",
"Unable to upload your file as it is a directory or has 0 bytes" => "無法上傳您的檔案因為它可能是一個目錄或檔案大小為0",
"Upload Error" => "上傳發生錯誤",
+"Close" => "關閉",
+"1 file uploading" => "1 個檔案正在上傳",
+"{count} files uploading" => "{count} 個檔案正在上傳",
"Upload cancelled." => "上傳取消",
-"File upload is in progress. Leaving the page now will cancel the upload." => "檔案上傳中. 離開此頁面將會取消上傳.",
-"Invalid name, '/' is not allowed." => "無效的名稱, '/'是不被允許的",
+"File upload is in progress. Leaving the page now will cancel the upload." => "檔案上傳中。離開此頁面將會取消上傳。",
+"URL cannot be empty." => "URL 不能為空白.",
+"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無效的資料夾名稱,'Shared' 的使用被 Owncloud 保留",
"Name" => "名稱",
"Size" => "大小",
"Modified" => "修改",
+"1 folder" => "1 個資料夾",
+"{count} folders" => "{count} 個資料夾",
+"1 file" => "1 個檔案",
+"{count} files" => "{count} 個檔案",
+"Upload" => "上傳",
"File handling" => "檔案處理",
-"Maximum upload size" => "最大上傳容量",
-"max. possible: " => "最大允許: ",
-"Needed for multi-file and folder downloads." => "針對多檔案和目錄下載是必填的",
+"Maximum upload size" => "最大上傳檔案大小",
+"max. possible: " => "最大允許:",
+"Needed for multi-file and folder downloads." => "針對多檔案和目錄下載是必填的。",
"Enable ZIP-download" => "啟用 Zip 下載",
"0 is unlimited" => "0代表沒有限制",
-"Maximum input size for ZIP files" => "針對ZIP檔案最大輸入大小",
+"Maximum input size for ZIP files" => "針對 ZIP 檔案最大輸入大小",
+"Save" => "儲存",
"New" => "新增",
"Text file" => "文字檔",
"Folder" => "資料夾",
-"From url" => "由 url ",
-"Upload" => "上傳",
+"From link" => "從連結",
"Cancel upload" => "取消上傳",
-"Nothing in here. Upload something!" => "沒有任何東西。請上傳內容!",
-"Share" => "分享",
+"Nothing in here. Upload something!" => "沒有任何東西。請上傳內容!",
"Download" => "下載",
+"Unshare" => "取消共享",
"Upload too large" => "上傳過大",
-"The files you are trying to upload exceed the maximum size for file uploads on this server." => "你試圖上傳的檔案已超過伺服器的最大容量限制。 ",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您試圖上傳的檔案已超過伺服器的最大檔案大小限制。 ",
"Files are being scanned, please wait." => "正在掃描檔案,請稍等。",
-"Current scanning" => "目前掃描"
+"Current scanning" => "目前掃描",
+"Upgrading filesystem cache..." => "正在更新檔案系統快取..."
);
diff --git a/apps/files/lib/capabilities.php b/apps/files/lib/capabilities.php
new file mode 100644
index 00000000000..90a5e2f4eb9
--- /dev/null
+++ b/apps/files/lib/capabilities.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright (c) 2013 Tom Needham <tom@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCA\Files;
+
+class Capabilities {
+
+ public static function getCapabilities() {
+ return new \OC_OCS_Result(array(
+ 'capabilities' => array(
+ 'files' => array(
+ 'bigfilechunking' => true,
+ 'undelete' => true,
+ ),
+ ),
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php
new file mode 100644
index 00000000000..f2b1f142e9b
--- /dev/null
+++ b/apps/files/lib/helper.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace OCA\files\lib;
+
+class Helper
+{
+ public static function buildFileStorageStatistics($dir) {
+ $l = new \OC_L10N('files');
+ $maxUploadFilesize = \OCP\Util::maxUploadFilesize($dir);
+ $maxHumanFilesize = \OCP\Util::humanFileSize($maxUploadFilesize);
+ $maxHumanFilesize = $l->t('Upload') . ' max. ' . $maxHumanFilesize;
+
+ // information about storage capacities
+ $storageInfo = \OC_Helper::getStorageInfo();
+
+ return array('uploadMaxFilesize' => $maxUploadFilesize,
+ 'maxHumanFilesize' => $maxHumanFilesize,
+ 'usedSpacePercent' => (int)$storageInfo['relative']);
+ }
+}
diff --git a/apps/files/settings.php b/apps/files/settings.php
index 52ec9fd0fe3..8687f013137 100644
--- a/apps/files/settings.php
+++ b/apps/files/settings.php
@@ -21,10 +21,6 @@
*
*/
-
-// Init owncloud
-
-
// Check if we are a user
OCP\User::checkLoggedIn();
@@ -36,7 +32,7 @@ OCP\Util::addscript( "files", "files" );
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir ) as $i ) {
+foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) {
$i["date"] = date( $CONFIG_DATEFORMAT, $i["mtime"] );
$files[] = $i;
}
diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php
index c4fe4c86569..e1ca6afc4ad 100644
--- a/apps/files/templates/admin.php
+++ b/apps/files/templates/admin.php
@@ -1,16 +1,28 @@
-<?php OCP\Util::addscript('files','admin'); ?>
+<?php OCP\Util::addscript('files', 'admin'); ?>
<form name="filesForm" action='#' method='post'>
<fieldset class="personalblock">
- <legend><strong><?php echo $l->t('File handling');?></strong></legend>
+ <legend><strong><?php p($l->t('File handling')); ?></strong></legend>
<?php if($_['uploadChangable']):?>
- <label for="maxUploadSize"><?php echo $l->t( 'Maximum upload size' ); ?> </label><input name='maxUploadSize' id="maxUploadSize" value='<?php echo $_['uploadMaxFilesize'] ?>'/>(<?php echo $l->t('max. possible: '); echo $_['maxPossibleUploadSize'] ?>)<br/>
+ <label for="maxUploadSize"><?php p($l->t( 'Maximum upload size' )); ?> </label>
+ <input name='maxUploadSize' id="maxUploadSize" value='<?php p($_['uploadMaxFilesize']) ?>'/>
+ <?php if($_['displayMaxPossibleUploadSize']):?>
+ (<?php p($l->t('max. possible: ')); p($_['maxPossibleUploadSize']) ?>)
+ <?php endif;?>
+ <br/>
<?php endif;?>
- <input type="checkbox" name="allowZipDownload" id="allowZipDownload" value="1" title="<?php echo $l->t( 'Needed for multi-file and folder downloads.' ); ?>"<?php if ($_['allowZipDownload']) echo ' checked="checked"'; ?> /> <label for="allowZipDownload"><?php echo $l->t( 'Enable ZIP-download' ); ?></label> <br/>
+ <input type="checkbox" name="allowZipDownload" id="allowZipDownload" value="1"
+ title="<?php p($l->t( 'Needed for multi-file and folder downloads.' )); ?>"
+ <?php if ($_['allowZipDownload']): ?> checked="checked"<?php endif; ?> />
+ <label for="allowZipDownload"><?php p($l->t( 'Enable ZIP-download' )); ?></label><br/>
- <input name="maxZipInputSize" id="maxZipInputSize" style="width:180px;" value='<?php echo $_['maxZipInputSize'] ?>' title="<?php echo $l->t( '0 is unlimited' ); ?>"<?php if (!$_['allowZipDownload']) echo ' disabled="disabled"'; ?> />
- <label for="maxZipInputSize"><?php echo $l->t( 'Maximum input size for ZIP files' ); ?> </label><br />
+ <input type="text" name="maxZipInputSize" id="maxZipInputSize" style="width:180px;" value='<?php p($_['maxZipInputSize']) ?>'
+ title="<?php p($l->t( '0 is unlimited' )); ?>"
+ <?php if (!$_['allowZipDownload']): ?> disabled="disabled"<?php endif; ?> /><br />
+ <em><?php p($l->t( 'Maximum input size for ZIP files' )); ?> </em><br />
- <input type="submit" name="submitFilesAdminSettings" id="submitFilesAdminSettings" value="<?php echo $l->t( 'Save' ); ?>"/>
+ <input type="hidden" value="<?php p($_['requesttoken']); ?>" name="requesttoken" />
+ <input type="submit" name="submitFilesAdminSettings" id="submitFilesAdminSettings"
+ value="<?php p($l->t( 'Save' )); ?>"/>
</fieldset>
</form>
diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php
index d49f2f4d5d3..a53df4e2d3e 100644
--- a/apps/files/templates/index.php
+++ b/apps/files/templates/index.php
@@ -1,90 +1,122 @@
<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]-->
<div id="controls">
- <?php echo($_['breadcrumb']); ?>
+ <?php print_unescaped($_['breadcrumb']); ?>
<?php if ($_['isCreatable']):?>
<div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptyfolder<?php endif; ?>">
- <div id='new' class='button'>
- <a><?php echo $l->t('New');?></a>
- <ul class="popup popupTop">
- <li style="background-image:url('<?php echo OCP\mimetype_icon('text/plain') ?>')" data-type='file'><p><?php echo $l->t('Text file');?></p></li>
- <li style="background-image:url('<?php echo OCP\mimetype_icon('dir') ?>')" data-type='folder'><p><?php echo $l->t('Folder');?></p></li>
- <li style="background-image:url('<?php echo OCP\image_path('core','actions/public.png') ?>')" data-type='web'><p><?php echo $l->t('From url');?></p></li>
+ <div id="new" class="button">
+ <a><?php p($l->t('New'));?></a>
+ <ul>
+ <li style="background-image:url('<?php p(OCP\mimetype_icon('text/plain')) ?>')"
+ data-type='file'><p><?php p($l->t('Text file'));?></p></li>
+ <li style="background-image:url('<?php p(OCP\mimetype_icon('dir')) ?>')"
+ data-type='folder'><p><?php p($l->t('Folder'));?></p></li>
+ <li style="background-image:url('<?php p(OCP\image_path('core', 'actions/public.png')) ?>')"
+ data-type='web'><p><?php p($l->t('From link'));?></p></li>
</ul>
</div>
- <div class="file_upload_wrapper svg">
- <form data-upload-id='1' id="data-upload-form" class="file_upload_form" action="<?php echo OCP\Util::linkTo('files', 'ajax/upload.php'); ?>" method="post" enctype="multipart/form-data" target="file_upload_target_1">
- <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
- <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
- <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir">
- <input class="file_upload_start" type="file" name='files[]'/>
- <a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo $l->t('Upload'); echo ' max. '.$_['uploadMaxHumanFilesize'] ?>"></a>
- <button class="file_upload_filename"></button>
- <iframe name="file_upload_target_1" class='file_upload_target' src=""></iframe>
+ <div id="upload" class="button"
+ title="<?php p($l->t('Upload') . ' max. '.$_['uploadMaxHumanFilesize']) ?>">
+ <form data-upload-id='1'
+ id="data-upload-form"
+ class="file_upload_form"
+ action="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>"
+ method="post"
+ enctype="multipart/form-data"
+ target="file_upload_target_1">
+ <input type="hidden" name="MAX_FILE_SIZE" id="max_upload"
+ value="<?php p($_['uploadMaxFilesize']) ?>">
+ <!-- Send the requesttoken, this is needed for older IE versions
+ because they don't send the CSRF token via HTTP header in this case -->
+ <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" id="requesttoken">
+ <input type="hidden" class="max_human_file_size"
+ value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)">
+ <input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
+ <input type="file" id="file_upload_start" name='files[]'/>
+ <a href="#" class="svg" onclick="return false;"></a>
</form>
</div>
- <div id="upload">
- <div id="uploadprogressbar"></div>
- <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Cancel upload');?>" onclick="javascript:Files.cancelUploads();" />
- </div>
-
+ <?php if ($_['trash'] ): ?>
+ <div id="trash" class="button">
+ <a><?php p($l->t('Deleted files'));?></a>
+ </div>
+ <?php endif; ?>
+ <div id="uploadprogresswrapper">
+ <div id="uploadprogressbar"></div>
+ <input type="button" class="stop" style="display:none"
+ value="<?php p($l->t('Cancel upload'));?>"
+ onclick="javascript:Files.cancelUploads();"
+ />
+ </div>
</div>
<div id="file_action_panel"></div>
<?php else:?>
- <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir">
+ <div class="crumb last"><?php p($l->t('You don’t have write permissions here.'))?></div>
+ <input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
<?php endif;?>
- <input type="hidden" name="permissions" value="<?php echo $_['permissions']; ?>" id="permissions">
+ <input type="hidden" name="permissions" value="<?php p($_['permissions']); ?>" id="permissions">
</div>
-<div id='notification'></div>
<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?>
- <div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div>
+ <div id="emptyfolder"><?php p($l->t('Nothing in here. Upload something!'))?></div>
<?php endif; ?>
-<table>
+<table id="filestable">
<thead>
<tr>
<th id='headerName'>
<input type="checkbox" id="select_all" />
- <span class='name'><?php echo $l->t( 'Name' ); ?></span>
+ <span class='name'><?php p($l->t( 'Name' )); ?></span>
<span class='selectedActions'>
-<!-- <a href="" class="share"><img class='svg' alt="Share" src="<?php echo OCP\image_path("core", "actions/share.svg"); ?>" /> <?php echo $l->t('Share')?></a> -->
<?php if($_['allowZipDownload']) : ?>
- <a href="" class="download"><img class='svg' alt="Download" src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" /> <?php echo $l->t('Download')?></a>
+ <a href="" class="download">
+ <img class="svg" alt="Download"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>" />
+ <?php p($l->t('Download'))?>
+ </a>
<?php endif; ?>
</span>
</th>
- <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th>
+ <th id="headerSize"><?php p($l->t( 'Size' )); ?></th>
<th id="headerDate">
- <span id="modified"><?php echo $l->t( 'Modified' ); ?></span>
- <?php if ($_['permissions'] & OCP\Share::PERMISSION_DELETE): ?>
+ <span id="modified"><?php p($l->t( 'Modified' )); ?></span>
+ <?php if ($_['permissions'] & OCP\PERMISSION_DELETE): ?>
<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder -->
<?php if ($_['dir'] == '/Shared'): ?>
- <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Unshare')?> <img class="svg" alt="<?php echo $l->t('Unshare')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span>
+ <span class="selectedActions"><a href="" class="delete">
+ <?php p($l->t('Unshare'))?>
+ <img class="svg" alt="<?php p($l->t('Unshare'))?>"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
+ </a></span>
<?php else: ?>
- <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span>
+ <span class="selectedActions"><a href="" class="delete">
+ <?php p($l->t('Delete'))?>
+ <img class="svg" alt="<?php p($l->t('Delete'))?>"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
+ </a></span>
<?php endif; ?>
<?php endif; ?>
</th>
</tr>
</thead>
<tbody id="fileList">
- <?php echo($_['fileList']); ?>
+ <?php print_unescaped($_['fileList']); ?>
</tbody>
</table>
<div id="editor"></div>
-<div id="uploadsize-message" title="<?php echo $l->t('Upload too large')?>">
+<div id="uploadsize-message" title="<?php p($l->t('Upload too large'))?>">
<p>
- <?php echo $l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?>
+ <?php p($l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.'));?>
</p>
</div>
<div id="scanning-message">
<h3>
- <?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></span>
+ <?php p($l->t('Files are being scanned, please wait.'));?> <span id='scan-count'></span>
</h3>
<p>
- <?php echo $l->t('Current scanning');?> <span id='scan-current'></span>
+ <?php p($l->t('Current scanning'));?> <span id='scan-current'></span>
</p>
</div>
<!-- config hints for javascript -->
-<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" />
+<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php p($_['allowZipDownload']); ?>" />
+<input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" />
diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php
index 71b695f65f8..7ea1755d1d7 100644
--- a/apps/files/templates/part.breadcrumb.php
+++ b/apps/files/templates/part.breadcrumb.php
@@ -1,6 +1,16 @@
- <?php for($i=0; $i<count($_["breadcrumb"]); $i++):
- $crumb = $_["breadcrumb"][$i]; ?>
- <div class="crumb <?php if($i == count($_["breadcrumb"])-1) echo 'last';?> svg" data-dir='<?php echo urlencode($crumb["dir"]);?>' style='background-image:url("<?php echo OCP\image_path('core','breadcrumb.png');?>")'>
- <a href="<?php echo $_['baseURL'].urlencode($crumb["dir"]); ?>"><?php echo OCP\Util::sanitizeHTML($crumb["name"]); ?></a>
- </div>
- <?php endfor;?>
+<?php if(count($_["breadcrumb"])):?>
+ <div class="crumb">
+ <a href="<?php print_unescaped($_['baseURL']); ?>">
+ <img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
+ </a>
+ </div>
+<?php endif;?>
+<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
+ $crumb = $_["breadcrumb"][$i];
+ $dir = str_replace('+', '%20', urlencode($crumb["dir"]));
+ $dir = str_replace('%2F', '/', $dir); ?>
+ <div class="crumb <?php if($i == count($_["breadcrumb"])-1) p('last');?> svg"
+ data-dir='<?php p($dir);?>'>
+ <a href="<?php p($_['baseURL'].$dir); ?>"><?php p($crumb["name"]); ?></a>
+ </div>
+<?php endfor;
diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php
index 1329b5dc5cd..59267690e66 100644
--- a/apps/files/templates/part.list.php
+++ b/apps/files/templates/part.list.php
@@ -1,41 +1,63 @@
- <script type="text/javascript">
- <?php if ( array_key_exists('publicListView', $_) && $_['publicListView'] == true ) {
- echo "var publicListView = true;";
- } else {
- echo "var publicListView = false;";
- }
- ?>
- </script>
-
- <?php foreach($_['files'] as $file):
- $simple_file_size = OCP\simple_file_size($file['size']);
- $simple_size_color = intval(200-$file['size']/(1024*1024)*2); // the bigger the file, the darker the shade of grey; megabytes*2
- if($simple_size_color<0) $simple_size_color = 0;
- $relative_modified_date = OCP\relative_modified_date($file['mtime']);
- $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); // the older the file, the brighter the shade of grey; days*14
- if($relative_date_color>200) $relative_date_color = 200;
- $name = str_replace('+','%20',urlencode($file['name']));
- $name = str_replace('%2F','/', $name);
- $directory = str_replace('+','%20',urlencode($file['directory']));
- $directory = str_replace('%2F','/', $directory); ?>
- <tr data-id="<?php echo $file['id']; ?>" data-file="<?php echo $name;?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mimetype']?>" data-size='<?php echo $file['size'];?>' data-permissions='<?php echo $file['permissions']; ?>'>
- <td class="filename svg" style="background-image:url(<?php if($file['type'] == 'dir') echo OCP\mimetype_icon('dir'); else echo OCP\mimetype_icon($file['mimetype']); ?>)">
- <?php if(!isset($_['readonly']) || !$_['readonly']) { ?><input type="checkbox" /><?php } ?>
- <a class="name" href="<?php if($file['type'] == 'dir') echo $_['baseURL'].$directory.'/'.$name; else echo $_['downloadURL'].$directory.'/'.$name; ?>" title="">
- <span class="nametext">
- <?php if($file['type'] == 'dir'):?>
- <?php echo htmlspecialchars($file['name']);?>
- <?php else:?>
- <?php echo htmlspecialchars($file['basename']);?><span class='extension'><?php echo $file['extension'];?></span>
- <?php endif;?>
- </span>
- <?php if($file['type'] == 'dir'):?>
- <span class="uploadtext" currentUploads="0">
- </span>
- <?php endif;?>
- </a>
- </td>
- <td class="filesize" title="<?php echo OCP\human_file_size($file['size']); ?>" style="color:rgb(<?php echo $simple_size_color.','.$simple_size_color.','.$simple_size_color ?>)"><?php echo $simple_file_size; ?></td>
- <td class="date"><span class="modified" title="<?php echo $file['date']; ?>" style="color:rgb(<?php echo $relative_date_color.','.$relative_date_color.','.$relative_date_color ?>)"><?php echo $relative_modified_date; ?></span></td>
- </tr>
- <?php endforeach; ?>
+<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>">
+
+<?php foreach($_['files'] as $file):
+ $simple_file_size = OCP\simple_file_size($file['size']);
+ // the bigger the file, the darker the shade of grey; megabytes*2
+ $simple_size_color = intval(200-$file['size']/(1024*1024)*2);
+ if($simple_size_color<0) $simple_size_color = 0;
+ $relative_modified_date = OCP\relative_modified_date($file['mtime']);
+ // the older the file, the brighter the shade of grey; days*14
+ $relative_date_color = round((time()-$file['mtime'])/60/60/24*14);
+ if($relative_date_color>200) $relative_date_color = 200;
+ $name = str_replace('+', '%20', urlencode($file['name']));
+ $name = str_replace('%2F', '/', $name);
+ $directory = str_replace('+', '%20', urlencode($file['directory']));
+ $directory = str_replace('%2F', '/', $directory); ?>
+ <tr data-id="<?php p($file['fileid']); ?>"
+ data-file="<?php p($name);?>"
+ data-type="<?php ($file['type'] == 'dir')?p('dir'):p('file')?>"
+ data-mime="<?php p($file['mimetype'])?>"
+ data-size='<?php p($file['size']);?>'
+ data-permissions='<?php p($file['permissions']); ?>'>
+ <td class="filename svg"
+ <?php if($file['type'] == 'dir'): ?>
+ style="background-image:url(<?php print_unescaped(OCP\mimetype_icon('dir')); ?>)"
+ <?php else: ?>
+ style="background-image:url(<?php print_unescaped(OCP\mimetype_icon($file['mimetype'])); ?>)"
+ <?php endif; ?>
+ >
+ <?php if(!isset($_['readonly']) || !$_['readonly']): ?><input type="checkbox" /><?php endif; ?>
+ <?php if($file['type'] == 'dir'): ?>
+ <a class="name" href="<?php p(rtrim($_['baseURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>" title="">
+ <?php else: ?>
+ <a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>" title="">
+ <?php endif; ?>
+ <span class="nametext">
+ <?php if($file['type'] == 'dir'):?>
+ <?php print_unescaped(htmlspecialchars($file['name']));?>
+ <?php else:?>
+ <?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span>
+ <?php endif;?>
+ </span>
+ <?php if($file['type'] == 'dir'):?>
+ <span class="uploadtext" currentUploads="0">
+ </span>
+ <?php endif;?>
+ </a>
+ </td>
+ <td class="filesize"
+ title="<?php p(OCP\human_file_size($file['size'])); ?>"
+ style="color:rgb(<?php p($simple_size_color.','.$simple_size_color.','.$simple_size_color) ?>)">
+ <?php print_unescaped($simple_file_size); ?>
+ </td>
+ <td class="date">
+ <span class="modified"
+ title="<?php p($file['date']); ?>"
+ style="color:rgb(<?php p($relative_date_color.','
+ .$relative_date_color.','
+ .$relative_date_color) ?>)">
+ <?php p($relative_modified_date); ?>
+ </span>
+ </td>
+ </tr>
+<?php endforeach;
diff --git a/apps/files/templates/upgrade.php b/apps/files/templates/upgrade.php
new file mode 100644
index 00000000000..e03f086e47d
--- /dev/null
+++ b/apps/files/templates/upgrade.php
@@ -0,0 +1,4 @@
+<div id="upgrade">
+ <?php p($l->t('Upgrading filesystem cache...'));?>
+ <div id="progressbar" />
+</div>
diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php
index bb130a366be..bf16fec3aea 100644
--- a/apps/files_encryption/appinfo/app.php
+++ b/apps/files_encryption/appinfo/app.php
@@ -1,19 +1,49 @@
<?php
-OC::$CLASSPATH['OC_Crypt'] = 'apps/files_encryption/lib/crypt.php';
-OC::$CLASSPATH['OC_CryptStream'] = 'apps/files_encryption/lib/cryptstream.php';
-OC::$CLASSPATH['OC_FileProxy_Encryption'] = 'apps/files_encryption/lib/proxy.php';
+OC::$CLASSPATH['OCA\Encryption\Crypt'] = 'files_encryption/lib/crypt.php';
+OC::$CLASSPATH['OCA\Encryption\Hooks'] = 'files_encryption/hooks/hooks.php';
+OC::$CLASSPATH['OCA\Encryption\Util'] = 'files_encryption/lib/util.php';
+OC::$CLASSPATH['OCA\Encryption\Keymanager'] = 'files_encryption/lib/keymanager.php';
+OC::$CLASSPATH['OCA\Encryption\Stream'] = 'files_encryption/lib/stream.php';
+OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php';
+OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
+OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
-OC_FileProxy::register(new OC_FileProxy_Encryption());
+OC_FileProxy::register( new OCA\Encryption\Proxy() );
-OCP\Util::connectHook('OC_User','post_login','OC_Crypt','loginListener');
+// User-related hooks
+OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
+OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
-stream_wrapper_register('crypt','OC_CryptStream');
+// Sharing-related hooks
+OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' );
+OCP\Util::connectHook( 'OCP\Share', 'pre_unshare', 'OCA\Encryption\Hooks', 'preUnshare' );
+OCP\Util::connectHook( 'OCP\Share', 'pre_unshareAll', 'OCA\Encryption\Hooks', 'preUnshareAll' );
-if(!isset($_SESSION['enckey']) and OCP\User::isLoggedIn()) {//force the user to re-loggin if the encryption key isn't unlocked (happens when a user is logged in before the encryption app is enabled)
+// Webdav-related hooks
+OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfile' );
+
+stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
+
+$session = new OCA\Encryption\Session();
+
+if (
+ ! $session->getPrivateKey( \OCP\USER::getUser() )
+ && OCP\User::isLoggedIn()
+ && OCA\Encryption\Crypt::mode() == 'server'
+) {
+
+ // Force the user to log-in again if the encryption key isn't unlocked
+ // (happens when a user is logged in before the encryption app is
+ // enabled)
OCP\User::logout();
- header("Location: ".OC::$WEBROOT.'/');
+
+ header( "Location: " . OC::$WEBROOT.'/' );
+
exit();
+
}
-OCP\App::registerAdmin('files_encryption', 'settings');
+// Register settings scripts
+OCP\App::registerAdmin( 'files_encryption', 'settings' );
+OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );
diff --git a/apps/files_encryption/appinfo/database.xml b/apps/files_encryption/appinfo/database.xml
new file mode 100644
index 00000000000..d294c35d63d
--- /dev/null
+++ b/apps/files_encryption/appinfo/database.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+ <charset>utf8</charset>
+ <table>
+ <name>*dbprefix*encryption</name>
+ <declaration>
+ <field>
+ <name>uid</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>mode</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ </declaration>
+ </table>
+</database> \ No newline at end of file
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
index 48a28fde78a..39ea155488f 100644
--- a/apps/files_encryption/appinfo/info.xml
+++ b/apps/files_encryption/appinfo/info.xml
@@ -2,10 +2,10 @@
<info>
<id>files_encryption</id>
<name>Encryption</name>
- <description>Server side encryption of files. DEPRECATED. This app is no longer supported and will be replaced with an improved version in ownCloud 5. Only enable this features if you want to read old encrypted data. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP.</description>
+ <description>Server side encryption of files. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP.</description>
<licence>AGPL</licence>
- <author>Robin Appelman</author>
- <require>4.9</require>
+ <author>Sam Tuke</author>
+ <require>4</require>
<shipped>true</shipped>
<types>
<filesystem/>
diff --git a/apps/files_encryption/appinfo/routes.php b/apps/files_encryption/appinfo/routes.php
new file mode 100644
index 00000000000..ab83432a4b2
--- /dev/null
+++ b/apps/files_encryption/appinfo/routes.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Copyright (c) 2013, Tom Needham <tom@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+// Register with the capabilities API
+OC_API::register('get', '/cloud/capabilities', array('OCA\Encryption\Capabilities', 'getCapabilities'), 'files_encryption', OC_API::USER_AUTH); \ No newline at end of file
diff --git a/apps/files_encryption/appinfo/spec.txt b/apps/files_encryption/appinfo/spec.txt
new file mode 100644
index 00000000000..2d22dffe08d
--- /dev/null
+++ b/apps/files_encryption/appinfo/spec.txt
@@ -0,0 +1,19 @@
+Encrypted files
+---------------
+
+- Each encrypted file has at least two components: the encrypted data file
+ ('catfile'), and it's corresponding key file ('keyfile'). Shared files have an
+ additional key file ('share key'). The catfile contains the encrypted data
+ concatenated with delimiter text, followed by the initialisation vector ('IV'),
+ and padding. e.g.:
+
+ [encrypted data string][delimiter][IV][padding]
+ [anhAAjAmcGXqj1X9g==][00iv00][MSHU5N5gECP7aAg7][xx] (square braces added)
+
+Notes
+-----
+
+- The user passphrase is required in order to set up or upgrade the app. New
+ keypair generation, and the re-encryption of legacy encrypted files requires
+ it. Therefore an appinfo/update.php script cannot be used, and upgrade logic
+ is handled in the login hook listener. \ No newline at end of file
diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version
index 2f4536184bc..1d71ef97443 100644
--- a/apps/files_encryption/appinfo/version
+++ b/apps/files_encryption/appinfo/version
@@ -1 +1 @@
-0.2 \ No newline at end of file
+0.3 \ No newline at end of file
diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php
new file mode 100644
index 00000000000..2731d5a92f7
--- /dev/null
+++ b/apps/files_encryption/hooks/hooks.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Sam Tuke
+ * @copyright 2012 Sam Tuke samtuke@owncloud.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 Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Encryption;
+
+/**
+ * Class for hook specific logic
+ */
+
+class Hooks {
+
+ // TODO: use passphrase for encrypting private key that is separate to
+ // the login password
+
+ /**
+ * @brief Startup encryption backend upon user login
+ * @note This method should never be called for users using client side encryption
+ */
+ public static function login( $params ) {
+
+ // Manually initialise Filesystem{} singleton with correct
+ // fake root path, in order to avoid fatal webdav errors
+ \OC\Files\Filesystem::init( $params['uid'], $params['uid'] . '/' . 'files' . '/' );
+
+ $view = new \OC_FilesystemView( '/' );
+
+ $util = new Util( $view, $params['uid'] );
+
+ // Check files_encryption infrastructure is ready for action
+ if ( ! $util->ready() ) {
+
+ \OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
+
+ return $util->setupServerSide( $params['password'] );
+
+ }
+
+ \OC_FileProxy::$enabled = false;
+
+ $encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] );
+
+ \OC_FileProxy::$enabled = true;
+
+ $privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] );
+
+ $session = new Session();
+
+ $session->setPrivateKey( $privateKey, $params['uid'] );
+
+ $view1 = new \OC_FilesystemView( '/' . $params['uid'] );
+
+ // Set legacy encryption key if it exists, to support
+ // depreciated encryption system
+ if (
+ $view1->file_exists( 'encryption.key' )
+ && $encLegacyKey = $view1->file_get_contents( 'encryption.key' )
+ ) {
+
+ $plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
+
+ $session->setLegacyKey( $plainLegacyKey );
+
+ }
+
+ $publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
+
+ // Encrypt existing user files:
+ // This serves to upgrade old versions of the encryption
+ // app (see appinfo/spec.txt)
+ if (
+ $util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
+ ) {
+
+ \OC_Log::write(
+ 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login'
+ , \OC_Log::INFO
+ );
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * @brief Change a user's encryption passphrase
+ * @param array $params keys: uid, password
+ */
+ public static function setPassphrase( $params ) {
+
+ // Only attempt to change passphrase if server-side encryption
+ // is in use (client-side encryption does not have access to
+ // the necessary keys)
+ if ( Crypt::mode() == 'server' ) {
+
+ $session = new Session();
+
+ // Get existing decrypted private key
+ $privateKey = $session->getPrivateKey();
+
+ // Encrypt private key with new user pwd as passphrase
+ $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $privateKey, $params['password'] );
+
+ // Save private key
+ Keymanager::setPrivateKey( $encryptedPrivateKey );
+
+ // NOTE: Session does not need to be updated as the
+ // private key has not changed, only the passphrase
+ // used to decrypt it has changed
+
+ }
+
+ }
+
+ /**
+ * @brief update the encryption key of the file uploaded by the client
+ */
+ public static function updateKeyfile( $params ) {
+
+ if ( Crypt::mode() == 'client' ) {
+
+ if ( isset( $params['properties']['key'] ) ) {
+
+ $view = new \OC_FilesystemView( '/' );
+ $userId = \OCP\User::getUser();
+
+ Keymanager::setFileKey( $view, $params['path'], $userId, $params['properties']['key'] );
+
+ } else {
+
+ \OC_Log::write(
+ 'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!"
+ , \OC_Log::ERROR
+ );
+
+ error_log( "Client side encryption is enabled but the client doesn't provide an encryption key for the file!" );
+
+ }
+
+ }
+
+ }
+
+ /**
+ * @brief
+ */
+ public static function postShared( $params ) {
+ }
+
+ /**
+ * @brief
+ */
+ public static function preUnshare( $params ) {
+
+ // Delete existing catfile
+
+ // Generate new catfile and env keys
+
+ // Save env keys to user folders
+ }
+
+ /**
+ * @brief
+ */
+ public static function preUnshareAll( $params ) {
+
+ trigger_error( "preUnshareAll" );
+
+ }
+
+}
diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js
index 6fc70eba7f6..0be857bb73e 100644
--- a/apps/files_encryption/js/settings.js
+++ b/apps/files_encryption/js/settings.js
@@ -9,16 +9,11 @@ $(document).ready(function(){
$('#encryption_blacklist').multiSelect({
oncheck:blackListChange,
onuncheck:blackListChange,
- createText:'...',
+ createText:'...'
});
-
+
function blackListChange(){
var blackList=$('#encryption_blacklist').val().join(',');
OC.AppConfig.setValue('files_encryption','type_blacklist',blackList);
}
-
- $('#enable_encryption').change(function(){
- var checked=$('#enable_encryption').is(':checked');
- OC.AppConfig.setValue('files_encryption','enable_encryption',(checked)?'true':'false');
- });
-});
+}) \ No newline at end of file
diff --git a/apps/files_encryption/l10n/ar.php b/apps/files_encryption/l10n/ar.php
new file mode 100644
index 00000000000..375fbd9a9a6
--- /dev/null
+++ b/apps/files_encryption/l10n/ar.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "التشفير",
+"None" => "لا شيء"
+);
diff --git a/apps/files_encryption/l10n/bg_BG.php b/apps/files_encryption/l10n/bg_BG.php
new file mode 100644
index 00000000000..07a97f5f8a6
--- /dev/null
+++ b/apps/files_encryption/l10n/bg_BG.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Криптиране",
+"None" => "Няма"
+);
diff --git a/apps/files_encryption/l10n/bn_BD.php b/apps/files_encryption/l10n/bn_BD.php
new file mode 100644
index 00000000000..43767d56518
--- /dev/null
+++ b/apps/files_encryption/l10n/bn_BD.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "সংকেতায়ন",
+"None" => "কোনটিই নয়"
+);
diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php
index 8e087b34620..0c661353a77 100644
--- a/apps/files_encryption/l10n/ca.php
+++ b/apps/files_encryption/l10n/ca.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Encriptatge",
-"Exclude the following file types from encryption" => "Exclou els tipus de fitxers següents de l'encriptatge",
-"None" => "Cap",
-"Enable Encryption" => "Activa l'encriptatge"
+"File encryption is enabled." => "L'encriptació de fitxers està activada.",
+"The following file types will not be encrypted:" => "Els tipus de fitxers següents no s'encriptaran:",
+"Exclude the following file types from encryption:" => "Exclou els tipus de fitxers següents de l'encriptatge:",
+"None" => "Cap"
);
diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php
index 9be2be98092..d225688a079 100644
--- a/apps/files_encryption/l10n/cs_CZ.php
+++ b/apps/files_encryption/l10n/cs_CZ.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Šifrování",
-"Exclude the following file types from encryption" => "Při šifrování vynechat následující typy souborů",
-"None" => "Žádné",
-"Enable Encryption" => "Povolit šifrování"
+"File encryption is enabled." => "Šifrování je povoleno.",
+"The following file types will not be encrypted:" => "Následující typy souborů nebudou šifrovány:",
+"Exclude the following file types from encryption:" => "Vyjmout následující typy souborů ze šifrování:",
+"None" => "Žádné"
);
diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php
index 144c9f97084..b085381ea7b 100644
--- a/apps/files_encryption/l10n/da.php
+++ b/apps/files_encryption/l10n/da.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Kryptering",
-"Exclude the following file types from encryption" => "Ekskluder følgende filtyper fra kryptering",
-"None" => "Ingen",
-"Enable Encryption" => "Aktivér kryptering"
+"File encryption is enabled." => "Fil kryptering aktiveret.",
+"The following file types will not be encrypted:" => "De følgende filtyper vil ikke blive krypteret:",
+"Exclude the following file types from encryption:" => "Ekskluder de følgende fil typer fra kryptering:",
+"None" => "Ingen"
);
diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php
index d486a82322b..cdcd8a40b23 100644
--- a/apps/files_encryption/l10n/de.php
+++ b/apps/files_encryption/l10n/de.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Verschlüsselung",
-"Exclude the following file types from encryption" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen",
-"None" => "Keine",
-"Enable Encryption" => "Verschlüsselung aktivieren"
+"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert",
+"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
+"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:",
+"None" => "Keine"
);
diff --git a/apps/files_encryption/l10n/de_DE.php b/apps/files_encryption/l10n/de_DE.php
new file mode 100644
index 00000000000..4f08b98eb29
--- /dev/null
+++ b/apps/files_encryption/l10n/de_DE.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Verschlüsselung",
+"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
+"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
+"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:",
+"None" => "Keine"
+);
diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php
index 40a7c6a3672..0031a731944 100644
--- a/apps/files_encryption/l10n/el.php
+++ b/apps/files_encryption/l10n/el.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Κρυπτογράφηση",
-"Exclude the following file types from encryption" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση",
-"None" => "Καμία",
-"Enable Encryption" => "Ενεργοποίηση Κρυπτογράφησης"
+"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.",
+"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:",
+"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:",
+"None" => "Καμία"
);
diff --git a/apps/files_encryption/l10n/eo.php b/apps/files_encryption/l10n/eo.php
index af3c9ae98e4..50847062c3b 100644
--- a/apps/files_encryption/l10n/eo.php
+++ b/apps/files_encryption/l10n/eo.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Ĉifrado",
-"Exclude the following file types from encryption" => "Malinkluzivigi la jenajn dosiertipojn el ĉifrado",
-"None" => "Nenio",
-"Enable Encryption" => "Kapabligi ĉifradon"
+"None" => "Nenio"
);
diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php
index b7e7601b35f..4ea87b92e7c 100644
--- a/apps/files_encryption/l10n/es.php
+++ b/apps/files_encryption/l10n/es.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Cifrado",
-"Exclude the following file types from encryption" => "Excluir del cifrado los siguientes tipos de archivo",
-"None" => "Ninguno",
-"Enable Encryption" => "Habilitar cifrado"
+"File encryption is enabled." => "La encriptacion de archivo esta activada.",
+"The following file types will not be encrypted:" => "Los siguientes tipos de archivo no seran encriptados:",
+"Exclude the following file types from encryption:" => "Excluir los siguientes tipos de archivo de la encriptacion:",
+"None" => "Ninguno"
);
diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php
index a15c37e730e..af522879e16 100644
--- a/apps/files_encryption/l10n/es_AR.php
+++ b/apps/files_encryption/l10n/es_AR.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Encriptación",
-"Exclude the following file types from encryption" => "Exceptuar de la encriptación los siguientes tipos de archivo",
-"None" => "Ninguno",
-"Enable Encryption" => "Habilitar encriptación"
+"File encryption is enabled." => "La encriptación de archivos no está habilitada",
+"The following file types will not be encrypted:" => "Los siguientes tipos de archivos no serán encriptados",
+"Exclude the following file types from encryption:" => "Excluir los siguientes tipos de archivos de encriptación:",
+"None" => "Ninguno"
);
diff --git a/apps/files_encryption/l10n/et_EE.php b/apps/files_encryption/l10n/et_EE.php
index a7cd9395bf0..0d189ac062e 100644
--- a/apps/files_encryption/l10n/et_EE.php
+++ b/apps/files_encryption/l10n/et_EE.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Krüpteerimine",
-"Exclude the following file types from encryption" => "Järgnevaid failitüüpe ära krüpteeri",
-"None" => "Pole",
-"Enable Encryption" => "Luba krüpteerimine"
+"File encryption is enabled." => "Faili krüpteerimine on sisse lülitatud.",
+"The following file types will not be encrypted:" => "Järgnevaid failitüüpe ei krüpteerita:",
+"Exclude the following file types from encryption:" => "Järgnevaid failitüüpe ei krüpteerita:",
+"None" => "Pole"
);
diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php
index 57b6a4927bf..5a22b65728e 100644
--- a/apps/files_encryption/l10n/eu.php
+++ b/apps/files_encryption/l10n/eu.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Enkriptazioa",
-"Exclude the following file types from encryption" => "Ez enkriptatu hurrengo fitxategi motak",
-"None" => "Bat ere ez",
-"Enable Encryption" => "Gaitu enkriptazioa"
+"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
+"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:",
+"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:",
+"None" => "Bat ere ez"
);
diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php
index 0faa3f3aae7..21ad7e56566 100644
--- a/apps/files_encryption/l10n/fa.php
+++ b/apps/files_encryption/l10n/fa.php
@@ -1,5 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "رمزگذاری",
-"None" => "هیچ‌کدام",
-"Enable Encryption" => "فعال کردن رمزگذاری"
+"None" => "هیچ‌کدام"
);
diff --git a/apps/files_encryption/l10n/fi_FI.php b/apps/files_encryption/l10n/fi_FI.php
index 5796499a26c..6352d396b3c 100644
--- a/apps/files_encryption/l10n/fi_FI.php
+++ b/apps/files_encryption/l10n/fi_FI.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Salaus",
-"Exclude the following file types from encryption" => "Jätä seuraavat tiedostotyypit salaamatta",
-"None" => "Ei mitään",
-"Enable Encryption" => "Käytä salausta"
+"File encryption is enabled." => "Tiedostojen salaus on käytössä.",
+"The following file types will not be encrypted:" => "Seuraavia tiedostotyyppejä ei salata:",
+"Exclude the following file types from encryption:" => "Älä salaa seuravia tiedostotyyppejä:",
+"None" => "Ei mitään"
);
diff --git a/apps/files_encryption/l10n/fr.php b/apps/files_encryption/l10n/fr.php
index c9367d1a312..88f1e4a393f 100644
--- a/apps/files_encryption/l10n/fr.php
+++ b/apps/files_encryption/l10n/fr.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Chiffrement",
-"Exclude the following file types from encryption" => "Ne pas chiffrer les fichiers dont les types sont les suivants",
-"None" => "Aucun",
-"Enable Encryption" => "Activer le chiffrement"
+"File encryption is enabled." => "Le chiffrement des fichiers est activé",
+"The following file types will not be encrypted:" => "Les fichiers de types suivants ne seront pas chiffrés :",
+"Exclude the following file types from encryption:" => "Ne pas chiffrer les fichiers dont les types sont les suivants :",
+"None" => "Aucun"
);
diff --git a/apps/files_encryption/l10n/gl.php b/apps/files_encryption/l10n/gl.php
index 1434ff48aac..3210f715453 100644
--- a/apps/files_encryption/l10n/gl.php
+++ b/apps/files_encryption/l10n/gl.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
-"Encryption" => "Encriptado",
-"Exclude the following file types from encryption" => "Excluír os seguintes tipos de ficheiro da encriptación",
-"None" => "Nada",
-"Enable Encryption" => "Habilitar encriptación"
+"Encryption" => "Cifrado",
+"File encryption is enabled." => "O cifrado de ficheiros está activado",
+"The following file types will not be encrypted:" => "Os seguintes tipos de ficheiros non van seren cifrados:",
+"Exclude the following file types from encryption:" => "Excluír os seguintes tipos de ficheiros do cifrado:",
+"None" => "Ningún"
);
diff --git a/apps/files_encryption/l10n/he.php b/apps/files_encryption/l10n/he.php
new file mode 100644
index 00000000000..cbb74bfee9a
--- /dev/null
+++ b/apps/files_encryption/l10n/he.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "הצפנה",
+"None" => "כלום"
+);
diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php
index 4352d8b7712..4043da108c0 100644
--- a/apps/files_encryption/l10n/hu_HU.php
+++ b/apps/files_encryption/l10n/hu_HU.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Titkosítás",
-"Exclude the following file types from encryption" => "A következő fájl típusok kizárása a titkosításból",
-"None" => "Egyik sem",
-"Enable Encryption" => "Titkosítás engedélyezése"
+"File encryption is enabled." => "Az állományok titkosítása be van kapcsolva.",
+"The following file types will not be encrypted:" => "A következő fájltípusok nem kerülnek titkosításra:",
+"Exclude the following file types from encryption:" => "Zárjuk ki a titkosításból a következő fájltípusokat:",
+"None" => "Egyik sem"
);
diff --git a/apps/files_encryption/l10n/id.php b/apps/files_encryption/l10n/id.php
new file mode 100644
index 00000000000..6044348e72e
--- /dev/null
+++ b/apps/files_encryption/l10n/id.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Enkripsi",
+"File encryption is enabled." => "Enkripsi berkas aktif.",
+"The following file types will not be encrypted:" => "Tipe berkas berikut tidak akan dienkripsi:",
+"Exclude the following file types from encryption:" => "Kecualikan tipe berkas berikut dari enkripsi:",
+"None" => "Tidak ada"
+);
diff --git a/apps/files_encryption/l10n/is.php b/apps/files_encryption/l10n/is.php
new file mode 100644
index 00000000000..bd964185c45
--- /dev/null
+++ b/apps/files_encryption/l10n/is.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Dulkóðun",
+"None" => "Ekkert"
+);
diff --git a/apps/files_encryption/l10n/it.php b/apps/files_encryption/l10n/it.php
index 5136b061797..9ab9bc492a0 100644
--- a/apps/files_encryption/l10n/it.php
+++ b/apps/files_encryption/l10n/it.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Cifratura",
-"Exclude the following file types from encryption" => "Escludi i seguenti tipi di file dalla cifratura",
-"None" => "Nessuna",
-"Enable Encryption" => "Abilita cifratura"
+"File encryption is enabled." => "La cifratura dei file è abilitata.",
+"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:",
+"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:",
+"None" => "Nessuna"
);
diff --git a/apps/files_encryption/l10n/ja_JP.php b/apps/files_encryption/l10n/ja_JP.php
index 2c3e5410de3..35fba615aec 100644
--- a/apps/files_encryption/l10n/ja_JP.php
+++ b/apps/files_encryption/l10n/ja_JP.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "暗号化",
-"Exclude the following file types from encryption" => "暗号化から除外するファイルタイプ",
-"None" => "なし",
-"Enable Encryption" => "暗号化を有効にする"
+"File encryption is enabled." => "ファイルの暗号化は有効です。",
+"The following file types will not be encrypted:" => "次のファイルタイプは暗号化されません:",
+"Exclude the following file types from encryption:" => "次のファイルタイプを暗号化から除外:",
+"None" => "なし"
);
diff --git a/apps/files_encryption/l10n/ko.php b/apps/files_encryption/l10n/ko.php
new file mode 100644
index 00000000000..bd1580578c4
--- /dev/null
+++ b/apps/files_encryption/l10n/ko.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "암호화",
+"None" => "없음"
+);
diff --git a/apps/files_encryption/l10n/ku_IQ.php b/apps/files_encryption/l10n/ku_IQ.php
index bd8977ac515..02c030014fa 100644
--- a/apps/files_encryption/l10n/ku_IQ.php
+++ b/apps/files_encryption/l10n/ku_IQ.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "نهێنیکردن",
-"Exclude the following file types from encryption" => "به‌ربه‌ست کردنی ئه‌م جۆره‌ په‌ڕگانه له‌ نهێنیکردن",
-"None" => "هیچ",
-"Enable Encryption" => "چالاکردنی نهێنیکردن"
+"None" => "هیچ"
);
diff --git a/apps/files_encryption/l10n/lt_LT.php b/apps/files_encryption/l10n/lt_LT.php
index b939df164c8..67769c8f365 100644
--- a/apps/files_encryption/l10n/lt_LT.php
+++ b/apps/files_encryption/l10n/lt_LT.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Šifravimas",
-"Exclude the following file types from encryption" => "Nešifruoti pasirinkto tipo failų",
-"None" => "Nieko",
-"Enable Encryption" => "Įjungti šifravimą"
+"None" => "Nieko"
);
diff --git a/apps/files_encryption/l10n/lv.php b/apps/files_encryption/l10n/lv.php
new file mode 100644
index 00000000000..fc31ccdb92d
--- /dev/null
+++ b/apps/files_encryption/l10n/lv.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Šifrēšana",
+"File encryption is enabled." => "Datņu šifrēšana ir aktivēta.",
+"The following file types will not be encrypted:" => "Sekojošās datnes netiks šifrētas:",
+"Exclude the following file types from encryption:" => "Sekojošos datņu tipus izslēgt no šifrēšanas:",
+"None" => "Nav"
+);
diff --git a/apps/files_encryption/l10n/mk.php b/apps/files_encryption/l10n/mk.php
new file mode 100644
index 00000000000..513606fadc3
--- /dev/null
+++ b/apps/files_encryption/l10n/mk.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Енкрипција",
+"None" => "Ништо"
+);
diff --git a/apps/files_encryption/l10n/nb_NO.php b/apps/files_encryption/l10n/nb_NO.php
index e65df7b6ce3..a5e16a03421 100644
--- a/apps/files_encryption/l10n/nb_NO.php
+++ b/apps/files_encryption/l10n/nb_NO.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Kryptering",
-"Exclude the following file types from encryption" => "Ekskluder følgende filer fra kryptering",
-"None" => "Ingen",
-"Enable Encryption" => "Slå på kryptering"
+"File encryption is enabled." => "Fil-kryptering er aktivert.",
+"The following file types will not be encrypted:" => "Følgende filtyper vil ikke bli kryptert:",
+"Exclude the following file types from encryption:" => "Ekskluder følgende filtyper fra kryptering:",
+"None" => "Ingen"
);
diff --git a/apps/files_encryption/l10n/nl.php b/apps/files_encryption/l10n/nl.php
index 1ea56006fc3..b1cba96aad7 100644
--- a/apps/files_encryption/l10n/nl.php
+++ b/apps/files_encryption/l10n/nl.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Versleuteling",
-"Exclude the following file types from encryption" => "Versleutel de volgende bestand types niet",
-"None" => "Geen",
-"Enable Encryption" => "Zet versleuteling aan"
+"File encryption is enabled." => "Bestandsversleuteling geactiveerd.",
+"The following file types will not be encrypted:" => "De volgende bestandstypen zullen niet worden versleuteld:",
+"Exclude the following file types from encryption:" => "Sluit de volgende bestandstypen uit van versleuteling:",
+"None" => "Geen"
);
diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php
index 5cfc707450e..2fa86f454f9 100644
--- a/apps/files_encryption/l10n/pl.php
+++ b/apps/files_encryption/l10n/pl.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Szyfrowanie",
-"Exclude the following file types from encryption" => "Wyłącz następujące typy plików z szyfrowania",
-"None" => "Brak",
-"Enable Encryption" => "Włącz szyfrowanie"
+"File encryption is enabled." => "Szyfrowanie plików jest włączone",
+"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:",
+"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:",
+"None" => "Brak"
);
diff --git a/apps/files_encryption/l10n/pt_BR.php b/apps/files_encryption/l10n/pt_BR.php
index 5c02f52217f..28807db72ce 100644
--- a/apps/files_encryption/l10n/pt_BR.php
+++ b/apps/files_encryption/l10n/pt_BR.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Criptografia",
-"Exclude the following file types from encryption" => "Excluir os seguintes tipos de arquivo da criptografia",
-"None" => "Nenhuma",
-"Enable Encryption" => "Habilitar Criptografia"
+"File encryption is enabled." => "A criptografia de arquivos está ativada.",
+"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:",
+"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:",
+"None" => "Nenhuma"
);
diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php
index 570462b414f..1c46011fc10 100644
--- a/apps/files_encryption/l10n/pt_PT.php
+++ b/apps/files_encryption/l10n/pt_PT.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Encriptação",
-"Exclude the following file types from encryption" => "Excluir da encriptação os seguintes tipo de ficheiros",
-"None" => "Nenhum",
-"Enable Encryption" => "Activar Encriptação"
+"File encryption is enabled." => "A encriptação de ficheiros está ligada",
+"The following file types will not be encrypted:" => "Os seguintes ficheiros não serão encriptados:",
+"Exclude the following file types from encryption:" => "Excluir da encriptação os seguintes tipos de ficheiro:",
+"None" => "Nenhum"
);
diff --git a/apps/files_encryption/l10n/ro.php b/apps/files_encryption/l10n/ro.php
index 97f3f262d76..a5a6fb3cb78 100644
--- a/apps/files_encryption/l10n/ro.php
+++ b/apps/files_encryption/l10n/ro.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Încriptare",
-"Exclude the following file types from encryption" => "Exclude următoarele tipuri de fișiere de la încriptare",
-"None" => "Niciuna",
-"Enable Encryption" => "Activare încriptare"
+"None" => "Niciuna"
);
diff --git a/apps/files_encryption/l10n/ru.php b/apps/files_encryption/l10n/ru.php
index 3a7e84b6d01..22c1e3da374 100644
--- a/apps/files_encryption/l10n/ru.php
+++ b/apps/files_encryption/l10n/ru.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Шифрование",
-"Exclude the following file types from encryption" => "Исключить шифрование следующих типов файлов",
-"None" => "Ничего",
-"Enable Encryption" => "Включить шифрование"
+"File encryption is enabled." => "Шифрование файла включено.",
+"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:",
+"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:",
+"None" => "Ничего"
);
diff --git a/apps/files_encryption/l10n/ru_RU.php b/apps/files_encryption/l10n/ru_RU.php
index 1328b0d0359..7222235485c 100644
--- a/apps/files_encryption/l10n/ru_RU.php
+++ b/apps/files_encryption/l10n/ru_RU.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Шифрование",
-"Exclude the following file types from encryption" => "Исключите следующие типы файлов из шифрования",
-"None" => "Ни один",
-"Enable Encryption" => "Включить шифрование"
+"None" => "Ни один"
);
diff --git a/apps/files_encryption/l10n/si_LK.php b/apps/files_encryption/l10n/si_LK.php
new file mode 100644
index 00000000000..d9cec4b7220
--- /dev/null
+++ b/apps/files_encryption/l10n/si_LK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "ගුප්ත කේතනය",
+"None" => "කිසිවක් නැත"
+);
diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php
index 598f1294f6e..bebb6234710 100644
--- a/apps/files_encryption/l10n/sk_SK.php
+++ b/apps/files_encryption/l10n/sk_SK.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Šifrovanie",
-"Exclude the following file types from encryption" => "Vynechať nasledujúce súbory pri šifrovaní",
-"None" => "Žiadne",
-"Enable Encryption" => "Zapnúť šifrovanie"
+"File encryption is enabled." => "Šifrovanie súborov nastavené.",
+"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:",
+"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov",
+"None" => "Žiadne"
);
diff --git a/apps/files_encryption/l10n/sl.php b/apps/files_encryption/l10n/sl.php
index 65807910e10..4754e21214e 100644
--- a/apps/files_encryption/l10n/sl.php
+++ b/apps/files_encryption/l10n/sl.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Šifriranje",
-"Exclude the following file types from encryption" => "Naslednje vrste datotek naj se ne šifrirajo",
-"None" => "Brez",
-"Enable Encryption" => "Omogoči šifriranje"
+"File encryption is enabled." => "Šifriranje datotek je omogočeno.",
+"The following file types will not be encrypted:" => "Navedene vrste datotek ne bodo šifrirane:",
+"Exclude the following file types from encryption:" => "Ne šifriraj navedenih vrst datotek:",
+"None" => "Brez"
);
diff --git a/apps/files_encryption/l10n/sr.php b/apps/files_encryption/l10n/sr.php
new file mode 100644
index 00000000000..91f7fc62a90
--- /dev/null
+++ b/apps/files_encryption/l10n/sr.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Шифровање",
+"None" => "Ништа"
+);
diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php
index 0a477f83460..e214a937a1d 100644
--- a/apps/files_encryption/l10n/sv.php
+++ b/apps/files_encryption/l10n/sv.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Kryptering",
-"Exclude the following file types from encryption" => "Exkludera följande filtyper från kryptering",
-"None" => "Ingen",
-"Enable Encryption" => "Aktivera kryptering"
+"File encryption is enabled." => "Filkryptering är aktiverat.",
+"The following file types will not be encrypted:" => "Följande filtyper kommer inte att krypteras:",
+"Exclude the following file types from encryption:" => "Exkludera följande filtyper från kryptering:",
+"None" => "Ingen"
);
diff --git a/apps/files_encryption/l10n/ta_LK.php b/apps/files_encryption/l10n/ta_LK.php
new file mode 100644
index 00000000000..152e631d0fc
--- /dev/null
+++ b/apps/files_encryption/l10n/ta_LK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "மறைக்குறியீடு",
+"None" => "ஒன்றுமில்லை"
+);
diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php
index c2685de6e3a..e46d2491186 100644
--- a/apps/files_encryption/l10n/th_TH.php
+++ b/apps/files_encryption/l10n/th_TH.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "การเข้ารหัส",
-"Exclude the following file types from encryption" => "ไม่ต้องรวมชนิดของไฟล์ดังต่อไปนี้จากการเข้ารหัส",
-"None" => "ไม่ต้อง",
-"Enable Encryption" => "เปิดใช้งานการเข้ารหัส"
+"None" => "ไม่ต้อง"
);
diff --git a/apps/files_encryption/l10n/tr.php b/apps/files_encryption/l10n/tr.php
new file mode 100644
index 00000000000..6b42c757e65
--- /dev/null
+++ b/apps/files_encryption/l10n/tr.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Şifreleme",
+"File encryption is enabled." => "Dosya şifreleme aktif.",
+"The following file types will not be encrypted:" => "Belirtilen dosya tipleri şifrelenmeyecek:",
+"Exclude the following file types from encryption:" => "Seçilen dosya tiplerini şifreleme:",
+"None" => "Hiçbiri"
+);
diff --git a/apps/files_encryption/l10n/uk.php b/apps/files_encryption/l10n/uk.php
new file mode 100644
index 00000000000..d4957141191
--- /dev/null
+++ b/apps/files_encryption/l10n/uk.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "Шифрування",
+"File encryption is enabled." => "Увімкнуто шифрування файлів.",
+"The following file types will not be encrypted:" => "Такі типи файлів шифруватись не будуть:",
+"Exclude the following file types from encryption:" => "Виключити наступні типи файлів з ​​шифрування:",
+"None" => "Жоден"
+);
diff --git a/apps/files_encryption/l10n/vi.php b/apps/files_encryption/l10n/vi.php
index cabf2da7dce..0a88d1b2db6 100644
--- a/apps/files_encryption/l10n/vi.php
+++ b/apps/files_encryption/l10n/vi.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "Mã hóa",
-"Exclude the following file types from encryption" => "Loại trừ các loại tập tin sau đây từ mã hóa",
-"None" => "none",
-"Enable Encryption" => "BẬT mã hóa"
+"File encryption is enabled." => "Mã hóa file đã mở",
+"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa",
+"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau",
+"None" => "Không có gì hết"
);
diff --git a/apps/files_encryption/l10n/zh_CN.GB2312.php b/apps/files_encryption/l10n/zh_CN.GB2312.php
index 297444fcf55..12d903e6567 100644
--- a/apps/files_encryption/l10n/zh_CN.GB2312.php
+++ b/apps/files_encryption/l10n/zh_CN.GB2312.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "加密",
-"Exclude the following file types from encryption" => "从加密中排除如下文件类型",
-"None" => "无",
-"Enable Encryption" => "启用加密"
+"None" => "无"
);
diff --git a/apps/files_encryption/l10n/zh_CN.php b/apps/files_encryption/l10n/zh_CN.php
index 1e1247d15ff..13fa95203e4 100644
--- a/apps/files_encryption/l10n/zh_CN.php
+++ b/apps/files_encryption/l10n/zh_CN.php
@@ -1,6 +1,7 @@
<?php $TRANSLATIONS = array(
"Encryption" => "加密",
-"Exclude the following file types from encryption" => "从加密中排除列出的文件类型",
-"None" => "None",
-"Enable Encryption" => "开启加密"
+"File encryption is enabled." => "文件加密已启用.",
+"The following file types will not be encrypted:" => "如下的文件类型将不会被加密:",
+"Exclude the following file types from encryption:" => "从加密中排除如下的文件类型:",
+"None" => "无"
);
diff --git a/apps/files_encryption/l10n/zh_HK.php b/apps/files_encryption/l10n/zh_HK.php
new file mode 100644
index 00000000000..0c0b709fdc1
--- /dev/null
+++ b/apps/files_encryption/l10n/zh_HK.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"Encryption" => "加密",
+"File encryption is enabled." => "檔案加密已開啟",
+"The following file types will not be encrypted:" => "以下文件類別將不會被加密",
+"None" => "空"
+);
diff --git a/apps/files_encryption/l10n/zh_TW.php b/apps/files_encryption/l10n/zh_TW.php
index 4c62130cf4f..1655e171433 100644
--- a/apps/files_encryption/l10n/zh_TW.php
+++ b/apps/files_encryption/l10n/zh_TW.php
@@ -1,6 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "加密",
-"Exclude the following file types from encryption" => "下列的檔案類型不加密",
-"None" => "無",
-"Enable Encryption" => "啟用加密"
+"None" => "無"
);
diff --git a/apps/files_encryption/lib/capabilities.php b/apps/files_encryption/lib/capabilities.php
new file mode 100644
index 00000000000..72baddcd049
--- /dev/null
+++ b/apps/files_encryption/lib/capabilities.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2013 Tom Needham <tom@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCA\Encryption;
+
+class Capabilities {
+
+ public static function getCapabilities() {
+ return new \OC_OCS_Result(array(
+ 'capabilities' => array(
+ 'files' => array(
+ 'encryption' => true,
+ ),
+ ),
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php
index 38d8edf28c3..437a18669e5 100644..100755
--- a/apps/files_encryption/lib/crypt.php
+++ b/apps/files_encryption/lib/crypt.php
@@ -1,219 +1,695 @@
-<?php
-/**
- * ownCloud
- *
- * @author Frank Karlitschek
- * @copyright 2012 Frank Karlitschek frank@owncloud.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 Affero General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-// Todo:
-// - Crypt/decrypt button in the userinterface
-// - Setting if crypto should be on by default
-// - Add a setting "Don´t encrypt files larger than xx because of performance reasons"
-// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is encrypted (.encrypted extension)
-// - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster
-// - IMPORTANT! Check if the block lenght of the encrypted data stays the same
-
-
-require_once 'Crypt_Blowfish/Blowfish.php';
-
-/**
- * This class is for crypting and decrypting
- */
-class OC_Crypt {
- static private $bf = null;
-
- public static function loginListener($params) {
- self::init($params['uid'],$params['password']);
- }
-
- public static function init($login,$password) {
- $view=new OC_FilesystemView('/');
- if(!$view->file_exists('/'.$login)) {
- $view->mkdir('/'.$login);
- }
-
- OC_FileProxy::$enabled=false;
- if(!$view->file_exists('/'.$login.'/encryption.key')) {// does key exist?
- OC_Crypt::createkey($login,$password);
- }
- $key=$view->file_get_contents('/'.$login.'/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);
-
- // Write the file
- $proxyEnabled=OC_FileProxy::$enabled;
- OC_FileProxy::$enabled=false;
- $view=new OC_FilesystemView('/'.$username);
- $view->file_put_contents('/encryption.key',$enckey);
- OC_FileProxy::$enabled=$proxyEnabled;
- }
-
- public static function changekeypasscode($oldPassword, $newPassword) {
- if(OCP\User::isLoggedIn()) {
- $username=OCP\USER::getUser();
- $view=new OC_FilesystemView('/'.$username);
-
- // read old key
- $key=$view->file_get_contents('/encryption.key');
-
- // decrypt key with old passcode
- $key=OC_Crypt::decrypt($key, $oldPassword);
-
- // encrypt again with new passcode
- $key=OC_Crypt::encrypt($key, $newPassword);
-
- // store the new key
- $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 (optional)
- * @returns encrypted content
- *
- * This function encrypts an content
- */
- 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);
- $data=$bf->decrypt($content);
- return $data;
- }
-
- /**
- * @brief encryption of a file
- * @param string $source
- * @param string $target
- * @param string $key the decryption key
- *
- * This function encrypts a file
- */
- public static function encryptFile( $source, $target, $key='') {
- $handleread = fopen($source, "rb");
- if($handleread!=FALSE) {
- $handlewrite = fopen($target, "wb");
- while (!feof($handleread)) {
- $content = fread($handleread, 8192);
- $enccontent=OC_CRYPT::encrypt( $content, $key);
- fwrite($handlewrite, $enccontent);
- }
- fclose($handlewrite);
- fclose($handleread);
- }
- }
-
-
- /**
- * @brief decryption of a file
- * @param string $source
- * @param string $target
- * @param string $key the decryption key
- *
- * This function decrypts a file
- */
- public static function decryptFile( $source, $target, $key='') {
- $handleread = fopen($source, "rb");
- if($handleread!=FALSE) {
- $handlewrite = fopen($target, "wb");
- while (!feof($handleread)) {
- $content = fread($handleread, 8192);
- $enccontent=OC_CRYPT::decrypt( $content, $key);
- if(feof($handleread)) {
- $enccontent=rtrim($enccontent, "\0");
- }
- fwrite($handlewrite, $enccontent);
- }
- fclose($handlewrite);
- fclose($handleread);
- }
- }
-
- /**
- * encrypt data in 8192b sized blocks
- */
- public static function blockEncrypt($data, $key='') {
- $result='';
- while(strlen($data)) {
- $result.=self::encrypt(substr($data,0,8192),$key);
- $data=substr($data,8192);
- }
- return $result;
- }
-
- /**
- * decrypt data in 8192b sized blocks
- */
- public static function blockDecrypt($data, $key='',$maxLength=0) {
- $result='';
- while(strlen($data)) {
- $result.=self::decrypt(substr($data,0,8192),$key);
- $data=substr($data,8192);
- }
- if($maxLength>0) {
- return substr($result,0,$maxLength);
- }else{
- return rtrim($result, "\0");
- }
- }
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Sam Tuke, Frank Karlitschek, Robin Appelman
+ * @copyright 2012 Sam Tuke samtuke@owncloud.com,
+ * Robin Appelman icewind@owncloud.com, Frank Karlitschek
+ * frank@owncloud.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 Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Encryption;
+
+require_once 'Crypt_Blowfish/Blowfish.php';
+
+// Todo:
+// - Add a setting "Don´t encrypt files larger than xx because of performance"
+// - Don't use a password directly as encryption key. but a key which is
+// stored on the server and encrypted with the user password. -> change pass
+// faster
+
+/**
+ * Class for common cryptography functionality
+ */
+
+class Crypt {
+
+ /**
+ * @brief return encryption mode client or server side encryption
+ * @param string user name (use system wide setting if name=null)
+ * @return string 'client' or 'server'
+ */
+ public static function mode( $user = null ) {
+
+ return 'server';
+
+ }
+
+ /**
+ * @brief Create a new encryption keypair
+ * @return array publicKey, privatekey
+ */
+ public static function createKeypair() {
+
+ $res = openssl_pkey_new();
+
+ // Get private key
+ openssl_pkey_export( $res, $privateKey );
+
+ // Get public key
+ $publicKey = openssl_pkey_get_details( $res );
+
+ $publicKey = $publicKey['key'];
+
+ return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) );
+
+ }
+
+ /**
+ * @brief Add arbitrary padding to encrypted data
+ * @param string $data data to be padded
+ * @return padded data
+ * @note In order to end up with data exactly 8192 bytes long we must
+ * add two letters. It is impossible to achieve exactly 8192 length
+ * blocks with encryption alone, hence padding is added to achieve the
+ * required length.
+ */
+ public static function addPadding( $data ) {
+
+ $padded = $data . 'xx';
+
+ return $padded;
+
+ }
+
+ /**
+ * @brief Remove arbitrary padding to encrypted data
+ * @param string $padded padded data to remove padding from
+ * @return unpadded data on success, false on error
+ */
+ public static function removePadding( $padded ) {
+
+ if ( substr( $padded, -2 ) == 'xx' ) {
+
+ $data = substr( $padded, 0, -2 );
+
+ return $data;
+
+ } else {
+
+ // TODO: log the fact that unpadded data was submitted for removal of padding
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Check if a file's contents contains an IV and is symmetrically encrypted
+ * @return true / false
+ * @note see also OCA\Encryption\Util->isEncryptedPath()
+ */
+ public static function isCatfile( $content ) {
+
+ if ( !$content ) {
+
+ return false;
+
+ }
+
+ $noPadding = self::removePadding( $content );
+
+ // Fetch encryption metadata from end of file
+ $meta = substr( $noPadding, -22 );
+
+ // Fetch IV from end of file
+ $iv = substr( $meta, -16 );
+
+ // Fetch identifier from start of metadata
+ $identifier = substr( $meta, 0, 6 );
+
+ if ( $identifier == '00iv00') {
+
+ return true;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Check if a file is encrypted according to database file cache
+ * @param string $path
+ * @return bool
+ */
+ public static function isEncryptedMeta( $path ) {
+
+ // TODO: Use DI to get \OC\Files\Filesystem out of here
+
+ // Fetch all file metadata from DB
+ $metadata = \OC\Files\Filesystem::getFileInfo( $path, '' );
+
+ // Return encryption status
+ return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted'];
+
+ }
+
+ /**
+ * @brief Check if a file is encrypted via legacy system
+ * @param string $relPath The path of the file, relative to user/data;
+ * e.g. filename or /Docs/filename, NOT admin/files/filename
+ * @return true / false
+ */
+ public static function isLegacyEncryptedContent( $data, $relPath ) {
+
+ // Fetch all file metadata from DB
+ $metadata = \OC\Files\Filesystem::getFileInfo( $relPath, '' );
+
+ // If a file is flagged with encryption in DB, but isn't a
+ // valid content + IV combination, it's probably using the
+ // legacy encryption system
+ if (
+ isset( $metadata['encrypted'] )
+ and $metadata['encrypted'] === true
+ and ! self::isCatfile( $data )
+ ) {
+
+ return true;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Symmetrically encrypt a string
+ * @returns encrypted file
+ */
+ public static function encrypt( $plainContent, $iv, $passphrase = '' ) {
+
+ if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
+
+ return $encryptedContent;
+
+ } else {
+
+ \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR );
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Symmetrically decrypt a string
+ * @returns decrypted file
+ */
+ public static function decrypt( $encryptedContent, $iv, $passphrase ) {
+
+ if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
+
+ return $plainContent;
+
+
+ } else {
+
+ throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' );
+
+ }
+
+ }
+
+ /**
+ * @brief Concatenate encrypted data with its IV and padding
+ * @param string $content content to be concatenated
+ * @param string $iv IV to be concatenated
+ * @returns string concatenated content
+ */
+ public static function concatIv ( $content, $iv ) {
+
+ $combined = $content . '00iv00' . $iv;
+
+ return $combined;
+
+ }
+
+ /**
+ * @brief Split concatenated data and IV into respective parts
+ * @param string $catFile concatenated data to be split
+ * @returns array keys: encrypted, iv
+ */
+ public static function splitIv ( $catFile ) {
+
+ // Fetch encryption metadata from end of file
+ $meta = substr( $catFile, -22 );
+
+ // Fetch IV from end of file
+ $iv = substr( $meta, -16 );
+
+ // Remove IV and IV identifier text to expose encrypted content
+ $encrypted = substr( $catFile, 0, -22 );
+
+ $split = array(
+ 'encrypted' => $encrypted
+ , 'iv' => $iv
+ );
+
+ return $split;
+
+ }
+
+ /**
+ * @brief Symmetrically encrypts a string and returns keyfile content
+ * @param $plainContent content to be encrypted in keyfile
+ * @returns encrypted content combined with IV
+ * @note IV need not be specified, as it will be stored in the returned keyfile
+ * and remain accessible therein.
+ */
+ public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) {
+
+ if ( !$plainContent ) {
+
+ return false;
+
+ }
+
+ $iv = self::generateIv();
+
+ if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) {
+
+ // Combine content to encrypt with IV identifier and actual IV
+ $catfile = self::concatIv( $encryptedContent, $iv );
+
+ $padded = self::addPadding( $catfile );
+
+ return $padded;
+
+ } else {
+
+ \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR );
+
+ return false;
+
+ }
+
+ }
+
+
+ /**
+ * @brief Symmetrically decrypts keyfile content
+ * @param string $source
+ * @param string $target
+ * @param string $key the decryption key
+ * @returns decrypted content
+ *
+ * This function decrypts a file
+ */
+ public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) {
+
+ if ( !$keyfileContent ) {
+
+ throw new \Exception( 'Encryption library: no data provided for decryption' );
+
+ }
+
+ // Remove padding
+ $noPadding = self::removePadding( $keyfileContent );
+
+ // Split into enc data and catfile
+ $catfile = self::splitIv( $noPadding );
+
+ if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) {
+
+ return $plainContent;
+
+ }
+
+ }
+
+ /**
+ * @brief Creates symmetric keyfile content using a generated key
+ * @param string $plainContent content to be encrypted
+ * @returns array keys: key, encrypted
+ * @note symmetricDecryptFileContent() can be used to decrypt files created using this method
+ *
+ * This function decrypts a file
+ */
+ public static function symmetricEncryptFileContentKeyfile( $plainContent ) {
+
+ $key = self::generateKey();
+
+ if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) {
+
+ return array(
+ 'key' => $key
+ , 'encrypted' => $encryptedContent
+ );
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Create asymmetrically encrypted keyfile content using a generated key
+ * @param string $plainContent content to be encrypted
+ * @returns array keys: key, encrypted
+ * @note symmetricDecryptFileContent() can be used to decrypt files created using this method
+ *
+ * This function decrypts a file
+ */
+ public static function multiKeyEncrypt( $plainContent, array $publicKeys ) {
+
+ // Set empty vars to be set by openssl by reference
+ $sealed = '';
+ $envKeys = array();
+
+ if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) {
+
+ return array(
+ 'keys' => $envKeys
+ , 'encrypted' => $sealed
+ );
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Asymmetrically encrypt a file using multiple public keys
+ * @param string $plainContent content to be encrypted
+ * @returns string $plainContent decrypted string
+ * @note symmetricDecryptFileContent() can be used to decrypt files created using this method
+ *
+ * This function decrypts a file
+ */
+ public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) {
+
+ if ( !$encryptedContent ) {
+
+ return false;
+
+ }
+
+ if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) {
+
+ return $plainContent;
+
+ } else {
+
+ \OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR );
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Asymmetrically encrypt a string using a public key
+ * @returns encrypted file
+ */
+ public static function keyEncrypt( $plainContent, $publicKey ) {
+
+ openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey );
+
+ return $encryptedContent;
+
+ }
+
+ /**
+ * @brief Asymetrically decrypt a file using a private key
+ * @returns decrypted file
+ */
+ public static function keyDecrypt( $encryptedContent, $privatekey ) {
+
+ openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
+
+ return $plainContent;
+
+ }
+
+ /**
+ * @brief Encrypts content symmetrically and generates keyfile asymmetrically
+ * @returns array containing catfile and new keyfile.
+ * keys: data, key
+ * @note this method is a wrapper for combining other crypt class methods
+ */
+ public static function keyEncryptKeyfile( $plainContent, $publicKey ) {
+
+ // Encrypt plain data, generate keyfile & encrypted file
+ $cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent );
+
+ // Encrypt keyfile
+ $cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey );
+
+ return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey );
+
+ }
+
+ /**
+ * @brief Takes catfile, keyfile, and private key, and
+ * performs decryption
+ * @returns decrypted content
+ * @note this method is a wrapper for combining other crypt class methods
+ */
+ public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) {
+
+ // Decrypt the keyfile with the user's private key
+ $decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey );
+
+ // Decrypt the catfile symmetrically using the decrypted keyfile
+ $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile );
+
+ return $decryptedData;
+
+ }
+
+ /**
+ * @brief Symmetrically encrypt a file by combining encrypted component data blocks
+ */
+ public static function symmetricBlockEncryptFileContent( $plainContent, $key ) {
+
+ $crypted = '';
+
+ $remaining = $plainContent;
+
+ $testarray = array();
+
+ while( strlen( $remaining ) ) {
+
+ //echo "\n\n\$block = ".substr( $remaining, 0, 6126 );
+
+ // Encrypt a chunk of unencrypted data and add it to the rest
+ $block = self::symmetricEncryptFileContent( substr( $remaining, 0, 6126 ), $key );
+
+ $padded = self::addPadding( $block );
+
+ $crypted .= $block;
+
+ $testarray[] = $block;
+
+ // Remove the data already encrypted from remaining unencrypted data
+ $remaining = substr( $remaining, 6126 );
+
+ }
+
+ return $crypted;
+
+ }
+
+
+ /**
+ * @brief Symmetrically decrypt a file by combining encrypted component data blocks
+ */
+ public static function symmetricBlockDecryptFileContent( $crypted, $key ) {
+
+ $decrypted = '';
+
+ $remaining = $crypted;
+
+ $testarray = array();
+
+ while( strlen( $remaining ) ) {
+
+ $testarray[] = substr( $remaining, 0, 8192 );
+
+ // Decrypt a chunk of unencrypted data and add it to the rest
+ $decrypted .= self::symmetricDecryptFileContent( $remaining, $key );
+
+ // Remove the data already encrypted from remaining unencrypted data
+ $remaining = substr( $remaining, 8192 );
+
+ }
+
+ return $decrypted;
+
+ }
+
+ /**
+ * @brief Generates a pseudo random initialisation vector
+ * @return String $iv generated IV
+ */
+ public static function generateIv() {
+
+ if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) {
+
+ if ( !$strong ) {
+
+ // If OpenSSL indicates randomness is insecure, log error
+ \OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN );
+
+ }
+
+ // We encode the iv purely for string manipulation
+ // purposes - it gets decoded before use
+ $iv = base64_encode( $random );
+
+ return $iv;
+
+ } else {
+
+ throw new \Exception( 'Generating IV failed' );
+
+ }
+
+ }
+
+ /**
+ * @brief Generate a pseudo random 1024kb ASCII key
+ * @returns $key Generated key
+ */
+ public static function generateKey() {
+
+ // Generate key
+ if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) {
+
+ if ( !$strong ) {
+
+ // If OpenSSL indicates randomness is insecure, log error
+ throw new \Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' );
+
+ }
+
+ return $key;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Get the blowfish encryption handeler for a key
+ * @param $key string (optional)
+ * @return Crypt_Blowfish blowfish object
+ *
+ * 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 {
+
+ return false;
+
+ }
+
+ }
+
+ public static function legacyCreateKey( $passphrase ) {
+
+ // Generate a random integer
+ $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 );
+
+ // Encrypt the key with the passphrase
+ $legacyEncKey = self::legacyEncrypt( $key, $passphrase );
+
+ return $legacyEncKey;
+
+ }
+
+ /**
+ * @brief encrypts content using legacy blowfish system
+ * @param $content the cleartext message you want to encrypt
+ * @param $key the encryption key (optional)
+ * @returns encrypted content
+ *
+ * This function encrypts an content
+ */
+ public static function legacyEncrypt( $content, $passphrase = '' ) {
+
+ $bf = self::getBlowfish( $passphrase );
+
+ return $bf->encrypt( $content );
+
+ }
+
+ /**
+ * @brief decrypts content using legacy blowfish system
+ * @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 legacyDecrypt( $content, $passphrase = '' ) {
+
+ $bf = self::getBlowfish( $passphrase );
+
+ $decrypted = $bf->decrypt( $content );
+
+ $trimmed = rtrim( $decrypted, "\0" );
+
+ return $trimmed;
+
+ }
+
+ public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) {
+
+ $decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase );
+
+ $recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey );
+
+ return $recrypted;
+
+ }
+
+ /**
+ * @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV
+ * @param $legacyContent the legacy encrypted content to re-encrypt
+ * @returns cleartext content
+ *
+ * This function decrypts an content
+ */
+ public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) {
+
+ // TODO: write me
+
+ }
+
}
diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php
deleted file mode 100644
index 721a1b955df..00000000000
--- a/apps/files_encryption/lib/cryptstream.php
+++ /dev/null
@@ -1,174 +0,0 @@
-<?php
-/**
- * ownCloud
- *
- * @author Robin Appelman
- * @copyright 2011 Robin Appelman icewind1991@gmail.com
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-/**
- * 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 $meta=array();//header/meta for source stream
- private $writeCache;
- private $size;
- private static $rootView;
-
- public function stream_open($path, $mode, $options, &$opened_path) {
- if(!self::$rootView) {
- self::$rootView=new OC_FilesystemView('');
- }
- $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'];
- $this->size=self::$sourceStreams[basename($path)]['size'];
- }else{
- $this->path=$path;
- if($mode=='w' or $mode=='w+' or $mode=='wb' or $mode=='wb+') {
- $this->size=0;
- }else{
- $this->size=self::$rootView->filesize($path,$mode);
- }
- OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
- $this->source=self::$rootView->fopen($path,$mode);
- OC_FileProxy::$enabled=true;
- if(!is_resource($this->source)) {
- OCP\Util::writeLog('files_encryption','failed to open '.$path,OCP\Util::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) {
- $this->flush();
- fseek($this->source,$offset,$whence);
- }
-
- public function stream_tell() {
- return ftell($this->source);
- }
-
- public function stream_read($count) {
- //$count will always be 8192 https://bugs.php.net/bug.php?id=21641
- //This makes this function a lot simpler but will breake everything the moment it's fixed
- $this->writeCache='';
- if($count!=8192) {
- OCP\Util::writeLog('files_encryption','php bug 21641 no longer holds, decryption will not work',OCP\Util::FATAL);
- die();
- }
- $pos=ftell($this->source);
- $data=fread($this->source,8192);
- if(strlen($data)) {
- $result=OC_Crypt::decrypt($data);
- }else{
- $result='';
- }
- $length=$this->size-$pos;
- if($length<8192) {
- $result=substr($result,0,$length);
- }
- return $result;
- }
-
- public function stream_write($data) {
- $length=strlen($data);
- $currentPos=ftell($this->source);
- if($this->writeCache) {
- $data=$this->writeCache.$data;
- $this->writeCache='';
- }
- 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;
- fseek($this->source,-($currentPos%8192),SEEK_CUR);
- }
- $currentPos=ftell($this->source);
- while($remainingLength=strlen($data)>0) {
- if($remainingLength<8192) {
- $this->writeCache=$data;
- $data='';
- }else{
- $encrypted=OC_Crypt::encrypt(substr($data,0,8192));
- fwrite($this->source,$encrypted);
- $data=substr($data,8192);
- }
- }
- $this->size=max($this->size,$currentPos+$length);
- 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);
- }
-
- private function flush() {
- if($this->writeCache) {
- $encrypted=OC_Crypt::encrypt($this->writeCache);
- fwrite($this->source,$encrypted);
- $this->writeCache='';
- }
- }
-
- public function stream_close() {
- $this->flush();
- if($this->meta['mode']!='r' and $this->meta['mode']!='rb') {
- OC_FileCache::put($this->path,array('encrypted'=>true,'size'=>$this->size),'');
- }
- return fclose($this->source);
- }
-}
diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php
new file mode 100755
index 00000000000..95587797154
--- /dev/null
+++ b/apps/files_encryption/lib/keymanager.php
@@ -0,0 +1,323 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2012 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Encryption;
+
+/**
+ * @brief Class to manage storage and retrieval of encryption keys
+ * @note Where a method requires a view object, it's root must be '/'
+ */
+class Keymanager {
+
+ /**
+ * @brief retrieve the ENCRYPTED private key from a user
+ *
+ * @return string private key or false
+ * @note the key returned by this method must be decrypted before use
+ */
+ public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
+
+ $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
+
+ $key = $view->file_get_contents( $path );
+
+ return $key;
+ }
+
+ /**
+ * @brief retrieve public key for a specified user
+ * @param \OC_FilesystemView $view
+ * @param $userId
+ * @return string public key or false
+ */
+ public static function getPublicKey( \OC_FilesystemView $view, $userId ) {
+
+ return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' );
+
+ }
+
+ /**
+ * @brief retrieve both keys from a user (private and public)
+ * @param \OC_FilesystemView $view
+ * @param $userId
+ * @return array keys: privateKey, publicKey
+ */
+ public static function getUserKeys( \OC_FilesystemView $view, $userId ) {
+
+ return array(
+ 'publicKey' => self::getPublicKey( $view, $userId )
+ , 'privateKey' => self::getPrivateKey( $view, $userId )
+ );
+
+ }
+
+ /**
+ * @brief Retrieve public keys of all users with access to a file
+ * @param string $path Path to file
+ * @return array of public keys for the given file
+ * @note Checks that the sharing app is enabled should be performed
+ * by client code, that isn't checked here
+ */
+ public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) {
+
+ $path = ltrim( $path, '/' );
+
+ $filepath = '/' . $userId . '/files/' . $filePath;
+
+ // Check if sharing is enabled
+ if ( OC_App::isEnabled( 'files_sharing' ) ) {
+
+
+
+ } else {
+
+ // check if it is a file owned by the user and not shared at all
+ $userview = new \OC_FilesystemView( '/'.$userId.'/files/' );
+
+ if ( $userview->file_exists( $path ) ) {
+
+ $users[] = $userId;
+
+ }
+
+ }
+
+ $view = new \OC_FilesystemView( '/public-keys/' );
+
+ $keylist = array();
+
+ $count = 0;
+
+ foreach ( $users as $user ) {
+
+ $keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' );
+
+ }
+
+ return $keylist;
+
+ }
+
+ /**
+ * @brief store file encryption key
+ *
+ * @param string $path relative path of the file, including filename
+ * @param string $key
+ * @return bool true/false
+ * @note The keyfile is not encrypted here. Client code must
+ * asymmetrically encrypt the keyfile before passing it to this method
+ */
+ public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) {
+
+ $basePath = '/' . $userId . '/files_encryption/keyfiles';
+
+ $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId );
+
+ if ( $view->is_dir( $basePath . '/' . $targetPath ) ) {
+
+
+
+ } else {
+
+ // Save the keyfile in parallel directory
+ return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
+
+ }
+
+ }
+
+ /**
+ * @brief retrieve keyfile for an encrypted file
+ * @param \OC_FilesystemView $view
+ * @param $userId
+ * @param $filePath
+ * @internal param \OCA\Encryption\file $string name
+ * @return string file key or false
+ * @note The keyfile returned is asymmetrically encrypted. Decryption
+ * of the keyfile must be performed by client code
+ */
+ public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) {
+
+ $filePath_f = ltrim( $filePath, '/' );
+
+ $catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key';
+
+ if ( $view->file_exists( $catfilePath ) ) {
+
+ return $view->file_get_contents( $catfilePath );
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Delete a keyfile
+ *
+ * @param OC_FilesystemView $view
+ * @param string $userId username
+ * @param string $path path of the file the key belongs to
+ * @return bool Outcome of unlink operation
+ * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT
+ * /data/admin/files/mydoc.txt
+ */
+ public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) {
+
+ $trimmed = ltrim( $path, '/' );
+ $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key';
+
+ // Unlink doesn't tell us if file was deleted (not found returns
+ // true), so we perform our own test
+ if ( $view->file_exists( $keyPath ) ) {
+
+ return $view->unlink( $keyPath );
+
+ } else {
+
+ \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR );
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief store private key from the user
+ * @param string key
+ * @return bool
+ * @note Encryption of the private key must be performed by client code
+ * as no encryption takes place here
+ */
+ public static function setPrivateKey( $key ) {
+
+ $user = \OCP\User::getUser();
+
+ $view = new \OC_FilesystemView( '/' . $user . '/files_encryption' );
+
+ \OC_FileProxy::$enabled = false;
+
+ if ( !$view->file_exists( '' ) )
+ $view->mkdir( '' );
+
+ return $view->file_put_contents( $user . '.private.key', $key );
+
+ }
+
+ /**
+ * @brief store private keys from the user
+ *
+ * @param string privatekey
+ * @param string publickey
+ * @return bool true/false
+ */
+ public static function setUserKeys($privatekey, $publickey) {
+
+ return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) );
+
+ }
+
+ /**
+ * @brief store public key of the user
+ *
+ * @param string key
+ * @return bool true/false
+ */
+ public static function setPublicKey( $key ) {
+
+ $view = new \OC_FilesystemView( '/public-keys' );
+
+ \OC_FileProxy::$enabled = false;
+
+ if ( !$view->file_exists( '' ) )
+ $view->mkdir( '' );
+
+ return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key );
+
+
+ }
+
+ /**
+ * @brief store file encryption key
+ *
+ * @param string $path relative path of the file, including filename
+ * @param string $key
+ * @param null $view
+ * @param string $dbClassName
+ * @return bool true/false
+ * @note The keyfile is not encrypted here. Client code must
+ * asymmetrically encrypt the keyfile before passing it to this method
+ */
+ public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) {
+
+ $basePath = '/' . $userId . '/files_encryption/share-keys';
+
+ $shareKeyPath = self::keySetPreparation( $view, $path, $basePath, $userId );
+
+ return $view->file_put_contents( $basePath . '/' . $shareKeyPath . '.shareKey', $shareKey );
+
+ }
+
+ /**
+ * @brief Make preparations to vars and filesystem for saving a keyfile
+ */
+ public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) {
+
+ $targetPath = ltrim( $path, '/' );
+
+ $path_parts = pathinfo( $targetPath );
+
+ // If the file resides within a subdirectory, create it
+ if (
+ isset( $path_parts['dirname'] )
+ && ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] )
+ ) {
+
+ $view->mkdir( $basePath . '/' . $path_parts['dirname'] );
+
+ }
+
+ return $targetPath;
+
+ }
+
+ /**
+ * @brief Fetch the legacy encryption key from user files
+ * @param string $login used to locate the legacy key
+ * @param string $passphrase used to decrypt the legacy key
+ * @return true / false
+ *
+ * if the key is left out, the default handler will be used
+ */
+ public function getLegacyKey() {
+
+ $user = \OCP\User::getUser();
+ $view = new \OC_FilesystemView( '/' . $user );
+ return $view->file_get_contents( 'encryption.key' );
+
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
index f61cd1e3773..55cddf2bec8 100644
--- a/apps/files_encryption/lib/proxy.php
+++ b/apps/files_encryption/lib/proxy.php
@@ -3,8 +3,9 @@
/**
* ownCloud
*
-* @author Robin Appelman
-* @copyright 2011 Robin Appelman icewind1991@gmail.com
+* @author Sam Tuke, Robin Appelman
+* @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman
+* icewind1991@gmail.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -22,109 +23,367 @@
*/
/**
- * transparent encryption
- */
+* @brief Encryption proxy which handles filesystem operations before and after
+* execution and encrypts, and handles keyfiles accordingly. Used for
+* webui.
+*/
+
+namespace OCA\Encryption;
-class OC_FileProxy_Encryption extends OC_FileProxy{
- private static $blackList=null; //mimetypes blacklisted from encryption
- private static $enableEncryption=null;
+class Proxy extends \OC_FileProxy {
+ private static $blackList = null; //mimetypes blacklisted from encryption
+
+ private static $enableEncryption = null;
+
/**
- * check if a file should be encrypted during write
+ * Check if a file requires encryption
* @param string $path
* @return bool
+ *
+ * Tests if server side encryption is enabled, and file is allowed by blacklists
*/
- private static function shouldEncrypt($path) {
- if(is_null(self::$enableEncryption)) {
- self::$enableEncryption=(OCP\Config::getAppValue('files_encryption','enable_encryption','true')=='true');
+ private static function shouldEncrypt( $path ) {
+
+ if ( is_null( self::$enableEncryption ) ) {
+
+ if (
+ \OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true'
+ && Crypt::mode() == 'server'
+ ) {
+
+ self::$enableEncryption = true;
+
+ } else {
+
+ self::$enableEncryption = false;
+
+ }
+
}
- if(!self::$enableEncryption) {
+
+ if ( !self::$enableEncryption ) {
+
return false;
+
}
- if(is_null(self::$blackList)) {
- self::$blackList=explode(',',OCP\Config::getAppValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
+
+ if ( is_null(self::$blackList ) ) {
+
+ self::$blackList = explode(',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
+
}
- if(self::isEncrypted($path)) {
+
+ if ( Crypt::isCatfile( $path ) ) {
+
return true;
+
}
- $extension=substr($path,strrpos($path,'.')+1);
- if(array_search($extension,self::$blackList)===false) {
+
+ $extension = substr( $path, strrpos( $path, '.' ) +1 );
+
+ if ( array_search( $extension, self::$blackList ) === false ) {
+
return true;
+
}
+
+ return false;
}
-
+
+ public function preFile_put_contents( $path, &$data ) {
+
+ if ( self::shouldEncrypt( $path ) ) {
+
+ if ( !is_resource( $data ) ) { //stream put contents should have been converted to fopen
+
+ $userId = \OCP\USER::getUser();
+
+ $rootView = new \OC_FilesystemView( '/' );
+
+ // Set the filesize for userland, before encrypting
+ $size = strlen( $data );
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ // TODO: Check if file is shared, if so, use multiKeyEncrypt
+
+ // Encrypt plain data and fetch key
+ $encrypted = Crypt::keyEncryptKeyfile( $data, Keymanager::getPublicKey( $rootView, $userId ) );
+
+ // Replace plain content with encrypted content by reference
+ $data = $encrypted['data'];
+
+ $filePath = explode( '/', $path );
+
+ $filePath = array_slice( $filePath, 3 );
+
+ $filePath = '/' . implode( '/', $filePath );
+
+ // TODO: make keyfile dir dynamic from app config
+
+ $view = new \OC_FilesystemView( '/' );
+
+ // Save keyfile for newly encrypted file in parallel directory tree
+ Keymanager::setFileKey( $view, $filePath, $userId, $encrypted['key'] );
+
+ // Update the file cache with file info
+ \OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' );
+
+ // Re-enable proxy - our work is done
+ \OC_FileProxy::$enabled = true;
+
+ }
+ }
+
+ }
+
/**
- * check if a file is encrypted
- * @param string $path
- * @return bool
+ * @param string $path Path of file from which has been read
+ * @param string $data Data that has been read from file
*/
- private static function isEncrypted($path) {
- $metadata=OC_FileCache_Cached::get($path,'');
- return isset($metadata['encrypted']) and (bool)$metadata['encrypted'];
- }
+ public function postFile_get_contents( $path, $data ) {
+
+ // TODO: Use dependency injection to add required args for view and user etc. to this method
- public function preFile_put_contents($path,&$data) {
- if(self::shouldEncrypt($path)) {
- if (!is_resource($data)) {//stream put contents should have been converter to fopen
- $size=strlen($data);
- $data=OC_Crypt::blockEncrypt($data);
- OC_FileCache::put($path,array('encrypted'=>true,'size'=>$size),'');
- }
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ // If data is a catfile
+ if (
+ Crypt::mode() == 'server'
+ && Crypt::isCatfile( $data )
+ ) {
+
+ $split = explode( '/', $path );
+
+ $filePath = array_slice( $split, 3 );
+
+ $filePath = '/' . implode( '/', $filePath );
+
+ //$cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
+
+ $view = new \OC_FilesystemView( '' );
+
+ $userId = \OCP\USER::getUser();
+
+ // TODO: Check if file is shared, if so, use multiKeyDecrypt
+
+ $encryptedKeyfile = Keymanager::getFileKey( $view, $userId, $filePath );
+
+ $session = new Session();
+
+ $decrypted = Crypt::keyDecryptKeyfile( $data, $encryptedKeyfile, $session->getPrivateKey( $split[1] ) );
+
+ } elseif (
+ Crypt::mode() == 'server'
+ && isset( $_SESSION['legacyenckey'] )
+ && Crypt::isEncryptedMeta( $path )
+ ) {
+
+ $decrypted = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] );
+
+ }
+
+ \OC_FileProxy::$enabled = true;
+
+ if ( ! isset( $decrypted ) ) {
+
+ $decrypted = $data;
+
}
+
+ return $decrypted;
+
}
-
- public function postFile_get_contents($path,$data) {
- if(self::isEncrypted($path)) {
- $cached=OC_FileCache_Cached::get($path,'');
- $data=OC_Crypt::blockDecrypt($data,'',$cached['size']);
+
+ /**
+ * @brief When a file is deleted, remove its keyfile also
+ */
+ public function preUnlink( $path ) {
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ $view = new \OC_FilesystemView( '/' );
+
+ $userId = \OCP\USER::getUser();
+
+ // Format path to be relative to user files dir
+ $trimmed = ltrim( $path, '/' );
+ $split = explode( '/', $trimmed );
+ $sliced = array_slice( $split, 2 );
+ $relPath = implode( '/', $sliced );
+
+ if ( $view->is_dir( $path ) ) {
+
+ // Dirs must be handled separately as deleteFileKey
+ // doesn't handle them
+ $view->unlink( $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/'. $relPath );
+
+ } else {
+
+ // Delete keyfile so it isn't orphaned
+ $result = Keymanager::deleteFileKey( $view, $userId, $relPath );
+
+ \OC_FileProxy::$enabled = true;
+
+ return $result;
+
}
- return $data;
+
}
- public function postFopen($path,&$result) {
- if(!$result) {
+ /**
+ * @brief When a file is renamed, rename its keyfile also
+ * @return bool Result of rename()
+ * @note This is pre rather than post because using post didn't work
+ */
+ public function preRename( $oldPath, $newPath ) {
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ $view = new \OC_FilesystemView( '/' );
+
+ $userId = \OCP\USER::getUser();
+
+ // Format paths to be relative to user files dir
+ $oldTrimmed = ltrim( $oldPath, '/' );
+ $oldSplit = explode( '/', $oldTrimmed );
+ $oldSliced = array_slice( $oldSplit, 2 );
+ $oldRelPath = implode( '/', $oldSliced );
+ $oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath . '.key';
+
+ $newTrimmed = ltrim( $newPath, '/' );
+ $newSplit = explode( '/', $newTrimmed );
+ $newSliced = array_slice( $newSplit, 2 );
+ $newRelPath = implode( '/', $newSliced );
+ $newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath . '.key';
+
+ // Rename keyfile so it isn't orphaned
+ $result = $view->rename( $oldKeyfilePath, $newKeyfilePath );
+
+ \OC_FileProxy::$enabled = true;
+
+ return $result;
+
+ }
+
+ 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
- OCP\Util::writeLog('files_encryption','Decrypting '.$path.' before writing',OCP\Util::DEBUG);
- $tmp=fopen('php://temp');
- OCP\Files::streamCopy($result,$tmp);
- fclose($result);
- OC_Filesystem::file_put_contents($path,$tmp);
- fclose($tmp);
+
+ // Reformat path for use with OC_FSV
+ $path_split = explode( '/', $path );
+ $path_f = implode( array_slice( $path_split, 3 ) );
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ $meta = stream_get_meta_data( $result );
+
+ $view = new \OC_FilesystemView( '' );
+
+ $util = new Util( $view, \OCP\USER::getUser());
+
+ // If file is already encrypted, decrypt using crypto protocol
+ if (
+ Crypt::mode() == 'server'
+ && $util->isEncryptedPath( $path )
+ ) {
+
+ // Close the original encrypted file
+ fclose( $result );
+
+ // Open the file using the crypto stream wrapper
+ // protocol and let it do the decryption work instead
+ $result = fopen( 'crypt://' . $path_f, $meta['mode'] );
+
+
+ } elseif (
+ self::shouldEncrypt( $path )
+ and $meta ['mode'] != 'r'
+ and $meta['mode'] != 'rb'
+ ) {
+ // If the file is not yet encrypted, but should be
+ // encrypted when it's saved (it's not read only)
+
+ // NOTE: this is the case for new files saved via WebDAV
+
+ if (
+ $view->file_exists( $path )
+ and $view->filesize( $path ) > 0
+ ) {
+ $x = $view->file_get_contents( $path );
+
+ $tmp = tmpfile();
+
+// // Make a temporary copy of the original file
+// \OCP\Files::streamCopy( $result, $tmp );
+//
+// // Close the original stream, we'll return another one
+// fclose( $result );
+//
+// $view->file_put_contents( $path_f, $tmp );
+//
+// fclose( $tmp );
+
}
- $result=fopen('crypt://'.$path,$meta['mode']);
+
+ $result = fopen( 'crypt://'.$path_f, $meta['mode'] );
+
}
+
+ // Re-enable the proxy
+ \OC_FileProxy::$enabled = true;
+
return $result;
+
}
- public function postGetMimeType($path,$mime) {
- if(self::isEncrypted($path)) {
- $mime=OCP\Files::getMimeType('crypt://'.$path,'w');
+ public function postGetMimeType( $path, $mime ) {
+
+ if ( Crypt::isCatfile( $path ) ) {
+
+ $mime = \OCP\Files::getMimeType( 'crypt://' . $path, 'w' );
+
}
+
return $mime;
+
}
- public function postStat($path,$data) {
- if(self::isEncrypted($path)) {
- $cached=OC_FileCache_Cached::get($path,'');
- $data['size']=$cached['size'];
+ public function postStat( $path, $data ) {
+
+ if ( Crypt::isCatfile( $path ) ) {
+
+ $cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
+
+ $data['size'] = $cached['size'];
+
}
+
return $data;
}
- public function postFileSize($path,$size) {
- if(self::isEncrypted($path)) {
- $cached=OC_FileCache_Cached::get($path,'');
+ public function postFileSize( $path, $size ) {
+
+ if ( Crypt::isCatfile( $path ) ) {
+
+ $cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
+
return $cached['size'];
- }else{
+
+ } else {
+
return $size;
+
}
}
}
diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php
new file mode 100644
index 00000000000..769a40b359f
--- /dev/null
+++ b/apps/files_encryption/lib/session.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Sam Tuke
+ * @copyright 2012 Sam Tuke samtuke@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Encryption;
+
+/**
+ * Class for handling encryption related session data
+ */
+
+class Session {
+
+ /**
+ * @brief Sets user private key to session
+ * @return bool
+ *
+ */
+ public function setPrivateKey( $privateKey ) {
+
+ $_SESSION['privateKey'] = $privateKey;
+
+ return true;
+
+ }
+
+ /**
+ * @brief Gets user private key from session
+ * @returns string $privateKey The user's plaintext private key
+ *
+ */
+ public function getPrivateKey() {
+
+ if (
+ isset( $_SESSION['privateKey'] )
+ && !empty( $_SESSION['privateKey'] )
+ ) {
+
+ return $_SESSION['privateKey'];
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Sets user legacy key to session
+ * @return bool
+ *
+ */
+ public function setLegacyKey( $legacyKey ) {
+
+ if ( $_SESSION['legacyKey'] = $legacyKey ) {
+
+ return true;
+
+ }
+
+ }
+
+ /**
+ * @brief Gets user legacy key from session
+ * @returns string $legacyKey The user's plaintext legacy key
+ *
+ */
+ public function getLegacyKey() {
+
+ if (
+ isset( $_SESSION['legacyKey'] )
+ && !empty( $_SESSION['legacyKey'] )
+ ) {
+
+ return $_SESSION['legacyKey'];
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php
new file mode 100644
index 00000000000..65d7d57a05a
--- /dev/null
+++ b/apps/files_encryption/lib/stream.php
@@ -0,0 +1,495 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Robin Appelman
+ * @copyright 2012 Sam Tuke <samtuke@owncloud.com>, 2011 Robin Appelman
+ * <icewind1991@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * transparently encrypted filestream
+ *
+ * you can use it as wrapper around an existing stream by setting CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream)
+ * and then fopen('crypt://streams/foo');
+ */
+
+namespace OCA\Encryption;
+
+/**
+ * @brief Provides 'crypt://' stream wrapper protocol.
+ * @note We use a stream wrapper because it is the most secure way to handle
+ * decrypted content transfers. There is no safe way to decrypt the entire file
+ * somewhere on the server, so we have to encrypt and decrypt blocks on the fly.
+ * @note Paths used with this protocol MUST BE RELATIVE. Use URLs like:
+ * crypt://filename, or crypt://subdirectory/filename, NOT
+ * crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in
+ * [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and
+ * will not be accessible to other methods.
+ * @note Data read and written must always be 8192 bytes long, as this is the
+ * buffer size used internally by PHP. The encryption process makes the input
+ * data longer, and input is chunked into smaller pieces in order to result in
+ * a 8192 encrypted block size.
+ */
+class Stream {
+
+ public static $sourceStreams = array();
+
+ // TODO: make all below properties private again once unit testing is
+ // configured correctly
+ public $rawPath; // The raw path received by stream_open
+ public $path_f; // The raw path formatted to include username and data dir
+ private $userId;
+ private $handle; // Resource returned by fopen
+ private $path;
+ private $readBuffer; // For streams that dont support seeking
+ private $meta = array(); // Header / meta for source stream
+ private $count;
+ private $writeCache;
+ public $size;
+ private $publicKey;
+ private $keyfile;
+ private $encKeyfile;
+ private static $view; // a fsview object set to user dir
+ private $rootView; // a fsview object set to '/'
+
+ public function stream_open( $path, $mode, $options, &$opened_path ) {
+
+ // Get access to filesystem via filesystemview object
+ if ( !self::$view ) {
+
+ self::$view = new \OC_FilesystemView( $this->userId . '/' );
+
+ }
+
+ // Set rootview object if necessary
+ if ( ! $this->rootView ) {
+
+ $this->rootView = new \OC_FilesystemView( $this->userId . '/' );
+
+ }
+
+ $this->userId = \OCP\User::getUser();
+
+ // Get the bare file path
+ $path = str_replace( 'crypt://', '', $path );
+
+ $this->rawPath = $path;
+
+ $this->path_f = $this->userId . '/files/' . $path;
+
+ if (
+ dirname( $path ) == 'streams'
+ and isset( self::$sourceStreams[basename( $path )] )
+ ) {
+
+ // Is this just for unit testing purposes?
+
+ $this->handle = self::$sourceStreams[basename( $path )]['stream'];
+
+ $this->path = self::$sourceStreams[basename( $path )]['path'];
+
+ $this->size = self::$sourceStreams[basename( $path )]['size'];
+
+ } else {
+
+ if (
+ $mode == 'w'
+ or $mode == 'w+'
+ or $mode == 'wb'
+ or $mode == 'wb+'
+ ) {
+
+ $this->size = 0;
+
+ } else {
+
+
+
+ $this->size = self::$view->filesize( $this->path_f, $mode );
+
+ //$this->size = filesize( $path );
+
+ }
+
+ // Disable fileproxies so we can open the source file without recursive encryption
+ \OC_FileProxy::$enabled = false;
+
+ //$this->handle = fopen( $path, $mode );
+
+ $this->handle = self::$view->fopen( $this->path_f, $mode );
+
+ \OC_FileProxy::$enabled = true;
+
+ if ( !is_resource( $this->handle ) ) {
+
+ \OCP\Util::writeLog( 'files_encryption', 'failed to open '.$path, \OCP\Util::ERROR );
+
+ }
+
+ }
+
+ if ( is_resource( $this->handle ) ) {
+
+ $this->meta = stream_get_meta_data( $this->handle );
+
+ }
+
+ return is_resource( $this->handle );
+
+ }
+
+ public function stream_seek( $offset, $whence = SEEK_SET ) {
+
+ $this->flush();
+
+ fseek( $this->handle, $offset, $whence );
+
+ }
+
+ public function stream_tell() {
+ return ftell($this->handle);
+ }
+
+ public function stream_read( $count ) {
+
+ $this->writeCache = '';
+
+ if ( $count != 8192 ) {
+
+ // $count will always be 8192 https://bugs.php.net/bug.php?id=21641
+ // This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed'
+ \OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL );
+
+ die();
+
+ }
+
+// $pos = ftell( $this->handle );
+//
+ // Get the data from the file handle
+ $data = fread( $this->handle, 8192 );
+
+ if ( strlen( $data ) ) {
+
+ $this->getKey();
+
+ $result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile );
+
+ } else {
+
+ $result = '';
+
+ }
+
+// $length = $this->size - $pos;
+//
+// if ( $length < 8192 ) {
+//
+// $result = substr( $result, 0, $length );
+//
+// }
+
+ return $result;
+
+ }
+
+ /**
+ * @brief Encrypt and pad data ready for writing to disk
+ * @param string $plainData data to be encrypted
+ * @param string $key key to use for encryption
+ * @return encrypted data on success, false on failure
+ */
+ public function preWriteEncrypt( $plainData, $key ) {
+
+ // Encrypt data to 'catfile', which includes IV
+ if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) {
+
+ return $encrypted;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Get the keyfile for the current file, generate one if necessary
+ * @param bool $generate if true, a new key will be generated if none can be found
+ * @return bool true on key found and set, false on key not found and new key generated and set
+ */
+ public function getKey() {
+
+ // If a keyfile already exists for a file named identically to
+ // file to be written
+ if ( self::$view->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->rawPath . '.key' ) ) {
+
+ // TODO: add error handling for when file exists but no
+ // keyfile
+
+ // Fetch existing keyfile
+ $this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->rawPath );
+
+ $this->getUser();
+
+ $session = new Session();
+
+ $privateKey = $session->getPrivateKey( $this->userId );
+
+ $this->keyfile = Crypt::keyDecrypt( $this->encKeyfile, $privateKey );
+
+ return true;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ public function getuser() {
+
+ // Only get the user again if it isn't already set
+ if ( empty( $this->userId ) ) {
+
+ // TODO: Move this user call out of here - it belongs
+ // elsewhere
+ $this->userId = \OCP\User::getUser();
+
+ }
+
+ // TODO: Add a method for getting the user in case OCP\User::
+ // getUser() doesn't work (can that scenario ever occur?)
+
+ }
+
+ /**
+ * @brief Handle plain data from the stream, and write it in 8192 byte blocks
+ * @param string $data data to be written to disk
+ * @note the data will be written to the path stored in the stream handle, set in stream_open()
+ * @note $data is only ever be a maximum of 8192 bytes long. This is set by PHP internally. stream_write() is called multiple times in a loop on data larger than 8192 bytes
+ * @note Because the encryption process used increases the length of $data, a writeCache is used to carry over data which would not fit in the required block size
+ * @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read
+ * @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek
+ */
+ public function stream_write( $data ) {
+
+ // Disable the file proxies so that encryption is not
+ // automatically attempted when the file is written to disk -
+ // we are handling that separately here and we don't want to
+ // get into an infinite loop
+ \OC_FileProxy::$enabled = false;
+
+ // Get the length of the unencrypted data that we are handling
+ $length = strlen( $data );
+
+ // So far this round, no data has been written
+ $written = 0;
+
+ // Find out where we are up to in the writing of data to the
+ // file
+ $pointer = ftell( $this->handle );
+
+ // Make sure the userId is set
+ $this->getuser();
+
+ // TODO: Check if file is shared, if so, use multiKeyEncrypt and
+ // save shareKeys in necessary user directories
+
+ // Get / generate the keyfile for the file we're handling
+ // If we're writing a new file (not overwriting an existing
+ // one), save the newly generated keyfile
+ if ( ! $this->getKey() ) {
+
+ $this->keyfile = Crypt::generateKey();
+
+ $this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
+
+ $this->encKeyfile = Crypt::keyEncrypt( $this->keyfile, $this->publicKey );
+
+ $view = new \OC_FilesystemView( '/' );
+ $userId = \OCP\User::getUser();
+
+ // Save the new encrypted file key
+ Keymanager::setFileKey( $view, $this->rawPath, $userId, $this->encKeyfile );
+
+ }
+
+ // If extra data is left over from the last round, make sure it
+ // is integrated into the next 6126 / 8192 block
+ if ( $this->writeCache ) {
+
+ // Concat writeCache to start of $data
+ $data = $this->writeCache . $data;
+
+ // Clear the write cache, ready for resuse - it has been
+ // flushed and its old contents processed
+ $this->writeCache = '';
+
+ }
+//
+// // Make sure we always start on a block start
+ if ( 0 != ( $pointer % 8192 ) ) {
+ // if the current position of
+ // file indicator is not aligned to a 8192 byte block, fix it
+ // so that it is
+
+// fseek( $this->handle, - ( $pointer % 8192 ), SEEK_CUR );
+//
+// $pointer = ftell( $this->handle );
+//
+// $unencryptedNewBlock = fread( $this->handle, 8192 );
+//
+// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
+//
+// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->keyfile );
+//
+// $x = substr( $block, 0, $currentPos % 8192 );
+//
+// $data = $x . $data;
+//
+// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
+//
+ }
+
+// $currentPos = ftell( $this->handle );
+
+// // While there still remains somed data to be processed & written
+ while( strlen( $data ) > 0 ) {
+//
+// // Remaining length for this iteration, not of the
+// // entire file (may be greater than 8192 bytes)
+// $remainingLength = strlen( $data );
+//
+// // If data remaining to be written is less than the
+// // size of 1 6126 byte block
+ if ( strlen( $data ) < 6126 ) {
+
+ // Set writeCache to contents of $data
+ // The writeCache will be carried over to the
+ // next write round, and added to the start of
+ // $data to ensure that written blocks are
+ // always the correct length. If there is still
+ // data in writeCache after the writing round
+ // has finished, then the data will be written
+ // to disk by $this->flush().
+ $this->writeCache = $data;
+
+ // Clear $data ready for next round
+ $data = '';
+//
+ } else {
+
+ // Read the chunk from the start of $data
+ $chunk = substr( $data, 0, 6126 );
+
+ $encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile );
+
+ // Write the data chunk to disk. This will be
+ // attended to the last data chunk if the file
+ // being handled totals more than 6126 bytes
+ fwrite( $this->handle, $encrypted );
+
+ $writtenLen = strlen( $encrypted );
+ //fseek( $this->handle, $writtenLen, SEEK_CUR );
+
+ // Remove the chunk we just processed from
+ // $data, leaving only unprocessed data in $data
+ // var, for handling on the next round
+ $data = substr( $data, 6126 );
+
+ }
+
+ }
+
+ $this->size = max( $this->size, $pointer + $length );
+
+ return $length;
+
+ }
+
+
+ public function stream_set_option( $option, $arg1, $arg2 ) {
+ switch($option) {
+ case STREAM_OPTION_BLOCKING:
+ stream_set_blocking( $this->handle, $arg1 );
+ break;
+ case STREAM_OPTION_READ_TIMEOUT:
+ stream_set_timeout( $this->handle, $arg1, $arg2 );
+ break;
+ case STREAM_OPTION_WRITE_BUFFER:
+ stream_set_write_buffer( $this->handle, $arg1, $arg2 );
+ }
+ }
+
+ public function stream_stat() {
+ return fstat($this->handle);
+ }
+
+ public function stream_lock( $mode ) {
+ flock( $this->handle, $mode );
+ }
+
+ public function stream_flush() {
+
+ return fflush( $this->handle );
+ // Not a typo: http://php.net/manual/en/function.fflush.php
+
+ }
+
+ public function stream_eof() {
+ return feof($this->handle);
+ }
+
+ private function flush() {
+
+ if ( $this->writeCache ) {
+
+ // Set keyfile property for file in question
+ $this->getKey();
+
+ $encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile );
+
+ fwrite( $this->handle, $encrypted );
+
+ $this->writeCache = '';
+
+ }
+
+ }
+
+ public function stream_close() {
+
+ $this->flush();
+
+ if (
+ $this->meta['mode']!='r'
+ and $this->meta['mode']!='rb'
+ ) {
+
+ \OC\Files\Filesystem::putFileInfo( $this->path, array( 'encrypted' => true, 'size' => $this->size ), '' );
+
+ }
+
+ return fclose( $this->handle );
+
+ }
+
+}
diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php
new file mode 100644
index 00000000000..52bc74db27a
--- /dev/null
+++ b/apps/files_encryption/lib/util.php
@@ -0,0 +1,476 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Sam Tuke, Frank Karlitschek
+ * @copyright 2012 Sam Tuke samtuke@owncloud.com,
+ * Frank Karlitschek frank@owncloud.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 Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Todo:
+// - Crypt/decrypt button in the userinterface
+// - Setting if crypto should be on by default
+// - Add a setting "Don´t encrypt files larger than xx because of performance
+// reasons"
+// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is
+// encrypted (.encrypted extension)
+// - Don't use a password directly as encryption key. but a key which is
+// stored on the server and encrypted with the user password. -> password
+// change faster
+// - IMPORTANT! Check if the block lenght of the encrypted data stays the same
+
+namespace OCA\Encryption;
+
+/**
+ * @brief Class for utilities relating to encrypted file storage system
+ * @param OC_FilesystemView $view expected to have OC '/' as root path
+ * @param string $userId ID of the logged in user
+ * @param int $client indicating status of client side encryption. Currently
+ * unused, likely to become obsolete shortly
+ */
+
+class Util {
+
+
+ // Web UI:
+
+ //// DONE: files created via web ui are encrypted
+ //// DONE: file created & encrypted via web ui are readable in web ui
+ //// DONE: file created & encrypted via web ui are readable via webdav
+
+
+ // WebDAV:
+
+ //// DONE: new data filled files added via webdav get encrypted
+ //// DONE: new data filled files added via webdav are readable via webdav
+ //// DONE: reading unencrypted files when encryption is enabled works via
+ //// webdav
+ //// DONE: files created & encrypted via web ui are readable via webdav
+
+
+ // Legacy support:
+
+ //// DONE: add method to check if file is encrypted using new system
+ //// DONE: add method to check if file is encrypted using old system
+ //// DONE: add method to fetch legacy key
+ //// DONE: add method to decrypt legacy encrypted data
+
+
+ // Admin UI:
+
+ //// DONE: changing user password also changes encryption passphrase
+
+ //// TODO: add support for optional recovery in case of lost passphrase / keys
+ //// TODO: add admin optional required long passphrase for users
+ //// TODO: add UI buttons for encrypt / decrypt everything
+ //// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
+
+
+ // Sharing:
+
+ //// TODO: add support for encrypting to multiple public keys
+ //// TODO: add support for decrypting to multiple private keys
+
+
+ // Integration testing:
+
+ //// TODO: test new encryption with versioning
+ //// TODO: test new encryption with sharing
+ //// TODO: test new encryption with proxies
+
+
+ private $view; // OC_FilesystemView object for filesystem operations
+ private $userId; // ID of the currently logged-in user
+ private $pwd; // User Password
+ private $client; // Client side encryption mode flag
+ private $publicKeyDir; // Dir containing all public user keys
+ private $encryptionDir; // Dir containing user's files_encryption
+ private $keyfilesPath; // Dir containing user's keyfiles
+ private $shareKeysPath; // Dir containing env keys for shared files
+ private $publicKeyPath; // Path to user's public key
+ private $privateKeyPath; // Path to user's private key
+
+ public function __construct( \OC_FilesystemView $view, $userId, $client = false ) {
+
+ $this->view = $view;
+ $this->userId = $userId;
+ $this->client = $client;
+ $this->userDir = '/' . $this->userId;
+ $this->userFilesDir = '/' . $this->userId . '/' . 'files';
+ $this->publicKeyDir = '/' . 'public-keys';
+ $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
+ $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
+ $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys';
+ $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
+ $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
+
+ }
+
+ public function ready() {
+
+ if(
+ !$this->view->file_exists( $this->encryptionDir )
+ or !$this->view->file_exists( $this->keyfilesPath )
+ or !$this->view->file_exists( $this->shareKeysPath )
+ or !$this->view->file_exists( $this->publicKeyPath )
+ or !$this->view->file_exists( $this->privateKeyPath )
+ ) {
+
+ return false;
+
+ } else {
+
+ return true;
+
+ }
+
+ }
+
+ /**
+ * @brief Sets up user folders and keys for serverside encryption
+ * @param $passphrase passphrase to encrypt server-stored private key with
+ */
+ public function setupServerSide( $passphrase = null ) {
+
+ // Create user dir
+ if( !$this->view->file_exists( $this->userDir ) ) {
+
+ $this->view->mkdir( $this->userDir );
+
+ }
+
+ // Create user files dir
+ if( !$this->view->file_exists( $this->userFilesDir ) ) {
+
+ $this->view->mkdir( $this->userFilesDir );
+
+ }
+
+ // Create shared public key directory
+ if( !$this->view->file_exists( $this->publicKeyDir ) ) {
+
+ $this->view->mkdir( $this->publicKeyDir );
+
+ }
+
+ // Create encryption app directory
+ if( !$this->view->file_exists( $this->encryptionDir ) ) {
+
+ $this->view->mkdir( $this->encryptionDir );
+
+ }
+
+ // Create mirrored keyfile directory
+ if( !$this->view->file_exists( $this->keyfilesPath ) ) {
+
+ $this->view->mkdir( $this->keyfilesPath );
+
+ }
+
+ // Create mirrored share env keys directory
+ if( !$this->view->file_exists( $this->shareKeysPath ) ) {
+
+ $this->view->mkdir( $this->shareKeysPath );
+
+ }
+
+ // Create user keypair
+ if (
+ ! $this->view->file_exists( $this->publicKeyPath )
+ or ! $this->view->file_exists( $this->privateKeyPath )
+ ) {
+
+ // Generate keypair
+ $keypair = Crypt::createKeypair();
+
+ \OC_FileProxy::$enabled = false;
+
+ // Save public key
+ $this->view->file_put_contents( $this->publicKeyPath, $keypair['publicKey'] );
+
+ // Encrypt private key with user pwd as passphrase
+ $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $passphrase );
+
+ // Save private key
+ $this->view->file_put_contents( $this->privateKeyPath, $encryptedPrivateKey );
+
+ \OC_FileProxy::$enabled = true;
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * @brief Find all files and their encryption status within a directory
+ * @param string $directory The path of the parent directory to search
+ * @return mixed false if 0 found, array on success. Keys: name, path
+
+ * @note $directory needs to be a path relative to OC data dir. e.g.
+ * /admin/files NOT /backup OR /home/www/oc/data/admin/files
+ */
+ public function findFiles( $directory ) {
+
+ // Disable proxy - we don't want files to be decrypted before
+ // we handle them
+ \OC_FileProxy::$enabled = false;
+
+ $found = array( 'plain' => array(), 'encrypted' => array(), 'legacy' => array() );
+
+ if (
+ $this->view->is_dir( $directory )
+ && $handle = $this->view->opendir( $directory )
+ ) {
+
+ while ( false !== ( $file = readdir( $handle ) ) ) {
+
+ if (
+ $file != "."
+ && $file != ".."
+ ) {
+
+ $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file );
+ $relPath = $this->stripUserFilesPath( $filePath );
+
+ // If the path is a directory, search
+ // its contents
+ if ( $this->view->is_dir( $filePath ) ) {
+
+ $this->findFiles( $filePath );
+
+ // If the path is a file, determine
+ // its encryption status
+ } elseif ( $this->view->is_file( $filePath ) ) {
+
+ // Disable proxies again, some-
+ // where they got re-enabled :/
+ \OC_FileProxy::$enabled = false;
+
+ $data = $this->view->file_get_contents( $filePath );
+
+ // If the file is encrypted
+ // NOTE: If the userId is
+ // empty or not set, file will
+ // detected as plain
+ // NOTE: This is inefficient;
+ // scanning every file like this
+ // will eat server resources :(
+ if (
+ Keymanager::getFileKey( $this->view, $this->userId, $file )
+ && Crypt::isCatfile( $data )
+ ) {
+
+ $found['encrypted'][] = array( 'name' => $file, 'path' => $filePath );
+
+ // If the file uses old
+ // encryption system
+ } elseif ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ), $relPath ) ) {
+
+ $found['legacy'][] = array( 'name' => $file, 'path' => $filePath );
+
+ // If the file is not encrypted
+ } else {
+
+ $found['plain'][] = array( 'name' => $file, 'path' => $filePath );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ \OC_FileProxy::$enabled = true;
+
+ if ( empty( $found ) ) {
+
+ return false;
+
+ } else {
+
+ return $found;
+
+ }
+
+ }
+
+ \OC_FileProxy::$enabled = true;
+
+ return false;
+
+ }
+
+ /**
+ * @brief Check if a given path identifies an encrypted file
+ * @return true / false
+ */
+ public function isEncryptedPath( $path ) {
+
+ // Disable encryption proxy so data retreived is in its
+ // original form
+ \OC_FileProxy::$enabled = false;
+
+ $data = $this->view->file_get_contents( $path );
+
+ \OC_FileProxy::$enabled = true;
+
+ return Crypt::isCatfile( $data );
+
+ }
+
+ /**
+ * @brief Format a path to be relative to the /user/files/ directory
+ */
+ public function stripUserFilesPath( $path ) {
+
+ $trimmed = ltrim( $path, '/' );
+ $split = explode( '/', $trimmed );
+ $sliced = array_slice( $split, 2 );
+ $relPath = implode( '/', $sliced );
+
+ return $relPath;
+
+ }
+
+ /**
+ * @brief Encrypt all files in a directory
+ * @param string $publicKey the public key to encrypt files with
+ * @param string $dirPath the directory whose files will be encrypted
+ * @note Encryption is recursive
+ */
+ public function encryptAll( $publicKey, $dirPath, $legacyPassphrase = null, $newPassphrase = null ) {
+
+ if ( $found = $this->findFiles( $dirPath ) ) {
+
+ // Disable proxy to prevent file being encrypted twice
+ \OC_FileProxy::$enabled = false;
+
+ // Encrypt unencrypted files
+ foreach ( $found['plain'] as $plainFile ) {
+
+ // Fetch data from file
+ $plainData = $this->view->file_get_contents( $plainFile['path'] );
+
+ // Encrypt data, generate catfile
+ $encrypted = Crypt::keyEncryptKeyfile( $plainData, $publicKey );
+
+ $relPath = $this->stripUserFilesPath( $plainFile['path'] );
+
+ // Save keyfile
+ Keymanager::setFileKey( $this->view, $relPath, $this->userId, $encrypted['key'] );
+
+ // Overwrite the existing file with the encrypted one
+ $this->view->file_put_contents( $plainFile['path'], $encrypted['data'] );
+
+ $size = strlen( $encrypted['data'] );
+
+ // Add the file to the cache
+ \OC\Files\Filesystem::putFileInfo( $plainFile['path'], array( 'encrypted'=>true, 'size' => $size ), '' );
+
+ }
+
+ // Encrypt legacy encrypted files
+ if (
+ ! empty( $legacyPassphrase )
+ && ! empty( $newPassphrase )
+ ) {
+
+ foreach ( $found['legacy'] as $legacyFile ) {
+
+ // Fetch data from file
+ $legacyData = $this->view->file_get_contents( $legacyFile['path'] );
+
+ // Recrypt data, generate catfile
+ $recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKey, $newPassphrase );
+
+ $relPath = $this->stripUserFilesPath( $legacyFile['path'] );
+
+ // Save keyfile
+ Keymanager::setFileKey( $this->view, $relPath, $this->userId, $recrypted['key'] );
+
+ // Overwrite the existing file with the encrypted one
+ $this->view->file_put_contents( $legacyFile['path'], $recrypted['data'] );
+
+ $size = strlen( $recrypted['data'] );
+
+ // Add the file to the cache
+ \OC\Files\Filesystem::putFileInfo( $legacyFile['path'], array( 'encrypted'=>true, 'size' => $size ), '' );
+
+ }
+
+ }
+
+ \OC_FileProxy::$enabled = true;
+
+ // If files were found, return true
+ return true;
+
+ } else {
+
+ // If no files were found, return false
+ return false;
+
+ }
+
+ }
+
+ /**
+ * @brief Return important encryption related paths
+ * @param string $pathName Name of the directory to return the path of
+ * @return string path
+ */
+ public function getPath( $pathName ) {
+
+ switch ( $pathName ) {
+
+ case 'publicKeyDir':
+
+ return $this->publicKeyDir;
+
+ break;
+
+ case 'encryptionDir':
+
+ return $this->encryptionDir;
+
+ break;
+
+ case 'keyfilesPath':
+
+ return $this->keyfilesPath;
+
+ break;
+
+ case 'publicKeyPath':
+
+ return $this->publicKeyPath;
+
+ break;
+
+ case 'privateKeyPath':
+
+ return $this->privateKeyPath;
+
+ break;
+
+ }
+
+ }
+
+}
diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php
new file mode 100644
index 00000000000..af0273cfdc4
--- /dev/null
+++ b/apps/files_encryption/settings-personal.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Copyright (c) 2013 Sam Tuke <samtuke@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
+
+$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
+
+$tmpl->assign( 'blacklist', $blackList );
+
+return $tmpl->fetchPage();
+
+return null;
diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php
index 0a0d4d1abba..d1260f44e9f 100644
--- a/apps/files_encryption/settings.php
+++ b/apps/files_encryption/settings.php
@@ -6,13 +6,16 @@
* See the COPYING-README file.
*/
-$tmpl = new OCP\Template( 'files_encryption', 'settings');
-$blackList=explode(',',OCP\Config::getAppValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
-$enabled=(OCP\Config::getAppValue('files_encryption','enable_encryption','true')=='true');
-$tmpl->assign('blacklist',$blackList);
-$tmpl->assign('encryption_enabled',$enabled);
+\OC_Util::checkAdminUser();
-OCP\Util::addscript('files_encryption','settings');
-OCP\Util::addscript('core','multiselect');
+$tmpl = new OCP\Template( 'files_encryption', 'settings' );
+
+$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
+
+$tmpl->assign( 'blacklist', $blackList );
+$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) );
+
+\OCP\Util::addscript( 'files_encryption', 'settings' );
+\OCP\Util::addscript( 'core', 'multiselect' );
return $tmpl->fetchPage();
diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php
new file mode 100644
index 00000000000..5f0accaed5f
--- /dev/null
+++ b/apps/files_encryption/templates/settings-personal.php
@@ -0,0 +1,22 @@
+<form id="encryption">
+ <fieldset class="personalblock">
+ <legend>
+ <?php p($l->t( 'Encryption' )); ?>
+ </legend>
+ <p>
+ <?php p($l->t( 'File encryption is enabled.' )); ?>
+ </p>
+ <?php if ( ! empty( $_["blacklist"] ) ): ?>
+ <p>
+ <?php p($l->t( 'The following file types will not be encrypted:' )); ?>
+ </p>
+ <ul>
+ <?php foreach( $_["blacklist"] as $type ): ?>
+ <li>
+ <?php p($type); ?>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+ </fieldset>
+</form>
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php
index 55e8cf1542c..b873d7f5aaf 100644
--- a/apps/files_encryption/templates/settings.php
+++ b/apps/files_encryption/templates/settings.php
@@ -1,12 +1,20 @@
-<form id="calendar">
+<form id="encryption">
<fieldset class="personalblock">
- <strong><?php echo $l->t('Encryption'); ?></strong>
- <?php echo $l->t("Exclude the following file types from encryption"); ?>
- <select id='encryption_blacklist' title="<?php echo $l->t('None')?>" multiple="multiple">
+
+ <p>
+ <strong><?php p($l->t( 'Encryption' )); ?></strong>
+
+ <?php p($l->t( "Exclude the following file types from encryption:" )); ?>
+ <br />
+
+ <select
+ id='encryption_blacklist'
+ title="<?php p($l->t( 'None' ))?>"
+ multiple="multiple">
<?php foreach($_["blacklist"] as $type): ?>
- <option selected="selected" value="<?php echo $type;?>"><?php echo $type;?></option>
+ <option selected="selected" value="<?php p($type); ?>"> <?php p($type); ?> </option>
<?php endforeach;?>
- </select>
- <input type='checkbox' id='enable_encryption' <?php if($_['encryption_enabled']) {echo 'checked="checked"';} ?>></input><label for='enable_encryption'><?php echo $l->t('Enable Encryption')?></label>
+ </select>
+ </p>
</fieldset>
</form>
diff --git a/apps/files_encryption/tests/binary b/apps/files_encryption/test/binary
index 79bc99479da..79bc99479da 100644
--- a/apps/files_encryption/tests/binary
+++ b/apps/files_encryption/test/binary
Binary files differ
diff --git a/apps/files_encryption/test/crypt.php b/apps/files_encryption/test/crypt.php
new file mode 100755
index 00000000000..aa87ec32821
--- /dev/null
+++ b/apps/files_encryption/test/crypt.php
@@ -0,0 +1,667 @@
+<?php
+/**
+ * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and
+ * Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+//require_once "PHPUnit/Framework/TestCase.php";
+require_once realpath( dirname(__FILE__).'/../../../3rdparty/Crypt_Blowfish/Blowfish.php' );
+require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
+require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
+require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
+require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
+require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
+require_once realpath( dirname(__FILE__).'/../lib/util.php' );
+require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
+
+use OCA\Encryption;
+
+// This has to go here because otherwise session errors arise, and the private
+// encryption key needs to be saved in the session
+\OC_User::login( 'admin', 'admin' );
+
+/**
+ * @note It would be better to use Mockery here for mocking out the session
+ * handling process, and isolate calls to session class and data from the unit
+ * tests relating to them (stream etc.). However getting mockery to work and
+ * overload classes whilst also using the OC autoloader is difficult due to
+ * load order Pear errors.
+ */
+
+class Test_Crypt extends \PHPUnit_Framework_TestCase {
+
+ function setUp() {
+
+ // set content for encrypting / decrypting in tests
+ $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
+ $this->dataShort = 'hats';
+ $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
+ $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
+ $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
+ $this->randomKey = Encryption\Crypt::generateKey();
+
+ $keypair = Encryption\Crypt::createKeypair();
+ $this->genPublicKey = $keypair['publicKey'];
+ $this->genPrivateKey = $keypair['privateKey'];
+
+ $this->view = new \OC_FilesystemView( '/' );
+
+ \OC_User::setUserId( 'admin' );
+ $this->userId = 'admin';
+ $this->pass = 'admin';
+
+ \OC_Filesystem::init( '/' );
+ \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
+
+ }
+
+ function tearDown() {
+
+ }
+
+ function testGenerateKey() {
+
+ # TODO: use more accurate (larger) string length for test confirmation
+
+ $key = Encryption\Crypt::generateKey();
+
+ $this->assertTrue( strlen( $key ) > 16 );
+
+ }
+
+ function testGenerateIv() {
+
+ $iv = Encryption\Crypt::generateIv();
+
+ $this->assertEquals( 16, strlen( $iv ) );
+
+ return $iv;
+
+ }
+
+ /**
+ * @depends testGenerateIv
+ */
+ function testConcatIv( $iv ) {
+
+ $catFile = Encryption\Crypt::concatIv( $this->dataLong, $iv );
+
+ // Fetch encryption metadata from end of file
+ $meta = substr( $catFile, -22 );
+
+ $identifier = substr( $meta, 0, 6);
+
+ // Fetch IV from end of file
+ $foundIv = substr( $meta, 6 );
+
+ $this->assertEquals( '00iv00', $identifier );
+
+ $this->assertEquals( $iv, $foundIv );
+
+ // Remove IV and IV identifier text to expose encrypted content
+ $data = substr( $catFile, 0, -22 );
+
+ $this->assertEquals( $this->dataLong, $data );
+
+ return array(
+ 'iv' => $iv
+ , 'catfile' => $catFile
+ );
+
+ }
+
+ /**
+ * @depends testConcatIv
+ */
+ function testSplitIv( $testConcatIv ) {
+
+ // Split catfile into components
+ $splitCatfile = Encryption\Crypt::splitIv( $testConcatIv['catfile'] );
+
+ // Check that original IV and split IV match
+ $this->assertEquals( $testConcatIv['iv'], $splitCatfile['iv'] );
+
+ // Check that original data and split data match
+ $this->assertEquals( $this->dataLong, $splitCatfile['encrypted'] );
+
+ }
+
+ function testAddPadding() {
+
+ $padded = Encryption\Crypt::addPadding( $this->dataLong );
+
+ $padding = substr( $padded, -2 );
+
+ $this->assertEquals( 'xx' , $padding );
+
+ return $padded;
+
+ }
+
+ /**
+ * @depends testAddPadding
+ */
+ function testRemovePadding( $padded ) {
+
+ $noPadding = Encryption\Crypt::RemovePadding( $padded );
+
+ $this->assertEquals( $this->dataLong, $noPadding );
+
+ }
+
+ function testEncrypt() {
+
+ $random = openssl_random_pseudo_bytes( 13 );
+
+ $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht
+
+ $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' );
+
+ $this->assertNotEquals( $this->dataUrl, $crypted );
+
+ }
+
+ function testDecrypt() {
+
+ $random = openssl_random_pseudo_bytes( 13 );
+
+ $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht
+
+ $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' );
+
+ $decrypt = Encryption\Crypt::decrypt( $crypted, $iv, 'hat' );
+
+ $this->assertEquals( $this->dataUrl, $decrypt );
+
+ }
+
+ function testSymmetricEncryptFileContent() {
+
+ # TODO: search in keyfile for actual content as IV will ensure this test always passes
+
+ $crypted = Encryption\Crypt::symmetricEncryptFileContent( $this->dataShort, 'hat' );
+
+ $this->assertNotEquals( $this->dataShort, $crypted );
+
+
+ $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted, 'hat' );
+
+ $this->assertEquals( $this->dataShort, $decrypt );
+
+ }
+
+ // These aren't used for now
+// function testSymmetricBlockEncryptShortFileContent() {
+//
+// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataShort, $this->randomKey );
+//
+// $this->assertNotEquals( $this->dataShort, $crypted );
+//
+//
+// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey );
+//
+// $this->assertEquals( $this->dataShort, $decrypt );
+//
+// }
+//
+// function testSymmetricBlockEncryptLongFileContent() {
+//
+// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataLong, $this->randomKey );
+//
+// $this->assertNotEquals( $this->dataLong, $crypted );
+//
+//
+// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey );
+//
+// $this->assertEquals( $this->dataLong, $decrypt );
+//
+// }
+
+ function testSymmetricStreamEncryptShortFileContent() {
+
+ $filename = 'tmp-'.time();
+
+ $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
+
+ // Test that data was successfully written
+ $this->assertTrue( is_int( $cryptedFile ) );
+
+
+ // Get file contents without using any wrapper to get it's actual contents on disk
+ $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
+
+ // Check that the file was encrypted before being written to disk
+ $this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
+
+ // Get private key
+ $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
+
+ $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
+
+
+ // Get keyfile
+ $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
+
+ $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
+
+
+ // Manually decrypt
+ $manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile );
+
+ // Check that decrypted data matches
+ $this->assertEquals( $this->dataShort, $manualDecrypt );
+
+ }
+
+ /**
+ * @brief Test that data that is written by the crypto stream wrapper
+ * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read
+ * @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
+ * reassembly of its data
+ */
+ function testSymmetricStreamEncryptLongFileContent() {
+
+ // Generate a a random filename
+ $filename = 'tmp-'.time();
+
+ // Save long data as encrypted file using stream wrapper
+ $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
+
+ // Test that data was successfully written
+ $this->assertTrue( is_int( $cryptedFile ) );
+
+ // Get file contents without using any wrapper to get it's actual contents on disk
+ $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
+
+// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n";
+
+ // Check that the file was encrypted before being written to disk
+ $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
+
+ // Manuallly split saved file into separate IVs and encrypted chunks
+ $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE);
+
+ //print_r($r);
+
+ // Join IVs and their respective data chunks
+ $e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] );
+
+ //print_r($e);
+
+
+ // Get private key
+ $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
+
+ $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
+
+
+ // Get keyfile
+ $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
+
+ $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
+
+
+ // Set var for reassembling decrypted content
+ $decrypt = '';
+
+ // Manually decrypt chunk
+ foreach ($e as $e) {
+
+// echo "\n\$e = $e";
+
+ $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile );
+
+ // Assemble decrypted chunks
+ $decrypt .= $chunkDecrypt;
+
+// echo "\n\$chunkDecrypt = $chunkDecrypt";
+
+ }
+
+// echo "\n\$decrypt = $decrypt";
+
+ $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
+
+ // Teardown
+
+ $this->view->unlink( $filename );
+
+ Encryption\Keymanager::deleteFileKey( $filename );
+
+ }
+
+ /**
+ * @brief Test that data that is read by the crypto stream wrapper
+ */
+ function testSymmetricStreamDecryptShortFileContent() {
+
+ $filename = 'tmp-'.time();
+
+ // Save long data as encrypted file using stream wrapper
+ $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
+
+ // Test that data was successfully written
+ $this->assertTrue( is_int( $cryptedFile ) );
+
+
+ // Get file contents without using any wrapper to get it's actual contents on disk
+ $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
+
+ $decrypt = file_get_contents( 'crypt://' . $filename );
+
+ $this->assertEquals( $this->dataShort, $decrypt );
+
+ }
+
+ function testSymmetricStreamDecryptLongFileContent() {
+
+ $filename = 'tmp-'.time();
+
+ // Save long data as encrypted file using stream wrapper
+ $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
+
+ // Test that data was successfully written
+ $this->assertTrue( is_int( $cryptedFile ) );
+
+
+ // Get file contents without using any wrapper to get it's actual contents on disk
+ $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
+
+ $decrypt = file_get_contents( 'crypt://' . $filename );
+
+ $this->assertEquals( $this->dataLong, $decrypt );
+
+ }
+
+ // Is this test still necessary?
+// function testSymmetricBlockStreamDecryptFileContent() {
+//
+// \OC_User::setUserId( 'admin' );
+//
+// // Disable encryption proxy to prevent unwanted en/decryption
+// \OC_FileProxy::$enabled = false;
+//
+// $cryptedFile = file_put_contents( 'crypt://' . '/blockEncrypt', $this->dataUrl );
+//
+// // Disable encryption proxy to prevent unwanted en/decryption
+// \OC_FileProxy::$enabled = false;
+//
+// echo "\n\n\$cryptedFile = " . $this->view->file_get_contents( '/blockEncrypt' );
+//
+// $retreivedCryptedFile = file_get_contents( 'crypt://' . '/blockEncrypt' );
+//
+// $this->assertEquals( $this->dataUrl, $retreivedCryptedFile );
+//
+// \OC_FileProxy::$enabled = false;
+//
+// }
+
+ function testSymmetricEncryptFileContentKeyfile() {
+
+ # TODO: search in keyfile for actual content as IV will ensure this test always passes
+
+ $crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl );
+
+ $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] );
+
+
+ $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted['encrypted'], $crypted['key'] );
+
+ $this->assertEquals( $this->dataUrl, $decrypt );
+
+ }
+
+ function testIsEncryptedContent() {
+
+ $this->assertFalse( Encryption\Crypt::isCatfile( $this->dataUrl ) );
+
+ $this->assertFalse( Encryption\Crypt::isCatfile( $this->legacyEncryptedData ) );
+
+ $keyfileContent = Encryption\Crypt::symmetricEncryptFileContent( $this->dataUrl, 'hat' );
+
+ $this->assertTrue( Encryption\Crypt::isCatfile( $keyfileContent ) );
+
+ }
+
+ function testMultiKeyEncrypt() {
+
+ # TODO: search in keyfile for actual content as IV will ensure this test always passes
+
+ $pair1 = Encryption\Crypt::createKeypair();
+
+ $this->assertEquals( 2, count( $pair1 ) );
+
+ $this->assertTrue( strlen( $pair1['publicKey'] ) > 1 );
+
+ $this->assertTrue( strlen( $pair1['privateKey'] ) > 1 );
+
+
+ $crypted = Encryption\Crypt::multiKeyEncrypt( $this->dataUrl, array( $pair1['publicKey'] ) );
+
+ $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] );
+
+
+ $decrypt = Encryption\Crypt::multiKeyDecrypt( $crypted['encrypted'], $crypted['keys'][0], $pair1['privateKey'] );
+
+ $this->assertEquals( $this->dataUrl, $decrypt );
+
+ }
+
+ function testKeyEncrypt() {
+
+ // Generate keypair
+ $pair1 = Encryption\Crypt::createKeypair();
+
+ // Encrypt data
+ $crypted = Encryption\Crypt::keyEncrypt( $this->dataUrl, $pair1['publicKey'] );
+
+ $this->assertNotEquals( $this->dataUrl, $crypted );
+
+ // Decrypt data
+ $decrypt = Encryption\Crypt::keyDecrypt( $crypted, $pair1['privateKey'] );
+
+ $this->assertEquals( $this->dataUrl, $decrypt );
+
+ }
+
+ // What is the point of this test? It doesn't use keyEncryptKeyfile()
+ function testKeyEncryptKeyfile() {
+
+ # TODO: Don't repeat encryption from previous tests, use PHPUnit test interdependency instead
+
+ // Generate keypair
+ $pair1 = Encryption\Crypt::createKeypair();
+
+ // Encrypt plain data, generate keyfile & encrypted file
+ $cryptedData = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl );
+
+ // Encrypt keyfile
+ $cryptedKey = Encryption\Crypt::keyEncrypt( $cryptedData['key'], $pair1['publicKey'] );
+
+ // Decrypt keyfile
+ $decryptKey = Encryption\Crypt::keyDecrypt( $cryptedKey, $pair1['privateKey'] );
+
+ // Decrypt encrypted file
+ $decryptData = Encryption\Crypt::symmetricDecryptFileContent( $cryptedData['encrypted'], $decryptKey );
+
+ $this->assertEquals( $this->dataUrl, $decryptData );
+
+ }
+
+ /**
+ * @brief test functionality of keyEncryptKeyfile() and
+ * keyDecryptKeyfile()
+ */
+ function testKeyDecryptKeyfile() {
+
+ $encrypted = Encryption\Crypt::keyEncryptKeyfile( $this->dataShort, $this->genPublicKey );
+
+ $this->assertNotEquals( $encrypted['data'], $this->dataShort );
+
+ $decrypted = Encryption\Crypt::keyDecryptKeyfile( $encrypted['data'], $encrypted['key'], $this->genPrivateKey );
+
+ $this->assertEquals( $decrypted, $this->dataShort );
+
+ }
+
+
+ /**
+ * @brief test encryption using legacy blowfish method
+ */
+ function testLegacyEncryptShort() {
+
+ $crypted = Encryption\Crypt::legacyEncrypt( $this->dataShort, $this->pass );
+
+ $this->assertNotEquals( $this->dataShort, $crypted );
+
+ # TODO: search inencrypted text for actual content to ensure it
+ # genuine transformation
+
+ return $crypted;
+
+ }
+
+ /**
+ * @brief test decryption using legacy blowfish method
+ * @depends testLegacyEncryptShort
+ */
+ function testLegacyDecryptShort( $crypted ) {
+
+ $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass );
+
+ $this->assertEquals( $this->dataShort, $decrypted );
+
+ }
+
+ /**
+ * @brief test encryption using legacy blowfish method
+ */
+ function testLegacyEncryptLong() {
+
+ $crypted = Encryption\Crypt::legacyEncrypt( $this->dataLong, $this->pass );
+
+ $this->assertNotEquals( $this->dataLong, $crypted );
+
+ # TODO: search inencrypted text for actual content to ensure it
+ # genuine transformation
+
+ return $crypted;
+
+ }
+
+ /**
+ * @brief test decryption using legacy blowfish method
+ * @depends testLegacyEncryptLong
+ */
+ function testLegacyDecryptLong( $crypted ) {
+
+ $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass );
+
+ $this->assertEquals( $this->dataLong, $decrypted );
+
+ }
+
+ /**
+ * @brief test generation of legacy encryption key
+ * @depends testLegacyDecryptShort
+ */
+ function testLegacyCreateKey() {
+
+ // Create encrypted key
+ $encKey = Encryption\Crypt::legacyCreateKey( $this->pass );
+
+ // Decrypt key
+ $key = Encryption\Crypt::legacyDecrypt( $encKey, $this->pass );
+
+ $this->assertTrue( is_numeric( $key ) );
+
+ // Check that key is correct length
+ $this->assertEquals( 20, strlen( $key ) );
+
+ }
+
+ /**
+ * @brief test decryption using legacy blowfish method
+ * @depends testLegacyEncryptLong
+ */
+ function testLegacyKeyRecryptKeyfileEncrypt( $crypted ) {
+
+ $recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile( $crypted, $this->pass, $this->genPublicKey, $this->pass );
+
+ $this->assertNotEquals( $this->dataLong, $recrypted['data'] );
+
+ return $recrypted;
+
+ # TODO: search inencrypted text for actual content to ensure it
+ # genuine transformation
+
+ }
+
+// function testEncryption(){
+//
+// $key=uniqid();
+// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
+// $source=file_get_contents($file); //nice large text file
+// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
+// $decrypted=rtrim($decrypted, "\0");
+// $this->assertNotEquals($encrypted,$source);
+// $this->assertEquals($decrypted,$source);
+//
+// $chunk=substr($source,0,8192);
+// $encrypted=OC_Encryption\Crypt::encrypt($chunk,$key);
+// $this->assertEquals(strlen($chunk),strlen($encrypted));
+// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
+// $decrypted=rtrim($decrypted, "\0");
+// $this->assertEquals($decrypted,$chunk);
+//
+// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
+// $this->assertNotEquals($encrypted,$source);
+// $this->assertEquals($decrypted,$source);
+//
+// $tmpFileEncrypted=OCP\Files::tmpFile();
+// OC_Encryption\Crypt::encryptfile($file,$tmpFileEncrypted,$key);
+// $encrypted=file_get_contents($tmpFileEncrypted);
+// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
+// $this->assertNotEquals($encrypted,$source);
+// $this->assertEquals($decrypted,$source);
+//
+// $tmpFileDecrypted=OCP\Files::tmpFile();
+// OC_Encryption\Crypt::decryptfile($tmpFileEncrypted,$tmpFileDecrypted,$key);
+// $decrypted=file_get_contents($tmpFileDecrypted);
+// $this->assertEquals($decrypted,$source);
+//
+// $file=OC::$SERVERROOT.'/core/img/weather-clear.png';
+// $source=file_get_contents($file); //binary file
+// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
+// $decrypted=rtrim($decrypted, "\0");
+// $this->assertEquals($decrypted,$source);
+//
+// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
+// $this->assertEquals($decrypted,$source);
+//
+// }
+//
+// function testBinary(){
+// $key=uniqid();
+//
+// $file=__DIR__.'/binary';
+// $source=file_get_contents($file); //binary file
+// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
+//
+// $decrypted=rtrim($decrypted, "\0");
+// $this->assertEquals($decrypted,$source);
+//
+// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
+// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key,strlen($source));
+// $this->assertEquals($decrypted,$source);
+// }
+
+}
diff --git a/apps/files_encryption/test/keymanager.php b/apps/files_encryption/test/keymanager.php
new file mode 100644
index 00000000000..bf453fe3163
--- /dev/null
+++ b/apps/files_encryption/test/keymanager.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+//require_once "PHPUnit/Framework/TestCase.php";
+require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
+require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
+require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
+require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
+require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
+require_once realpath( dirname(__FILE__).'/../lib/util.php' );
+require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
+
+use OCA\Encryption;
+
+// This has to go here because otherwise session errors arise, and the private
+// encryption key needs to be saved in the session
+\OC_User::login( 'admin', 'admin' );
+
+class Test_Keymanager extends \PHPUnit_Framework_TestCase {
+
+ function setUp() {
+
+ \OC_FileProxy::$enabled = false;
+
+ // set content for encrypting / decrypting in tests
+ $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
+ $this->dataShort = 'hats';
+ $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
+ $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
+ $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
+ $this->randomKey = Encryption\Crypt::generateKey();
+
+ $keypair = Encryption\Crypt::createKeypair();
+ $this->genPublicKey = $keypair['publicKey'];
+ $this->genPrivateKey = $keypair['privateKey'];
+
+ $this->view = new \OC_FilesystemView( '/' );
+
+ \OC_User::setUserId( 'admin' );
+ $this->userId = 'admin';
+ $this->pass = 'admin';
+
+ \OC_Filesystem::init( '/' );
+ \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
+
+ }
+
+ function tearDown(){
+
+ \OC_FileProxy::$enabled = true;
+
+ }
+
+ function testGetPrivateKey() {
+
+ $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
+
+ // Will this length vary? Perhaps we should use a range instead
+ $this->assertEquals( 2296, strlen( $key ) );
+
+ }
+
+ function testGetPublicKey() {
+
+ $key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId );
+
+ $this->assertEquals( 451, strlen( $key ) );
+
+ $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) );
+ }
+
+ function testSetFileKey() {
+
+ # NOTE: This cannot be tested until we are able to break out
+ # of the FileSystemView data directory root
+
+ $key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' );
+
+ $path = 'unittest-'.time().'txt';
+
+ //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
+
+ Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] );
+
+ }
+
+// /**
+// * @depends testGetPrivateKey
+// */
+// function testGetPrivateKey_decrypt() {
+//
+// $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
+//
+// # TODO: replace call to Crypt with a mock object?
+// $decrypted = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->passphrase );
+//
+// $this->assertEquals( 1704, strlen( $decrypted ) );
+//
+// $this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $decrypted, 0, 27 ) );
+//
+// }
+
+ function testGetUserKeys() {
+
+ $keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId );
+
+ $this->assertEquals( 451, strlen( $keys['publicKey'] ) );
+ $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) );
+ $this->assertEquals( 2296, strlen( $keys['privateKey'] ) );
+
+ }
+
+ function testGetPublicKeys() {
+
+ # TODO: write me
+
+ }
+
+ function testGetFileKey() {
+
+// Encryption\Keymanager::getFileKey( $this->view, $this->userId, $this->filePath );
+
+ }
+
+}
diff --git a/apps/files_encryption/test/legacy-encrypted-text.txt b/apps/files_encryption/test/legacy-encrypted-text.txt
new file mode 100644
index 00000000000..cb5bf50550d
--- /dev/null
+++ b/apps/files_encryption/test/legacy-encrypted-text.txt
Binary files differ
diff --git a/apps/files_encryption/test/proxy.php b/apps/files_encryption/test/proxy.php
new file mode 100644
index 00000000000..709730f7609
--- /dev/null
+++ b/apps/files_encryption/test/proxy.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>,
+ * and Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+// require_once "PHPUnit/Framework/TestCase.php";
+// require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Generator.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/MockInterface.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Mock.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Container.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Configuration.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CompositeExpectation.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/ExpectationDirector.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Expectation.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Exception.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/CountValidatorAbstract.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exception.php' );
+// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exact.php' );
+//
+// use \Mockery as m;
+// use OCA\Encryption;
+
+// class Test_Util extends \PHPUnit_Framework_TestCase {
+//
+// public function setUp() {
+//
+// $this->proxy = new Encryption\Proxy();
+//
+// $this->tmpFileName = "tmpFile-".time();
+//
+// $this->privateKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.public.key' ) );
+// $this->publicKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.private.key' ) );
+// $this->encDataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester-enc' ) );
+// $this->encDataShortKey = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester.key' ) );
+//
+// $this->dataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester' ) );
+// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
+// $this->longDataPath = realpath( dirname(__FILE__).'/../lib/crypt.php' );
+//
+// $this->data1 = file_get_contents( realpath( dirname(__FILE__).'/../../../data/admin/files/enc-test.txt' ) );
+//
+// \OC_FileProxy::$enabled = false;
+// $this->Encdata1 = file_get_contents( realpath( dirname(__FILE__).'/../../../data/admin/files/enc-test.txt' ) );
+// \OC_FileProxy::$enabled = true;
+//
+// $this->userId = 'admin';
+// $this->pass = 'admin';
+//
+// $this->session = new Encryption\Session();
+//
+// $this->session->setPrivateKey(
+// '-----BEGIN PRIVATE KEY-----
+// MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiH3EA4EpFA7Fx
+// s2dyyfL5jwXeYXrTqQJ6DqKgGn8VsbT3eu8R9KzM2XitVwZe8c8L52DvJ06o5vg0
+// GqPYxilFdOFJe/ggac5Tq8UmJiZS4EqYEMwxBIfIyWTxeGV06/0HOwnVAkqHMcBz
+// 64qldtgi5O8kZMEM2/gKBgU0kMLJzM+8oEWhL1+gsUWQhxd8cKLXypS6iWgqFJrz
+// f/X0hJsJR+gyYxNpahtnjzd/LxLAETrOMsl2tue+BAxmjbAM0aG0NEM0div+b59s
+// 2uz/iWbxImp5pOdYVKcVW89D4XBMyGegR40trV2VwiuX1blKCfdjMsJhiaL9pymp
+// ug1wzyQFAgMBAAECggEAK6c+PZkPPXuVCgpEcliiW6NM0r2m5K3AGKgypQ34csu3
+// z/8foCvIIFPrhCtEw5eTDQ1CHWlNOjY8vHJYJ0U6Onpx86nHIRrMBkMm8FJ1G5LJ
+// U8oKYXwqaozWu/cuPwA//OFc6I5krOzh5n8WaRMkbrgbor8AtebRX74By0AXGrXe
+// cswJI7zR96oFn4Dm7Pgvpg5Zhk1vFJ+w6QtH+4DDJ6PBvlZsRkGxYBLGVd/3qhAI
+// sBAyjFlSzuP4eCRhHOhHC/e4gmAH9evFVXB88jFyRZm3K+jQ5W5CwrVRBCV2lph6
+// 2B6P7CBJN+IjGKMhy+75y13UvvKPv9IwH8Fzl2x1gQKBgQD8qQOr7a6KhSj16wQE
+// jim2xqt9gQ2jH5No405NrKs/PFQQZnzD4YseQsiK//NUjOJiUhaT+L5jhIpzINHt
+// RJpt3bGkEZmLyjdjgTpB3GwZdXa28DNK9VdXZ19qIl/ZH0qAjKmJCRahUDASMnVi
+// M4Pkk9yx9ZIKkri4TcuMWqc0DQKBgQDlHKBTITZq/arYPD6Nl3NsoOdqVRqJrGay
+// 0TjXAVbBXe46+z5lnMsqwXb79nx14hdmSEsZULrw/3f+MnQbdjMTYLFP24visZg9
+// MN8vAiALiiiR1a+Crz+DTA1Q8sGOMVCMqMDmD7QBys3ZuWxuapm0txAiIYUtsjJZ
+// XN76T4nZ2QKBgQCHaT3igzwsWTmesxowJtEMeGWomeXpKx8h89EfqA8PkRGsyIDN
+// qq+YxEoe1RZgljEuaLhZDdNcGsjo8woPk9kAUPTH7fbRCMuutK+4ZJ469s1tNkcH
+// QX5SBcEJbOrZvv967ehe3VQXmJZq6kgnHVzuwKBjcC2ZJRGDFY6l5l/+cQKBgCqh
+// +Adf/8NK7paMJ0urqfPFwSodKfICXZ3apswDWMRkmSbqh4La+Uc8dsqN5Dz/VEFZ
+// JHhSeGbN8uMfOlG93eU2MehdPxtw1pZUWMNjjtj23XO9ooob2CKzbSrp8TBnZsi1
+// widNNr66oTFpeo7VUUK6acsgF6sYJJxSVr+XO1yJAoGAEhvitq8shNKcEY0xCipS
+// k1kbgyS7KKB7opVxI5+ChEqyUDijS3Y9FZixrRIWE6i2uGu86UG+v2lbKvSbM4Qm
+// xvbOcX9OVMnlRb7n8woOP10UMY+ZE2x+YEUXQTLtPYq7F66e1OfxltstMxLQA+3d
+// Y1d5piFV8PXK3Fg2F+Cj5qg=
+// -----END PRIVATE KEY-----
+// '
+// , $this->userId
+// );
+//
+// \OC_User::setUserId( $this->userId );
+//
+// }
+//
+// public function testpreFile_get_contents() {
+//
+// // This won't work for now because mocking of the static keymanager class isn't working :(
+//
+// // $mock = m::mock( 'alias:OCA\Encryption\Keymanager' );
+// //
+// // $mock->shouldReceive( 'getFileKey' )->times(2)->andReturn( $this->encDataShort );
+// //
+// // $encrypted = $this->proxy->postFile_get_contents( 'data/'.$this->tmpFileName, $this->encDataShortKey );
+// //
+// // $this->assertNotEquals( $this->dataShort, $encrypted );
+//
+// $decrypted = $this->proxy->postFile_get_contents( 'data/admin/files/enc-test.txt', $this->data1 );
+//
+// }
+//
+// }
+
+// class Test_CryptProxy extends PHPUnit_Framework_TestCase {
+// private $oldConfig;
+// private $oldKey;
+//
+// public function setUp(){
+// $user=OC_User::getUser();
+//
+// $this->oldConfig=OCP\Config::getAppValue('files_encryption','enable_encryption','true');
+// OCP\Config::setAppValue('files_encryption','enable_encryption','true');
+// $this->oldKey=isset($_SESSION['privateKey'])?$_SESSION['privateKey']:null;
+//
+//
+// //set testing key
+// $_SESSION['privateKey']=md5(time());
+//
+// //clear all proxies and hooks so we can do clean testing
+// OC_FileProxy::clearProxies();
+// OC_Hook::clear('OC_Filesystem');
+//
+// //enable only the encryption hook
+// OC_FileProxy::register(new OC_FileProxy_Encryption());
+//
+// //set up temporary storage
+// OC_Filesystem::clearMounts();
+// OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/');
+//
+// OC_Filesystem::init('/'.$user.'/files');
+//
+// //set up the users home folder in the temp storage
+// $rootView=new OC_FilesystemView('');
+// $rootView->mkdir('/'.$user);
+// $rootView->mkdir('/'.$user.'/files');
+// }
+//
+// public function tearDown(){
+// OCP\Config::setAppValue('files_encryption','enable_encryption',$this->oldConfig);
+// if(!is_null($this->oldKey)){
+// $_SESSION['privateKey']=$this->oldKey;
+// }
+// }
+//
+// public function testSimple(){
+// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
+// $original=file_get_contents($file);
+//
+// OC_Filesystem::file_put_contents('/file',$original);
+//
+// OC_FileProxy::$enabled=false;
+// $stored=OC_Filesystem::file_get_contents('/file');
+// OC_FileProxy::$enabled=true;
+//
+// $fromFile=OC_Filesystem::file_get_contents('/file');
+// $this->assertNotEquals($original,$stored);
+// $this->assertEquals(strlen($original),strlen($fromFile));
+// $this->assertEquals($original,$fromFile);
+//
+// }
+//
+// public function testView(){
+// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
+// $original=file_get_contents($file);
+//
+// $rootView=new OC_FilesystemView('');
+// $view=new OC_FilesystemView('/'.OC_User::getUser());
+// $userDir='/'.OC_User::getUser().'/files';
+//
+// $rootView->file_put_contents($userDir.'/file',$original);
+//
+// OC_FileProxy::$enabled=false;
+// $stored=$rootView->file_get_contents($userDir.'/file');
+// OC_FileProxy::$enabled=true;
+//
+// $this->assertNotEquals($original,$stored);
+// $fromFile=$rootView->file_get_contents($userDir.'/file');
+// $this->assertEquals($original,$fromFile);
+//
+// $fromFile=$view->file_get_contents('files/file');
+// $this->assertEquals($original,$fromFile);
+// }
+//
+// public function testBinary(){
+// $file=__DIR__.'/binary';
+// $original=file_get_contents($file);
+//
+// OC_Filesystem::file_put_contents('/file',$original);
+//
+// OC_FileProxy::$enabled=false;
+// $stored=OC_Filesystem::file_get_contents('/file');
+// OC_FileProxy::$enabled=true;
+//
+// $fromFile=OC_Filesystem::file_get_contents('/file');
+// $this->assertNotEquals($original,$stored);
+// $this->assertEquals(strlen($original),strlen($fromFile));
+// $this->assertEquals($original,$fromFile);
+//
+// $file=__DIR__.'/zeros';
+// $original=file_get_contents($file);
+//
+// OC_Filesystem::file_put_contents('/file',$original);
+//
+// OC_FileProxy::$enabled=false;
+// $stored=OC_Filesystem::file_get_contents('/file');
+// OC_FileProxy::$enabled=true;
+//
+// $fromFile=OC_Filesystem::file_get_contents('/file');
+// $this->assertNotEquals($original,$stored);
+// $this->assertEquals(strlen($original),strlen($fromFile));
+// }
+// }
diff --git a/apps/files_encryption/test/stream.php b/apps/files_encryption/test/stream.php
new file mode 100644
index 00000000000..ba82ac80eab
--- /dev/null
+++ b/apps/files_encryption/test/stream.php
@@ -0,0 +1,226 @@
+// <?php
+// /**
+// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+// * This file is licensed under the Affero General Public License version 3 or
+// * later.
+// * See the COPYING-README file.
+// */
+//
+// namespace OCA\Encryption;
+//
+// class Test_Stream extends \PHPUnit_Framework_TestCase {
+//
+// function setUp() {
+//
+// \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
+//
+// $this->empty = '';
+//
+// $this->stream = new Stream();
+//
+// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
+// $this->dataShort = 'hats';
+//
+// $this->emptyTmpFilePath = \OCP\Files::tmpFile();
+//
+// $this->dataTmpFilePath = \OCP\Files::tmpFile();
+//
+// file_put_contents( $this->dataTmpFilePath, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est." );
+//
+// }
+//
+// function testStreamOpen() {
+//
+// $stream1 = new Stream();
+//
+// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'wb', array(), $this->empty );
+//
+// // Test that resource was returned successfully
+// $this->assertTrue( $handle1 );
+//
+// // Test that file has correct size
+// $this->assertEquals( 0, $stream1->size );
+//
+// // Test that path is correct
+// $this->assertEquals( $this->emptyTmpFilePath, $stream1->rawPath );
+//
+// $stream2 = new Stream();
+//
+// $handle2 = $stream2->stream_open( 'crypt://' . $this->emptyTmpFilePath, 'wb', array(), $this->empty );
+//
+// // Test that protocol identifier is removed from path
+// $this->assertEquals( $this->emptyTmpFilePath, $stream2->rawPath );
+//
+// // "Stat failed error" prevents this test from executing
+// // $stream3 = new Stream();
+// //
+// // $handle3 = $stream3->stream_open( $this->dataTmpFilePath, 'r', array(), $this->empty );
+// //
+// // $this->assertEquals( 0, $stream3->size );
+//
+// }
+//
+// function testStreamWrite() {
+//
+// $stream1 = new Stream();
+//
+// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'r+b', array(), $this->empty );
+//
+// # what about the keymanager? there is no key for the newly created temporary file!
+//
+// $stream1->stream_write( $this->dataShort );
+//
+// }
+//
+// // function getStream( $id, $mode, $size ) {
+// //
+// // if ( $id === '' ) {
+// //
+// // $id = uniqid();
+// // }
+// //
+// //
+// // if ( !isset( $this->tmpFiles[$id] ) ) {
+// //
+// // // If tempfile with given name does not already exist, create it
+// //
+// // $file = OCP\Files::tmpFile();
+// //
+// // $this->tmpFiles[$id] = $file;
+// //
+// // } else {
+// //
+// // $file = $this->tmpFiles[$id];
+// //
+// // }
+// //
+// // $stream = fopen( $file, $mode );
+// //
+// // Stream::$sourceStreams[$id] = array( 'path' => 'dummy' . $id, 'stream' => $stream, 'size' => $size );
+// //
+// // return fopen( 'crypt://streams/'.$id, $mode );
+// //
+// // }
+// //
+// // function testStream( ){
+// //
+// // $stream = $this->getStream( 'test1', 'w', strlen( 'foobar' ) );
+// //
+// // fwrite( $stream, 'foobar' );
+// //
+// // fclose( $stream );
+// //
+// //
+// // $stream = $this->getStream( 'test1', 'r', strlen( 'foobar' ) );
+// //
+// // $data = fread( $stream, 6 );
+// //
+// // fclose( $stream );
+// //
+// // $this->assertEquals( 'foobar', $data );
+// //
+// //
+// // $file = OC::$SERVERROOT.'/3rdparty/MDB2.php';
+// //
+// // $source = fopen( $file, 'r' );
+// //
+// // $target = $this->getStream( 'test2', 'w', 0 );
+// //
+// // OCP\Files::streamCopy( $source, $target );
+// //
+// // fclose( $target );
+// //
+// // fclose( $source );
+// //
+// //
+// // $stream = $this->getStream( 'test2', 'r', filesize( $file ) );
+// //
+// // $data = stream_get_contents( $stream );
+// //
+// // $original = file_get_contents( $file );
+// //
+// // $this->assertEquals( strlen( $original ), strlen( $data ) );
+// //
+// // $this->assertEquals( $original, $data );
+// //
+// // }
+//
+// }
+//
+// // class Test_CryptStream extends PHPUnit_Framework_TestCase {
+// // private $tmpFiles=array();
+// //
+// // function testStream(){
+// // $stream=$this->getStream('test1','w',strlen('foobar'));
+// // fwrite($stream,'foobar');
+// // fclose($stream);
+// //
+// // $stream=$this->getStream('test1','r',strlen('foobar'));
+// // $data=fread($stream,6);
+// // fclose($stream);
+// // $this->assertEquals('foobar',$data);
+// //
+// // $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
+// // $source=fopen($file,'r');
+// // $target=$this->getStream('test2','w',0);
+// // OCP\Files::streamCopy($source,$target);
+// // fclose($target);
+// // fclose($source);
+// //
+// // $stream=$this->getStream('test2','r',filesize($file));
+// // $data=stream_get_contents($stream);
+// // $original=file_get_contents($file);
+// // $this->assertEquals(strlen($original),strlen($data));
+// // $this->assertEquals($original,$data);
+// // }
+// //
+// // /**
+// // * get a cryptstream to a temporary file
+// // * @param string $id
+// // * @param string $mode
+// // * @param int size
+// // * @return resource
+// // */
+// // function getStream($id,$mode,$size){
+// // if($id===''){
+// // $id=uniqid();
+// // }
+// // if(!isset($this->tmpFiles[$id])){
+// // $file=OCP\Files::tmpFile();
+// // $this->tmpFiles[$id]=$file;
+// // }else{
+// // $file=$this->tmpFiles[$id];
+// // }
+// // $stream=fopen($file,$mode);
+// // OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id,'stream'=>$stream,'size'=>$size);
+// // return fopen('crypt://streams/'.$id,$mode);
+// // }
+// //
+// // function testBinary(){
+// // $file=__DIR__.'/binary';
+// // $source=file_get_contents($file);
+// //
+// // $stream=$this->getStream('test','w',strlen($source));
+// // fwrite($stream,$source);
+// // fclose($stream);
+// //
+// // $stream=$this->getStream('test','r',strlen($source));
+// // $data=stream_get_contents($stream);
+// // fclose($stream);
+// // $this->assertEquals(strlen($data),strlen($source));
+// // $this->assertEquals($source,$data);
+// //
+// // $file=__DIR__.'/zeros';
+// // $source=file_get_contents($file);
+// //
+// // $stream=$this->getStream('test2','w',strlen($source));
+// // fwrite($stream,$source);
+// // fclose($stream);
+// //
+// // $stream=$this->getStream('test2','r',strlen($source));
+// // $data=stream_get_contents($stream);
+// // fclose($stream);
+// // $this->assertEquals(strlen($data),strlen($source));
+// // $this->assertEquals($source,$data);
+// // }
+// // }
diff --git a/apps/files_encryption/test/util.php b/apps/files_encryption/test/util.php
new file mode 100755
index 00000000000..1cdeff8008d
--- /dev/null
+++ b/apps/files_encryption/test/util.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+//require_once "PHPUnit/Framework/TestCase.php";
+require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
+require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
+require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
+require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
+require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
+require_once realpath( dirname(__FILE__).'/../lib/util.php' );
+require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
+
+// Load mockery files
+require_once 'Mockery/Loader.php';
+require_once 'Hamcrest/Hamcrest.php';
+$loader = new \Mockery\Loader;
+$loader->register();
+
+use \Mockery as m;
+use OCA\Encryption;
+
+class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
+
+ function setUp() {
+
+ \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
+
+ // set content for encrypting / decrypting in tests
+ $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
+ $this->dataShort = 'hats';
+ $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
+ $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
+ $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
+
+ $this->userId = 'admin';
+ $this->pass = 'admin';
+
+ $keypair = Encryption\Crypt::createKeypair();
+
+ $this->genPublicKey = $keypair['publicKey'];
+ $this->genPrivateKey = $keypair['privateKey'];
+
+ $this->publicKeyDir = '/' . 'public-keys';
+ $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
+ $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
+ $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
+ $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
+
+ $this->view = new \OC_FilesystemView( '/' );
+
+ $this->mockView = m::mock('OC_FilesystemView');
+ $this->util = new Encryption\Util( $this->mockView, $this->userId );
+
+ }
+
+ function tearDown(){
+
+ m::close();
+
+ }
+
+ /**
+ * @brief test that paths set during User construction are correct
+ */
+ function testKeyPaths() {
+
+ $mockView = m::mock('OC_FilesystemView');
+
+ $util = new Encryption\Util( $mockView, $this->userId );
+
+ $this->assertEquals( $this->publicKeyDir, $util->getPath( 'publicKeyDir' ) );
+ $this->assertEquals( $this->encryptionDir, $util->getPath( 'encryptionDir' ) );
+ $this->assertEquals( $this->keyfilesPath, $util->getPath( 'keyfilesPath' ) );
+ $this->assertEquals( $this->publicKeyPath, $util->getPath( 'publicKeyPath' ) );
+ $this->assertEquals( $this->privateKeyPath, $util->getPath( 'privateKeyPath' ) );
+
+ }
+
+ /**
+ * @brief test setup of encryption directories when they don't yet exist
+ */
+ function testSetupServerSideNotSetup() {
+
+ $mockView = m::mock('OC_FilesystemView');
+
+ $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false );
+ $mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true );
+ $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
+
+ $util = new Encryption\Util( $mockView, $this->userId );
+
+ $this->assertEquals( true, $util->setupServerSide( $this->pass ) );
+
+ }
+
+ /**
+ * @brief test setup of encryption directories when they already exist
+ */
+ function testSetupServerSideIsSetup() {
+
+ $mockView = m::mock('OC_FilesystemView');
+
+ $mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true );
+ $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
+
+ $util = new Encryption\Util( $mockView, $this->userId );
+
+ $this->assertEquals( true, $util->setupServerSide( $this->pass ) );
+
+ }
+
+ /**
+ * @brief test checking whether account is ready for encryption, when it isn't ready
+ */
+ function testReadyNotReady() {
+
+ $mockView = m::mock('OC_FilesystemView');
+
+ $mockView->shouldReceive( 'file_exists' )->times(1)->andReturn( false );
+
+ $util = new Encryption\Util( $mockView, $this->userId );
+
+ $this->assertEquals( false, $util->ready() );
+
+ # TODO: Add more tests here to check that if any of the dirs are
+ # then false will be returned. Use strict ordering?
+
+ }
+
+ /**
+ * @brief test checking whether account is ready for encryption, when it is ready
+ */
+ function testReadyIsReady() {
+
+ $mockView = m::mock('OC_FilesystemView');
+
+ $mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true );
+
+ $util = new Encryption\Util( $mockView, $this->userId );
+
+ $this->assertEquals( true, $util->ready() );
+
+ # TODO: Add more tests here to check that if any of the dirs are
+ # then false will be returned. Use strict ordering?
+
+ }
+
+ function testFindFiles() {
+
+// $this->view->chroot( "/data/{$this->userId}/files" );
+
+ $util = new Encryption\Util( $this->view, $this->userId );
+
+ $files = $util->findFiles( '/', 'encrypted' );
+
+ var_dump( $files );
+
+ # TODO: Add more tests here to check that if any of the dirs are
+ # then false will be returned. Use strict ordering?
+
+ }
+
+// /**
+// * @brief test decryption using legacy blowfish method
+// * @depends testLegacyEncryptLong
+// */
+// function testLegacyKeyRecryptKeyfileDecrypt( $recrypted ) {
+//
+// $decrypted = Encryption\Crypt::keyDecryptKeyfile( $recrypted['data'], $recrypted['key'], $this->genPrivateKey );
+//
+// $this->assertEquals( $this->dataLong, $decrypted );
+//
+// }
+
+// // Cannot use this test for now due to hidden dependencies in OC_FileCache
+// function testIsLegacyEncryptedContent() {
+//
+// $keyfileContent = OCA\Encryption\Crypt::symmetricEncryptFileContent( $this->legacyEncryptedData, 'hat' );
+//
+// $this->assertFalse( OCA\Encryption\Crypt::isLegacyEncryptedContent( $keyfileContent, '/files/admin/test.txt' ) );
+//
+// OC_FileCache::put( '/admin/files/legacy-encrypted-test.txt', $this->legacyEncryptedData );
+//
+// $this->assertTrue( OCA\Encryption\Crypt::isLegacyEncryptedContent( $this->legacyEncryptedData, '/files/admin/test.txt' ) );
+//
+// }
+
+// // Cannot use this test for now due to need for different root in OC_Filesystem_view class
+// function testGetLegacyKey() {
+//
+// $c = new \OCA\Encryption\Util( $view, false );
+//
+// $bool = $c->getLegacyKey( 'admin' );
+//
+// $this->assertTrue( $bool );
+//
+// $this->assertTrue( $c->legacyKey );
+//
+// $this->assertTrue( is_int( $c->legacyKey ) );
+//
+// $this->assertTrue( strlen( $c->legacyKey ) == 20 );
+//
+// }
+
+// // Cannot use this test for now due to need for different root in OC_Filesystem_view class
+// function testLegacyDecrypt() {
+//
+// $c = new OCA\Encryption\Util( $this->view, false );
+//
+// $bool = $c->getLegacyKey( 'admin' );
+//
+// $encrypted = $c->legacyEncrypt( $this->data, $c->legacyKey );
+//
+// $decrypted = $c->legacyDecrypt( $encrypted, $c->legacyKey );
+//
+// $this->assertEquals( $decrypted, $this->data );
+//
+// }
+
+} \ No newline at end of file
diff --git a/apps/files_encryption/tests/zeros b/apps/files_encryption/test/zeros
index ff982acf423..ff982acf423 100644
--- a/apps/files_encryption/tests/zeros
+++ b/apps/files_encryption/test/zeros
Binary files differ
diff --git a/apps/files_encryption/tests/encryption.php b/apps/files_encryption/tests/encryption.php
deleted file mode 100644
index 89397f6ef2c..00000000000
--- a/apps/files_encryption/tests/encryption.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-class Test_Encryption extends UnitTestCase {
- function testEncryption() {
- $key=uniqid();
- $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
- $source=file_get_contents($file); //nice large text file
- $encrypted=OC_Crypt::encrypt($source,$key);
- $decrypted=OC_Crypt::decrypt($encrypted,$key);
- $decrypted=rtrim($decrypted, "\0");
- $this->assertNotEqual($encrypted,$source);
- $this->assertEqual($decrypted,$source);
-
- $chunk=substr($source,0,8192);
- $encrypted=OC_Crypt::encrypt($chunk,$key);
- $this->assertEqual(strlen($chunk),strlen($encrypted));
- $decrypted=OC_Crypt::decrypt($encrypted,$key);
- $decrypted=rtrim($decrypted, "\0");
- $this->assertEqual($decrypted,$chunk);
-
- $encrypted=OC_Crypt::blockEncrypt($source,$key);
- $decrypted=OC_Crypt::blockDecrypt($encrypted,$key);
- $this->assertNotEqual($encrypted,$source);
- $this->assertEqual($decrypted,$source);
-
- $tmpFileEncrypted=OCP\Files::tmpFile();
- OC_Crypt::encryptfile($file,$tmpFileEncrypted,$key);
- $encrypted=file_get_contents($tmpFileEncrypted);
- $decrypted=OC_Crypt::blockDecrypt($encrypted,$key);
- $this->assertNotEqual($encrypted,$source);
- $this->assertEqual($decrypted,$source);
-
- $tmpFileDecrypted=OCP\Files::tmpFile();
- OC_Crypt::decryptfile($tmpFileEncrypted,$tmpFileDecrypted,$key);
- $decrypted=file_get_contents($tmpFileDecrypted);
- $this->assertEqual($decrypted,$source);
-
- $file=OC::$SERVERROOT.'/core/img/weather-clear.png';
- $source=file_get_contents($file); //binary file
- $encrypted=OC_Crypt::encrypt($source,$key);
- $decrypted=OC_Crypt::decrypt($encrypted,$key);
- $decrypted=rtrim($decrypted, "\0");
- $this->assertEqual($decrypted,$source);
-
- $encrypted=OC_Crypt::blockEncrypt($source,$key);
- $decrypted=OC_Crypt::blockDecrypt($encrypted,$key);
- $this->assertEqual($decrypted,$source);
-
- }
-
- function testBinary() {
- $key=uniqid();
-
- $file=__DIR__.'/binary';
- $source=file_get_contents($file); //binary file
- $encrypted=OC_Crypt::encrypt($source,$key);
- $decrypted=OC_Crypt::decrypt($encrypted,$key);
-
- $decrypted=rtrim($decrypted, "\0");
- $this->assertEqual($decrypted,$source);
-
- $encrypted=OC_Crypt::blockEncrypt($source,$key);
- $decrypted=OC_Crypt::blockDecrypt($encrypted,$key,strlen($source));
- $this->assertEqual($decrypted,$source);
- }
-}
diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php
deleted file mode 100644
index 042011a6c87..00000000000
--- a/apps/files_encryption/tests/proxy.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-class Test_CryptProxy extends UnitTestCase {
- private $oldConfig;
- private $oldKey;
-
- public function setUp() {
- $user=OC_User::getUser();
-
- $this->oldConfig=OCP\Config::getAppValue('files_encryption','enable_encryption','true');
- OCP\Config::setAppValue('files_encryption','enable_encryption','true');
- $this->oldKey=isset($_SESSION['enckey'])?$_SESSION['enckey']:null;
-
-
- //set testing key
- $_SESSION['enckey']=md5(time());
-
- //clear all proxies and hooks so we can do clean testing
- OC_FileProxy::clearProxies();
- OC_Hook::clear('OC_Filesystem');
-
- //enable only the encryption hook
- OC_FileProxy::register(new OC_FileProxy_Encryption());
-
- //set up temporary storage
- OC_Filesystem::clearMounts();
- OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/');
-
- OC_Filesystem::init('/'.$user.'/files');
-
- //set up the users home folder in the temp storage
- $rootView=new OC_FilesystemView('');
- $rootView->mkdir('/'.$user);
- $rootView->mkdir('/'.$user.'/files');
- }
-
- public function tearDown() {
- OCP\Config::setAppValue('files_encryption','enable_encryption',$this->oldConfig);
- if(!is_null($this->oldKey)) {
- $_SESSION['enckey']=$this->oldKey;
- }
- }
-
- public function testSimple() {
- $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
- $original=file_get_contents($file);
-
- OC_Filesystem::file_put_contents('/file',$original);
-
- OC_FileProxy::$enabled=false;
- $stored=OC_Filesystem::file_get_contents('/file');
- OC_FileProxy::$enabled=true;
-
- $fromFile=OC_Filesystem::file_get_contents('/file');
- $this->assertNotEqual($original,$stored);
- $this->assertEqual(strlen($original),strlen($fromFile));
- $this->assertEqual($original,$fromFile);
-
- }
-
- public function testView() {
- $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
- $original=file_get_contents($file);
-
- $rootView=new OC_FilesystemView('');
- $view=new OC_FilesystemView('/'.OC_User::getUser());
- $userDir='/'.OC_User::getUser().'/files';
-
- $rootView->file_put_contents($userDir.'/file',$original);
-
- OC_FileProxy::$enabled=false;
- $stored=$rootView->file_get_contents($userDir.'/file');
- OC_FileProxy::$enabled=true;
-
- $this->assertNotEqual($original,$stored);
- $fromFile=$rootView->file_get_contents($userDir.'/file');
- $this->assertEqual($original,$fromFile);
-
- $fromFile=$view->file_get_contents('files/file');
- $this->assertEqual($original,$fromFile);
- }
-
- public function testBinary() {
- $file=__DIR__.'/binary';
- $original=file_get_contents($file);
-
- OC_Filesystem::file_put_contents('/file',$original);
-
- OC_FileProxy::$enabled=false;
- $stored=OC_Filesystem::file_get_contents('/file');
- OC_FileProxy::$enabled=true;
-
- $fromFile=OC_Filesystem::file_get_contents('/file');
- $this->assertNotEqual($original,$stored);
- $this->assertEqual(strlen($original),strlen($fromFile));
- $this->assertEqual($original,$fromFile);
-
- $file=__DIR__.'/zeros';
- $original=file_get_contents($file);
-
- OC_Filesystem::file_put_contents('/file',$original);
-
- OC_FileProxy::$enabled=false;
- $stored=OC_Filesystem::file_get_contents('/file');
- OC_FileProxy::$enabled=true;
-
- $fromFile=OC_Filesystem::file_get_contents('/file');
- $this->assertNotEqual($original,$stored);
- $this->assertEqual(strlen($original),strlen($fromFile));
- }
-}
diff --git a/apps/files_encryption/tests/stream.php b/apps/files_encryption/tests/stream.php
deleted file mode 100644
index 39b13620782..00000000000
--- a/apps/files_encryption/tests/stream.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-class Test_CryptStream extends UnitTestCase {
- private $tmpFiles=array();
-
- function testStream() {
- $stream=$this->getStream('test1','w',strlen('foobar'));
- fwrite($stream,'foobar');
- fclose($stream);
-
- $stream=$this->getStream('test1','r',strlen('foobar'));
- $data=fread($stream,6);
- fclose($stream);
- $this->assertEqual('foobar',$data);
-
- $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
- $source=fopen($file,'r');
- $target=$this->getStream('test2','w',0);
- OCP\Files::streamCopy($source,$target);
- fclose($target);
- fclose($source);
-
- $stream=$this->getStream('test2','r',filesize($file));
- $data=stream_get_contents($stream);
- $original=file_get_contents($file);
- $this->assertEqual(strlen($original),strlen($data));
- $this->assertEqual($original,$data);
- }
-
- /**
- * get a cryptstream to a temporary file
- * @param string $id
- * @param string $mode
- * @param int size
- * @return resource
- */
- function getStream($id,$mode,$size) {
- if($id==='') {
- $id=uniqid();
- }
- if(!isset($this->tmpFiles[$id])) {
- $file=OCP\Files::tmpFile();
- $this->tmpFiles[$id]=$file;
- }else{
- $file=$this->tmpFiles[$id];
- }
- $stream=fopen($file,$mode);
- OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id,'stream'=>$stream,'size'=>$size);
- return fopen('crypt://streams/'.$id,$mode);
- }
-
- function testBinary() {
- $file=__DIR__.'/binary';
- $source=file_get_contents($file);
-
- $stream=$this->getStream('test','w',strlen($source));
- fwrite($stream,$source);
- fclose($stream);
-
- $stream=$this->getStream('test','r',strlen($source));
- $data=stream_get_contents($stream);
- fclose($stream);
- $this->assertEqual(strlen($data),strlen($source));
- $this->assertEqual($source,$data);
-
- $file=__DIR__.'/zeros';
- $source=file_get_contents($file);
-
- $stream=$this->getStream('test2','w',strlen($source));
- fwrite($stream,$source);
- fclose($stream);
-
- $stream=$this->getStream('test2','r',strlen($source));
- $data=stream_get_contents($stream);
- fclose($stream);
- $this->assertEqual(strlen($data),strlen($source));
- $this->assertEqual($source,$data);
- }
-}
diff --git a/apps/files_external/3rdparty/phpseclib/AUTHORS b/apps/files_external/3rdparty/phpseclib/AUTHORS
new file mode 100644
index 00000000000..7bae8ab94e1
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/AUTHORS
@@ -0,0 +1,3 @@
+phpseclib Lead Developer: TerraFrost (Jim Wigginton)
+
+phpseclib Developers: monnerat (Patrick Monnerat) \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/LICENSE b/apps/files_external/3rdparty/phpseclib/LICENSE
new file mode 100644
index 00000000000..6ecd9b9bec1
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/LICENSE
@@ -0,0 +1,21 @@
+Copyright 2007-2012 TerraFrost and other contributors
+http://phpseclib.sourceforge.net/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/README.md b/apps/files_external/3rdparty/phpseclib/README.md
new file mode 100644
index 00000000000..fbd58bd82b1
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/README.md
@@ -0,0 +1,16 @@
+# phpseclib - PHP Secure Communications Library
+
+[![Build Status](https://secure.travis-ci.org/phpseclib/phpseclib.png?branch=master)](http://travis-ci.org/phpseclib/phpseclib)
+
+MIT-licensed pure-PHP implementations of an arbitrary-precision integer
+arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael,
+AES, SSH-1, SSH-2, SFTP, and X.509
+
+* [Download (0.3.1)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.1.zip/download)
+* [Browse Git](https://github.com/phpseclib/phpseclib)
+* [Documentation](http://phpseclib.sourceforge.net/)
+* [Support](http://www.frostjedi.com/phpbb/viewforum.php?f=46)
+* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/latest/)
+
+<img src="http://phpseclib.sourceforge.net/pear-icon.png" alt="PEAR Channel" width="16" height="16">
+PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm)
diff --git a/apps/files_external/3rdparty/phpseclib/composer.json b/apps/files_external/3rdparty/phpseclib/composer.json
new file mode 100644
index 00000000000..11008cd81d1
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/composer.json
@@ -0,0 +1,48 @@
+{
+ "name": "phpseclib/phpseclib",
+ "type": "library",
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+ "keywords": [
+ "security",
+ "crypto",
+ "cryptography",
+ "encryption",
+ "signature",
+ "signing",
+ "rsa",
+ "aes",
+ "ssh",
+ "sftp",
+ "x509",
+ "x.509",
+ "asn1",
+ "asn.1",
+ "BigInteger"
+ ],
+ "homepage": "http://phpseclib.sourceforge.net",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Developer"
+ }
+ ],
+ "require": {
+ "php": ">=5.0.0"
+ },
+ "suggest": {
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.",
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
+ "pear-pear/PHP_Compat": "Install PHP_Compat to get phpseclib working on PHP >= 4.3.3."
+ },
+ "include-path": ["phpseclib/"],
+ "autoload": {
+ "psr-0": {
+ "Crypt": "phpseclib/",
+ "File": "phpseclib/",
+ "Math": "phpseclib/",
+ "Net": "phpseclib/"
+ }
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php
new file mode 100644
index 00000000000..bc05adf67a7
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php
@@ -0,0 +1,946 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of AES.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP versions 4 and 5
+ *
+ * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
+ * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
+ * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
+ * is called, again, at which point, it'll be recalculated.
+ *
+ * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
+ * make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
+ * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/AES.php');
+ *
+ * $aes = new Crypt_AES();
+ *
+ * $aes->setKey('abcdefghijklmnop');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $aes->decrypt($aes->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_AES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVIII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Crypt_Rijndael
+ */
+if (!class_exists('Crypt_Rijndael')) {
+ require_once 'Rijndael.php';
+}
+
+/**#@+
+ * @access public
+ * @see Crypt_AES::encrypt()
+ * @see Crypt_AES::decrypt()
+ */
+/**
+ * Encrypt / decrypt using the Counter mode.
+ *
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
+ */
+define('CRYPT_AES_MODE_CTR', -1);
+/**
+ * Encrypt / decrypt using the Electronic Code Book mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
+ */
+define('CRYPT_AES_MODE_ECB', 1);
+/**
+ * Encrypt / decrypt using the Code Book Chaining mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+ */
+define('CRYPT_AES_MODE_CBC', 2);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
+ */
+define('CRYPT_AES_MODE_CFB', 3);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
+ */
+define('CRYPT_AES_MODE_OFB', 4);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_AES::Crypt_AES()
+ */
+/**
+ * Toggles the internal implementation
+ */
+define('CRYPT_AES_MODE_INTERNAL', 1);
+/**
+ * Toggles the mcrypt implementation
+ */
+define('CRYPT_AES_MODE_MCRYPT', 2);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of AES.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_AES
+ */
+class Crypt_AES extends Crypt_Rijndael {
+ /**
+ * mcrypt resource for encryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_AES::encrypt()
+ * @var String
+ * @access private
+ */
+ var $enmcrypt;
+
+ /**
+ * mcrypt resource for decryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_AES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $demcrypt;
+
+ /**
+ * mcrypt resource for CFB mode
+ *
+ * @see Crypt_AES::encrypt()
+ * @see Crypt_AES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $ecb;
+
+ /**
+ * The SubByte S-Box
+ *
+ * @see Crypt_AES::_encryptBlock()
+ * @var Array
+ * @access intern
+ */
+ var $sbox = array(
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
+ );
+
+ /**
+ * The inverse SubByte S-Box
+ *
+ * @see Crypt_AES::_decryptBlock()
+ * @var Array
+ * @access intern
+ */
+ var $isbox = array(
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
+ );
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
+ * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
+ *
+ * @param optional Integer $mode
+ * @return Crypt_AES
+ * @access public
+ */
+ function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
+ {
+ if ( !defined('CRYPT_AES_MODE') ) {
+ switch (true) {
+ case extension_loaded('mcrypt') && in_array('rijndael-128', mcrypt_list_algorithms()):
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
+ break;
+ default:
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
+ }
+ }
+
+ switch ( CRYPT_AES_MODE ) {
+ case CRYPT_AES_MODE_MCRYPT:
+ switch ($mode) {
+ case CRYPT_AES_MODE_ECB:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_ECB;
+ break;
+ case CRYPT_AES_MODE_CTR:
+ // ctr doesn't have a constant associated with it even though it appears to be fairly widely
+ // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
+ // include a compatibility layer. the layer has been implemented but, for now, is commented out.
+ $this->mode = 'ctr';
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
+ break;
+ case CRYPT_AES_MODE_CFB:
+ $this->mode = 'ncfb';
+ break;
+ case CRYPT_AES_MODE_OFB:
+ $this->mode = MCRYPT_MODE_NOFB;
+ break;
+ case CRYPT_AES_MODE_CBC:
+ default:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_CBC;
+ }
+
+ break;
+ default:
+ switch ($mode) {
+ case CRYPT_AES_MODE_ECB:
+ $this->paddable = true;
+ $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
+ break;
+ case CRYPT_AES_MODE_CTR:
+ $this->mode = CRYPT_RIJNDAEL_MODE_CTR;
+ break;
+ case CRYPT_AES_MODE_CFB:
+ $this->mode = CRYPT_RIJNDAEL_MODE_CFB;
+ break;
+ case CRYPT_AES_MODE_OFB:
+ $this->mode = CRYPT_RIJNDAEL_MODE_OFB;
+ break;
+ case CRYPT_AES_MODE_CBC:
+ default:
+ $this->paddable = true;
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
+ }
+ }
+
+ if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
+ parent::Crypt_Rijndael($this->mode);
+ }
+ }
+
+ /**
+ * Extended Crypt_Rijndael::_setup()
+ *
+ * Optimizing the key schedule arrays ($w, $dw) for _encryptBlock() and _decryptBlock() after Crypt_Rijndael::_setup()
+ *
+ * @see Crypt_Rijndael::_setup()
+ * @access private
+ */
+ function _setup()
+ {
+ if (!$this->changed) {
+ return;
+ }
+
+ $this->w = $this->dw = array();
+ parent::_setup();
+
+ $this->dw = array_reverse($this->dw);
+ $w = array_pop($this->w);
+ $dw = array_pop($this->dw);
+ foreach ($this->w as $r => $wr) {
+ foreach ($wr as $c => $wc) {
+ $w[] = $wc;
+ $dw[] = $this->dw[$r][$c];
+ }
+ }
+ $this->w = $w;
+ $this->dw = $dw;
+ }
+
+ /**
+ * Dummy function
+ *
+ * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
+ *
+ * @access public
+ * @param Integer $length
+ */
+ function setBlockLength($length)
+ {
+ return;
+ }
+
+ /**
+ * Sets the initialization vector. (optional)
+ *
+ * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
+ * to be all zero's.
+ *
+ * @access public
+ * @param String $iv
+ */
+ function setIV($iv)
+ {
+ parent::setIV($iv);
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
+ $this->changed = true;
+ }
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
+ * URL:
+ *
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
+ *
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
+ * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
+ * length.
+ *
+ * @see Crypt_AES::decrypt()
+ * @access public
+ * @param String $plaintext
+ */
+ function encrypt($plaintext)
+ {
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
+ $this->_mcryptSetup();
+
+ // re: http://phpseclib.sourceforge.net/cfb-demo.phps
+ // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's
+ // rewritten CFB implementation the above outputs the same thing twice.
+ if ($this->mode == 'ncfb' && $this->continuousBuffer) {
+ $iv = &$this->encryptIV;
+ $pos = &$this->enbuffer['pos'];
+ $len = strlen($plaintext);
+ $ciphertext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 16 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
+ $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
+ $this->enbuffer['enmcrypt_init'] = true;
+ }
+ if ($len >= 16) {
+ if ($this->enbuffer['enmcrypt_init'] === false || $len > 280) {
+ if ($this->enbuffer['enmcrypt_init'] === true) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
+ $this->enbuffer['enmcrypt_init'] = false;
+ }
+ $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16));
+ $iv = substr($ciphertext, -16);
+ $len%= 16;
+ } else {
+ while ($len >= 16) {
+ $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 16);
+ $ciphertext.= $iv;
+ $len-= 16;
+ $i+= 16;
+ }
+ }
+ }
+
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $block = $iv ^ substr($plaintext, -$len);
+ $iv = substr_replace($iv, $block, 0, $len);
+ $ciphertext.= $block;
+ $pos = $len;
+ }
+
+ return $ciphertext;
+ }
+
+ if ($this->paddable) {
+ $plaintext = $this->_pad($plaintext);
+ }
+
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
+ }
+
+ return $ciphertext;
+ }
+
+ return parent::encrypt($plaintext);
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
+ *
+ * @see Crypt_AES::encrypt()
+ * @access public
+ * @param String $ciphertext
+ */
+ function decrypt($ciphertext)
+ {
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
+ $this->_mcryptSetup();
+
+ if ($this->mode == 'ncfb' && $this->continuousBuffer) {
+ $iv = &$this->decryptIV;
+ $pos = &$this->debuffer['pos'];
+ $len = strlen($ciphertext);
+ $plaintext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 16 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
+ $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
+ $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
+ }
+ if ($len >= 16) {
+ $cb = substr($ciphertext, $i, $len - $len % 16);
+ $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
+ $iv = substr($cb, -16);
+ $len%= 16;
+ }
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $plaintext.= $iv ^ substr($ciphertext, -$len);
+ $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
+ $pos = $len;
+ }
+
+ return $plaintext;
+ }
+
+ if ($this->paddable) {
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
+ }
+
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ return parent::decrypt($ciphertext);
+ }
+
+ /**
+ * Setup mcrypt
+ *
+ * Validates all the variables.
+ *
+ * @access private
+ */
+ function _mcryptSetup()
+ {
+ if (!$this->changed) {
+ return;
+ }
+
+ if (!$this->explicit_key_length) {
+ // this just copied from Crypt_Rijndael::_setup()
+ $length = strlen($this->key) >> 2;
+ if ($length > 8) {
+ $length = 8;
+ } else if ($length < 4) {
+ $length = 4;
+ }
+ $this->Nk = $length;
+ $this->key_size = $length << 2;
+ }
+
+ switch ($this->Nk) {
+ case 4: // 128
+ $this->key_size = 16;
+ break;
+ case 5: // 160
+ case 6: // 192
+ $this->key_size = 24;
+ break;
+ case 7: // 224
+ case 8: // 256
+ $this->key_size = 32;
+ }
+
+ $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
+
+ if (!isset($this->enmcrypt)) {
+ $mode = $this->mode;
+ //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
+
+ $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
+
+ if ($mode == 'ncfb') {
+ $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
+ }
+
+ } // else should mcrypt_generic_deinit be called?
+
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
+
+ if ($this->mode == 'ncfb') {
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+ }
+
+ $this->changed = false;
+ }
+
+ /**
+ * Encrypts a block
+ *
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
+ *
+ * @see Crypt_Rijndael::_encryptBlock()
+ * @access private
+ * @param String $in
+ * @return String
+ */
+ function _encryptBlock($in)
+ {
+ $state = unpack('N*', $in);
+
+ $sbox = $this->sbox;
+ $w = $this->w;
+ $t0 = $this->t0;
+ $t1 = $this->t1;
+ $t2 = $this->t2;
+ $t3 = $this->t3;
+
+ // addRoundKey
+ $s0 = $state[1] ^ $w[4];
+ $s1 = $state[2] ^ $w[5];
+ $s2 = $state[3] ^ $w[6];
+ $s3 = $state[4] ^ $w[7];
+
+ // shiftRows + subWord + mixColumns + addRoundKey
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[8];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[9];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[10];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[11];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[12];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[13];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[14];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[15];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[16];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[17];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[18];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[19];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[20];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[21];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[22];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[23];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[24];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[25];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[26];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[27];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[28];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[29];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[30];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[31];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[32];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[33];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[34];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[35];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[36];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[37];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[38];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[39];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[40];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[41];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[42];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[43];
+
+ switch ($this->Nr) {
+ case 10:
+ break;
+
+ case 14:
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[56];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[57];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[58];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[59];
+ break;
+
+ case 12:
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51];
+ break;
+
+ case 13:
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46];
+ $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47];
+
+ $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48];
+ $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49];
+ $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50];
+ $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51];
+
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54];
+ $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55];
+ // Note: Here we skip $s3 but using $e3
+
+ $e0 = $s0;
+ $e1 = $s1;
+ $e2 = $s2;
+ // $e3 = $s3;
+ break;
+
+ default: // 11
+ $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44];
+ $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45];
+ $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46];
+ $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47];
+ // Note: Here we skip $s3 but using $e3
+
+ $e0 = $s0;
+ $e1 = $s1;
+ $e2 = $s2;
+ // $e3 = $s3;
+ }
+
+ // subWord
+ $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24);
+ $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24);
+ $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24);
+ $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24);
+
+ // shiftRows + addRoundKey
+ return pack('N*',
+ ($e0 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $w[0],
+ ($e1 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $w[1],
+ ($e2 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $w[2],
+ ($e3 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $w[3]
+ );
+ }
+
+ /**
+ * Decrypts a block
+ *
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
+ *
+ * @see Crypt_Rijndael::_decryptBlock()
+ * @access private
+ * @param String $in
+ * @return String
+ */
+ function _decryptBlock($in)
+ {
+ $state = unpack('N*', $in);
+
+ $sbox = $this->isbox;
+ $dw = $this->dw;
+ $dt0 = $this->dt0;
+ $dt1 = $this->dt1;
+ $dt2 = $this->dt2;
+ $dt3 = $this->dt3;
+
+ // addRoundKey
+ $s0 = $state[1] ^ $dw[4];
+ $s1 = $state[2] ^ $dw[5];
+ $s2 = $state[3] ^ $dw[6];
+ $s3 = $state[4] ^ $dw[7];
+
+ // invShiftRows + invSubBytes + invMixColumns + addRoundKey
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[8];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[9];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[10];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[11];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[12];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[13];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[14];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[15];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[16];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[17];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[18];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[19];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[20];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[21];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[22];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[23];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[24];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[25];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[26];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[27];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[28];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[29];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[30];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[31];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[32];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[33];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[34];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[35];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[36];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[37];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[38];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[39];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[40];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[41];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[42];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[43];
+
+ switch ($this->Nr) {
+ case 10:
+ break;
+
+ case 14:
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[56];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[57];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[58];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[59];
+ break;
+
+ case 12:
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51];
+ break;
+
+ case 13:
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46];
+ $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47];
+
+ $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48];
+ $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49];
+ $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50];
+ $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51];
+
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54];
+ $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55];
+ // Note: Here we skip $s3 but using $e3
+
+ $e0 = $s0;
+ $e1 = $s1;
+ $e2 = $s2;
+ // $e3 = $s3;
+ break;
+
+ default: // 11
+ $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44];
+ $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45];
+ $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46];
+ $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47];
+ // Note: Here we skip $s3 but using $e3
+
+ $e0 = $s0;
+ $e1 = $s1;
+ $e2 = $s2;
+ // $e3 = $s3;
+ }
+
+ // invSubWord
+ $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24);
+ $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24);
+ $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24);
+ $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24);
+
+ // invShiftRows + addRoundKey
+ return pack('N*',
+ ($e0 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $dw[0],
+ ($e1 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $dw[1],
+ ($e2 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $dw[2],
+ ($e3 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $dw[3]
+ );
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_Rijndael::disableContinuousBuffer()
+ * @access public
+ */
+ function enableContinuousBuffer()
+ {
+ parent::enableContinuousBuffer();
+
+ if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {
+ $this->enbuffer['enmcrypt_init'] = true;
+ $this->debuffer['demcrypt_init'] = true;
+ }
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_Rijndael::enableContinuousBuffer()
+ * @access public
+ */
+ function disableContinuousBuffer()
+ {
+ parent::disableContinuousBuffer();
+
+ if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
+ }
+ }
+}
+
+// vim: ts=4:sw=4:et:
+// vim6: fdl=1:
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php
new file mode 100644
index 00000000000..1197a50ab72
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php
@@ -0,0 +1,1334 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of DES.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP versions 4 and 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
+ * - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
+ * - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/DES.php');
+ *
+ * $des = new Crypt_DES();
+ *
+ * $des->setKey('abcdefgh');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $des->decrypt($des->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_DES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**#@+
+ * @access private
+ * @see Crypt_DES::_prepareKey()
+ * @see Crypt_DES::_processBlock()
+ */
+/**
+ * Contains array_reverse($keys[CRYPT_DES_DECRYPT])
+ */
+define('CRYPT_DES_ENCRYPT', 0);
+/**
+ * Contains array_reverse($keys[CRYPT_DES_ENCRYPT])
+ */
+define('CRYPT_DES_DECRYPT', 1);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Crypt_DES::encrypt()
+ * @see Crypt_DES::decrypt()
+ */
+/**
+ * Encrypt / decrypt using the Counter mode.
+ *
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
+ */
+define('CRYPT_DES_MODE_CTR', -1);
+/**
+ * Encrypt / decrypt using the Electronic Code Book mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
+ */
+define('CRYPT_DES_MODE_ECB', 1);
+/**
+ * Encrypt / decrypt using the Code Book Chaining mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+ */
+define('CRYPT_DES_MODE_CBC', 2);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
+ */
+define('CRYPT_DES_MODE_CFB', 3);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
+ */
+define('CRYPT_DES_MODE_OFB', 4);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_DES::Crypt_DES()
+ */
+/**
+ * Toggles the internal implementation
+ */
+define('CRYPT_DES_MODE_INTERNAL', 1);
+/**
+ * Toggles the mcrypt implementation
+ */
+define('CRYPT_DES_MODE_MCRYPT', 2);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of DES.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_DES
+ */
+class Crypt_DES {
+ /**
+ * The Key Schedule
+ *
+ * @see Crypt_DES::setKey()
+ * @var Array
+ * @access private
+ */
+ var $keys = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * The Encryption Mode
+ *
+ * @see Crypt_DES::Crypt_DES()
+ * @var Integer
+ * @access private
+ */
+ var $mode;
+
+ /**
+ * Continuous Buffer status
+ *
+ * @see Crypt_DES::enableContinuousBuffer()
+ * @var Boolean
+ * @access private
+ */
+ var $continuousBuffer = false;
+
+ /**
+ * Padding status
+ *
+ * @see Crypt_DES::enablePadding()
+ * @var Boolean
+ * @access private
+ */
+ var $padding = true;
+
+ /**
+ * The Initialization Vector
+ *
+ * @see Crypt_DES::setIV()
+ * @var String
+ * @access private
+ */
+ var $iv = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_DES::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_DES::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * mcrypt resource for encryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_DES::encrypt()
+ * @var String
+ * @access private
+ */
+ var $enmcrypt;
+
+ /**
+ * mcrypt resource for decryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_DES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $demcrypt;
+
+ /**
+ * Does the enmcrypt resource need to be (re)initialized?
+ *
+ * @see Crypt_DES::setKey()
+ * @see Crypt_DES::setIV()
+ * @var Boolean
+ * @access private
+ */
+ var $enchanged = true;
+
+ /**
+ * Does the demcrypt resource need to be (re)initialized?
+ *
+ * @see Crypt_DES::setKey()
+ * @see Crypt_DES::setIV()
+ * @var Boolean
+ * @access private
+ */
+ var $dechanged = true;
+
+ /**
+ * Is the mode one that is paddable?
+ *
+ * @see Crypt_DES::Crypt_DES()
+ * @var Boolean
+ * @access private
+ */
+ var $paddable = false;
+
+ /**
+ * Encryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_DES::encrypt()
+ * @var Array
+ * @access private
+ */
+ var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
+
+ /**
+ * Decryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_DES::decrypt()
+ * @var Array
+ * @access private
+ */
+ var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
+
+ /**
+ * mcrypt resource for CFB mode
+ *
+ * @see Crypt_DES::encrypt()
+ * @see Crypt_DES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $ecb;
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
+ *
+ * @param optional Integer $mode
+ * @return Crypt_DES
+ * @access public
+ */
+ function Crypt_DES($mode = CRYPT_DES_MODE_CBC)
+ {
+ if ( !defined('CRYPT_DES_MODE') ) {
+ switch (true) {
+ case extension_loaded('mcrypt') && in_array('des', mcrypt_list_algorithms()):
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
+ break;
+ default:
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
+ }
+ }
+
+ switch ( CRYPT_DES_MODE ) {
+ case CRYPT_DES_MODE_MCRYPT:
+ switch ($mode) {
+ case CRYPT_DES_MODE_ECB:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_ECB;
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $this->mode = 'ctr';
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
+ break;
+ case CRYPT_DES_MODE_CFB:
+ $this->mode = 'ncfb';
+ $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, '');
+ break;
+ case CRYPT_DES_MODE_OFB:
+ $this->mode = MCRYPT_MODE_NOFB;
+ break;
+ case CRYPT_DES_MODE_CBC:
+ default:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_CBC;
+ }
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
+ $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
+
+ break;
+ default:
+ switch ($mode) {
+ case CRYPT_DES_MODE_ECB:
+ case CRYPT_DES_MODE_CBC:
+ $this->paddable = true;
+ $this->mode = $mode;
+ break;
+ case CRYPT_DES_MODE_CTR:
+ case CRYPT_DES_MODE_CFB:
+ case CRYPT_DES_MODE_OFB:
+ $this->mode = $mode;
+ break;
+ default:
+ $this->paddable = true;
+ $this->mode = CRYPT_DES_MODE_CBC;
+ }
+ }
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
+ * only use the first eight, if $key has more then eight characters in it, and pad $key with the
+ * null byte if it is less then eight characters long.
+ *
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
+ *
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
+ *
+ * @access public
+ * @param String $key
+ */
+ function setKey($key)
+ {
+ $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key);
+ $this->enchanged = true;
+ $this->dechanged = true;
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
+ * $hash, $salt, $count
+ *
+ * @param String $password
+ * @param optional String $method
+ * @access public
+ */
+ function setPassword($password, $method = 'pbkdf2')
+ {
+ $key = '';
+
+ switch ($method) {
+ default: // 'pbkdf2'
+ list(, , $hash, $salt, $count) = func_get_args();
+ if (!isset($hash)) {
+ $hash = 'sha1';
+ }
+ // WPA and WPA2 use the SSID as the salt
+ if (!isset($salt)) {
+ $salt = 'phpseclib/salt';
+ }
+ // RFC2898#section-4.2 uses 1,000 iterations by default
+ // WPA and WPA2 use 4,096.
+ if (!isset($count)) {
+ $count = 1000;
+ }
+
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+
+ $i = 1;
+ while (strlen($key) < 8) { // $dkLen == 8
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
+ $hmac = new Crypt_Hash();
+ $hmac->setHash($hash);
+ $hmac->setKey($password);
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
+ for ($j = 2; $j <= $count; $j++) {
+ $u = $hmac->hash($u);
+ $f^= $u;
+ }
+ $key.= $f;
+ }
+ }
+
+ $this->setKey($key);
+ }
+
+ /**
+ * Sets the initialization vector. (optional)
+ *
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
+ * to be all zero's.
+ *
+ * @access public
+ * @param String $iv
+ */
+ function setIV($iv)
+ {
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
+ $this->enchanged = true;
+ $this->dechanged = true;
+ }
+
+ /**
+ * Generate CTR XOR encryption key
+ *
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
+ * plaintext / ciphertext in CTR mode.
+ *
+ * @see Crypt_DES::decrypt()
+ * @see Crypt_DES::encrypt()
+ * @access public
+ * @param String $iv
+ */
+ function _generate_xor(&$iv)
+ {
+ $xor = $iv;
+ for ($j = 4; $j <= 8; $j+=4) {
+ $temp = substr($iv, -$j, 4);
+ switch ($temp) {
+ case "\xFF\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
+ break;
+ case "\x7F\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
+ break 2;
+ default:
+ extract(unpack('Ncount', $temp));
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
+ break 2;
+ }
+ }
+
+ return $xor;
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
+ * URL:
+ *
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
+ *
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
+ * length.
+ *
+ * @see Crypt_DES::decrypt()
+ * @access public
+ * @param String $plaintext
+ */
+ function encrypt($plaintext)
+ {
+ if ($this->paddable) {
+ $plaintext = $this->_pad($plaintext);
+ }
+
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
+ if ($this->enchanged) {
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
+ if ($this->mode == 'ncfb') {
+ mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
+ }
+ $this->enchanged = false;
+ }
+
+ if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
+ } else {
+ $iv = &$this->encryptIV;
+ $pos = &$this->enbuffer['pos'];
+ $len = strlen($plaintext);
+ $ciphertext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
+ $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
+ $this->enbuffer['enmcrypt_init'] = true;
+ }
+ if ($len >= 8) {
+ if ($this->enbuffer['enmcrypt_init'] === false || $len > 600) {
+ if ($this->enbuffer['enmcrypt_init'] === true) {
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $iv);
+ $this->enbuffer['enmcrypt_init'] = false;
+ }
+ $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));
+ $iv = substr($ciphertext, -8);
+ $len%= 8;
+ } else {
+ while ($len >= 8) {
+ $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);
+ $ciphertext.= $iv;
+ $len-= 8;
+ $i+= 8;
+ }
+ }
+ }
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $block = $iv ^ substr($plaintext, -$len);
+ $iv = substr_replace($iv, $block, 0, $len);
+ $ciphertext.= $block;
+ $pos = $len;
+ }
+ return $ciphertext;
+ }
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
+ }
+
+ return $ciphertext;
+ }
+
+ if (!is_array($this->keys)) {
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
+ }
+
+ $buffer = &$this->enbuffer;
+ $continuousBuffer = $this->continuousBuffer;
+ $ciphertext = '';
+ switch ($this->mode) {
+ case CRYPT_DES_MODE_ECB:
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT);
+ }
+ break;
+ case CRYPT_DES_MODE_CBC:
+ $xor = $this->encryptIV;
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT);
+ $xor = $block;
+ $ciphertext.= $block;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ }
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['encrypted'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $buffer['encrypted'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT);
+ $key = $this->_string_shift($buffer['encrypted'], 8);
+ $ciphertext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT);
+ $ciphertext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) & 7) {
+ $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
+ }
+ }
+ break;
+ case CRYPT_DES_MODE_CFB:
+ if ($this->continuousBuffer) {
+ $iv = &$this->encryptIV;
+ $pos = &$buffer['pos'];
+ } else {
+ $iv = $this->encryptIV;
+ $pos = 0;
+ }
+ $len = strlen($plaintext);
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
+ $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
+ }
+ while ($len >= 8) {
+ $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT) ^ substr($plaintext, $i, 8);
+ $ciphertext.= $iv;
+ $len-= 8;
+ $i+= 8;
+ }
+ if ($len) {
+ $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT);
+ $block = $iv ^ substr($plaintext, $i);
+ $iv = substr_replace($iv, $block, 0, $len);
+ $ciphertext.= $block;
+ $pos = $len;
+ }
+ return $ciphertext;
+ case CRYPT_DES_MODE_OFB:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], 8);
+ $ciphertext.= substr($plaintext, $i, 8) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $ciphertext.= substr($plaintext, $i, 8) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) & 7) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $ciphertext;
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is.
+ *
+ * @see Crypt_DES::encrypt()
+ * @access public
+ * @param String $ciphertext
+ */
+ function decrypt($ciphertext)
+ {
+ if ($this->paddable) {
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
+ }
+
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
+ if ($this->dechanged) {
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
+ if ($this->mode == 'ncfb') {
+ mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
+ }
+ $this->dechanged = false;
+ }
+
+ if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
+ } else {
+ $iv = &$this->decryptIV;
+ $pos = &$this->debuffer['pos'];
+ $len = strlen($ciphertext);
+ $plaintext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
+ $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
+ }
+ if ($len >= 8) {
+ $cb = substr($ciphertext, $i, $len - $len % 8);
+ $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
+ $iv = substr($cb, -8);
+ $len%= 8;
+ }
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $plaintext.= $iv ^ substr($ciphertext, -$len);
+ $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
+ $pos = $len;
+ }
+ return $plaintext;
+ }
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ if (!is_array($this->keys)) {
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
+ }
+
+ $buffer = &$this->debuffer;
+ $continuousBuffer = $this->continuousBuffer;
+ $plaintext = '';
+ switch ($this->mode) {
+ case CRYPT_DES_MODE_ECB:
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT);
+ }
+ break;
+ case CRYPT_DES_MODE_CBC:
+ $xor = $this->decryptIV;
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor;
+ $xor = $block;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ }
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['ciphertext'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $buffer['ciphertext'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT);
+ $key = $this->_string_shift($buffer['ciphertext'], 8);
+ $plaintext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT);
+ $plaintext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($ciphertext) % 8) {
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
+ }
+ }
+ break;
+ case CRYPT_DES_MODE_CFB:
+ if ($this->continuousBuffer) {
+ $iv = &$this->decryptIV;
+ $pos = &$buffer['pos'];
+ } else {
+ $iv = $this->decryptIV;
+ $pos = 0;
+ }
+ $len = strlen($ciphertext);
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
+ $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
+ }
+ while ($len >= 8) {
+ $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT);
+ $cb = substr($ciphertext, $i, 8);
+ $plaintext.= $iv ^ $cb;
+ $iv = $cb;
+ $len-= 8;
+ $i+= 8;
+ }
+ if ($len) {
+ $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT);
+ $plaintext.= $iv ^ substr($ciphertext, $i);
+ $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
+ $pos = $len;
+ }
+ return $plaintext;
+ case CRYPT_DES_MODE_OFB:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], 8);
+ $plaintext.= substr($ciphertext, $i, 8) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $plaintext.= substr($ciphertext, $i, 8) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($ciphertext) % 8) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
+ * will yield different outputs:
+ *
+ * <code>
+ * echo $des->encrypt(substr($plaintext, 0, 8));
+ * echo $des->encrypt(substr($plaintext, 8, 8));
+ * </code>
+ * <code>
+ * echo $des->encrypt($plaintext);
+ * </code>
+ *
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
+ * another, as demonstrated with the following:
+ *
+ * <code>
+ * $des->encrypt(substr($plaintext, 0, 8));
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ * <code>
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ *
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
+ *
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
+ * however, they are also less intuitive and more likely to cause you problems.
+ *
+ * @see Crypt_DES::disableContinuousBuffer()
+ * @access public
+ */
+ function enableContinuousBuffer()
+ {
+ $this->continuousBuffer = true;
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_DES::enableContinuousBuffer()
+ * @access public
+ */
+ function disableContinuousBuffer()
+ {
+ $this->continuousBuffer = false;
+ $this->encryptIV = $this->iv;
+ $this->decryptIV = $this->iv;
+ $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
+ $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
+
+ if (CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT) {
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->iv);
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->iv);
+ }
+ }
+
+ /**
+ * Pad "packets".
+ *
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
+ *
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
+ * transmitted separately)
+ *
+ * @see Crypt_DES::disablePadding()
+ * @access public
+ */
+ function enablePadding()
+ {
+ $this->padding = true;
+ }
+
+ /**
+ * Do not pad packets.
+ *
+ * @see Crypt_DES::enablePadding()
+ * @access public
+ */
+ function disablePadding()
+ {
+ $this->padding = false;
+ }
+
+ /**
+ * Pads a string
+ *
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
+ *
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
+ * and padding will, hence forth, be enabled.
+ *
+ * @see Crypt_DES::_unpad()
+ * @access private
+ */
+ function _pad($text)
+ {
+ $length = strlen($text);
+
+ if (!$this->padding) {
+ if (($length & 7) == 0) {
+ return $text;
+ } else {
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)");
+ $this->padding = true;
+ }
+ }
+
+ $pad = 8 - ($length & 7);
+ return str_pad($text, $length + $pad, chr($pad));
+ }
+
+ /**
+ * Unpads a string
+ *
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
+ * and false will be returned.
+ *
+ * @see Crypt_DES::_pad()
+ * @access private
+ */
+ function _unpad($text)
+ {
+ if (!$this->padding) {
+ return $text;
+ }
+
+ $length = ord($text[strlen($text) - 1]);
+
+ if (!$length || $length > 8) {
+ return false;
+ }
+
+ return substr($text, 0, -$length);
+ }
+
+ /**
+ * Encrypts or decrypts a 64-bit block
+ *
+ * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See
+ * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
+ * idea of what this function does.
+ *
+ * @access private
+ * @param String $block
+ * @param Integer $mode
+ * @return String
+ */
+ function _processBlock($block, $mode)
+ {
+ // s-boxes. in the official DES docs, they're described as being matrices that
+ // one accesses by using the first and last bits to determine the row and the
+ // middle four bits to determine the column. in this implementation, they've
+ // been converted to vectors
+ static $sbox = array(
+ array(
+ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
+ 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
+ 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
+ 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
+ ),
+ array(
+ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
+ 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
+ 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
+ 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
+ ),
+ array(
+ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
+ 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
+ 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
+ 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
+ ),
+ array(
+ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
+ 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
+ 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
+ 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
+ ),
+ array(
+ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
+ 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
+ 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
+ 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
+ ),
+ array(
+ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
+ 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
+ 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
+ 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
+ ),
+ array(
+ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
+ 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
+ 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
+ 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
+ ),
+ array(
+ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
+ 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
+ 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
+ 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
+ )
+ );
+
+ $keys = $this->keys;
+
+ $temp = unpack('Na/Nb', $block);
+ $block = array($temp['a'], $temp['b']);
+
+ // because php does arithmetic right shifts, if the most significant bits are set, right
+ // shifting those into the correct position will add 1's - not 0's. this will intefere
+ // with the | operation unless a second & is done. so we isolate these bits and left shift
+ // them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added
+ // for any other shifts.
+ $msb = array(
+ ($block[0] >> 31) & 1,
+ ($block[1] >> 31) & 1
+ );
+ $block[0] &= 0x7FFFFFFF;
+ $block[1] &= 0x7FFFFFFF;
+
+ // we isolate the appropriate bit in the appropriate integer and shift as appropriate. in
+ // some cases, there are going to be multiple bits in the same integer that need to be shifted
+ // in the same way. we combine those into one shift operation.
+ $block = array(
+ (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) |
+ (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) |
+ (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) |
+ (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) |
+ (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) |
+ (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) |
+ (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) |
+ (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) |
+ (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) |
+ (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) |
+ (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) |
+ (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) |
+ (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) |
+ (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24)
+ ,
+ (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) |
+ (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) |
+ (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) |
+ (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) |
+ ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) |
+ (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) |
+ (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) |
+ (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) |
+ (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) |
+ (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) |
+ (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) |
+ (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) |
+ (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) |
+ (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) |
+ ($msb[1] << 28) | ($msb[0] << 24)
+ );
+
+ for ($i = 0; $i < 16; $i++) {
+ // start of "the Feistel (F) function" - see the following URL:
+ // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
+ $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
+ | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
+ | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
+ | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
+ | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
+ | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
+ | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
+ | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
+
+ $msb = ($temp >> 31) & 1;
+ $temp &= 0x7FFFFFFF;
+ $newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5)
+ | (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10)
+ | (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6)
+ | (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9)
+ | (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27)
+ | (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8)
+ | (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16)
+ | (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15)
+ | (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20)
+ | (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3)
+ | (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7)
+ | (($temp & 0x00200000) >> 19) | ($msb << 23);
+ // end of "the Feistel (F) function" - $newBlock is F's output
+
+ $temp = $block[1];
+ $block[1] = $block[0] ^ $newBlock;
+ $block[0] = $temp;
+ }
+
+ $msb = array(
+ ($block[0] >> 31) & 1,
+ ($block[1] >> 31) & 1
+ );
+ $block[0] &= 0x7FFFFFFF;
+ $block[1] &= 0x7FFFFFFF;
+
+ $block = array(
+ (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) |
+ (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) |
+ (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) |
+ (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) |
+ (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) |
+ (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) |
+ (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) |
+ (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) |
+ (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) |
+ (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) |
+ (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) |
+ (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) |
+ (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) |
+ (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9)
+ ,
+ (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) |
+ (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) |
+ (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) |
+ (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) |
+ (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) |
+ ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) |
+ (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) |
+ (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) |
+ (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) |
+ (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) |
+ (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) |
+ (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) |
+ (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) |
+ ($msb[0] << 7) | ($msb[1] << 6)
+ );
+
+ return pack('NN', $block[0], $block[1]);
+ }
+
+ /**
+ * Creates the key schedule.
+ *
+ * @access private
+ * @param String $key
+ * @return Array
+ */
+ function _prepareKey($key)
+ {
+ static $shifts = array( // number of key bits shifted per round
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+ );
+
+ // pad the key and remove extra characters as appropriate.
+ $key = str_pad(substr($key, 0, 8), 8, chr(0));
+
+ $temp = unpack('Na/Nb', $key);
+ $key = array($temp['a'], $temp['b']);
+ $msb = array(
+ ($key[0] >> 31) & 1,
+ ($key[1] >> 31) & 1
+ );
+ $key[0] &= 0x7FFFFFFF;
+ $key[1] &= 0x7FFFFFFF;
+
+ $key = array(
+ (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) |
+ (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) |
+ (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) |
+ (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) |
+ (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) |
+ (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) |
+ (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) |
+ (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28)
+ ,
+ (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) |
+ (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) |
+ (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) |
+ (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) |
+ (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) |
+ (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) |
+ (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) |
+ (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) |
+ (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) |
+ (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) |
+ (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) |
+ (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) |
+ (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) |
+ ($msb[1] << 24) | ($msb[0] << 20)
+ );
+
+ $keys = array();
+ for ($i = 0; $i < 16; $i++) {
+ $key[0] <<= $shifts[$i];
+ $temp = ($key[0] & 0xF0000000) >> 28;
+ $key[0] = ($key[0] | $temp) & 0x0FFFFFFF;
+
+ $key[1] <<= $shifts[$i];
+ $temp = ($key[1] & 0xF0000000) >> 28;
+ $key[1] = ($key[1] | $temp) & 0x0FFFFFFF;
+
+ $temp = array(
+ (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) |
+ (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) |
+ (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23)
+ ,
+ (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) |
+ (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) |
+ (($key[1] & 0x00000080) >> 6)
+ ,
+ ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) |
+ (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) |
+ (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20)
+ ,
+ (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) |
+ (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) |
+ (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26)
+ ,
+ (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) |
+ (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) |
+ (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1)
+ ,
+ (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) |
+ (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) |
+ (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8)
+ ,
+ (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) |
+ (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) |
+ (($key[0] & 0x00400000) >> 21)
+ ,
+ (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) |
+ (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) |
+ (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24)
+ );
+
+ $keys[] = $temp;
+ }
+
+ $temp = array(
+ CRYPT_DES_ENCRYPT => $keys,
+ CRYPT_DES_DECRYPT => array_reverse($keys)
+ );
+
+ return $temp;
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+}
+
+// vim: ts=4:sw=4:et:
+// vim6: fdl=1:
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php
new file mode 100644
index 00000000000..c5d314f009f
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php
@@ -0,0 +1,825 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ *
+ * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
+ *
+ * md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512
+ *
+ * If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
+ * the hash. If no valid algorithm is provided, sha1 will be used.
+ *
+ * PHP versions 4 and 5
+ *
+ * {@internal The variable names are the same as those in
+ * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/Hash.php');
+ *
+ * $hash = new Crypt_Hash('sha1');
+ *
+ * $hash->setKey('abcdefg');
+ *
+ * echo base64_encode($hash->hash('abcdefg'));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_Hash
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**#@+
+ * @access private
+ * @see Crypt_Hash::Crypt_Hash()
+ */
+/**
+ * Toggles the internal implementation
+ */
+define('CRYPT_HASH_MODE_INTERNAL', 1);
+/**
+ * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
+ */
+define('CRYPT_HASH_MODE_MHASH', 2);
+/**
+ * Toggles the hash() implementation, which works on PHP 5.1.2+.
+ */
+define('CRYPT_HASH_MODE_HASH', 3);
+/**#@-*/
+
+/**
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_Hash
+ */
+class Crypt_Hash {
+ /**
+ * Byte-length of compression blocks / key (Internal HMAC)
+ *
+ * @see Crypt_Hash::setAlgorithm()
+ * @var Integer
+ * @access private
+ */
+ var $b;
+
+ /**
+ * Byte-length of hash output (Internal HMAC)
+ *
+ * @see Crypt_Hash::setHash()
+ * @var Integer
+ * @access private
+ */
+ var $l = false;
+
+ /**
+ * Hash Algorithm
+ *
+ * @see Crypt_Hash::setHash()
+ * @var String
+ * @access private
+ */
+ var $hash;
+
+ /**
+ * Key
+ *
+ * @see Crypt_Hash::setKey()
+ * @var String
+ * @access private
+ */
+ var $key = false;
+
+ /**
+ * Outer XOR (Internal HMAC)
+ *
+ * @see Crypt_Hash::setKey()
+ * @var String
+ * @access private
+ */
+ var $opad;
+
+ /**
+ * Inner XOR (Internal HMAC)
+ *
+ * @see Crypt_Hash::setKey()
+ * @var String
+ * @access private
+ */
+ var $ipad;
+
+ /**
+ * Default Constructor.
+ *
+ * @param optional String $hash
+ * @return Crypt_Hash
+ * @access public
+ */
+ function Crypt_Hash($hash = 'sha1')
+ {
+ if ( !defined('CRYPT_HASH_MODE') ) {
+ switch (true) {
+ case extension_loaded('hash'):
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
+ break;
+ case extension_loaded('mhash'):
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
+ break;
+ default:
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
+ }
+ }
+
+ $this->setHash($hash);
+ }
+
+ /**
+ * Sets the key for HMACs
+ *
+ * Keys can be of any length.
+ *
+ * @access public
+ * @param String $key
+ */
+ function setKey($key = false)
+ {
+ $this->key = $key;
+ }
+
+ /**
+ * Sets the hash function.
+ *
+ * @access public
+ * @param String $hash
+ */
+ function setHash($hash)
+ {
+ $hash = strtolower($hash);
+ switch ($hash) {
+ case 'md5-96':
+ case 'sha1-96':
+ $this->l = 12; // 96 / 8 = 12
+ break;
+ case 'md2':
+ case 'md5':
+ $this->l = 16;
+ break;
+ case 'sha1':
+ $this->l = 20;
+ break;
+ case 'sha256':
+ $this->l = 32;
+ break;
+ case 'sha384':
+ $this->l = 48;
+ break;
+ case 'sha512':
+ $this->l = 64;
+ }
+
+ switch ($hash) {
+ case 'md2':
+ $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ?
+ CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL;
+ break;
+ case 'sha384':
+ case 'sha512':
+ $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
+ break;
+ default:
+ $mode = CRYPT_HASH_MODE;
+ }
+
+ switch ( $mode ) {
+ case CRYPT_HASH_MODE_MHASH:
+ switch ($hash) {
+ case 'md5':
+ case 'md5-96':
+ $this->hash = MHASH_MD5;
+ break;
+ case 'sha256':
+ $this->hash = MHASH_SHA256;
+ break;
+ case 'sha1':
+ case 'sha1-96':
+ default:
+ $this->hash = MHASH_SHA1;
+ }
+ return;
+ case CRYPT_HASH_MODE_HASH:
+ switch ($hash) {
+ case 'md5':
+ case 'md5-96':
+ $this->hash = 'md5';
+ return;
+ case 'md2':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ $this->hash = $hash;
+ return;
+ case 'sha1':
+ case 'sha1-96':
+ default:
+ $this->hash = 'sha1';
+ }
+ return;
+ }
+
+ switch ($hash) {
+ case 'md2':
+ $this->b = 16;
+ $this->hash = array($this, '_md2');
+ break;
+ case 'md5':
+ case 'md5-96':
+ $this->b = 64;
+ $this->hash = array($this, '_md5');
+ break;
+ case 'sha256':
+ $this->b = 64;
+ $this->hash = array($this, '_sha256');
+ break;
+ case 'sha384':
+ case 'sha512':
+ $this->b = 128;
+ $this->hash = array($this, '_sha512');
+ break;
+ case 'sha1':
+ case 'sha1-96':
+ default:
+ $this->b = 64;
+ $this->hash = array($this, '_sha1');
+ }
+
+ $this->ipad = str_repeat(chr(0x36), $this->b);
+ $this->opad = str_repeat(chr(0x5C), $this->b);
+ }
+
+ /**
+ * Compute the HMAC.
+ *
+ * @access public
+ * @param String $text
+ * @return String
+ */
+ function hash($text)
+ {
+ $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
+
+ if (!empty($this->key) || is_string($this->key)) {
+ switch ( $mode ) {
+ case CRYPT_HASH_MODE_MHASH:
+ $output = mhash($this->hash, $text, $this->key);
+ break;
+ case CRYPT_HASH_MODE_HASH:
+ $output = hash_hmac($this->hash, $text, $this->key, true);
+ break;
+ case CRYPT_HASH_MODE_INTERNAL:
+ /* "Applications that use keys longer than B bytes will first hash the key using H and then use the
+ resultant L byte string as the actual key to HMAC."
+
+ -- http://tools.ietf.org/html/rfc2104#section-2 */
+ $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key;
+
+ $key = str_pad($key, $this->b, chr(0)); // step 1
+ $temp = $this->ipad ^ $key; // step 2
+ $temp .= $text; // step 3
+ $temp = call_user_func($this->hash, $temp); // step 4
+ $output = $this->opad ^ $key; // step 5
+ $output.= $temp; // step 6
+ $output = call_user_func($this->hash, $output); // step 7
+ }
+ } else {
+ switch ( $mode ) {
+ case CRYPT_HASH_MODE_MHASH:
+ $output = mhash($this->hash, $text);
+ break;
+ case CRYPT_HASH_MODE_HASH:
+ $output = hash($this->hash, $text, true);
+ break;
+ case CRYPT_HASH_MODE_INTERNAL:
+ $output = call_user_func($this->hash, $text);
+ }
+ }
+
+ return substr($output, 0, $this->l);
+ }
+
+ /**
+ * Returns the hash length (in bytes)
+ *
+ * @access public
+ * @return Integer
+ */
+ function getLength()
+ {
+ return $this->l;
+ }
+
+ /**
+ * Wrapper for MD5
+ *
+ * @access private
+ * @param String $text
+ */
+ function _md5($m)
+ {
+ return pack('H*', md5($m));
+ }
+
+ /**
+ * Wrapper for SHA1
+ *
+ * @access private
+ * @param String $text
+ */
+ function _sha1($m)
+ {
+ return pack('H*', sha1($m));
+ }
+
+ /**
+ * Pure-PHP implementation of MD2
+ *
+ * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
+ *
+ * @access private
+ * @param String $text
+ */
+ function _md2($m)
+ {
+ static $s = array(
+ 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
+ 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
+ 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
+ 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
+ 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
+ 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
+ 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
+ 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
+ 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
+ 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
+ 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
+ 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
+ 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
+ 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
+ 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
+ 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
+ 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
+ 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
+ );
+
+ // Step 1. Append Padding Bytes
+ $pad = 16 - (strlen($m) & 0xF);
+ $m.= str_repeat(chr($pad), $pad);
+
+ $length = strlen($m);
+
+ // Step 2. Append Checksum
+ $c = str_repeat(chr(0), 16);
+ $l = chr(0);
+ for ($i = 0; $i < $length; $i+= 16) {
+ for ($j = 0; $j < 16; $j++) {
+ // RFC1319 incorrectly states that C[j] should be set to S[c xor L]
+ //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
+ // per <http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j] should be set to S[c xor L] xor C[j]
+ $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
+ $l = $c[$j];
+ }
+ }
+ $m.= $c;
+
+ $length+= 16;
+
+ // Step 3. Initialize MD Buffer
+ $x = str_repeat(chr(0), 48);
+
+ // Step 4. Process Message in 16-Byte Blocks
+ for ($i = 0; $i < $length; $i+= 16) {
+ for ($j = 0; $j < 16; $j++) {
+ $x[$j + 16] = $m[$i + $j];
+ $x[$j + 32] = $x[$j + 16] ^ $x[$j];
+ }
+ $t = chr(0);
+ for ($j = 0; $j < 18; $j++) {
+ for ($k = 0; $k < 48; $k++) {
+ $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
+ //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
+ }
+ $t = chr(ord($t) + $j);
+ }
+ }
+
+ // Step 5. Output
+ return substr($x, 0, 16);
+ }
+
+ /**
+ * Pure-PHP implementation of SHA256
+ *
+ * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
+ *
+ * @access private
+ * @param String $text
+ */
+ function _sha256($m)
+ {
+ if (extension_loaded('suhosin')) {
+ return pack('H*', sha256($m));
+ }
+
+ // Initialize variables
+ $hash = array(
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+ );
+ // Initialize table of round constants
+ // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
+ static $k = array(
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ );
+
+ // Pre-processing
+ $length = strlen($m);
+ // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
+ $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
+ $m[$length] = chr(0x80);
+ // we don't support hashing strings 512MB long
+ $m.= pack('N2', 0, $length << 3);
+
+ // Process the message in successive 512-bit chunks
+ $chunks = str_split($m, 64);
+ foreach ($chunks as $chunk) {
+ $w = array();
+ for ($i = 0; $i < 16; $i++) {
+ extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
+ $w[] = $temp;
+ }
+
+ // Extend the sixteen 32-bit words into sixty-four 32-bit words
+ for ($i = 16; $i < 64; $i++) {
+ $s0 = $this->_rightRotate($w[$i - 15], 7) ^
+ $this->_rightRotate($w[$i - 15], 18) ^
+ $this->_rightShift( $w[$i - 15], 3);
+ $s1 = $this->_rightRotate($w[$i - 2], 17) ^
+ $this->_rightRotate($w[$i - 2], 19) ^
+ $this->_rightShift( $w[$i - 2], 10);
+ $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
+
+ }
+
+ // Initialize hash value for this chunk
+ list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
+
+ // Main loop
+ for ($i = 0; $i < 64; $i++) {
+ $s0 = $this->_rightRotate($a, 2) ^
+ $this->_rightRotate($a, 13) ^
+ $this->_rightRotate($a, 22);
+ $maj = ($a & $b) ^
+ ($a & $c) ^
+ ($b & $c);
+ $t2 = $this->_add($s0, $maj);
+
+ $s1 = $this->_rightRotate($e, 6) ^
+ $this->_rightRotate($e, 11) ^
+ $this->_rightRotate($e, 25);
+ $ch = ($e & $f) ^
+ ($this->_not($e) & $g);
+ $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
+
+ $h = $g;
+ $g = $f;
+ $f = $e;
+ $e = $this->_add($d, $t1);
+ $d = $c;
+ $c = $b;
+ $b = $a;
+ $a = $this->_add($t1, $t2);
+ }
+
+ // Add this chunk's hash to result so far
+ $hash = array(
+ $this->_add($hash[0], $a),
+ $this->_add($hash[1], $b),
+ $this->_add($hash[2], $c),
+ $this->_add($hash[3], $d),
+ $this->_add($hash[4], $e),
+ $this->_add($hash[5], $f),
+ $this->_add($hash[6], $g),
+ $this->_add($hash[7], $h)
+ );
+ }
+
+ // Produce the final hash value (big-endian)
+ return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
+ }
+
+ /**
+ * Pure-PHP implementation of SHA384 and SHA512
+ *
+ * @access private
+ * @param String $text
+ */
+ function _sha512($m)
+ {
+ if (!class_exists('Math_BigInteger')) {
+ require_once('Math/BigInteger.php');
+ }
+
+ static $init384, $init512, $k;
+
+ if (!isset($k)) {
+ // Initialize variables
+ $init384 = array( // initial values for SHA384
+ 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
+ '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
+ );
+ $init512 = array( // initial values for SHA512
+ '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
+ '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
+ );
+
+ for ($i = 0; $i < 8; $i++) {
+ $init384[$i] = new Math_BigInteger($init384[$i], 16);
+ $init384[$i]->setPrecision(64);
+ $init512[$i] = new Math_BigInteger($init512[$i], 16);
+ $init512[$i]->setPrecision(64);
+ }
+
+ // Initialize table of round constants
+ // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
+ $k = array(
+ '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
+ '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
+ 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
+ '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
+ 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
+ '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
+ '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
+ 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
+ '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
+ '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
+ 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
+ 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
+ '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
+ '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
+ '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
+ '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
+ 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
+ '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
+ '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
+ '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
+ );
+
+ for ($i = 0; $i < 80; $i++) {
+ $k[$i] = new Math_BigInteger($k[$i], 16);
+ }
+ }
+
+ $hash = $this->l == 48 ? $init384 : $init512;
+
+ // Pre-processing
+ $length = strlen($m);
+ // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
+ $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
+ $m[$length] = chr(0x80);
+ // we don't support hashing strings 512MB long
+ $m.= pack('N4', 0, 0, 0, $length << 3);
+
+ // Process the message in successive 1024-bit chunks
+ $chunks = str_split($m, 128);
+ foreach ($chunks as $chunk) {
+ $w = array();
+ for ($i = 0; $i < 16; $i++) {
+ $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
+ $temp->setPrecision(64);
+ $w[] = $temp;
+ }
+
+ // Extend the sixteen 32-bit words into eighty 32-bit words
+ for ($i = 16; $i < 80; $i++) {
+ $temp = array(
+ $w[$i - 15]->bitwise_rightRotate(1),
+ $w[$i - 15]->bitwise_rightRotate(8),
+ $w[$i - 15]->bitwise_rightShift(7)
+ );
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
+ $s0 = $s0->bitwise_xor($temp[2]);
+ $temp = array(
+ $w[$i - 2]->bitwise_rightRotate(19),
+ $w[$i - 2]->bitwise_rightRotate(61),
+ $w[$i - 2]->bitwise_rightShift(6)
+ );
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
+ $s1 = $s1->bitwise_xor($temp[2]);
+ $w[$i] = $w[$i - 16]->copy();
+ $w[$i] = $w[$i]->add($s0);
+ $w[$i] = $w[$i]->add($w[$i - 7]);
+ $w[$i] = $w[$i]->add($s1);
+ }
+
+ // Initialize hash value for this chunk
+ $a = $hash[0]->copy();
+ $b = $hash[1]->copy();
+ $c = $hash[2]->copy();
+ $d = $hash[3]->copy();
+ $e = $hash[4]->copy();
+ $f = $hash[5]->copy();
+ $g = $hash[6]->copy();
+ $h = $hash[7]->copy();
+
+ // Main loop
+ for ($i = 0; $i < 80; $i++) {
+ $temp = array(
+ $a->bitwise_rightRotate(28),
+ $a->bitwise_rightRotate(34),
+ $a->bitwise_rightRotate(39)
+ );
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
+ $s0 = $s0->bitwise_xor($temp[2]);
+ $temp = array(
+ $a->bitwise_and($b),
+ $a->bitwise_and($c),
+ $b->bitwise_and($c)
+ );
+ $maj = $temp[0]->bitwise_xor($temp[1]);
+ $maj = $maj->bitwise_xor($temp[2]);
+ $t2 = $s0->add($maj);
+
+ $temp = array(
+ $e->bitwise_rightRotate(14),
+ $e->bitwise_rightRotate(18),
+ $e->bitwise_rightRotate(41)
+ );
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
+ $s1 = $s1->bitwise_xor($temp[2]);
+ $temp = array(
+ $e->bitwise_and($f),
+ $g->bitwise_and($e->bitwise_not())
+ );
+ $ch = $temp[0]->bitwise_xor($temp[1]);
+ $t1 = $h->add($s1);
+ $t1 = $t1->add($ch);
+ $t1 = $t1->add($k[$i]);
+ $t1 = $t1->add($w[$i]);
+
+ $h = $g->copy();
+ $g = $f->copy();
+ $f = $e->copy();
+ $e = $d->add($t1);
+ $d = $c->copy();
+ $c = $b->copy();
+ $b = $a->copy();
+ $a = $t1->add($t2);
+ }
+
+ // Add this chunk's hash to result so far
+ $hash = array(
+ $hash[0]->add($a),
+ $hash[1]->add($b),
+ $hash[2]->add($c),
+ $hash[3]->add($d),
+ $hash[4]->add($e),
+ $hash[5]->add($f),
+ $hash[6]->add($g),
+ $hash[7]->add($h)
+ );
+ }
+
+ // Produce the final hash value (big-endian)
+ // (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
+ $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
+ $hash[4]->toBytes() . $hash[5]->toBytes();
+ if ($this->l != 48) {
+ $temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
+ }
+
+ return $temp;
+ }
+
+ /**
+ * Right Rotate
+ *
+ * @access private
+ * @param Integer $int
+ * @param Integer $amt
+ * @see _sha256()
+ * @return Integer
+ */
+ function _rightRotate($int, $amt)
+ {
+ $invamt = 32 - $amt;
+ $mask = (1 << $invamt) - 1;
+ return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
+ }
+
+ /**
+ * Right Shift
+ *
+ * @access private
+ * @param Integer $int
+ * @param Integer $amt
+ * @see _sha256()
+ * @return Integer
+ */
+ function _rightShift($int, $amt)
+ {
+ $mask = (1 << (32 - $amt)) - 1;
+ return ($int >> $amt) & $mask;
+ }
+
+ /**
+ * Not
+ *
+ * @access private
+ * @param Integer $int
+ * @see _sha256()
+ * @return Integer
+ */
+ function _not($int)
+ {
+ return ~$int & 0xFFFFFFFF;
+ }
+
+ /**
+ * Add
+ *
+ * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
+ * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @see _sha256()
+ * @access private
+ */
+ function _add()
+ {
+ static $mod;
+ if (!isset($mod)) {
+ $mod = pow(2, 32);
+ }
+
+ $result = 0;
+ $arguments = func_get_args();
+ foreach ($arguments as $argument) {
+ $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
+ }
+
+ return fmod($result, $mod);
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php
new file mode 100644
index 00000000000..390108e0480
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php
@@ -0,0 +1,519 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of RC4.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP versions 4 and 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
+ * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
+ *
+ * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
+ * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/RC4.php');
+ *
+ * $rc4 = new Crypt_RC4();
+ *
+ * $rc4->setKey('abcdefgh');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $rc4->decrypt($rc4->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_RC4
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**#@+
+ * @access private
+ * @see Crypt_RC4::Crypt_RC4()
+ */
+/**
+ * Toggles the internal implementation
+ */
+define('CRYPT_RC4_MODE_INTERNAL', 1);
+/**
+ * Toggles the mcrypt implementation
+ */
+define('CRYPT_RC4_MODE_MCRYPT', 2);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_RC4::_crypt()
+ */
+define('CRYPT_RC4_ENCRYPT', 0);
+define('CRYPT_RC4_DECRYPT', 1);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of RC4.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_RC4
+ */
+class Crypt_RC4 {
+ /**
+ * The Key
+ *
+ * @see Crypt_RC4::setKey()
+ * @var String
+ * @access private
+ */
+ var $key = "\0";
+
+ /**
+ * The Key Stream for encryption
+ *
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
+ *
+ * @see Crypt_RC4::setKey()
+ * @var Array
+ * @access private
+ */
+ var $encryptStream = false;
+
+ /**
+ * The Key Stream for decryption
+ *
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
+ *
+ * @see Crypt_RC4::setKey()
+ * @var Array
+ * @access private
+ */
+ var $decryptStream = false;
+
+ /**
+ * The $i and $j indexes for encryption
+ *
+ * @see Crypt_RC4::_crypt()
+ * @var Integer
+ * @access private
+ */
+ var $encryptIndex = 0;
+
+ /**
+ * The $i and $j indexes for decryption
+ *
+ * @see Crypt_RC4::_crypt()
+ * @var Integer
+ * @access private
+ */
+ var $decryptIndex = 0;
+
+ /**
+ * The Encryption Algorithm
+ *
+ * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
+ *
+ * @see Crypt_RC4::Crypt_RC4()
+ * @var Integer
+ * @access private
+ */
+ var $mode;
+
+ /**
+ * Continuous Buffer status
+ *
+ * @see Crypt_RC4::enableContinuousBuffer()
+ * @var Boolean
+ * @access private
+ */
+ var $continuousBuffer = false;
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * @return Crypt_RC4
+ * @access public
+ */
+ function Crypt_RC4()
+ {
+ if ( !defined('CRYPT_RC4_MODE') ) {
+ switch (true) {
+ case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()):
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
+ break;
+ default:
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
+ }
+ }
+
+ switch ( CRYPT_RC4_MODE ) {
+ case CRYPT_RC4_MODE_MCRYPT:
+ switch (true) {
+ case defined('MCRYPT_ARCFOUR'):
+ $this->mode = MCRYPT_ARCFOUR;
+ break;
+ case defined('MCRYPT_RC4');
+ $this->mode = MCRYPT_RC4;
+ }
+ $this->encryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
+ $this->decryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
+
+ }
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
+ * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
+ *
+ * @access public
+ * @param String $key
+ */
+ function setKey($key)
+ {
+ $this->key = $key;
+
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
+ mcrypt_generic_init($this->encryptStream, $this->key, '');
+ mcrypt_generic_init($this->decryptStream, $this->key, '');
+ return;
+ }
+
+ $keyLength = strlen($key);
+ $keyStream = array();
+ for ($i = 0; $i < 256; $i++) {
+ $keyStream[$i] = $i;
+ }
+ $j = 0;
+ for ($i = 0; $i < 256; $i++) {
+ $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
+ $temp = $keyStream[$i];
+ $keyStream[$i] = $keyStream[$j];
+ $keyStream[$j] = $temp;
+ }
+
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
+ $this->encryptStream = $this->decryptStream = $keyStream;
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
+ * $hash, $salt, $count, $dkLen
+ *
+ * @param String $password
+ * @param optional String $method
+ * @access public
+ */
+ function setPassword($password, $method = 'pbkdf2')
+ {
+ $key = '';
+
+ switch ($method) {
+ default: // 'pbkdf2'
+ list(, , $hash, $salt, $count) = func_get_args();
+ if (!isset($hash)) {
+ $hash = 'sha1';
+ }
+ // WPA and WPA2 use the SSID as the salt
+ if (!isset($salt)) {
+ $salt = 'phpseclib/salt';
+ }
+ // RFC2898#section-4.2 uses 1,000 iterations by default
+ // WPA and WPA2 use 4,096.
+ if (!isset($count)) {
+ $count = 1000;
+ }
+ if (!isset($dkLen)) {
+ $dkLen = 128;
+ }
+
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+
+ $i = 1;
+ while (strlen($key) < $dkLen) {
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
+ $hmac = new Crypt_Hash();
+ $hmac->setHash($hash);
+ $hmac->setKey($password);
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
+ for ($j = 2; $j <= $count; $j++) {
+ $u = $hmac->hash($u);
+ $f^= $u;
+ }
+ $key.= $f;
+ }
+ }
+
+ $this->setKey(substr($key, 0, $dkLen));
+ }
+
+ /**
+ * Dummy function.
+ *
+ * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
+ * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
+ * calling setKey().
+ *
+ * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
+ * the IV's are relatively easy to predict, an attack described by
+ * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
+ * can be used to quickly guess at the rest of the key. The following links elaborate:
+ *
+ * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
+ * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
+ *
+ * @param String $iv
+ * @see Crypt_RC4::setKey()
+ * @access public
+ */
+ function setIV($iv)
+ {
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * @see Crypt_RC4::_crypt()
+ * @access public
+ * @param String $plaintext
+ */
+ function encrypt($plaintext)
+ {
+ return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
+ * Atleast if the continuous buffer is disabled.
+ *
+ * @see Crypt_RC4::_crypt()
+ * @access public
+ * @param String $ciphertext
+ */
+ function decrypt($ciphertext)
+ {
+ return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
+ }
+
+ /**
+ * Encrypts or decrypts a message.
+ *
+ * @see Crypt_RC4::encrypt()
+ * @see Crypt_RC4::decrypt()
+ * @access private
+ * @param String $text
+ * @param Integer $mode
+ */
+ function _crypt($text, $mode)
+ {
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
+ $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->$keyStream, $this->key, '');
+ }
+
+ return mcrypt_generic($this->$keyStream, $text);
+ }
+
+ if ($this->encryptStream === false) {
+ $this->setKey($this->key);
+ }
+
+ switch ($mode) {
+ case CRYPT_RC4_ENCRYPT:
+ $keyStream = $this->encryptStream;
+ list($i, $j) = $this->encryptIndex;
+ break;
+ case CRYPT_RC4_DECRYPT:
+ $keyStream = $this->decryptStream;
+ list($i, $j) = $this->decryptIndex;
+ }
+
+ $newText = '';
+ for ($k = 0; $k < strlen($text); $k++) {
+ $i = ($i + 1) & 255;
+ $j = ($j + $keyStream[$i]) & 255;
+ $temp = $keyStream[$i];
+ $keyStream[$i] = $keyStream[$j];
+ $keyStream[$j] = $temp;
+ $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
+ $newText.= chr(ord($text[$k]) ^ $temp);
+ }
+
+ if ($this->continuousBuffer) {
+ switch ($mode) {
+ case CRYPT_RC4_ENCRYPT:
+ $this->encryptStream = $keyStream;
+ $this->encryptIndex = array($i, $j);
+ break;
+ case CRYPT_RC4_DECRYPT:
+ $this->decryptStream = $keyStream;
+ $this->decryptIndex = array($i, $j);
+ }
+ }
+
+ return $newText;
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
+ * will yield different outputs:
+ *
+ * <code>
+ * echo $rc4->encrypt(substr($plaintext, 0, 8));
+ * echo $rc4->encrypt(substr($plaintext, 8, 8));
+ * </code>
+ * <code>
+ * echo $rc4->encrypt($plaintext);
+ * </code>
+ *
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
+ * another, as demonstrated with the following:
+ *
+ * <code>
+ * $rc4->encrypt(substr($plaintext, 0, 8));
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ * <code>
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ *
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
+ *
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
+ * however, they are also less intuitive and more likely to cause you problems.
+ *
+ * @see Crypt_RC4::disableContinuousBuffer()
+ * @access public
+ */
+ function enableContinuousBuffer()
+ {
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
+ mcrypt_generic_init($this->encryptStream, $this->key, '');
+ mcrypt_generic_init($this->decryptStream, $this->key, '');
+ }
+
+ $this->continuousBuffer = true;
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_RC4::enableContinuousBuffer()
+ * @access public
+ */
+ function disableContinuousBuffer()
+ {
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
+ $this->encryptStream = $this->decryptStream = false;
+ }
+
+ $this->continuousBuffer = false;
+ }
+
+ /**
+ * Dummy function.
+ *
+ * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
+ * included is so that you can switch between a block cipher and a stream cipher transparently.
+ *
+ * @see Crypt_RC4::disablePadding()
+ * @access public
+ */
+ function enablePadding()
+ {
+ }
+
+ /**
+ * Dummy function.
+ *
+ * @see Crypt_RC4::enablePadding()
+ * @access public
+ */
+ function disablePadding()
+ {
+ }
+
+ /**
+ * Class destructor.
+ *
+ * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
+ * needs to be called if mcrypt is being used.
+ *
+ * @access public
+ */
+ function __destruct()
+ {
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
+ $this->_closeMCrypt();
+ }
+ }
+
+ /**
+ * Properly close the MCrypt objects.
+ *
+ * @access prviate
+ */
+ function _closeMCrypt()
+ {
+ mcrypt_module_close($this->encryptStream);
+ mcrypt_module_close($this->decryptStream);
+ }
+}
+
+// vim: ts=4:sw=4:et:
+// vim6: fdl=1: \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php
new file mode 100644
index 00000000000..db1ba1581b6
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php
@@ -0,0 +1,2646 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
+ *
+ * PHP versions 4 and 5
+ *
+ * Here's an example of how to encrypt and decrypt text with this library:
+ * <code>
+ * <?php
+ * include('Crypt/RSA.php');
+ *
+ * $rsa = new Crypt_RSA();
+ * extract($rsa->createKey());
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $rsa->loadKey($privatekey);
+ * $ciphertext = $rsa->encrypt($plaintext);
+ *
+ * $rsa->loadKey($publickey);
+ * echo $rsa->decrypt($ciphertext);
+ * ?>
+ * </code>
+ *
+ * Here's an example of how to create signatures and verify signatures with this library:
+ * <code>
+ * <?php
+ * include('Crypt/RSA.php');
+ *
+ * $rsa = new Crypt_RSA();
+ * extract($rsa->createKey());
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $rsa->loadKey($privatekey);
+ * $signature = $rsa->sign($plaintext);
+ *
+ * $rsa->loadKey($publickey);
+ * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_RSA
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMIX Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Math_BigInteger
+ */
+if (!class_exists('Math_BigInteger')) {
+ require_once('Math/BigInteger.php');
+}
+
+/**
+ * Include Crypt_Random
+ */
+// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
+// will trigger a call to __autoload() if you're wanting to auto-load classes
+// call function_exists() a second time to stop the require_once from being called outside
+// of the auto loader
+if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) {
+ require_once('Crypt/Random.php');
+}
+
+/**
+ * Include Crypt_Hash
+ */
+if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+}
+
+/**#@+
+ * @access public
+ * @see Crypt_RSA::encrypt()
+ * @see Crypt_RSA::decrypt()
+ */
+/**
+ * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
+ * (OAEP) for encryption / decryption.
+ *
+ * Uses sha1 by default.
+ *
+ * @see Crypt_RSA::setHash()
+ * @see Crypt_RSA::setMGFHash()
+ */
+define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
+/**
+ * Use PKCS#1 padding.
+ *
+ * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
+ * compatability with protocols (like SSH-1) written before OAEP's introduction.
+ */
+define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Crypt_RSA::sign()
+ * @see Crypt_RSA::verify()
+ * @see Crypt_RSA::setHash()
+ */
+/**
+ * Use the Probabilistic Signature Scheme for signing
+ *
+ * Uses sha1 by default.
+ *
+ * @see Crypt_RSA::setSaltLength()
+ * @see Crypt_RSA::setMGFHash()
+ */
+define('CRYPT_RSA_SIGNATURE_PSS', 1);
+/**
+ * Use the PKCS#1 scheme by default.
+ *
+ * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
+ * compatability with protocols (like SSH-2) written before PSS's introduction.
+ */
+define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_RSA::createKey()
+ */
+/**
+ * ASN1 Integer
+ */
+define('CRYPT_RSA_ASN1_INTEGER', 2);
+/**
+ * ASN1 Bit String
+ */
+define('CRYPT_RSA_ASN1_BITSTRING', 3);
+/**
+ * ASN1 Sequence (with the constucted bit set)
+ */
+define('CRYPT_RSA_ASN1_SEQUENCE', 48);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_RSA::Crypt_RSA()
+ */
+/**
+ * To use the pure-PHP implementation
+ */
+define('CRYPT_RSA_MODE_INTERNAL', 1);
+/**
+ * To use the OpenSSL library
+ *
+ * (if enabled; otherwise, the internal implementation will be used)
+ */
+define('CRYPT_RSA_MODE_OPENSSL', 2);
+/**#@-*/
+
+/**
+ * Default openSSL configuration file.
+ */
+define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');
+
+
+/**#@+
+ * @access public
+ * @see Crypt_RSA::createKey()
+ * @see Crypt_RSA::setPrivateKeyFormat()
+ */
+/**
+ * PKCS#1 formatted private key
+ *
+ * Used by OpenSSH
+ */
+define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
+/**
+ * PuTTY formatted private key
+ */
+define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
+/**
+ * XML formatted private key
+ */
+define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Crypt_RSA::createKey()
+ * @see Crypt_RSA::setPublicKeyFormat()
+ */
+/**
+ * Raw public key
+ *
+ * An array containing two Math_BigInteger objects.
+ *
+ * The exponent can be indexed with any of the following:
+ *
+ * 0, e, exponent, publicExponent
+ *
+ * The modulus can be indexed with any of the following:
+ *
+ * 1, n, modulo, modulus
+ */
+define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
+/**
+ * PKCS#1 formatted public key (raw)
+ *
+ * Used by File/X509.php
+ */
+define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
+/**
+ * XML formatted public key
+ */
+define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
+/**
+ * OpenSSH formatted public key
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ */
+define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
+/**
+ * PKCS#1 formatted public key (encapsulated)
+ *
+ * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
+ */
+define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 7);
+/**#@-*/
+
+/**
+ * Pure-PHP PKCS#1 compliant implementation of RSA.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_RSA
+ */
+class Crypt_RSA {
+ /**
+ * Precomputed Zero
+ *
+ * @var Array
+ * @access private
+ */
+ var $zero;
+
+ /**
+ * Precomputed One
+ *
+ * @var Array
+ * @access private
+ */
+ var $one;
+
+ /**
+ * Private Key Format
+ *
+ * @var Integer
+ * @access private
+ */
+ var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
+
+ /**
+ * Public Key Format
+ *
+ * @var Integer
+ * @access public
+ */
+ var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1;
+
+ /**
+ * Modulus (ie. n)
+ *
+ * @var Math_BigInteger
+ * @access private
+ */
+ var $modulus;
+
+ /**
+ * Modulus length
+ *
+ * @var Math_BigInteger
+ * @access private
+ */
+ var $k;
+
+ /**
+ * Exponent (ie. e or d)
+ *
+ * @var Math_BigInteger
+ * @access private
+ */
+ var $exponent;
+
+ /**
+ * Primes for Chinese Remainder Theorem (ie. p and q)
+ *
+ * @var Array
+ * @access private
+ */
+ var $primes;
+
+ /**
+ * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
+ *
+ * @var Array
+ * @access private
+ */
+ var $exponents;
+
+ /**
+ * Coefficients for Chinese Remainder Theorem (ie. qInv)
+ *
+ * @var Array
+ * @access private
+ */
+ var $coefficients;
+
+ /**
+ * Hash name
+ *
+ * @var String
+ * @access private
+ */
+ var $hashName;
+
+ /**
+ * Hash function
+ *
+ * @var Crypt_Hash
+ * @access private
+ */
+ var $hash;
+
+ /**
+ * Length of hash function output
+ *
+ * @var Integer
+ * @access private
+ */
+ var $hLen;
+
+ /**
+ * Length of salt
+ *
+ * @var Integer
+ * @access private
+ */
+ var $sLen;
+
+ /**
+ * Hash function for the Mask Generation Function
+ *
+ * @var Crypt_Hash
+ * @access private
+ */
+ var $mgfHash;
+
+ /**
+ * Length of MGF hash function output
+ *
+ * @var Integer
+ * @access private
+ */
+ var $mgfHLen;
+
+ /**
+ * Encryption mode
+ *
+ * @var Integer
+ * @access private
+ */
+ var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
+
+ /**
+ * Signature mode
+ *
+ * @var Integer
+ * @access private
+ */
+ var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
+
+ /**
+ * Public Exponent
+ *
+ * @var Mixed
+ * @access private
+ */
+ var $publicExponent = false;
+
+ /**
+ * Password
+ *
+ * @var String
+ * @access private
+ */
+ var $password = false;
+
+ /**
+ * Components
+ *
+ * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
+ * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
+ *
+ * @see Crypt_RSA::_start_element_handler()
+ * @var Array
+ * @access private
+ */
+ var $components = array();
+
+ /**
+ * Current String
+ *
+ * For use with parsing XML formatted keys.
+ *
+ * @see Crypt_RSA::_character_handler()
+ * @see Crypt_RSA::_stop_element_handler()
+ * @var Mixed
+ * @access private
+ */
+ var $current;
+
+ /**
+ * OpenSSL configuration file name.
+ *
+ * Set to NULL to use system configuration file.
+ * @see Crypt_RSA::createKey()
+ * @var Mixed
+ * @Access public
+ */
+ var $configFile;
+
+ /**
+ * The constructor
+ *
+ * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
+ * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
+ * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
+ *
+ * @return Crypt_RSA
+ * @access public
+ */
+ function Crypt_RSA()
+ {
+ $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;
+
+ if ( !defined('CRYPT_RSA_MODE') ) {
+ switch (true) {
+ case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
+ define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
+ break;
+ default:
+ define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
+ }
+ }
+
+ if (!defined('CRYPT_RSA_COMMENT')) {
+ define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
+ }
+
+ $this->zero = new Math_BigInteger();
+ $this->one = new Math_BigInteger(1);
+
+ $this->hash = new Crypt_Hash('sha1');
+ $this->hLen = $this->hash->getLength();
+ $this->hashName = 'sha1';
+ $this->mgfHash = new Crypt_Hash('sha1');
+ $this->mgfHLen = $this->mgfHash->getLength();
+ }
+
+ /**
+ * Create public / private key pair
+ *
+ * Returns an array with the following three elements:
+ * - 'privatekey': The private key.
+ * - 'publickey': The public key.
+ * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
+ * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
+ *
+ * @access public
+ * @param optional Integer $bits
+ * @param optional Integer $timeout
+ * @param optional Math_BigInteger $p
+ */
+ function createKey($bits = 1024, $timeout = false, $partial = array())
+ {
+ if (!defined('CRYPT_RSA_EXPONENT')) {
+ // http://en.wikipedia.org/wiki/65537_%28number%29
+ define('CRYPT_RSA_EXPONENT', '65537');
+ }
+ // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
+ // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
+ // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
+ // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
+ // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
+ // generation when there's a chance neither gmp nor OpenSSL are installed)
+ if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
+ define('CRYPT_RSA_SMALLEST_PRIME', 4096);
+ }
+
+ // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
+ if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
+ $config = array();
+ if (isset($this->configFile)) {
+ $config['config'] = $this->configFile;
+ }
+ $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
+ openssl_pkey_export($rsa, $privatekey, NULL, $config);
+ $publickey = openssl_pkey_get_details($rsa);
+ $publickey = $publickey['key'];
+
+ $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
+ $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
+
+ // clear the buffer of error strings stemming from a minimalistic openssl.cnf
+ while (openssl_error_string() !== false);
+
+ return array(
+ 'privatekey' => $privatekey,
+ 'publickey' => $publickey,
+ 'partialkey' => false
+ );
+ }
+
+ static $e;
+ if (!isset($e)) {
+ $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
+ }
+
+ extract($this->_generateMinMax($bits));
+ $absoluteMin = $min;
+ $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
+ if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
+ $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
+ $temp = CRYPT_RSA_SMALLEST_PRIME;
+ } else {
+ $num_primes = 2;
+ }
+ extract($this->_generateMinMax($temp + $bits % $temp));
+ $finalMax = $max;
+ extract($this->_generateMinMax($temp));
+
+ $generator = new Math_BigInteger();
+
+ $n = $this->one->copy();
+ if (!empty($partial)) {
+ extract(unserialize($partial));
+ } else {
+ $exponents = $coefficients = $primes = array();
+ $lcm = array(
+ 'top' => $this->one->copy(),
+ 'bottom' => false
+ );
+ }
+
+ $start = time();
+ $i0 = count($primes) + 1;
+
+ do {
+ for ($i = $i0; $i <= $num_primes; $i++) {
+ if ($timeout !== false) {
+ $timeout-= time() - $start;
+ $start = time();
+ if ($timeout <= 0) {
+ return array(
+ 'privatekey' => '',
+ 'publickey' => '',
+ 'partialkey' => serialize(array(
+ 'primes' => $primes,
+ 'coefficients' => $coefficients,
+ 'lcm' => $lcm,
+ 'exponents' => $exponents
+ ))
+ );
+ }
+ }
+
+ if ($i == $num_primes) {
+ list($min, $temp) = $absoluteMin->divide($n);
+ if (!$temp->equals($this->zero)) {
+ $min = $min->add($this->one); // ie. ceil()
+ }
+ $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
+ } else {
+ $primes[$i] = $generator->randomPrime($min, $max, $timeout);
+ }
+
+ if ($primes[$i] === false) { // if we've reached the timeout
+ if (count($primes) > 1) {
+ $partialkey = '';
+ } else {
+ array_pop($primes);
+ $partialkey = serialize(array(
+ 'primes' => $primes,
+ 'coefficients' => $coefficients,
+ 'lcm' => $lcm,
+ 'exponents' => $exponents
+ ));
+ }
+
+ return array(
+ 'privatekey' => '',
+ 'publickey' => '',
+ 'partialkey' => $partialkey
+ );
+ }
+
+ // the first coefficient is calculated differently from the rest
+ // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
+ if ($i > 2) {
+ $coefficients[$i] = $n->modInverse($primes[$i]);
+ }
+
+ $n = $n->multiply($primes[$i]);
+
+ $temp = $primes[$i]->subtract($this->one);
+
+ // textbook RSA implementations use Euler's totient function instead of the least common multiple.
+ // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
+ $lcm['top'] = $lcm['top']->multiply($temp);
+ $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
+
+ $exponents[$i] = $e->modInverse($temp);
+ }
+
+ list($lcm) = $lcm['top']->divide($lcm['bottom']);
+ $gcd = $lcm->gcd($e);
+ $i0 = 1;
+ } while (!$gcd->equals($this->one));
+
+ $d = $e->modInverse($lcm);
+
+ $coefficients[2] = $primes[2]->modInverse($primes[1]);
+
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
+ // RSAPrivateKey ::= SEQUENCE {
+ // version Version,
+ // modulus INTEGER, -- n
+ // publicExponent INTEGER, -- e
+ // privateExponent INTEGER, -- d
+ // prime1 INTEGER, -- p
+ // prime2 INTEGER, -- q
+ // exponent1 INTEGER, -- d mod (p-1)
+ // exponent2 INTEGER, -- d mod (q-1)
+ // coefficient INTEGER, -- (inverse of q) mod p
+ // otherPrimeInfos OtherPrimeInfos OPTIONAL
+ // }
+
+ return array(
+ 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
+ 'publickey' => $this->_convertPublicKey($n, $e),
+ 'partialkey' => false
+ );
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @access private
+ * @see setPrivateKeyFormat()
+ * @param String $RSAPrivateKey
+ * @return String
+ */
+ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
+ {
+ $num_primes = count($primes);
+ $raw = array(
+ 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
+ 'modulus' => $n->toBytes(true),
+ 'publicExponent' => $e->toBytes(true),
+ 'privateExponent' => $d->toBytes(true),
+ 'prime1' => $primes[1]->toBytes(true),
+ 'prime2' => $primes[2]->toBytes(true),
+ 'exponent1' => $exponents[1]->toBytes(true),
+ 'exponent2' => $exponents[2]->toBytes(true),
+ 'coefficient' => $coefficients[2]->toBytes(true)
+ );
+
+ // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
+ // call _convertPublicKey() instead.
+ switch ($this->privateKeyFormat) {
+ case CRYPT_RSA_PRIVATE_FORMAT_XML:
+ if ($num_primes != 2) {
+ return false;
+ }
+ return "<RSAKeyValue>\r\n" .
+ ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
+ ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
+ ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
+ ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
+ ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
+ ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
+ ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
+ ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
+ '</RSAKeyValue>';
+ break;
+ case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
+ if ($num_primes != 2) {
+ return false;
+ }
+ $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
+ $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
+ $key.= $encryption;
+ $key.= "\r\nComment: " . CRYPT_RSA_COMMENT . "\r\n";
+ $public = pack('Na*Na*Na*',
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
+ );
+ $source = pack('Na*Na*Na*Na*',
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
+ strlen(CRYPT_RSA_COMMENT), CRYPT_RSA_COMMENT, strlen($public), $public
+ );
+ $public = base64_encode($public);
+ $key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n";
+ $key.= chunk_split($public, 64);
+ $private = pack('Na*Na*Na*Na*',
+ strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
+ strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
+ );
+ if (empty($this->password) && !is_string($this->password)) {
+ $source.= pack('Na*', strlen($private), $private);
+ $hashkey = 'putty-private-key-file-mac-key';
+ } else {
+ $private.= crypt_random_string(16 - (strlen($private) & 15));
+ $source.= pack('Na*', strlen($private), $private);
+ if (!class_exists('Crypt_AES')) {
+ require_once('Crypt/AES.php');
+ }
+ $sequence = 0;
+ $symkey = '';
+ while (strlen($symkey) < 32) {
+ $temp = pack('Na*', $sequence++, $this->password);
+ $symkey.= pack('H*', sha1($temp));
+ }
+ $symkey = substr($symkey, 0, 32);
+ $crypto = new Crypt_AES();
+
+ $crypto->setKey($symkey);
+ $crypto->disablePadding();
+ $private = $crypto->encrypt($private);
+ $hashkey = 'putty-private-key-file-mac-key' . $this->password;
+ }
+
+ $private = base64_encode($private);
+ $key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n";
+ $key.= chunk_split($private, 64);
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+ $hash = new Crypt_Hash('sha1');
+ $hash->setKey(pack('H*', sha1($hashkey)));
+ $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
+
+ return $key;
+ default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
+ $components = array();
+ foreach ($raw as $name => $value) {
+ $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
+ }
+
+ $RSAPrivateKey = implode('', $components);
+
+ if ($num_primes > 2) {
+ $OtherPrimeInfos = '';
+ for ($i = 3; $i <= $num_primes; $i++) {
+ // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
+ //
+ // OtherPrimeInfo ::= SEQUENCE {
+ // prime INTEGER, -- ri
+ // exponent INTEGER, -- di
+ // coefficient INTEGER -- ti
+ // }
+ $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
+ $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
+ }
+ $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
+ }
+
+ $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
+
+ if (!empty($this->password) || is_string($this->password)) {
+ $iv = crypt_random_string(8);
+ $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
+ if (!class_exists('Crypt_TripleDES')) {
+ require_once('Crypt/TripleDES.php');
+ }
+ $des = new Crypt_TripleDES();
+ $des->setKey($symkey);
+ $des->setIV($iv);
+ $iv = strtoupper(bin2hex($iv));
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
+ "Proc-Type: 4,ENCRYPTED\r\n" .
+ "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
+ "\r\n" .
+ chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
+ '-----END RSA PRIVATE KEY-----';
+ } else {
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
+ chunk_split(base64_encode($RSAPrivateKey), 64) .
+ '-----END RSA PRIVATE KEY-----';
+ }
+
+ return $RSAPrivateKey;
+ }
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @access private
+ * @see setPublicKeyFormat()
+ * @param String $RSAPrivateKey
+ * @return String
+ */
+ function _convertPublicKey($n, $e)
+ {
+ $modulus = $n->toBytes(true);
+ $publicExponent = $e->toBytes(true);
+
+ switch ($this->publicKeyFormat) {
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
+ return array('e' => $e->copy(), 'n' => $n->copy());
+ case CRYPT_RSA_PUBLIC_FORMAT_XML:
+ return "<RSAKeyValue>\r\n" .
+ ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
+ ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
+ '</RSAKeyValue>';
+ break;
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
+ // from <http://tools.ietf.org/html/rfc4253#page-15>:
+ // string "ssh-rsa"
+ // mpint e
+ // mpint n
+ $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
+ $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT;
+
+ return $RSAPublicKey;
+ default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
+ // RSAPublicKey ::= SEQUENCE {
+ // modulus INTEGER, -- n
+ // publicExponent INTEGER -- e
+ // }
+ $components = array(
+ 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
+ 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
+ );
+
+ $RSAPublicKey = pack('Ca*a*a*',
+ CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
+ $components['modulus'], $components['publicExponent']
+ );
+
+ if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) {
+ // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
+ $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
+ $RSAPublicKey = chr(0) . $RSAPublicKey;
+ $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
+
+ $RSAPublicKey = pack('Ca*a*',
+ CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
+ );
+ }
+
+ $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(base64_encode($RSAPublicKey), 64) .
+ '-----END PUBLIC KEY-----';
+
+ return $RSAPublicKey;
+ }
+ }
+
+ /**
+ * Break a public or private key down into its constituant components
+ *
+ * @access private
+ * @see _convertPublicKey()
+ * @see _convertPrivateKey()
+ * @param String $key
+ * @param Integer $type
+ * @return Array
+ */
+ function _parseKey($key, $type)
+ {
+ if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
+ return false;
+ }
+
+ switch ($type) {
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
+ if (!is_array($key)) {
+ return false;
+ }
+ $components = array();
+ switch (true) {
+ case isset($key['e']):
+ $components['publicExponent'] = $key['e']->copy();
+ break;
+ case isset($key['exponent']):
+ $components['publicExponent'] = $key['exponent']->copy();
+ break;
+ case isset($key['publicExponent']):
+ $components['publicExponent'] = $key['publicExponent']->copy();
+ break;
+ case isset($key[0]):
+ $components['publicExponent'] = $key[0]->copy();
+ }
+ switch (true) {
+ case isset($key['n']):
+ $components['modulus'] = $key['n']->copy();
+ break;
+ case isset($key['modulo']):
+ $components['modulus'] = $key['modulo']->copy();
+ break;
+ case isset($key['modulus']):
+ $components['modulus'] = $key['modulus']->copy();
+ break;
+ case isset($key[1]):
+ $components['modulus'] = $key[1]->copy();
+ }
+ return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
+ case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
+ case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
+ /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
+ "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
+ protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
+ two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
+
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.1
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.3
+
+ DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
+ DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
+ function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
+ own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
+ implementation are part of the standard, as well.
+
+ * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
+ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
+ $iv = pack('H*', trim($matches[2]));
+ $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
+ $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key);
+ $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
+ if ($ciphertext === false) {
+ $ciphertext = $key;
+ }
+ switch ($matches[1]) {
+ case 'AES-128-CBC':
+ if (!class_exists('Crypt_AES')) {
+ require_once('Crypt/AES.php');
+ }
+ $symkey = substr($symkey, 0, 16);
+ $crypto = new Crypt_AES();
+ break;
+ case 'DES-EDE3-CFB':
+ if (!class_exists('Crypt_TripleDES')) {
+ require_once('Crypt/TripleDES.php');
+ }
+ $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
+ break;
+ case 'DES-EDE3-CBC':
+ if (!class_exists('Crypt_TripleDES')) {
+ require_once('Crypt/TripleDES.php');
+ }
+ $crypto = new Crypt_TripleDES();
+ break;
+ case 'DES-CBC':
+ if (!class_exists('Crypt_DES')) {
+ require_once('Crypt/DES.php');
+ }
+ $crypto = new Crypt_DES();
+ break;
+ default:
+ return false;
+ }
+ $crypto->setKey($symkey);
+ $crypto->setIV($iv);
+ $decoded = $crypto->decrypt($ciphertext);
+ } else {
+ $decoded = preg_replace('#-.+-|[\r\n]| #', '', $key);
+ $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
+ }
+
+ if ($decoded !== false) {
+ $key = $decoded;
+ }
+
+ $components = array();
+
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
+ return false;
+ }
+ if ($this->_decodeLength($key) != strlen($key)) {
+ return false;
+ }
+
+ $tag = ord($this->_string_shift($key));
+ /* intended for keys for which OpenSSL's asn1parse returns the following:
+
+ 0:d=0 hl=4 l= 631 cons: SEQUENCE
+ 4:d=1 hl=2 l= 1 prim: INTEGER :00
+ 7:d=1 hl=2 l= 13 cons: SEQUENCE
+ 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
+ 20:d=2 hl=2 l= 0 prim: NULL
+ 22:d=1 hl=4 l= 609 prim: OCTET STRING */
+
+ if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
+ $this->_string_shift($key, 3);
+ $tag = CRYPT_RSA_ASN1_SEQUENCE;
+ }
+
+ if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
+ /* intended for keys for which OpenSSL's asn1parse returns the following:
+
+ 0:d=0 hl=4 l= 290 cons: SEQUENCE
+ 4:d=1 hl=2 l= 13 cons: SEQUENCE
+ 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
+ 17:d=2 hl=2 l= 0 prim: NULL
+ 19:d=1 hl=4 l= 271 prim: BIT STRING */
+ $this->_string_shift($key, $this->_decodeLength($key));
+ $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
+ $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
+ // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
+ // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
+ // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
+ if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
+ $this->_string_shift($key);
+ }
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
+ return false;
+ }
+ if ($this->_decodeLength($key) != strlen($key)) {
+ return false;
+ }
+ $tag = ord($this->_string_shift($key));
+ }
+ if ($tag != CRYPT_RSA_ASN1_INTEGER) {
+ return false;
+ }
+
+ $length = $this->_decodeLength($key);
+ $temp = $this->_string_shift($key, $length);
+ if (strlen($temp) != 1 || ord($temp) > 2) {
+ $components['modulus'] = new Math_BigInteger($temp, 256);
+ $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
+ $length = $this->_decodeLength($key);
+ $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+
+ return $components;
+ }
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
+ return false;
+ }
+ $length = $this->_decodeLength($key);
+ $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
+
+ if (!empty($key)) {
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
+ return false;
+ }
+ $this->_decodeLength($key);
+ while (!empty($key)) {
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
+ return false;
+ }
+ $this->_decodeLength($key);
+ $key = substr($key, 1);
+ $length = $this->_decodeLength($key);
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ $this->_string_shift($key);
+ $length = $this->_decodeLength($key);
+ $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
+ }
+ }
+
+ return $components;
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
+ $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key));
+ if ($key === false) {
+ return false;
+ }
+
+ $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
+
+ if (strlen($key) <= 4) {
+ return false;
+ }
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
+ $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
+ if (strlen($key) <= 4) {
+ return false;
+ }
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
+ $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
+
+ if ($cleanup && strlen($key)) {
+ if (strlen($key) <= 4) {
+ return false;
+ }
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
+ $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
+ return strlen($key) ? false : array(
+ 'modulus' => $realModulus,
+ 'publicExponent' => $modulus
+ );
+ } else {
+ return strlen($key) ? false : array(
+ 'modulus' => $modulus,
+ 'publicExponent' => $publicExponent
+ );
+ }
+ // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
+ // http://en.wikipedia.org/wiki/XML_Signature
+ case CRYPT_RSA_PRIVATE_FORMAT_XML:
+ case CRYPT_RSA_PUBLIC_FORMAT_XML:
+ $this->components = array();
+
+ $xml = xml_parser_create('UTF-8');
+ xml_set_object($xml, $this);
+ xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
+ xml_set_character_data_handler($xml, '_data_handler');
+ // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
+ if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
+ return false;
+ }
+
+ return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
+ // from PuTTY's SSHPUBK.C
+ case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
+ $components = array();
+ $key = preg_split('#\r\n|\r|\n#', $key);
+ $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
+ if ($type != 'ssh-rsa') {
+ return false;
+ }
+ $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
+
+ $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
+ $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
+ $public = substr($public, 11);
+ extract(unpack('Nlength', $this->_string_shift($public, 4)));
+ $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
+ extract(unpack('Nlength', $this->_string_shift($public, 4)));
+ $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
+
+ $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
+ $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
+
+ switch ($encryption) {
+ case 'aes256-cbc':
+ if (!class_exists('Crypt_AES')) {
+ require_once('Crypt/AES.php');
+ }
+ $symkey = '';
+ $sequence = 0;
+ while (strlen($symkey) < 32) {
+ $temp = pack('Na*', $sequence++, $this->password);
+ $symkey.= pack('H*', sha1($temp));
+ }
+ $symkey = substr($symkey, 0, 32);
+ $crypto = new Crypt_AES();
+ }
+
+ if ($encryption != 'none') {
+ $crypto->setKey($symkey);
+ $crypto->disablePadding();
+ $private = $crypto->decrypt($private);
+ if ($private === false) {
+ return false;
+ }
+ }
+
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
+ if (strlen($private) < $length) {
+ return false;
+ }
+ $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
+ if (strlen($private) < $length) {
+ return false;
+ }
+ $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
+ if (strlen($private) < $length) {
+ return false;
+ }
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
+
+ $temp = $components['primes'][1]->subtract($this->one);
+ $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
+ $temp = $components['primes'][2]->subtract($this->one);
+ $components['exponents'][] = $components['publicExponent']->modInverse($temp);
+
+ extract(unpack('Nlength', $this->_string_shift($private, 4)));
+ if (strlen($private) < $length) {
+ return false;
+ }
+ $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
+
+ return $components;
+ }
+ }
+
+ /**
+ * Returns the key size
+ *
+ * More specifically, this returns the size of the modulo in bits.
+ *
+ * @access public
+ * @return Integer
+ */
+ function getSize()
+ {
+ return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
+ }
+
+ /**
+ * Start Element Handler
+ *
+ * Called by xml_set_element_handler()
+ *
+ * @access private
+ * @param Resource $parser
+ * @param String $name
+ * @param Array $attribs
+ */
+ function _start_element_handler($parser, $name, $attribs)
+ {
+ //$name = strtoupper($name);
+ switch ($name) {
+ case 'MODULUS':
+ $this->current = &$this->components['modulus'];
+ break;
+ case 'EXPONENT':
+ $this->current = &$this->components['publicExponent'];
+ break;
+ case 'P':
+ $this->current = &$this->components['primes'][1];
+ break;
+ case 'Q':
+ $this->current = &$this->components['primes'][2];
+ break;
+ case 'DP':
+ $this->current = &$this->components['exponents'][1];
+ break;
+ case 'DQ':
+ $this->current = &$this->components['exponents'][2];
+ break;
+ case 'INVERSEQ':
+ $this->current = &$this->components['coefficients'][2];
+ break;
+ case 'D':
+ $this->current = &$this->components['privateExponent'];
+ break;
+ default:
+ unset($this->current);
+ }
+ $this->current = '';
+ }
+
+ /**
+ * Stop Element Handler
+ *
+ * Called by xml_set_element_handler()
+ *
+ * @access private
+ * @param Resource $parser
+ * @param String $name
+ */
+ function _stop_element_handler($parser, $name)
+ {
+ //$name = strtoupper($name);
+ if ($name == 'RSAKEYVALUE') {
+ return;
+ }
+ $this->current = new Math_BigInteger(base64_decode($this->current), 256);
+ }
+
+ /**
+ * Data Handler
+ *
+ * Called by xml_set_character_data_handler()
+ *
+ * @access private
+ * @param Resource $parser
+ * @param String $data
+ */
+ function _data_handler($parser, $data)
+ {
+ if (!isset($this->current) || is_object($this->current)) {
+ return;
+ }
+ $this->current.= trim($data);
+ }
+
+ /**
+ * Loads a public or private key
+ *
+ * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
+ *
+ * @access public
+ * @param String $key
+ * @param Integer $type optional
+ */
+ function loadKey($key, $type = false)
+ {
+ if ($type === false) {
+ $types = array(
+ CRYPT_RSA_PUBLIC_FORMAT_RAW,
+ CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
+ CRYPT_RSA_PRIVATE_FORMAT_XML,
+ CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
+ CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
+ );
+ foreach ($types as $type) {
+ $components = $this->_parseKey($key, $type);
+ if ($components !== false) {
+ break;
+ }
+ }
+
+ } else {
+ $components = $this->_parseKey($key, $type);
+ }
+
+ if ($components === false) {
+ return false;
+ }
+
+ $this->modulus = $components['modulus'];
+ $this->k = strlen($this->modulus->toBytes());
+ $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
+ if (isset($components['primes'])) {
+ $this->primes = $components['primes'];
+ $this->exponents = $components['exponents'];
+ $this->coefficients = $components['coefficients'];
+ $this->publicExponent = $components['publicExponent'];
+ } else {
+ $this->primes = array();
+ $this->exponents = array();
+ $this->coefficients = array();
+ $this->publicExponent = false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets the password
+ *
+ * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
+ * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
+ *
+ * @see createKey()
+ * @see loadKey()
+ * @access public
+ * @param String $password
+ */
+ function setPassword($password = false)
+ {
+ $this->password = $password;
+ }
+
+ /**
+ * Defines the public key
+ *
+ * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
+ * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
+ * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
+ * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
+ * exponent this won't work unless you manually add the public exponent.
+ *
+ * Do note that when a new key is loaded the index will be cleared.
+ *
+ * Returns true on success, false on failure
+ *
+ * @see getPublicKey()
+ * @access public
+ * @param String $key optional
+ * @param Integer $type optional
+ * @return Boolean
+ */
+ function setPublicKey($key = false, $type = false)
+ {
+ if ($key === false && !empty($this->modulus)) {
+ $this->publicExponent = $this->exponent;
+ return true;
+ }
+
+ if ($type === false) {
+ $types = array(
+ CRYPT_RSA_PUBLIC_FORMAT_RAW,
+ CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
+ CRYPT_RSA_PUBLIC_FORMAT_XML,
+ CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
+ );
+ foreach ($types as $type) {
+ $components = $this->_parseKey($key, $type);
+ if ($components !== false) {
+ break;
+ }
+ }
+ } else {
+ $components = $this->_parseKey($key, $type);
+ }
+
+ if ($components === false) {
+ return false;
+ }
+
+ if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
+ $this->modulus = $components['modulus'];
+ $this->exponent = $this->publicExponent = $components['publicExponent'];
+ return true;
+ }
+
+ $this->publicExponent = $components['publicExponent'];
+
+ return true;
+ }
+
+ /**
+ * Returns the public key
+ *
+ * The public key is only returned under two circumstances - if the private key had the public key embedded within it
+ * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
+ * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
+ *
+ * @see getPublicKey()
+ * @access public
+ * @param String $key
+ * @param Integer $type optional
+ */
+ function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
+ {
+ if (empty($this->modulus) || empty($this->publicExponent)) {
+ return false;
+ }
+
+ $oldFormat = $this->publicKeyFormat;
+ $this->publicKeyFormat = $type;
+ $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
+ $this->publicKeyFormat = $oldFormat;
+ return $temp;
+ }
+
+ /**
+ * Returns the private key
+ *
+ * The private key is only returned if the currently loaded key contains the constituent prime numbers.
+ *
+ * @see getPublicKey()
+ * @access public
+ * @param String $key
+ * @param Integer $type optional
+ */
+ function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
+ {
+ if (empty($this->primes)) {
+ return false;
+ }
+
+ $oldFormat = $this->privateKeyFormat;
+ $this->privateKeyFormat = $type;
+ $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
+ $this->privateKeyFormat = $oldFormat;
+ return $temp;
+ }
+
+ /**
+ * Returns a minimalistic private key
+ *
+ * Returns the private key without the prime number constituants. Structurally identical to a public key that
+ * hasn't been set as the public key
+ *
+ * @see getPrivateKey()
+ * @access private
+ * @param String $key
+ * @param Integer $type optional
+ */
+ function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
+ {
+ if (empty($this->modulus) || empty($this->exponent)) {
+ return false;
+ }
+
+ $oldFormat = $this->publicKeyFormat;
+ $this->publicKeyFormat = $mode;
+ $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
+ $this->publicKeyFormat = $oldFormat;
+ return $temp;
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * @access public
+ */
+ function __toString()
+ {
+ $key = $this->getPrivateKey($this->privateKeyFormat);
+ if ($key !== false) {
+ return $key;
+ }
+ $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
+ return $key !== false ? $key : '';
+ }
+
+ /**
+ * Generates the smallest and largest numbers requiring $bits bits
+ *
+ * @access private
+ * @param Integer $bits
+ * @return Array
+ */
+ function _generateMinMax($bits)
+ {
+ $bytes = $bits >> 3;
+ $min = str_repeat(chr(0), $bytes);
+ $max = str_repeat(chr(0xFF), $bytes);
+ $msb = $bits & 7;
+ if ($msb) {
+ $min = chr(1 << ($msb - 1)) . $min;
+ $max = chr((1 << $msb) - 1) . $max;
+ } else {
+ $min[0] = chr(0x80);
+ }
+
+ return array(
+ 'min' => new Math_BigInteger($min, 256),
+ 'max' => new Math_BigInteger($max, 256)
+ );
+ }
+
+ /**
+ * DER-decode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information.
+ *
+ * @access private
+ * @param String $string
+ * @return Integer
+ */
+ function _decodeLength(&$string)
+ {
+ $length = ord($this->_string_shift($string));
+ if ( $length & 0x80 ) { // definite length, long form
+ $length&= 0x7F;
+ $temp = $this->_string_shift($string, $length);
+ list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
+ }
+ return $length;
+ }
+
+ /**
+ * DER-encode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information.
+ *
+ * @access private
+ * @param Integer $length
+ * @return String
+ */
+ function _encodeLength($length)
+ {
+ if ($length <= 0x7F) {
+ return chr($length);
+ }
+
+ $temp = ltrim(pack('N', $length), chr(0));
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+
+ /**
+ * Determines the private key format
+ *
+ * @see createKey()
+ * @access public
+ * @param Integer $format
+ */
+ function setPrivateKeyFormat($format)
+ {
+ $this->privateKeyFormat = $format;
+ }
+
+ /**
+ * Determines the public key format
+ *
+ * @see createKey()
+ * @access public
+ * @param Integer $format
+ */
+ function setPublicKeyFormat($format)
+ {
+ $this->publicKeyFormat = $format;
+ }
+
+ /**
+ * Determines which hashing function should be used
+ *
+ * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
+ * decryption. If $hash isn't supported, sha1 is used.
+ *
+ * @access public
+ * @param String $hash
+ */
+ function setHash($hash)
+ {
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
+ switch ($hash) {
+ case 'md2':
+ case 'md5':
+ case 'sha1':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ $this->hash = new Crypt_Hash($hash);
+ $this->hashName = $hash;
+ break;
+ default:
+ $this->hash = new Crypt_Hash('sha1');
+ $this->hashName = 'sha1';
+ }
+ $this->hLen = $this->hash->getLength();
+ }
+
+ /**
+ * Determines which hashing function should be used for the mask generation function
+ *
+ * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
+ * best if Hash and MGFHash are set to the same thing this is not a requirement.
+ *
+ * @access public
+ * @param String $hash
+ */
+ function setMGFHash($hash)
+ {
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
+ switch ($hash) {
+ case 'md2':
+ case 'md5':
+ case 'sha1':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ $this->mgfHash = new Crypt_Hash($hash);
+ break;
+ default:
+ $this->mgfHash = new Crypt_Hash('sha1');
+ }
+ $this->mgfHLen = $this->mgfHash->getLength();
+ }
+
+ /**
+ * Determines the salt length
+ *
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
+ *
+ * Typical salt lengths in octets are hLen (the length of the output
+ * of the hash function Hash) and 0.
+ *
+ * @access public
+ * @param Integer $format
+ */
+ function setSaltLength($sLen)
+ {
+ $this->sLen = $sLen;
+ }
+
+ /**
+ * Integer-to-Octet-String primitive
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
+ *
+ * @access private
+ * @param Math_BigInteger $x
+ * @param Integer $xLen
+ * @return String
+ */
+ function _i2osp($x, $xLen)
+ {
+ $x = $x->toBytes();
+ if (strlen($x) > $xLen) {
+ user_error('Integer too large');
+ return false;
+ }
+ return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
+ }
+
+ /**
+ * Octet-String-to-Integer primitive
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
+ *
+ * @access private
+ * @param String $x
+ * @return Math_BigInteger
+ */
+ function _os2ip($x)
+ {
+ return new Math_BigInteger($x, 256);
+ }
+
+ /**
+ * Exponentiate with or without Chinese Remainder Theorem
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
+ *
+ * @access private
+ * @param Math_BigInteger $x
+ * @return Math_BigInteger
+ */
+ function _exponentiate($x)
+ {
+ if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
+ return $x->modPow($this->exponent, $this->modulus);
+ }
+
+ $num_primes = count($this->primes);
+
+ if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
+ $m_i = array(
+ 1 => $x->modPow($this->exponents[1], $this->primes[1]),
+ 2 => $x->modPow($this->exponents[2], $this->primes[2])
+ );
+ $h = $m_i[1]->subtract($m_i[2]);
+ $h = $h->multiply($this->coefficients[2]);
+ list(, $h) = $h->divide($this->primes[1]);
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
+
+ $r = $this->primes[1];
+ for ($i = 3; $i <= $num_primes; $i++) {
+ $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
+
+ $r = $r->multiply($this->primes[$i - 1]);
+
+ $h = $m_i->subtract($m);
+ $h = $h->multiply($this->coefficients[$i]);
+ list(, $h) = $h->divide($this->primes[$i]);
+
+ $m = $m->add($r->multiply($h));
+ }
+ } else {
+ $smallest = $this->primes[1];
+ for ($i = 2; $i <= $num_primes; $i++) {
+ if ($smallest->compare($this->primes[$i]) > 0) {
+ $smallest = $this->primes[$i];
+ }
+ }
+
+ $one = new Math_BigInteger(1);
+
+ $r = $one->random($one, $smallest->subtract($one));
+
+ $m_i = array(
+ 1 => $this->_blind($x, $r, 1),
+ 2 => $this->_blind($x, $r, 2)
+ );
+ $h = $m_i[1]->subtract($m_i[2]);
+ $h = $h->multiply($this->coefficients[2]);
+ list(, $h) = $h->divide($this->primes[1]);
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
+
+ $r = $this->primes[1];
+ for ($i = 3; $i <= $num_primes; $i++) {
+ $m_i = $this->_blind($x, $r, $i);
+
+ $r = $r->multiply($this->primes[$i - 1]);
+
+ $h = $m_i->subtract($m);
+ $h = $h->multiply($this->coefficients[$i]);
+ list(, $h) = $h->divide($this->primes[$i]);
+
+ $m = $m->add($r->multiply($h));
+ }
+ }
+
+ return $m;
+ }
+
+ /**
+ * Performs RSA Blinding
+ *
+ * Protects against timing attacks by employing RSA Blinding.
+ * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
+ *
+ * @access private
+ * @param Math_BigInteger $x
+ * @param Math_BigInteger $r
+ * @param Integer $i
+ * @return Math_BigInteger
+ */
+ function _blind($x, $r, $i)
+ {
+ $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
+ $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
+
+ $r = $r->modInverse($this->primes[$i]);
+ $x = $x->multiply($r);
+ list(, $x) = $x->divide($this->primes[$i]);
+
+ return $x;
+ }
+
+ /**
+ * Performs blinded RSA equality testing
+ *
+ * Protects against a particular type of timing attack described.
+ *
+ * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Dont use MessageDigest.isEquals)}
+ *
+ * Thanks for the heads up singpolyma!
+ *
+ * @access private
+ * @param String $x
+ * @param String $y
+ * @return Boolean
+ */
+ function _equals($x, $y)
+ {
+ if (strlen($x) != strlen($y)) {
+ return false;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < strlen($x); $i++) {
+ $result |= ord($x[$i]) ^ ord($y[$i]);
+ }
+
+ return $result == 0;
+ }
+
+ /**
+ * RSAEP
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
+ *
+ * @access private
+ * @param Math_BigInteger $m
+ * @return Math_BigInteger
+ */
+ function _rsaep($m)
+ {
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
+ user_error('Message representative out of range');
+ return false;
+ }
+ return $this->_exponentiate($m);
+ }
+
+ /**
+ * RSADP
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
+ *
+ * @access private
+ * @param Math_BigInteger $c
+ * @return Math_BigInteger
+ */
+ function _rsadp($c)
+ {
+ if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
+ user_error('Ciphertext representative out of range');
+ return false;
+ }
+ return $this->_exponentiate($c);
+ }
+
+ /**
+ * RSASP1
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
+ *
+ * @access private
+ * @param Math_BigInteger $m
+ * @return Math_BigInteger
+ */
+ function _rsasp1($m)
+ {
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
+ user_error('Message representative out of range');
+ return false;
+ }
+ return $this->_exponentiate($m);
+ }
+
+ /**
+ * RSAVP1
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
+ *
+ * @access private
+ * @param Math_BigInteger $s
+ * @return Math_BigInteger
+ */
+ function _rsavp1($s)
+ {
+ if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
+ user_error('Signature representative out of range');
+ return false;
+ }
+ return $this->_exponentiate($s);
+ }
+
+ /**
+ * MGF1
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
+ *
+ * @access private
+ * @param String $mgfSeed
+ * @param Integer $mgfLen
+ * @return String
+ */
+ function _mgf1($mgfSeed, $maskLen)
+ {
+ // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
+
+ $t = '';
+ $count = ceil($maskLen / $this->mgfHLen);
+ for ($i = 0; $i < $count; $i++) {
+ $c = pack('N', $i);
+ $t.= $this->mgfHash->hash($mgfSeed . $c);
+ }
+
+ return substr($t, 0, $maskLen);
+ }
+
+ /**
+ * RSAES-OAEP-ENCRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
+ * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
+ *
+ * @access private
+ * @param String $m
+ * @param String $l
+ * @return String
+ */
+ function _rsaes_oaep_encrypt($m, $l = '')
+ {
+ $mLen = strlen($m);
+
+ // Length checking
+
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ if ($mLen > $this->k - 2 * $this->hLen - 2) {
+ user_error('Message too long');
+ return false;
+ }
+
+ // EME-OAEP encoding
+
+ $lHash = $this->hash->hash($l);
+ $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
+ $db = $lHash . $ps . chr(1) . $m;
+ $seed = crypt_random_string($this->hLen);
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
+ $maskedDB = $db ^ $dbMask;
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
+ $maskedSeed = $seed ^ $seedMask;
+ $em = chr(0) . $maskedSeed . $maskedDB;
+
+ // RSA encryption
+
+ $m = $this->_os2ip($em);
+ $c = $this->_rsaep($m);
+ $c = $this->_i2osp($c, $this->k);
+
+ // Output the ciphertext C
+
+ return $c;
+ }
+
+ /**
+ * RSAES-OAEP-DECRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
+ * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
+ *
+ * Note. Care must be taken to ensure that an opponent cannot
+ * distinguish the different error conditions in Step 3.g, whether by
+ * error message or timing, or, more generally, learn partial
+ * information about the encoded message EM. Otherwise an opponent may
+ * be able to obtain useful information about the decryption of the
+ * ciphertext C, leading to a chosen-ciphertext attack such as the one
+ * observed by Manger [36].
+ *
+ * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
+ *
+ * Both the encryption and the decryption operations of RSAES-OAEP take
+ * the value of a label L as input. In this version of PKCS #1, L is
+ * the empty string; other uses of the label are outside the scope of
+ * this document.
+ *
+ * @access private
+ * @param String $c
+ * @param String $l
+ * @return String
+ */
+ function _rsaes_oaep_decrypt($c, $l = '')
+ {
+ // Length checking
+
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
+ user_error('Decryption error');
+ return false;
+ }
+
+ // RSA decryption
+
+ $c = $this->_os2ip($c);
+ $m = $this->_rsadp($c);
+ if ($m === false) {
+ user_error('Decryption error');
+ return false;
+ }
+ $em = $this->_i2osp($m, $this->k);
+
+ // EME-OAEP decoding
+
+ $lHash = $this->hash->hash($l);
+ $y = ord($em[0]);
+ $maskedSeed = substr($em, 1, $this->hLen);
+ $maskedDB = substr($em, $this->hLen + 1);
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
+ $seed = $maskedSeed ^ $seedMask;
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
+ $db = $maskedDB ^ $dbMask;
+ $lHash2 = substr($db, 0, $this->hLen);
+ $m = substr($db, $this->hLen);
+ if ($lHash != $lHash2) {
+ user_error('Decryption error');
+ return false;
+ }
+ $m = ltrim($m, chr(0));
+ if (ord($m[0]) != 1) {
+ user_error('Decryption error');
+ return false;
+ }
+
+ // Output the message M
+
+ return substr($m, 1);
+ }
+
+ /**
+ * RSAES-PKCS1-V1_5-ENCRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
+ *
+ * @access private
+ * @param String $m
+ * @return String
+ */
+ function _rsaes_pkcs1_v1_5_encrypt($m)
+ {
+ $mLen = strlen($m);
+
+ // Length checking
+
+ if ($mLen > $this->k - 11) {
+ user_error('Message too long');
+ return false;
+ }
+
+ // EME-PKCS1-v1_5 encoding
+ $psLen = $this->k - $mLen - 3;
+ $ps = '';
+ while (strlen($ps) != $psLen) {
+ $temp = crypt_random_string($psLen - strlen($ps));
+ $temp = str_replace("\x00", '', $temp);
+ $ps.= $temp;
+ }
+ $em = chr(0) . chr(2) . $ps . chr(0) . $m;
+
+ // RSA encryption
+ $m = $this->_os2ip($em);
+ $c = $this->_rsaep($m);
+ $c = $this->_i2osp($c, $this->k);
+
+ // Output the ciphertext C
+
+ return $c;
+ }
+
+ /**
+ * RSAES-PKCS1-V1_5-DECRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
+ *
+ * For compatability purposes, this function departs slightly from the description given in RFC3447.
+ * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
+ * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
+ * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
+ * to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the
+ * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
+ *
+ * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
+ * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
+ * not private key encrypted ciphertext's.
+ *
+ * @access private
+ * @param String $c
+ * @return String
+ */
+ function _rsaes_pkcs1_v1_5_decrypt($c)
+ {
+ // Length checking
+
+ if (strlen($c) != $this->k) { // or if k < 11
+ user_error('Decryption error');
+ return false;
+ }
+
+ // RSA decryption
+
+ $c = $this->_os2ip($c);
+ $m = $this->_rsadp($c);
+
+ if ($m === false) {
+ user_error('Decryption error');
+ return false;
+ }
+ $em = $this->_i2osp($m, $this->k);
+
+ // EME-PKCS1-v1_5 decoding
+
+ if (ord($em[0]) != 0 || ord($em[1]) > 2) {
+ user_error('Decryption error');
+ return false;
+ }
+
+ $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
+ $m = substr($em, strlen($ps) + 3);
+
+ if (strlen($ps) < 8) {
+ user_error('Decryption error');
+ return false;
+ }
+
+ // Output M
+
+ return $m;
+ }
+
+ /**
+ * EMSA-PSS-ENCODE
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
+ *
+ * @access private
+ * @param String $m
+ * @param Integer $emBits
+ */
+ function _emsa_pss_encode($m, $emBits)
+ {
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
+
+ $mHash = $this->hash->hash($m);
+ if ($emLen < $this->hLen + $sLen + 2) {
+ user_error('Encoding error');
+ return false;
+ }
+
+ $salt = crypt_random_string($sLen);
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
+ $h = $this->hash->hash($m2);
+ $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
+ $db = $ps . chr(1) . $salt;
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
+ $maskedDB = $db ^ $dbMask;
+ $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
+ $em = $maskedDB . $h . chr(0xBC);
+
+ return $em;
+ }
+
+ /**
+ * EMSA-PSS-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
+ *
+ * @access private
+ * @param String $m
+ * @param String $em
+ * @param Integer $emBits
+ * @return String
+ */
+ function _emsa_pss_verify($m, $em, $emBits)
+ {
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
+
+ $mHash = $this->hash->hash($m);
+ if ($emLen < $this->hLen + $sLen + 2) {
+ return false;
+ }
+
+ if ($em[strlen($em) - 1] != chr(0xBC)) {
+ return false;
+ }
+
+ $maskedDB = substr($em, 0, -$this->hLen - 1);
+ $h = substr($em, -$this->hLen - 1, $this->hLen);
+ $temp = chr(0xFF << ($emBits & 7));
+ if ((~$maskedDB[0] & $temp) != $temp) {
+ return false;
+ }
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
+ $db = $maskedDB ^ $dbMask;
+ $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
+ $temp = $emLen - $this->hLen - $sLen - 2;
+ if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
+ return false;
+ }
+ $salt = substr($db, $temp + 1); // should be $sLen long
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
+ $h2 = $this->hash->hash($m2);
+ return $this->_equals($h, $h2);
+ }
+
+ /**
+ * RSASSA-PSS-SIGN
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
+ *
+ * @access private
+ * @param String $m
+ * @return String
+ */
+ function _rsassa_pss_sign($m)
+ {
+ // EMSA-PSS encoding
+
+ $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
+
+ // RSA signature
+
+ $m = $this->_os2ip($em);
+ $s = $this->_rsasp1($m);
+ $s = $this->_i2osp($s, $this->k);
+
+ // Output the signature S
+
+ return $s;
+ }
+
+ /**
+ * RSASSA-PSS-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
+ *
+ * @access private
+ * @param String $m
+ * @param String $s
+ * @return String
+ */
+ function _rsassa_pss_verify($m, $s)
+ {
+ // Length checking
+
+ if (strlen($s) != $this->k) {
+ user_error('Invalid signature');
+ return false;
+ }
+
+ // RSA verification
+
+ $modBits = 8 * $this->k;
+
+ $s2 = $this->_os2ip($s);
+ $m2 = $this->_rsavp1($s2);
+ if ($m2 === false) {
+ user_error('Invalid signature');
+ return false;
+ }
+ $em = $this->_i2osp($m2, $modBits >> 3);
+ if ($em === false) {
+ user_error('Invalid signature');
+ return false;
+ }
+
+ // EMSA-PSS verification
+
+ return $this->_emsa_pss_verify($m, $em, $modBits - 1);
+ }
+
+ /**
+ * EMSA-PKCS1-V1_5-ENCODE
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
+ *
+ * @access private
+ * @param String $m
+ * @param Integer $emLen
+ * @return String
+ */
+ function _emsa_pkcs1_v1_5_encode($m, $emLen)
+ {
+ $h = $this->hash->hash($m);
+ if ($h === false) {
+ return false;
+ }
+
+ // see http://tools.ietf.org/html/rfc3447#page-43
+ switch ($this->hashName) {
+ case 'md2':
+ $t = pack('H*', '3020300c06082a864886f70d020205000410');
+ break;
+ case 'md5':
+ $t = pack('H*', '3020300c06082a864886f70d020505000410');
+ break;
+ case 'sha1':
+ $t = pack('H*', '3021300906052b0e03021a05000414');
+ break;
+ case 'sha256':
+ $t = pack('H*', '3031300d060960864801650304020105000420');
+ break;
+ case 'sha384':
+ $t = pack('H*', '3041300d060960864801650304020205000430');
+ break;
+ case 'sha512':
+ $t = pack('H*', '3051300d060960864801650304020305000440');
+ }
+ $t.= $h;
+ $tLen = strlen($t);
+
+ if ($emLen < $tLen + 11) {
+ user_error('Intended encoded message length too short');
+ return false;
+ }
+
+ $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
+
+ $em = "\0\1$ps\0$t";
+
+ return $em;
+ }
+
+ /**
+ * RSASSA-PKCS1-V1_5-SIGN
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
+ *
+ * @access private
+ * @param String $m
+ * @return String
+ */
+ function _rsassa_pkcs1_v1_5_sign($m)
+ {
+ // EMSA-PKCS1-v1_5 encoding
+
+ $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
+ if ($em === false) {
+ user_error('RSA modulus too short');
+ return false;
+ }
+
+ // RSA signature
+
+ $m = $this->_os2ip($em);
+ $s = $this->_rsasp1($m);
+ $s = $this->_i2osp($s, $this->k);
+
+ // Output the signature S
+
+ return $s;
+ }
+
+ /**
+ * RSASSA-PKCS1-V1_5-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
+ *
+ * @access private
+ * @param String $m
+ * @return String
+ */
+ function _rsassa_pkcs1_v1_5_verify($m, $s)
+ {
+ // Length checking
+
+ if (strlen($s) != $this->k) {
+ user_error('Invalid signature');
+ return false;
+ }
+
+ // RSA verification
+
+ $s = $this->_os2ip($s);
+ $m2 = $this->_rsavp1($s);
+ if ($m2 === false) {
+ user_error('Invalid signature');
+ return false;
+ }
+ $em = $this->_i2osp($m2, $this->k);
+ if ($em === false) {
+ user_error('Invalid signature');
+ return false;
+ }
+
+ // EMSA-PKCS1-v1_5 encoding
+
+ $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
+ if ($em2 === false) {
+ user_error('RSA modulus too short');
+ return false;
+ }
+
+ // Compare
+ return $this->_equals($em, $em2);
+ }
+
+ /**
+ * Set Encryption Mode
+ *
+ * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
+ *
+ * @access public
+ * @param Integer $mode
+ */
+ function setEncryptionMode($mode)
+ {
+ $this->encryptionMode = $mode;
+ }
+
+ /**
+ * Set Signature Mode
+ *
+ * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
+ *
+ * @access public
+ * @param Integer $mode
+ */
+ function setSignatureMode($mode)
+ {
+ $this->signatureMode = $mode;
+ }
+
+ /**
+ * Encryption
+ *
+ * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
+ * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
+ * be concatenated together.
+ *
+ * @see decrypt()
+ * @access public
+ * @param String $plaintext
+ * @return String
+ */
+ function encrypt($plaintext)
+ {
+ switch ($this->encryptionMode) {
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
+ $length = $this->k - 11;
+ if ($length <= 0) {
+ return false;
+ }
+
+ $plaintext = str_split($plaintext, $length);
+ $ciphertext = '';
+ foreach ($plaintext as $m) {
+ $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
+ }
+ return $ciphertext;
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
+ default:
+ $length = $this->k - 2 * $this->hLen - 2;
+ if ($length <= 0) {
+ return false;
+ }
+
+ $plaintext = str_split($plaintext, $length);
+ $ciphertext = '';
+ foreach ($plaintext as $m) {
+ $ciphertext.= $this->_rsaes_oaep_encrypt($m);
+ }
+ return $ciphertext;
+ }
+ }
+
+ /**
+ * Decryption
+ *
+ * @see encrypt()
+ * @access public
+ * @param String $plaintext
+ * @return String
+ */
+ function decrypt($ciphertext)
+ {
+ if ($this->k <= 0) {
+ return false;
+ }
+
+ $ciphertext = str_split($ciphertext, $this->k);
+ $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
+
+ $plaintext = '';
+
+ switch ($this->encryptionMode) {
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
+ $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
+ break;
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
+ default:
+ $decrypt = '_rsaes_oaep_decrypt';
+ }
+
+ foreach ($ciphertext as $c) {
+ $temp = $this->$decrypt($c);
+ if ($temp === false) {
+ return false;
+ }
+ $plaintext.= $temp;
+ }
+
+ return $plaintext;
+ }
+
+ /**
+ * Create a signature
+ *
+ * @see verify()
+ * @access public
+ * @param String $message
+ * @return String
+ */
+ function sign($message)
+ {
+ if (empty($this->modulus) || empty($this->exponent)) {
+ return false;
+ }
+
+ switch ($this->signatureMode) {
+ case CRYPT_RSA_SIGNATURE_PKCS1:
+ return $this->_rsassa_pkcs1_v1_5_sign($message);
+ //case CRYPT_RSA_SIGNATURE_PSS:
+ default:
+ return $this->_rsassa_pss_sign($message);
+ }
+ }
+
+ /**
+ * Verifies a signature
+ *
+ * @see sign()
+ * @access public
+ * @param String $message
+ * @param String $signature
+ * @return Boolean
+ */
+ function verify($message, $signature)
+ {
+ if (empty($this->modulus) || empty($this->exponent)) {
+ return false;
+ }
+
+ switch ($this->signatureMode) {
+ case CRYPT_RSA_SIGNATURE_PKCS1:
+ return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
+ //case CRYPT_RSA_SIGNATURE_PSS:
+ default:
+ return $this->_rsassa_pss_verify($message, $signature);
+ }
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php
new file mode 100644
index 00000000000..a60857df95d
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php
@@ -0,0 +1,243 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Random Number Generator
+ *
+ * PHP versions 4 and 5
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/Random.php');
+ *
+ * echo bin2hex(crypt_random_string(8));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_Random
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Generate a random string.
+ *
+ * Although microoptimizations are generally discouraged as they impair readability this function is ripe with
+ * microoptimizations because this function has the potential of being called a huge number of times.
+ * eg. for RSA key generation.
+ *
+ * @param Integer $length
+ * @return String
+ * @access public
+ */
+function crypt_random_string($length) {
+ // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
+ if ((PHP_OS & "\xDF\xDF\xDF") === 'WIN') {
+ // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
+ // ie. class_alias is a function that was introduced in PHP 5.3
+ if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) {
+ return mcrypt_create_iv($length);
+ }
+ // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
+ // to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
+ // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
+ // call php_win32_get_random_bytes():
+ //
+ // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
+ // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
+ //
+ // php_win32_get_random_bytes() is defined thusly:
+ //
+ // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
+ //
+ // we're calling it, all the same, in the off chance that the mcrypt extension is not available
+ if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
+ return openssl_random_pseudo_bytes($length);
+ }
+ } else {
+ // method 1. the fastest
+ if (function_exists('openssl_random_pseudo_bytes')) {
+ return openssl_random_pseudo_bytes($length);
+ }
+ // method 2
+ static $fp = true;
+ if ($fp === true) {
+ // warning's will be output unles the error suppression operator is used. errors such as
+ // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
+ $fp = @fopen('/dev/urandom', 'rb');
+ }
+ if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
+ return fread($fp, $length);
+ }
+ // method 3. pretty much does the same thing as method 2 per the following url:
+ // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
+ // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
+ // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
+ // restrictions or some such
+ if (function_exists('mcrypt_create_iv')) {
+ return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ }
+ }
+ // at this point we have no choice but to use a pure-PHP CSPRNG
+
+ // cascade entropy across multiple PHP instances by fixing the session and collecting all
+ // environmental variables, including the previous session data and the current session
+ // data.
+ //
+ // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
+ // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
+ // PHP isn't low level to be able to use those as sources and on a web server there's not likely
+ // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
+ // however. a ton of people visiting the website. obviously you don't want to base your seeding
+ // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
+ // by the user and (2) this isn't just looking at the data sent by the current user - it's based
+ // on the data sent by all users. one user requests the page and a hash of their info is saved.
+ // another user visits the page and the serialization of their data is utilized along with the
+ // server envirnment stuff and a hash of the previous http request data (which itself utilizes
+ // a hash of the session data before that). certainly an attacker should be assumed to have
+ // full control over his own http requests. he, however, is not going to have control over
+ // everyone's http requests.
+ static $crypto = false, $v;
+ if ($crypto === false) {
+ // save old session data
+ $old_session_id = session_id();
+ $old_use_cookies = ini_get('session.use_cookies');
+ $old_session_cache_limiter = session_cache_limiter();
+ if (isset($_SESSION)) {
+ $_OLD_SESSION = $_SESSION;
+ }
+ if ($old_session_id != '') {
+ session_write_close();
+ }
+
+ session_id(1);
+ ini_set('session.use_cookies', 0);
+ session_cache_limiter('');
+ session_start();
+
+ $v = $seed = $_SESSION['seed'] = pack('H*', sha1(
+ serialize($_SERVER) .
+ serialize($_POST) .
+ serialize($_GET) .
+ serialize($_COOKIE) .
+ serialize($_GLOBAL) .
+ serialize($_SESSION) .
+ serialize($_OLD_SESSION)
+ ));
+ if (!isset($_SESSION['count'])) {
+ $_SESSION['count'] = 0;
+ }
+ $_SESSION['count']++;
+
+ session_write_close();
+
+ // restore old session data
+ if ($old_session_id != '') {
+ session_id($old_session_id);
+ session_start();
+ ini_set('session.use_cookies', $old_use_cookies);
+ session_cache_limiter($old_session_cache_limiter);
+ } else {
+ if (isset($_OLD_SESSION)) {
+ $_SESSION = $_OLD_SESSION;
+ unset($_OLD_SESSION);
+ } else {
+ unset($_SESSION);
+ }
+ }
+
+ // in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
+ // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
+ // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
+ // original hash and the current hash. we'll be emulating that. for more info see the following URL:
+ //
+ // http://tools.ietf.org/html/rfc4253#section-7.2
+ //
+ // see the is_string($crypto) part for an example of how to expand the keys
+ $key = pack('H*', sha1($seed . 'A'));
+ $iv = pack('H*', sha1($seed . 'C'));
+
+ // ciphers are used as per the nist.gov link below. also, see this link:
+ //
+ // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
+ switch (true) {
+ case class_exists('Crypt_AES'):
+ $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
+ break;
+ case class_exists('Crypt_TripleDES'):
+ $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
+ break;
+ case class_exists('Crypt_DES'):
+ $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
+ break;
+ case class_exists('Crypt_RC4'):
+ $crypto = new Crypt_RC4();
+ break;
+ default:
+ $crypto = $seed;
+ return crypt_random_string($length);
+ }
+
+ $crypto->setKey($key);
+ $crypto->setIV($iv);
+ $crypto->enableContinuousBuffer();
+ }
+
+ if (is_string($crypto)) {
+ // the following is based off of ANSI X9.31:
+ //
+ // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
+ //
+ // OpenSSL uses that same standard for it's random numbers:
+ //
+ // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
+ // (do a search for "ANS X9.31 A.2.4")
+ //
+ // ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see
+ // later on in the code) but if they're not we'll use sha1
+ $result = '';
+ while (strlen($result) < $length) { // each loop adds 20 bytes
+ // microtime() isn't packed as "densely" as it could be but then neither is that the idea.
+ // the idea is simply to ensure that each "block" has a unique element to it.
+ $i = pack('H*', sha1(microtime()));
+ $r = pack('H*', sha1($i ^ $v));
+ $v = pack('H*', sha1($r ^ $i));
+ $result.= $r;
+ }
+ return substr($result, 0, $length);
+ }
+
+ //return $crypto->encrypt(str_repeat("\0", $length));
+
+ $result = '';
+ while (strlen($result) < $length) {
+ $i = $crypto->encrypt(microtime());
+ $r = $crypto->encrypt($i ^ $v);
+ $v = $crypto->encrypt($r ^ $i);
+ $result.= $r;
+ }
+ return substr($result, 0, $length);
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php
new file mode 100644
index 00000000000..335d5233c4d
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php
@@ -0,0 +1,1525 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of Rijndael.
+ *
+ * Does not use mcrypt, even when available, for reasons that are explained below.
+ *
+ * PHP versions 4 and 5
+ *
+ * If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
+ * {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
+ * {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
+ * 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until
+ * {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated.
+ *
+ * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
+ * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
+ * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
+ * are first defined as valid key / block lengths in
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
+ * Extensions: Other block and Cipher Key lengths.
+ *
+ * {@internal The variable names are the same as those in
+ * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/Rijndael.php');
+ *
+ * $rijndael = new Crypt_Rijndael();
+ *
+ * $rijndael->setKey('abcdefghijklmnop');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $rijndael->decrypt($rijndael->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_Rijndael
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVIII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**#@+
+ * @access public
+ * @see Crypt_Rijndael::encrypt()
+ * @see Crypt_Rijndael::decrypt()
+ */
+/**
+ * Encrypt / decrypt using the Counter mode.
+ *
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
+ */
+define('CRYPT_RIJNDAEL_MODE_CTR', -1);
+/**
+ * Encrypt / decrypt using the Electronic Code Book mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
+ */
+define('CRYPT_RIJNDAEL_MODE_ECB', 1);
+/**
+ * Encrypt / decrypt using the Code Book Chaining mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+ */
+define('CRYPT_RIJNDAEL_MODE_CBC', 2);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
+ */
+define('CRYPT_RIJNDAEL_MODE_CFB', 3);
+/**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
+ */
+define('CRYPT_RIJNDAEL_MODE_OFB', 4);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Crypt_Rijndael::Crypt_Rijndael()
+ */
+/**
+ * Toggles the internal implementation
+ */
+define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1);
+/**
+ * Toggles the mcrypt implementation
+ */
+define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of Rijndael.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_Rijndael
+ */
+class Crypt_Rijndael {
+ /**
+ * The Encryption Mode
+ *
+ * @see Crypt_Rijndael::Crypt_Rijndael()
+ * @var Integer
+ * @access private
+ */
+ var $mode;
+
+ /**
+ * The Key
+ *
+ * @see Crypt_Rijndael::setKey()
+ * @var String
+ * @access private
+ */
+ var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+ /**
+ * The Initialization Vector
+ *
+ * @see Crypt_Rijndael::setIV()
+ * @var String
+ * @access private
+ */
+ var $iv = '';
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_Rijndael::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $encryptIV = '';
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_Rijndael::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $decryptIV = '';
+
+ /**
+ * Continuous Buffer status
+ *
+ * @see Crypt_Rijndael::enableContinuousBuffer()
+ * @var Boolean
+ * @access private
+ */
+ var $continuousBuffer = false;
+
+ /**
+ * Padding status
+ *
+ * @see Crypt_Rijndael::enablePadding()
+ * @var Boolean
+ * @access private
+ */
+ var $padding = true;
+
+ /**
+ * Does the key schedule need to be (re)calculated?
+ *
+ * @see setKey()
+ * @see setBlockLength()
+ * @see setKeyLength()
+ * @var Boolean
+ * @access private
+ */
+ var $changed = true;
+
+ /**
+ * Has the key length explicitly been set or should it be derived from the key, itself?
+ *
+ * @see setKeyLength()
+ * @var Boolean
+ * @access private
+ */
+ var $explicit_key_length = false;
+
+ /**
+ * The Key Schedule
+ *
+ * @see _setup()
+ * @var Array
+ * @access private
+ */
+ var $w;
+
+ /**
+ * The Inverse Key Schedule
+ *
+ * @see _setup()
+ * @var Array
+ * @access private
+ */
+ var $dw;
+
+ /**
+ * The Block Length
+ *
+ * @see setBlockLength()
+ * @var Integer
+ * @access private
+ * @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with
+ * $Nb because we need this value and not $Nb to pad strings appropriately.
+ */
+ var $block_size = 16;
+
+ /**
+ * The Block Length divided by 32
+ *
+ * @see setBlockLength()
+ * @var Integer
+ * @access private
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
+ * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
+ * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
+ * of that, we'll just precompute it once.
+ *
+ */
+ var $Nb = 4;
+
+ /**
+ * The Key Length
+ *
+ * @see setKeyLength()
+ * @var Integer
+ * @access private
+ * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size
+ * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could
+ * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
+ * of that, we'll just precompute it once.
+ */
+ var $key_size = 16;
+
+ /**
+ * The Key Length divided by 32
+ *
+ * @see setKeyLength()
+ * @var Integer
+ * @access private
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
+ */
+ var $Nk = 4;
+
+ /**
+ * The Number of Rounds
+ *
+ * @var Integer
+ * @access private
+ * @internal The max value is 14, the min value is 10.
+ */
+ var $Nr;
+
+ /**
+ * Shift offsets
+ *
+ * @var Array
+ * @access private
+ */
+ var $c;
+
+ /**
+ * Precomputed mixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $t0;
+
+ /**
+ * Precomputed mixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $t1;
+
+ /**
+ * Precomputed mixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $t2;
+
+ /**
+ * Precomputed mixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $t3;
+
+ /**
+ * Precomputed invMixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $dt0;
+
+ /**
+ * Precomputed invMixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $dt1;
+
+ /**
+ * Precomputed invMixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $dt2;
+
+ /**
+ * Precomputed invMixColumns table
+ *
+ * @see Crypt_Rijndael()
+ * @var Array
+ * @access private
+ */
+ var $dt3;
+
+ /**
+ * Is the mode one that is paddable?
+ *
+ * @see Crypt_Rijndael::Crypt_Rijndael()
+ * @var Boolean
+ * @access private
+ */
+ var $paddable = false;
+
+ /**
+ * Encryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_Rijndael::encrypt()
+ * @var String
+ * @access private
+ */
+ var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0);
+
+ /**
+ * Decryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_Rijndael::decrypt()
+ * @var String
+ * @access private
+ */
+ var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0);
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
+ * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used.
+ *
+ * @param optional Integer $mode
+ * @return Crypt_Rijndael
+ * @access public
+ */
+ function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC)
+ {
+ switch ($mode) {
+ case CRYPT_RIJNDAEL_MODE_ECB:
+ case CRYPT_RIJNDAEL_MODE_CBC:
+ $this->paddable = true;
+ $this->mode = $mode;
+ break;
+ case CRYPT_RIJNDAEL_MODE_CTR:
+ case CRYPT_RIJNDAEL_MODE_CFB:
+ case CRYPT_RIJNDAEL_MODE_OFB:
+ $this->mode = $mode;
+ break;
+ default:
+ $this->paddable = true;
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
+ }
+
+ $t3 = &$this->t3;
+ $t2 = &$this->t2;
+ $t1 = &$this->t1;
+ $t0 = &$this->t0;
+
+ $dt3 = &$this->dt3;
+ $dt2 = &$this->dt2;
+ $dt1 = &$this->dt1;
+ $dt0 = &$this->dt0;
+
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
+ // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
+ // those are the names we'll use.
+ $t3 = array(
+ 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
+ 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
+ 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
+ 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
+ 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
+ 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
+ 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
+ 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
+ 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
+ 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
+ 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
+ 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
+ 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
+ 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
+ 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
+ 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
+ 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
+ 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
+ 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
+ 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
+ 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
+ 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
+ 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
+ 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
+ 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
+ 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
+ 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
+ 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
+ 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
+ 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
+ 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
+ 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
+ );
+
+ $dt3 = array(
+ 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
+ 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
+ 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
+ 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
+ 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
+ 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
+ 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
+ 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
+ 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
+ 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
+ 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
+ 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
+ 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
+ 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
+ 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
+ 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
+ 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
+ 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
+ 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
+ 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
+ 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
+ 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
+ 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
+ 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
+ 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
+ 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
+ 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
+ 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
+ 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
+ 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
+ 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
+ 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
+ );
+
+ for ($i = 0; $i < 256; $i++) {
+ $t2[] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF);
+ $t1[] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF);
+ $t0[] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF);
+
+ $dt2[] = (($dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF);
+ $dt1[] = (($dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF);
+ $dt0[] = (($dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF);
+ }
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
+ * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length
+ * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the
+ * excess bits.
+ *
+ * If the key is not explicitly set, it'll be assumed to be all null bytes.
+ *
+ * @access public
+ * @param String $key
+ */
+ function setKey($key)
+ {
+ $this->key = $key;
+ $this->changed = true;
+ }
+
+ /**
+ * Sets the initialization vector. (optional)
+ *
+ * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
+ * to be all zero's.
+ *
+ * @access public
+ * @param String $iv
+ */
+ function setIV($iv)
+ {
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0));
+ }
+
+ /**
+ * Sets the key length
+ *
+ * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
+ *
+ * @access public
+ * @param Integer $length
+ */
+ function setKeyLength($length)
+ {
+ $length >>= 5;
+ if ($length > 8) {
+ $length = 8;
+ } else if ($length < 4) {
+ $length = 4;
+ }
+ $this->Nk = $length;
+ $this->key_size = $length << 2;
+
+ $this->explicit_key_length = true;
+ $this->changed = true;
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
+ * $hash, $salt, $method
+ * Set $dkLen by calling setKeyLength()
+ *
+ * @param String $password
+ * @param optional String $method
+ * @access public
+ */
+ function setPassword($password, $method = 'pbkdf2')
+ {
+ $key = '';
+
+ switch ($method) {
+ default: // 'pbkdf2'
+ list(, , $hash, $salt, $count) = func_get_args();
+ if (!isset($hash)) {
+ $hash = 'sha1';
+ }
+ // WPA and WPA2 use the SSID as the salt
+ if (!isset($salt)) {
+ $salt = 'phpseclib';
+ }
+ // RFC2898#section-4.2 uses 1,000 iterations by default
+ // WPA and WPA2 use 4,096.
+ if (!isset($count)) {
+ $count = 1000;
+ }
+
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+
+ $i = 1;
+ while (strlen($key) < $this->key_size) { // $dkLen == $this->key_size
+ //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
+ $hmac = new Crypt_Hash();
+ $hmac->setHash($hash);
+ $hmac->setKey($password);
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
+ for ($j = 2; $j <= $count; $j++) {
+ $u = $hmac->hash($u);
+ $f^= $u;
+ }
+ $key.= $f;
+ }
+ }
+
+ $this->setKey(substr($key, 0, $this->key_size));
+ }
+
+ /**
+ * Sets the block length
+ *
+ * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
+ *
+ * @access public
+ * @param Integer $length
+ */
+ function setBlockLength($length)
+ {
+ $length >>= 5;
+ if ($length > 8) {
+ $length = 8;
+ } else if ($length < 4) {
+ $length = 4;
+ }
+ $this->Nb = $length;
+ $this->block_size = $length << 2;
+ $this->changed = true;
+ }
+
+ /**
+ * Generate CTR XOR encryption key
+ *
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
+ * plaintext / ciphertext in CTR mode.
+ *
+ * @see Crypt_Rijndael::decrypt()
+ * @see Crypt_Rijndael::encrypt()
+ * @access public
+ * @param Integer $length
+ * @param String $iv
+ */
+ function _generate_xor($length, &$iv)
+ {
+ $xor = '';
+ $block_size = $this->block_size;
+ $num_blocks = floor(($length + ($block_size - 1)) / $block_size);
+ for ($i = 0; $i < $num_blocks; $i++) {
+ $xor.= $iv;
+ for ($j = 4; $j <= $block_size; $j+=4) {
+ $temp = substr($iv, -$j, 4);
+ switch ($temp) {
+ case "\xFF\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
+ break;
+ case "\x7F\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
+ break 2;
+ default:
+ extract(unpack('Ncount', $temp));
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
+ break 2;
+ }
+ }
+ }
+
+ return $xor;
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael
+ * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's
+ * necessary are discussed in the following
+ * URL:
+ *
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
+ *
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
+ * length.
+ *
+ * @see Crypt_Rijndael::decrypt()
+ * @access public
+ * @param String $plaintext
+ */
+ function encrypt($plaintext)
+ {
+ $this->_setup();
+ if ($this->paddable) {
+ $plaintext = $this->_pad($plaintext);
+ }
+
+ $block_size = $this->block_size;
+ $buffer = &$this->enbuffer;
+ $ciphertext = '';
+ switch ($this->mode) {
+ case CRYPT_RIJNDAEL_MODE_ECB:
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CBC:
+ $xor = $this->encryptIV;
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $block = substr($plaintext, $i, $block_size);
+ $block = $this->_encryptBlock($block ^ $xor);
+ $xor = $block;
+ $ciphertext.= $block;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CTR:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['encrypted'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $block = substr($plaintext, $i, $block_size);
+ $buffer['encrypted'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
+ $key = $this->_string_shift($buffer['encrypted'], $block_size);
+ $ciphertext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $block = substr($plaintext, $i, $block_size);
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
+ $ciphertext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) % $block_size) {
+ $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
+ }
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CFB:
+ // cfb loosely routines inspired by openssl's:
+ // http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1
+ if ($this->continuousBuffer) {
+ $iv = &$this->encryptIV;
+ $pos = &$buffer['pos'];
+ } else {
+ $iv = $this->encryptIV;
+ $pos = 0;
+ }
+ $len = strlen($plaintext);
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = $block_size - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
+ $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
+ $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
+ }
+ while ($len >= $block_size) {
+ $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
+ $ciphertext.= $iv;
+ $len-= $block_size;
+ $i+= $block_size;
+ }
+ if ($len) {
+ $iv = $this->_encryptBlock($iv);
+ $block = $iv ^ substr($plaintext, $i);
+ $iv = substr_replace($iv, $block, 0, $len);
+ $ciphertext.= $block;
+ $pos = $len;
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_OFB:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $xor = $this->_encryptBlock($xor);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], $block_size);
+ $ciphertext.= substr($plaintext, $i, $block_size) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ $xor = $this->_encryptBlock($xor);
+ $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) % $block_size) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $ciphertext;
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
+ * it is.
+ *
+ * @see Crypt_Rijndael::encrypt()
+ * @access public
+ * @param String $ciphertext
+ */
+ function decrypt($ciphertext)
+ {
+ $this->_setup();
+
+ if ($this->paddable) {
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
+ $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
+ }
+
+ $block_size = $this->block_size;
+ $buffer = &$this->debuffer;
+ $plaintext = '';
+ switch ($this->mode) {
+ case CRYPT_RIJNDAEL_MODE_ECB:
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CBC:
+ $xor = $this->decryptIV;
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $block = substr($ciphertext, $i, $block_size);
+ $plaintext.= $this->_decryptBlock($block) ^ $xor;
+ $xor = $block;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CTR:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['ciphertext'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $block = substr($ciphertext, $i, $block_size);
+ $buffer['ciphertext'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
+ $key = $this->_string_shift($buffer['ciphertext'], $block_size);
+ $plaintext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $block = substr($ciphertext, $i, $block_size);
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
+ $plaintext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($ciphertext) % $block_size) {
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
+ }
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_CFB:
+ if ($this->continuousBuffer) {
+ $iv = &$this->decryptIV;
+ $pos = &$buffer['pos'];
+ } else {
+ $iv = $this->decryptIV;
+ $pos = 0;
+ }
+ $len = strlen($ciphertext);
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = $block_size - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
+ $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
+ $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
+ }
+ while ($len >= $block_size) {
+ $iv = $this->_encryptBlock($iv);
+ $cb = substr($ciphertext, $i, $block_size);
+ $plaintext.= $iv ^ $cb;
+ $iv = $cb;
+ $len-= $block_size;
+ $i+= $block_size;
+ }
+ if ($len) {
+ $iv = $this->_encryptBlock($iv);
+ $plaintext.= $iv ^ substr($ciphertext, $i);
+ $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
+ $pos = $len;
+ }
+ break;
+ case CRYPT_RIJNDAEL_MODE_OFB:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $xor = $this->_encryptBlock($xor);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], $block_size);
+ $plaintext.= substr($ciphertext, $i, $block_size) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ $xor = $this->_encryptBlock($xor);
+ $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($ciphertext) % $block_size) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ /**
+ * Encrypts a block
+ *
+ * @access private
+ * @param String $in
+ * @return String
+ */
+ function _encryptBlock($in)
+ {
+ $state = array();
+ $words = unpack('N*word', $in);
+
+ $w = $this->w;
+ $t0 = $this->t0;
+ $t1 = $this->t1;
+ $t2 = $this->t2;
+ $t3 = $this->t3;
+ $Nb = $this->Nb;
+ $Nr = $this->Nr;
+ $c = $this->c;
+
+ // addRoundKey
+ $i = -1;
+ foreach ($words as $word) {
+ $state[] = $word ^ $w[0][++$i];
+ }
+
+ // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
+ // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
+ // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
+ // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
+ // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
+ // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
+
+ // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
+ $temp = array();
+ for ($round = 1; $round < $Nr; ++$round) {
+ $i = 0; // $c[0] == 0
+ $j = $c[1];
+ $k = $c[2];
+ $l = $c[3];
+
+ while ($i < $Nb) {
+ $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
+ $t1[$state[$j] >> 16 & 0x000000FF] ^
+ $t2[$state[$k] >> 8 & 0x000000FF] ^
+ $t3[$state[$l] & 0x000000FF] ^
+ $w[$round][$i];
+ ++$i;
+ $j = ($j + 1) % $Nb;
+ $k = ($k + 1) % $Nb;
+ $l = ($l + 1) % $Nb;
+ }
+ $state = $temp;
+ }
+
+ // subWord
+ for ($i = 0; $i < $Nb; ++$i) {
+ $state[$i] = $this->_subWord($state[$i]);
+ }
+
+ // shiftRows + addRoundKey
+ $i = 0; // $c[0] == 0
+ $j = $c[1];
+ $k = $c[2];
+ $l = $c[3];
+ while ($i < $Nb) {
+ $temp[$i] = ($state[$i] & 0xFF000000) ^
+ ($state[$j] & 0x00FF0000) ^
+ ($state[$k] & 0x0000FF00) ^
+ ($state[$l] & 0x000000FF) ^
+ $w[$Nr][$i];
+ ++$i;
+ $j = ($j + 1) % $Nb;
+ $k = ($k + 1) % $Nb;
+ $l = ($l + 1) % $Nb;
+ }
+
+ // 100% ugly switch/case code... but ~5% faster (meaning: ~half second faster de/encrypting 1MB text, tested with php5.4.9 on linux/32bit with an AMD Athlon II P360 CPU) then the commented smart code below. Don't know it's worth or not
+ switch ($Nb) {
+ case 8:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
+ case 7:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
+ case 6:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
+ case 5:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
+ default:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
+ }
+ /*
+ $state = $temp;
+
+ array_unshift($state, 'N*');
+
+ return call_user_func_array('pack', $state);
+ */
+ }
+
+ /**
+ * Decrypts a block
+ *
+ * @access private
+ * @param String $in
+ * @return String
+ */
+ function _decryptBlock($in)
+ {
+ $state = array();
+ $words = unpack('N*word', $in);
+
+ $dw = $this->dw;
+ $dt0 = $this->dt0;
+ $dt1 = $this->dt1;
+ $dt2 = $this->dt2;
+ $dt3 = $this->dt3;
+ $Nb = $this->Nb;
+ $Nr = $this->Nr;
+ $c = $this->c;
+
+ // addRoundKey
+ $i = -1;
+ foreach ($words as $word) {
+ $state[] = $word ^ $dw[$Nr][++$i];
+ }
+
+ $temp = array();
+ for ($round = $Nr - 1; $round > 0; --$round) {
+ $i = 0; // $c[0] == 0
+ $j = $Nb - $c[1];
+ $k = $Nb - $c[2];
+ $l = $Nb - $c[3];
+
+ while ($i < $Nb) {
+ $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
+ $dt1[$state[$j] >> 16 & 0x000000FF] ^
+ $dt2[$state[$k] >> 8 & 0x000000FF] ^
+ $dt3[$state[$l] & 0x000000FF] ^
+ $dw[$round][$i];
+ ++$i;
+ $j = ($j + 1) % $Nb;
+ $k = ($k + 1) % $Nb;
+ $l = ($l + 1) % $Nb;
+ }
+ $state = $temp;
+ }
+
+ // invShiftRows + invSubWord + addRoundKey
+ $i = 0; // $c[0] == 0
+ $j = $Nb - $c[1];
+ $k = $Nb - $c[2];
+ $l = $Nb - $c[3];
+
+ while ($i < $Nb) {
+ $temp[$i] = $dw[0][$i] ^
+ $this->_invSubWord(($state[$i] & 0xFF000000) |
+ ($state[$j] & 0x00FF0000) |
+ ($state[$k] & 0x0000FF00) |
+ ($state[$l] & 0x000000FF));
+ ++$i;
+ $j = ($j + 1) % $Nb;
+ $k = ($k + 1) % $Nb;
+ $l = ($l + 1) % $Nb;
+ }
+
+ switch ($Nb) {
+ case 8:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
+ case 7:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
+ case 6:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
+ case 5:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
+ default:
+ return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
+ }
+ /*
+ $state = $temp;
+
+ array_unshift($state, 'N*');
+
+ return call_user_func_array('pack', $state);
+ */
+ }
+
+ /**
+ * Setup Rijndael
+ *
+ * Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key
+ * key schedule.
+ *
+ * @access private
+ */
+ function _setup()
+ {
+ // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
+ // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
+ static $rcon = array(0,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+ 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
+ 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
+ 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
+ 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
+ );
+
+ if (!$this->changed) {
+ return;
+ }
+
+ if (!$this->explicit_key_length) {
+ // we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits
+ $length = strlen($this->key) >> 2;
+ if ($length > 8) {
+ $length = 8;
+ } else if ($length < 4) {
+ $length = 4;
+ }
+ $this->Nk = $length;
+ $this->key_size = $length << 2;
+ }
+
+ $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0));
+
+ // see Rijndael-ammended.pdf#page=44
+ $this->Nr = max($this->Nk, $this->Nb) + 6;
+
+ // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
+ // "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
+ // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
+ // "Table 2: Shift offsets for different block lengths"
+ switch ($this->Nb) {
+ case 4:
+ case 5:
+ case 6:
+ $this->c = array(0, 1, 2, 3);
+ break;
+ case 7:
+ $this->c = array(0, 1, 2, 4);
+ break;
+ case 8:
+ $this->c = array(0, 1, 3, 4);
+ }
+
+ $key = $this->key;
+
+ $w = array_values(unpack('N*words', $key));
+
+ $length = $this->Nb * ($this->Nr + 1);
+ for ($i = $this->Nk; $i < $length; $i++) {
+ $temp = $w[$i - 1];
+ if ($i % $this->Nk == 0) {
+ // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
+ // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
+ // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
+ // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
+ $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
+ $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
+ } else if ($this->Nk > 6 && $i % $this->Nk == 4) {
+ $temp = $this->_subWord($temp);
+ }
+ $w[$i] = $w[$i - $this->Nk] ^ $temp;
+ }
+
+ // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
+ // and generate the inverse key schedule. more specifically,
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
+ // "The key expansion for the Inverse Cipher is defined as follows:
+ // 1. Apply the Key Expansion.
+ // 2. Apply InvMixColumn to all Round Keys except the first and the last one."
+ // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
+ $temp = array();
+ for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
+ if ($col == $this->Nb) {
+ if ($row == 0) {
+ $this->dw[0] = $this->w[0];
+ } else {
+ // subWord + invMixColumn + invSubWord = invMixColumn
+ $j = 0;
+ while ($j < $this->Nb) {
+ $dw = $this->_subWord($this->w[$row][$j]);
+ $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^
+ $this->dt1[$dw >> 16 & 0x000000FF] ^
+ $this->dt2[$dw >> 8 & 0x000000FF] ^
+ $this->dt3[$dw & 0x000000FF];
+ $j++;
+ }
+ $this->dw[$row] = $temp;
+ }
+
+ $col = 0;
+ $row++;
+ }
+ $this->w[$row][$col] = $w[$i];
+ }
+
+ $this->dw[$row] = $this->w[$row];
+
+ $this->changed = false;
+ }
+
+ /**
+ * Performs S-Box substitutions
+ *
+ * @access private
+ */
+ function _subWord($word)
+ {
+ static $sbox0, $sbox1, $sbox2, $sbox3;
+
+ if (empty($sbox0)) {
+ $sbox0 = array(
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
+ );
+
+ $sbox1 = array();
+ $sbox2 = array();
+ $sbox3 = array();
+
+ for ($i = 0; $i < 256; $i++) {
+ $sbox1[] = $sbox0[$i] << 8;
+ $sbox2[] = $sbox0[$i] << 16;
+ $sbox3[] = $sbox0[$i] << 24;
+ }
+ }
+
+ return $sbox0[$word & 0x000000FF] |
+ $sbox1[$word >> 8 & 0x000000FF] |
+ $sbox2[$word >> 16 & 0x000000FF] |
+ $sbox3[$word >> 24 & 0x000000FF];
+ }
+
+
+ /**
+ * Performs inverse S-Box substitutions
+ *
+ * @access private
+ */
+ function _invSubWord($word)
+ {
+ static $sbox0, $sbox1, $sbox2, $sbox3;
+
+ if (empty($sbox0)) {
+ $sbox0 = array(
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
+ );
+
+ $sbox1 = array();
+ $sbox2 = array();
+ $sbox3 = array();
+
+ for ($i = 0; $i < 256; $i++) {
+ $sbox1[] = $sbox0[$i] << 8;
+ $sbox2[] = $sbox0[$i] << 16;
+ $sbox3[] = $sbox0[$i] << 24;
+ }
+ }
+
+ return $sbox0[$word & 0x000000FF] |
+ $sbox1[$word >> 8 & 0x000000FF] |
+ $sbox2[$word >> 16 & 0x000000FF] |
+ $sbox3[$word >> 24 & 0x000000FF];
+ }
+
+ /**
+ * Pad "packets".
+ *
+ * Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple
+ * of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
+ * pad the input so that it is of the proper length.
+ *
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH,
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
+ * transmitted separately)
+ *
+ * @see Crypt_Rijndael::disablePadding()
+ * @access public
+ */
+ function enablePadding()
+ {
+ $this->padding = true;
+ }
+
+ /**
+ * Do not pad packets.
+ *
+ * @see Crypt_Rijndael::enablePadding()
+ * @access public
+ */
+ function disablePadding()
+ {
+ $this->padding = false;
+ }
+
+ /**
+ * Pads a string
+ *
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
+ * $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to
+ * chr($block_size - (strlen($text) % $block_size)
+ *
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
+ * and padding will, hence forth, be enabled.
+ *
+ * @see Crypt_Rijndael::_unpad()
+ * @access private
+ */
+ function _pad($text)
+ {
+ $length = strlen($text);
+
+ if (!$this->padding) {
+ if ($length % $this->block_size == 0) {
+ return $text;
+ } else {
+ user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})");
+ $this->padding = true;
+ }
+ }
+
+ $pad = $this->block_size - ($length % $this->block_size);
+
+ return str_pad($text, $length + $pad, chr($pad));
+ }
+
+ /**
+ * Unpads a string.
+ *
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
+ * and false will be returned.
+ *
+ * @see Crypt_Rijndael::_pad()
+ * @access private
+ */
+ function _unpad($text)
+ {
+ if (!$this->padding) {
+ return $text;
+ }
+
+ $length = ord($text[strlen($text) - 1]);
+
+ if (!$length || $length > $this->block_size) {
+ return false;
+ }
+
+ return substr($text, 0, -$length);
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets
+ * will yield different outputs:
+ *
+ * <code>
+ * echo $rijndael->encrypt(substr($plaintext, 0, 16));
+ * echo $rijndael->encrypt(substr($plaintext, 16, 16));
+ * </code>
+ * <code>
+ * echo $rijndael->encrypt($plaintext);
+ * </code>
+ *
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
+ * another, as demonstrated with the following:
+ *
+ * <code>
+ * $rijndael->encrypt(substr($plaintext, 0, 16));
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
+ * </code>
+ * <code>
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
+ * </code>
+ *
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
+ *
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
+ * however, they are also less intuitive and more likely to cause you problems.
+ *
+ * @see Crypt_Rijndael::disableContinuousBuffer()
+ * @access public
+ */
+ function enableContinuousBuffer()
+ {
+ $this->continuousBuffer = true;
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_Rijndael::enableContinuousBuffer()
+ * @access public
+ */
+ function disableContinuousBuffer()
+ {
+ $this->continuousBuffer = false;
+ $this->encryptIV = $this->iv;
+ $this->decryptIV = $this->iv;
+ $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0);
+ $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0);
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+}
+
+// vim: ts=4:sw=4:et:
+// vim6: fdl=1: \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php
new file mode 100644
index 00000000000..3b4c8c3622f
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php
@@ -0,0 +1,1080 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of Triple DES.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
+ *
+ * PHP versions 4 and 5
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Crypt/TripleDES.php');
+ *
+ * $des = new Crypt_TripleDES();
+ *
+ * $des->setKey('abcdefghijklmnopqrstuvwx');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $des->decrypt($des->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Crypt
+ * @package Crypt_TripleDES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Crypt_DES
+ */
+if (!class_exists('Crypt_DES')) {
+ require_once('DES.php');
+}
+
+/**
+ * Encrypt / decrypt using inner chaining
+ *
+ * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
+ */
+define('CRYPT_DES_MODE_3CBC', -2);
+
+/**
+ * Encrypt / decrypt using outer chaining
+ *
+ * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
+ */
+define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
+
+/**
+ * Pure-PHP implementation of Triple DES.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Crypt_TerraDES
+ */
+class Crypt_TripleDES {
+ /**
+ * The Three Keys
+ *
+ * @see Crypt_TripleDES::setKey()
+ * @var String
+ * @access private
+ */
+ var $key = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * The Encryption Mode
+ *
+ * @see Crypt_TripleDES::Crypt_TripleDES()
+ * @var Integer
+ * @access private
+ */
+ var $mode = CRYPT_DES_MODE_CBC;
+
+ /**
+ * Continuous Buffer status
+ *
+ * @see Crypt_TripleDES::enableContinuousBuffer()
+ * @var Boolean
+ * @access private
+ */
+ var $continuousBuffer = false;
+
+ /**
+ * Padding status
+ *
+ * @see Crypt_TripleDES::enablePadding()
+ * @var Boolean
+ * @access private
+ */
+ var $padding = true;
+
+ /**
+ * The Initialization Vector
+ *
+ * @see Crypt_TripleDES::setIV()
+ * @var String
+ * @access private
+ */
+ var $iv = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_TripleDES::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * A "sliding" Initialization Vector
+ *
+ * @see Crypt_TripleDES::enableContinuousBuffer()
+ * @var String
+ * @access private
+ */
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
+
+ /**
+ * The Crypt_DES objects
+ *
+ * @var Array
+ * @access private
+ */
+ var $des;
+
+ /**
+ * mcrypt resource for encryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_TripleDES::encrypt()
+ * @var String
+ * @access private
+ */
+ var $enmcrypt;
+
+ /**
+ * mcrypt resource for decryption
+ *
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
+ *
+ * @see Crypt_TripleDES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $demcrypt;
+
+ /**
+ * Does the enmcrypt resource need to be (re)initialized?
+ *
+ * @see Crypt_TripleDES::setKey()
+ * @see Crypt_TripleDES::setIV()
+ * @var Boolean
+ * @access private
+ */
+ var $enchanged = true;
+
+ /**
+ * Does the demcrypt resource need to be (re)initialized?
+ *
+ * @see Crypt_TripleDES::setKey()
+ * @see Crypt_TripleDES::setIV()
+ * @var Boolean
+ * @access private
+ */
+ var $dechanged = true;
+
+ /**
+ * Is the mode one that is paddable?
+ *
+ * @see Crypt_TripleDES::Crypt_TripleDES()
+ * @var Boolean
+ * @access private
+ */
+ var $paddable = false;
+
+ /**
+ * Encryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_TripleDES::encrypt()
+ * @var Array
+ * @access private
+ */
+ var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
+
+ /**
+ * Decryption buffer for CTR, OFB and CFB modes
+ *
+ * @see Crypt_TripleDES::decrypt()
+ * @var Array
+ * @access private
+ */
+ var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
+
+ /**
+ * mcrypt resource for CFB mode
+ *
+ * @see Crypt_TripleDES::encrypt()
+ * @see Crypt_TripleDES::decrypt()
+ * @var String
+ * @access private
+ */
+ var $ecb;
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
+ *
+ * @param optional Integer $mode
+ * @return Crypt_TripleDES
+ * @access public
+ */
+ function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
+ {
+ if ( !defined('CRYPT_DES_MODE') ) {
+ switch (true) {
+ case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()):
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
+ break;
+ default:
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
+ }
+ }
+
+ if ( $mode == CRYPT_DES_MODE_3CBC ) {
+ $this->mode = CRYPT_DES_MODE_3CBC;
+ $this->des = array(
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
+ new Crypt_DES(CRYPT_DES_MODE_CBC)
+ );
+ $this->paddable = true;
+
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
+ $this->des[0]->disablePadding();
+ $this->des[1]->disablePadding();
+ $this->des[2]->disablePadding();
+
+ return;
+ }
+
+ switch ( CRYPT_DES_MODE ) {
+ case CRYPT_DES_MODE_MCRYPT:
+ switch ($mode) {
+ case CRYPT_DES_MODE_ECB:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_ECB;
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $this->mode = 'ctr';
+ break;
+ case CRYPT_DES_MODE_CFB:
+ $this->mode = 'ncfb';
+ $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
+ break;
+ case CRYPT_DES_MODE_OFB:
+ $this->mode = MCRYPT_MODE_NOFB;
+ break;
+ case CRYPT_DES_MODE_CBC:
+ default:
+ $this->paddable = true;
+ $this->mode = MCRYPT_MODE_CBC;
+ }
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
+ $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
+
+ break;
+ default:
+ $this->des = array(
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
+ new Crypt_DES(CRYPT_DES_MODE_ECB)
+ );
+
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
+ $this->des[0]->disablePadding();
+ $this->des[1]->disablePadding();
+ $this->des[2]->disablePadding();
+
+ switch ($mode) {
+ case CRYPT_DES_MODE_ECB:
+ case CRYPT_DES_MODE_CBC:
+ $this->paddable = true;
+ $this->mode = $mode;
+ break;
+ case CRYPT_DES_MODE_CTR:
+ case CRYPT_DES_MODE_CFB:
+ case CRYPT_DES_MODE_OFB:
+ $this->mode = $mode;
+ break;
+ default:
+ $this->paddable = true;
+ $this->mode = CRYPT_DES_MODE_CBC;
+ }
+ }
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
+ * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
+ *
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
+ *
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
+ *
+ * @access public
+ * @param String $key
+ */
+ function setKey($key)
+ {
+ $length = strlen($key);
+ if ($length > 8) {
+ $key = str_pad($key, 24, chr(0));
+ // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
+ // http://php.net/function.mcrypt-encrypt#47973
+ //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
+ } else {
+ $key = str_pad($key, 8, chr(0));
+ }
+ $this->key = $key;
+ switch (true) {
+ case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:
+ case $this->mode == CRYPT_DES_MODE_3CBC:
+ $this->des[0]->setKey(substr($key, 0, 8));
+ $this->des[1]->setKey(substr($key, 8, 8));
+ $this->des[2]->setKey(substr($key, 16, 8));
+ }
+ $this->enchanged = $this->dechanged = true;
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
+ * $hash, $salt, $method
+ *
+ * @param String $password
+ * @param optional String $method
+ * @access public
+ */
+ function setPassword($password, $method = 'pbkdf2')
+ {
+ $key = '';
+
+ switch ($method) {
+ default: // 'pbkdf2'
+ list(, , $hash, $salt, $count) = func_get_args();
+ if (!isset($hash)) {
+ $hash = 'sha1';
+ }
+ // WPA and WPA2 use the SSID as the salt
+ if (!isset($salt)) {
+ $salt = 'phpseclib';
+ }
+ // RFC2898#section-4.2 uses 1,000 iterations by default
+ // WPA and WPA2 use 4,096.
+ if (!isset($count)) {
+ $count = 1000;
+ }
+
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+
+ $i = 1;
+ while (strlen($key) < 24) { // $dkLen == 24
+ $hmac = new Crypt_Hash();
+ $hmac->setHash($hash);
+ $hmac->setKey($password);
+ $f = $u = $hmac->hash($salt . pack('N', $i++));
+ for ($j = 2; $j <= $count; $j++) {
+ $u = $hmac->hash($u);
+ $f^= $u;
+ }
+ $key.= $f;
+ }
+ }
+
+ $this->setKey($key);
+ }
+
+ /**
+ * Sets the initialization vector. (optional)
+ *
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
+ * to be all zero's.
+ *
+ * @access public
+ * @param String $iv
+ */
+ function setIV($iv)
+ {
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
+ $this->des[0]->setIV($iv);
+ $this->des[1]->setIV($iv);
+ $this->des[2]->setIV($iv);
+ }
+ $this->enchanged = $this->dechanged = true;
+ }
+
+ /**
+ * Generate CTR XOR encryption key
+ *
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
+ * plaintext / ciphertext in CTR mode.
+ *
+ * @see Crypt_TripleDES::decrypt()
+ * @see Crypt_TripleDES::encrypt()
+ * @access private
+ * @param String $iv
+ */
+ function _generate_xor(&$iv)
+ {
+ $xor = $iv;
+ for ($j = 4; $j <= 8; $j+=4) {
+ $temp = substr($iv, -$j, 4);
+ switch ($temp) {
+ case "\xFF\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
+ break;
+ case "\x7F\xFF\xFF\xFF":
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
+ break 2;
+ default:
+ extract(unpack('Ncount', $temp));
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
+ break 2;
+ }
+ }
+
+ return $xor;
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * @access public
+ * @param String $plaintext
+ */
+ function encrypt($plaintext)
+ {
+ if ($this->paddable) {
+ $plaintext = $this->_pad($plaintext);
+ }
+
+ // if the key is smaller then 8, do what we'd normally do
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
+ $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
+
+ return $ciphertext;
+ }
+
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
+ if ($this->enchanged) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
+ if ($this->mode == 'ncfb') {
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
+ }
+ $this->enchanged = false;
+ }
+
+ if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
+ } else {
+ $iv = &$this->encryptIV;
+ $pos = &$this->enbuffer['pos'];
+ $len = strlen($plaintext);
+ $ciphertext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
+ $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
+ $this->enbuffer['enmcrypt_init'] = true;
+ }
+ if ($len >= 8) {
+ if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) {
+ if ($this->enbuffer['enmcrypt_init'] === true) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
+ $this->enbuffer['enmcrypt_init'] = false;
+ }
+ $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));
+ $iv = substr($ciphertext, -8);
+ $i = strlen($ciphertext);
+ $len%= 8;
+ } else {
+ while ($len >= 8) {
+ $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);
+ $ciphertext.= $iv;
+ $len-= 8;
+ $i+= 8;
+ }
+ }
+ }
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $block = $iv ^ substr($plaintext, $i);
+ $iv = substr_replace($iv, $block, 0, $len);
+ $ciphertext.= $block;
+ $pos = $len;
+ }
+ return $ciphertext;
+ }
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
+ }
+
+ return $ciphertext;
+ }
+
+ if (strlen($this->key) <= 8) {
+ $this->des[0]->mode = $this->mode;
+
+ return $this->des[0]->encrypt($plaintext);
+ }
+
+ $des = $this->des;
+
+ $buffer = &$this->enbuffer;
+ $continuousBuffer = $this->continuousBuffer;
+ $ciphertext = '';
+ switch ($this->mode) {
+ case CRYPT_DES_MODE_ECB:
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.
+ // only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that
+ // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make
+ // encryption and decryption take more time, per this:
+ //
+ // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $ciphertext.= $block;
+ }
+ break;
+ case CRYPT_DES_MODE_CBC:
+ $xor = $this->encryptIV;
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8) ^ $xor;
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $xor = $block;
+ $ciphertext.= $block;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ }
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['encrypted'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $key = $this->_generate_xor($xor);
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $buffer['encrypted'].= $key;
+ $key = $this->_string_shift($buffer['encrypted'], 8);
+ $ciphertext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $key = $this->_generate_xor($xor);
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $ciphertext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) & 7) {
+ $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
+ }
+ }
+ break;
+ case CRYPT_DES_MODE_CFB:
+ if (strlen($buffer['xor'])) {
+ $ciphertext = $plaintext ^ $buffer['xor'];
+ $iv = $buffer['encrypted'] . $ciphertext;
+ $start = strlen($ciphertext);
+ $buffer['encrypted'].= $ciphertext;
+ $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
+ } else {
+ $ciphertext = '';
+ $iv = $this->encryptIV;
+ $start = 0;
+ }
+
+ for ($i = $start; $i < strlen($plaintext); $i+=8) {
+ $block = substr($plaintext, $i, 8);
+ $iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT);
+ $iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT);
+ $xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT);
+
+ $iv = $block ^ $xor;
+ if ($continuousBuffer && strlen($iv) != 8) {
+ $buffer = array(
+ 'encrypted' => $iv,
+ 'xor' => substr($xor, strlen($iv))
+ );
+ }
+ $ciphertext.= $iv;
+ }
+
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $iv;
+ }
+ break;
+ case CRYPT_DES_MODE_OFB:
+ $xor = $this->encryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], 8);
+ $ciphertext.= substr($plaintext, $i, 8) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $ciphertext.= substr($plaintext, $i, 8) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $xor;
+ if ($start = strlen($plaintext) & 7) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $ciphertext;
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * @access public
+ * @param String $ciphertext
+ */
+ function decrypt($ciphertext)
+ {
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
+ $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
+
+ return $this->_unpad($plaintext);
+ }
+
+ if ($this->paddable) {
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
+ }
+
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
+ if ($this->dechanged) {
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
+ if ($this->mode == 'ncfb') {
+ mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
+ }
+ $this->dechanged = false;
+ }
+
+ if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
+ } else {
+ $iv = &$this->decryptIV;
+ $pos = &$this->debuffer['pos'];
+ $len = strlen($ciphertext);
+ $plaintext = '';
+ $i = 0;
+ if ($pos) {
+ $orig_pos = $pos;
+ $max = 8 - $pos;
+ if ($len >= $max) {
+ $i = $max;
+ $len-= $max;
+ $pos = 0;
+ } else {
+ $i = $len;
+ $pos+= $len;
+ $len = 0;
+ }
+ $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
+ $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
+ }
+ if ($len >= 8) {
+ $cb = substr($ciphertext, $i, $len - $len % 8);
+ $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
+ $iv = substr($cb, -8);
+ $len%= 8;
+ }
+ if ($len) {
+ $iv = mcrypt_generic($this->ecb, $iv);
+ $cb = substr($ciphertext, -$len);
+ $plaintext.= $iv ^ $cb;
+ $iv = substr_replace($iv, $cb, 0, $len);
+ $pos = $len;
+ }
+ return $plaintext;
+ }
+
+ if (!$this->continuousBuffer) {
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ if (strlen($this->key) <= 8) {
+ $this->des[0]->mode = $this->mode;
+ $plaintext = $this->des[0]->decrypt($ciphertext);
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ $des = $this->des;
+
+ $buffer = &$this->debuffer;
+ $continuousBuffer = $this->continuousBuffer;
+ $plaintext = '';
+ switch ($this->mode) {
+ case CRYPT_DES_MODE_ECB:
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $plaintext.= $block;
+ }
+ break;
+ case CRYPT_DES_MODE_CBC:
+ $xor = $this->decryptIV;
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $orig = $block = substr($ciphertext, $i, 8);
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
+ $plaintext.= $block ^ $xor;
+ $xor = $orig;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ }
+ break;
+ case CRYPT_DES_MODE_CTR:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['ciphertext'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $key = $this->_generate_xor($xor);
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $buffer['ciphertext'].= $key;
+ $key = $this->_string_shift($buffer['ciphertext'], 8);
+ $plaintext.= $block ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $key = $this->_generate_xor($xor);
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
+ $plaintext.= $block ^ $key;
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($plaintext) & 7) {
+ $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
+ }
+ }
+ break;
+ case CRYPT_DES_MODE_CFB:
+ if (strlen($buffer['ciphertext'])) {
+ $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
+ $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
+ if (strlen($buffer['ciphertext']) != 8) {
+ $block = $this->decryptIV;
+ } else {
+ $block = $buffer['ciphertext'];
+ $xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $buffer['ciphertext'] = '';
+ }
+ $start = strlen($plaintext);
+ } else {
+ $plaintext = '';
+ $xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $start = 0;
+ }
+
+ for ($i = $start; $i < strlen($ciphertext); $i+=8) {
+ $block = substr($ciphertext, $i, 8);
+ $plaintext.= $block ^ $xor;
+ if ($continuousBuffer && strlen($block) != 8) {
+ $buffer['ciphertext'].= $block;
+ $block = $xor;
+ } else if (strlen($block) == 8) {
+ $xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ }
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $block;
+ }
+ break;
+ case CRYPT_DES_MODE_OFB:
+ $xor = $this->decryptIV;
+ if (strlen($buffer['xor'])) {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $buffer['xor'].= $xor;
+ $key = $this->_string_shift($buffer['xor'], 8);
+ $plaintext.= substr($ciphertext, $i, 8) ^ $key;
+ }
+ } else {
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
+ $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
+ $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
+ $plaintext.= substr($ciphertext, $i, 8) ^ $xor;
+ }
+ $key = $xor;
+ }
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $xor;
+ if ($start = strlen($ciphertext) & 7) {
+ $buffer['xor'] = substr($key, $start) . $buffer['xor'];
+ }
+ }
+ }
+
+ return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
+ * will yield different outputs:
+ *
+ * <code>
+ * echo $des->encrypt(substr($plaintext, 0, 8));
+ * echo $des->encrypt(substr($plaintext, 8, 8));
+ * </code>
+ * <code>
+ * echo $des->encrypt($plaintext);
+ * </code>
+ *
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
+ * another, as demonstrated with the following:
+ *
+ * <code>
+ * $des->encrypt(substr($plaintext, 0, 8));
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ * <code>
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
+ * </code>
+ *
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
+ *
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
+ * however, they are also less intuitive and more likely to cause you problems.
+ *
+ * @see Crypt_TripleDES::disableContinuousBuffer()
+ * @access public
+ */
+ function enableContinuousBuffer()
+ {
+ $this->continuousBuffer = true;
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
+ $this->des[0]->enableContinuousBuffer();
+ $this->des[1]->enableContinuousBuffer();
+ $this->des[2]->enableContinuousBuffer();
+ }
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see Crypt_TripleDES::enableContinuousBuffer()
+ * @access public
+ */
+ function disableContinuousBuffer()
+ {
+ $this->continuousBuffer = false;
+ $this->encryptIV = $this->iv;
+ $this->decryptIV = $this->iv;
+ $this->enchanged = true;
+ $this->dechanged = true;
+ $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
+ $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
+
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
+ $this->des[0]->disableContinuousBuffer();
+ $this->des[1]->disableContinuousBuffer();
+ $this->des[2]->disableContinuousBuffer();
+ }
+ }
+
+ /**
+ * Pad "packets".
+ *
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
+ *
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
+ * transmitted separately)
+ *
+ * @see Crypt_TripleDES::disablePadding()
+ * @access public
+ */
+ function enablePadding()
+ {
+ $this->padding = true;
+ }
+
+ /**
+ * Do not pad packets.
+ *
+ * @see Crypt_TripleDES::enablePadding()
+ * @access public
+ */
+ function disablePadding()
+ {
+ $this->padding = false;
+ }
+
+ /**
+ * Pads a string
+ *
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
+ *
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
+ * and padding will, hence forth, be enabled.
+ *
+ * @see Crypt_TripleDES::_unpad()
+ * @access private
+ */
+ function _pad($text)
+ {
+ $length = strlen($text);
+
+ if (!$this->padding) {
+ if (($length & 7) == 0) {
+ return $text;
+ } else {
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
+ $this->padding = true;
+ }
+ }
+
+ $pad = 8 - ($length & 7);
+ return str_pad($text, $length + $pad, chr($pad));
+ }
+
+ /**
+ * Unpads a string
+ *
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
+ * and false will be returned.
+ *
+ * @see Crypt_TripleDES::_pad()
+ * @access private
+ */
+ function _unpad($text)
+ {
+ if (!$this->padding) {
+ return $text;
+ }
+
+ $length = ord($text[strlen($text) - 1]);
+
+ if (!$length || $length > 8) {
+ return false;
+ }
+
+ return substr($text, 0, -$length);
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+}
+
+// vim: ts=4:sw=4:et:
+// vim6: fdl=1:
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php
new file mode 100644
index 00000000000..29ad949e104
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php
@@ -0,0 +1,540 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP ANSI Decoder
+ *
+ * PHP versions 4 and 5
+ *
+ * If you call read() in Net_SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
+ * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC). They tell a
+ * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
+ * color to display them in, etc. File_ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category File
+ * @package File_ANSI
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMXII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id$
+ * @link htp://phpseclib.sourceforge.net
+ */
+
+/**
+ * Pure-PHP ANSI Decoder
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.3.0
+ * @access public
+ * @package File_ANSI
+ */
+class File_ANSI {
+ /**
+ * Max Width
+ *
+ * @var Integer
+ * @access private
+ */
+ var $max_x;
+
+ /**
+ * Max Height
+ *
+ * @var Integer
+ * @access private
+ */
+ var $max_y;
+
+ /**
+ * Max History
+ *
+ * @var Integer
+ * @access private
+ */
+ var $max_history;
+
+ /**
+ * History
+ *
+ * @var Array
+ * @access private
+ */
+ var $history;
+
+ /**
+ * History Attributes
+ *
+ * @var Array
+ * @access private
+ */
+ var $history_attrs;
+
+ /**
+ * Current Column
+ *
+ * @var Integer
+ * @access private
+ */
+ var $x;
+
+ /**
+ * Current Row
+ *
+ * @var Integer
+ * @access private
+ */
+ var $y;
+
+ /**
+ * Old Column
+ *
+ * @var Integer
+ * @access private
+ */
+ var $old_x;
+
+ /**
+ * Old Row
+ *
+ * @var Integer
+ * @access private
+ */
+ var $old_y;
+
+ /**
+ * An empty attribute row
+ *
+ * @var Array
+ * @access private
+ */
+ var $attr_row;
+
+ /**
+ * The current screen text
+ *
+ * @var Array
+ * @access private
+ */
+ var $screen;
+
+ /**
+ * The current screen attributes
+ *
+ * @var Array
+ * @access private
+ */
+ var $attrs;
+
+ /**
+ * The current foreground color
+ *
+ * @var String
+ * @access private
+ */
+ var $foreground;
+
+ /**
+ * The current background color
+ *
+ * @var String
+ * @access private
+ */
+ var $background;
+
+ /**
+ * Bold flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $bold;
+
+ /**
+ * Underline flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $underline;
+
+ /**
+ * Blink flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $blink;
+
+ /**
+ * Reverse flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $reverse;
+
+ /**
+ * Color flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $color;
+
+ /**
+ * Current ANSI code
+ *
+ * @var String
+ * @access private
+ */
+ var $ansi;
+
+ /**
+ * Default Constructor.
+ *
+ * @return File_ANSI
+ * @access public
+ */
+ function File_ANSI()
+ {
+ $this->setHistory(200);
+ $this->setDimensions(80, 24);
+ }
+
+ /**
+ * Set terminal width and height
+ *
+ * Resets the screen as well
+ *
+ * @param Integer $x
+ * @param Integer $y
+ * @access public
+ */
+ function setDimensions($x, $y)
+ {
+ $this->max_x = $x - 1;
+ $this->max_y = $y - 1;
+ $this->x = $this->y = 0;
+ $this->history = $this->history_attrs = array();
+ $this->attr_row = array_fill(0, $this->max_x + 1, '');
+ $this->screen = array_fill(0, $this->max_y + 1, '');
+ $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
+ $this->foreground = 'white';
+ $this->background = 'black';
+ $this->bold = false;
+ $this->underline = false;
+ $this->blink = false;
+ $this->reverse = false;
+ $this->color = false;
+
+ $this->ansi = '';
+ }
+
+ /**
+ * Set the number of lines that should be logged past the terminal height
+ *
+ * @param Integer $x
+ * @param Integer $y
+ * @access public
+ */
+ function setHistory($history)
+ {
+ $this->max_history = $history;
+ }
+
+ /**
+ * Load a string
+ *
+ * @param String $source
+ * @access public
+ */
+ function loadString($source)
+ {
+ $this->setDimensions($this->max_x + 1, $this->max_y + 1);
+ $this->appendString($source);
+ }
+
+ /**
+ * Appdend a string
+ *
+ * @param String $source
+ * @access public
+ */
+ function appendString($source)
+ {
+ for ($i = 0; $i < strlen($source); $i++) {
+ if (strlen($this->ansi)) {
+ $this->ansi.= $source[$i];
+ $chr = ord($source[$i]);
+ // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
+ // single character CSI's not currently supported
+ switch (true) {
+ case $this->ansi == "\x1B=":
+ $this->ansi = '';
+ continue 2;
+ case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
+ case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
+ break;
+ default:
+ continue 2;
+ }
+ // http://ascii-table.com/ansi-escape-sequences-vt-100.php
+ switch ($this->ansi) {
+ case "\x1B[H":
+ $this->old_x = $this->x;
+ $this->old_y = $this->y;
+ $this->x = $this->y = 0;
+ break;
+ case "\x1B[J":
+ $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
+ $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));
+
+ $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
+ $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));
+
+ if (count($this->history) == $this->max_history) {
+ array_shift($this->history);
+ array_shift($this->history_attrs);
+ }
+ case "\x1B[K":
+ $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);
+
+ array_splice($this->attrs[$this->y], $this->x + 1);
+ break;
+ case "\x1B[?1h": // set cursor key to application
+ break;
+ default:
+ switch (true) {
+ case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match):
+ $this->old_x = $this->x;
+ $this->old_y = $this->y;
+ $this->x = $match[2] - 1;
+ $this->y = $match[1] - 1;
+ break;
+ case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match):
+ $this->old_x = $this->x;
+ $x = $match[1] - 1;
+ break;
+ case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
+ break;
+ case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match):
+ $mods = explode(';', $match[1]);
+ foreach ($mods as $mod) {
+ switch ($mod) {
+ case 0:
+ $this->attrs[$this->y][$this->x] = '';
+
+ if ($this->bold) $this->attrs[$this->y][$this->x].= '</b>';
+ if ($this->underline) $this->attrs[$this->y][$this->x].= '</underline>';
+ if ($this->blink) $this->attrs[$this->y][$this->x].= '</blink>';
+ if ($this->color) $this->attrs[$this->y][$this->x].= '</span>';
+
+ if ($this->reverse) {
+ $temp = $this->background;
+ $this->background = $this->foreground;
+ $this->foreground = $temp;
+ }
+
+ $this->bold = $this->underline = $this->blink = $this->color = $this->reverse = false;
+ break;
+ case 1:
+ if (!$this->bold) {
+ $this->attrs[$this->y][$this->x] = '<b>';
+ $this->bold = true;
+ }
+ break;
+ case 4:
+ if (!$this->underline) {
+ $this->attrs[$this->y][$this->x] = '<u>';
+ $this->underline = true;
+ }
+ break;
+ case 5:
+ if (!$this->blink) {
+ $this->attrs[$this->y][$this->x] = '<blink>';
+ $this->blink = true;
+ }
+ break;
+ case 7:
+ $this->reverse = !$this->reverse;
+ $temp = $this->background;
+ $this->background = $this->foreground;
+ $this->foreground = $temp;
+ $this->attrs[$this->y][$this->x] = '<span style="color: ' . $this->foreground . '; background: ' . $this->background . '">';
+ if ($this->color) {
+ $this->attrs[$this->y][$this->x] = '</span>' . $this->attrs[$this->y][$this->x];
+ }
+ $this->color = true;
+ break;
+ default:
+ //$front = $this->reverse ? &$this->background : &$this->foreground;
+ $front = &$this->{ $this->reverse ? 'background' : 'foreground' };
+ //$back = $this->reverse ? &$this->foreground : &$this->background;
+ $back = &$this->{ $this->reverse ? 'foreground' : 'background' };
+ switch ($mod) {
+ case 30: $front = 'black'; break;
+ case 31: $front = 'red'; break;
+ case 32: $front = 'green'; break;
+ case 33: $front = 'yellow'; break;
+ case 34: $front = 'blue'; break;
+ case 35: $front = 'magenta'; break;
+ case 36: $front = 'cyan'; break;
+ case 37: $front = 'white'; break;
+
+ case 40: $back = 'black'; break;
+ case 41: $back = 'red'; break;
+ case 42: $back = 'green'; break;
+ case 43: $back = 'yellow'; break;
+ case 44: $back = 'blue'; break;
+ case 45: $back = 'magenta'; break;
+ case 46: $back = 'cyan'; break;
+ case 47: $back = 'white'; break;
+
+ default:
+ user_error('Unsupported attribute: ' . $mod);
+ $this->ansi = '';
+ break 2;
+ }
+
+ unset($temp);
+ $this->attrs[$this->y][$this->x] = '<span style="color: ' . $this->foreground . '; background: ' . $this->background . '">';
+ if ($this->color) {
+ $this->attrs[$this->y][$this->x] = '</span>' . $this->attrs[$this->y][$this->x];
+ }
+ $this->color = true;
+ }
+ }
+ break;
+ default:
+ echo "{$this->ansi} unsupported\r\n";
+ }
+ }
+ $this->ansi = '';
+ continue;
+ }
+
+ switch ($source[$i]) {
+ case "\r":
+ $this->x = 0;
+ break;
+ case "\n":
+ //if ($this->y < $this->max_y) {
+ // $this->y++;
+ //}
+
+ while ($this->y >= $this->max_y) {
+ $this->history = array_merge($this->history, array(array_shift($this->screen)));
+ $this->screen[] = '';
+
+ $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
+ $this->attrs[] = $this->attr_row;
+
+ if (count($this->history) >= $this->max_history) {
+ array_shift($this->history);
+ array_shift($this->history_attrs);
+ }
+
+ $this->y--;
+ }
+ $this->y++;
+ break;
+ case "\x0F": // shift
+ break;
+ case "\x1B": // start ANSI escape code
+ $this->ansi.= "\x1B";
+ break;
+ default:
+ $this->screen[$this->y] = substr_replace(
+ $this->screen[$this->y],
+ $source[$i],
+ $this->x,
+ 1
+ );
+
+ if ($this->x > $this->max_x) {
+ $this->x = 0;
+ $this->y++;
+ } else {
+ $this->x++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the current screen without preformating
+ *
+ * @access private
+ * @return String
+ */
+ function _getScreen()
+ {
+ $output = '';
+ for ($i = 0; $i <= $this->max_y; $i++) {
+ for ($j = 0; $j <= $this->max_x + 1; $j++) {
+ if (isset($this->attrs[$i][$j])) {
+ $output.= $this->attrs[$i][$j];
+ }
+ if (isset($this->screen[$i][$j])) {
+ $output.= htmlspecialchars($this->screen[$i][$j]);
+ }
+ }
+ $output.= "\r\n";
+ }
+ return rtrim($output);
+ }
+
+ /**
+ * Returns the current screen
+ *
+ * @access public
+ * @return String
+ */
+ function getScreen()
+ {
+ return '<pre style="color: white; background: black" width="' . ($this->max_x + 1) . '">' . $this->_getScreen() . '</pre>';
+ }
+
+ /**
+ * Returns the current screen and the x previous lines
+ *
+ * @access public
+ * @return String
+ */
+ function getHistory()
+ {
+ $scrollback = '';
+ for ($i = 0; $i < count($this->history); $i++) {
+ for ($j = 0; $j <= $this->max_x + 1; $j++) {
+ if (isset($this->history_attrs[$i][$j])) {
+ $scrollback.= $this->history_attrs[$i][$j];
+ }
+ if (isset($this->history[$i][$j])) {
+ $scrollback.= htmlspecialchars($this->history[$i][$j]);
+ }
+ }
+ $scrollback.= "\r\n";
+ }
+ $scrollback.= $this->_getScreen();
+
+ return '<pre style="color: white; background: black" width="' . ($this->max_x + 1) . '">' . $scrollback . '</pre>';
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php
new file mode 100644
index 00000000000..766c6e7ebf4
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php
@@ -0,0 +1,1277 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP ASN.1 Parser
+ *
+ * PHP versions 4 and 5
+ *
+ * ASN.1 provides the semantics for data encoded using various schemes. The most commonly
+ * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded
+ * DER blobs.
+ *
+ * File_ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
+ *
+ * Uses the 1988 ASN.1 syntax.
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category File
+ * @package File_ASN1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMXII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id$
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Math_BigInteger
+ */
+if (!class_exists('Math_BigInteger')) {
+ require_once('Math/BigInteger.php');
+}
+
+/**#@+
+ * Tag Classes
+ *
+ * @access private
+ * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
+ */
+define('FILE_ASN1_CLASS_UNIVERSAL', 0);
+define('FILE_ASN1_CLASS_APPLICATION', 1);
+define('FILE_ASN1_CLASS_CONTEXT_SPECIFIC', 2);
+define('FILE_ASN1_CLASS_PRIVATE', 3);
+/**#@-*/
+
+/**#@+
+ * Tag Classes
+ *
+ * @access private
+ * @link http://www.obj-sys.com/asn1tutorial/node124.html
+ */
+define('FILE_ASN1_TYPE_BOOLEAN', 1);
+define('FILE_ASN1_TYPE_INTEGER', 2);
+define('FILE_ASN1_TYPE_BIT_STRING', 3);
+define('FILE_ASN1_TYPE_OCTET_STRING', 4);
+define('FILE_ASN1_TYPE_NULL', 5);
+define('FILE_ASN1_TYPE_OBJECT_IDENTIFIER',6);
+//define('FILE_ASN1_TYPE_OBJECT_DESCRIPTOR',7);
+//define('FILE_ASN1_TYPE_INSTANCE_OF', 8); // EXTERNAL
+define('FILE_ASN1_TYPE_REAL', 9);
+define('FILE_ASN1_TYPE_ENUMERATED', 10);
+//define('FILE_ASN1_TYPE_EMBEDDED', 11);
+define('FILE_ASN1_TYPE_UTF8_STRING', 12);
+//define('FILE_ASN1_TYPE_RELATIVE_OID', 13);
+define('FILE_ASN1_TYPE_SEQUENCE', 16); // SEQUENCE OF
+define('FILE_ASN1_TYPE_SET', 17); // SET OF
+/**#@-*/
+/**#@+
+ * More Tag Classes
+ *
+ * @access private
+ * @link http://www.obj-sys.com/asn1tutorial/node10.html
+ */
+define('FILE_ASN1_TYPE_NUMERIC_STRING', 18);
+define('FILE_ASN1_TYPE_PRINTABLE_STRING',19);
+define('FILE_ASN1_TYPE_TELETEX_STRING', 20); // T61String
+define('FILE_ASN1_TYPE_VIDEOTEX_STRING', 21);
+define('FILE_ASN1_TYPE_IA5_STRING', 22);
+define('FILE_ASN1_TYPE_UTC_TIME', 23);
+define('FILE_ASN1_TYPE_GENERALIZED_TIME',24);
+define('FILE_ASN1_TYPE_GRAPHIC_STRING', 25);
+define('FILE_ASN1_TYPE_VISIBLE_STRING', 26); // ISO646String
+define('FILE_ASN1_TYPE_GENERAL_STRING', 27);
+define('FILE_ASN1_TYPE_UNIVERSAL_STRING',28);
+//define('FILE_ASN1_TYPE_CHARACTER_STRING',29);
+define('FILE_ASN1_TYPE_BMP_STRING', 30);
+/**#@-*/
+
+/**#@+
+ * Tag Aliases
+ *
+ * These tags are kinda place holders for other tags.
+ *
+ * @access private
+ */
+define('FILE_ASN1_TYPE_CHOICE', -1);
+define('FILE_ASN1_TYPE_ANY', -2);
+/**#@-*/
+
+/**
+ * ASN.1 Element
+ *
+ * Bypass normal encoding rules in File_ASN1::encodeDER()
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.3.0
+ * @access public
+ * @package File_ASN1
+ */
+class File_ASN1_Element {
+ /**
+ * Raw element value
+ *
+ * @var String
+ * @access private
+ */
+ var $element;
+
+ /**
+ * Constructor
+ *
+ * @param String $encoded
+ * @return File_ASN1_Element
+ * @access public
+ */
+ function File_ASN1_Element($encoded)
+ {
+ $this->element = $encoded;
+ }
+}
+
+/**
+ * Pure-PHP ASN.1 Parser
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.3.0
+ * @access public
+ * @package File_ASN1
+ */
+class File_ASN1 {
+ /**
+ * ASN.1 object identifier
+ *
+ * @var Array
+ * @access private
+ * @link http://en.wikipedia.org/wiki/Object_identifier
+ */
+ var $oids = array();
+
+ /**
+ * Default date format
+ *
+ * @var String
+ * @access private
+ * @link http://php.net/class.datetime
+ */
+ var $format = 'D, d M y H:i:s O';
+
+ /**
+ * Default date format
+ *
+ * @var Array
+ * @access private
+ * @see File_ASN1::setTimeFormat()
+ * @see File_ASN1::asn1map()
+ * @link http://php.net/class.datetime
+ */
+ var $encoded;
+
+ /**
+ * Filters
+ *
+ * If the mapping type is FILE_ASN1_TYPE_ANY what do we actually encode it as?
+ *
+ * @var Array
+ * @access private
+ * @see File_ASN1::_encode_der()
+ */
+ var $filters;
+
+ /**
+ * Type mapping table for the ANY type.
+ *
+ * Structured or unknown types are mapped to a FILE_ASN1_Element.
+ * Unambiguous types get the direct mapping (int/real/bool).
+ * Others are mapped as a choice, with an extra indexing level.
+ *
+ * @var Array
+ * @access public
+ */
+ var $ANYmap = array(
+ FILE_ASN1_TYPE_BOOLEAN => true,
+ FILE_ASN1_TYPE_INTEGER => true,
+ FILE_ASN1_TYPE_BIT_STRING => 'bitString',
+ FILE_ASN1_TYPE_OCTET_STRING => 'octetString',
+ FILE_ASN1_TYPE_NULL => 'null',
+ FILE_ASN1_TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
+ FILE_ASN1_TYPE_REAL => true,
+ FILE_ASN1_TYPE_ENUMERATED => 'enumerated',
+ FILE_ASN1_TYPE_UTF8_STRING => 'utf8String',
+ FILE_ASN1_TYPE_NUMERIC_STRING => 'numericString',
+ FILE_ASN1_TYPE_PRINTABLE_STRING => 'printableString',
+ FILE_ASN1_TYPE_TELETEX_STRING => 'teletexString',
+ FILE_ASN1_TYPE_VIDEOTEX_STRING => 'videotexString',
+ FILE_ASN1_TYPE_IA5_STRING => 'ia5String',
+ FILE_ASN1_TYPE_UTC_TIME => 'utcTime',
+ FILE_ASN1_TYPE_GENERALIZED_TIME => 'generalTime',
+ FILE_ASN1_TYPE_GRAPHIC_STRING => 'graphicString',
+ FILE_ASN1_TYPE_VISIBLE_STRING => 'visibleString',
+ FILE_ASN1_TYPE_GENERAL_STRING => 'generalString',
+ FILE_ASN1_TYPE_UNIVERSAL_STRING => 'universalString',
+ //FILE_ASN1_TYPE_CHARACTER_STRING => 'characterString',
+ FILE_ASN1_TYPE_BMP_STRING => 'bmpString'
+ );
+
+ /**
+ * String type to character size mapping table.
+ *
+ * Non-convertable types are absent from this table.
+ * size == 0 indicates variable length encoding.
+ *
+ * @var Array
+ * @access public
+ */
+ var $stringTypeSize = array(
+ FILE_ASN1_TYPE_UTF8_STRING => 0,
+ FILE_ASN1_TYPE_BMP_STRING => 2,
+ FILE_ASN1_TYPE_UNIVERSAL_STRING => 4,
+ FILE_ASN1_TYPE_PRINTABLE_STRING => 1,
+ FILE_ASN1_TYPE_TELETEX_STRING => 1,
+ FILE_ASN1_TYPE_IA5_STRING => 1,
+ FILE_ASN1_TYPE_VISIBLE_STRING => 1,
+ );
+
+ /**
+ * Parse BER-encoding
+ *
+ * Serves a similar purpose to openssl's asn1parse
+ *
+ * @param String $encoded
+ * @return Array
+ * @access public
+ */
+ function decodeBER($encoded)
+ {
+ if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') {
+ $encoded = $encoded->element;
+ }
+
+ $this->encoded = $encoded;
+ return $this->_decode_ber($encoded);
+ }
+
+ /**
+ * Parse BER-encoding (Helper function)
+ *
+ * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode.
+ * $encoded is passed by reference for the recursive calls done for FILE_ASN1_TYPE_BIT_STRING and
+ * FILE_ASN1_TYPE_OCTET_STRING. In those cases, the indefinite length is used.
+ *
+ * @param String $encoded
+ * @param Integer $start
+ * @return Array
+ * @access private
+ */
+ function _decode_ber(&$encoded, $start = 0)
+ {
+ $decoded = array();
+
+ while ( strlen($encoded) ) {
+ $current = array('start' => $start);
+
+ $type = ord($this->_string_shift($encoded));
+ $start++;
+
+ $constructed = ($type >> 5) & 1;
+
+ $tag = $type & 0x1F;
+ if ($tag == 0x1F) {
+ $tag = 0;
+ // process septets (since the eighth bit is ignored, it's not an octet)
+ do {
+ $loop = ord($encoded[0]) >> 7;
+ $tag <<= 7;
+ $tag |= ord($this->_string_shift($encoded)) & 0x7F;
+ $start++;
+ } while ( $loop );
+ }
+
+ // Length, as discussed in 8.1.3 of X.690-0207.pdf#page=13
+ $length = ord($this->_string_shift($encoded));
+ $start++;
+ if ( $length == 0x80 ) { // indefinite length
+ // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
+ // immediately available." -- 8.1.3.2.c
+ //if ( !$constructed ) {
+ // return false;
+ //}
+ $length = strlen($encoded);
+ } elseif ( $length & 0x80 ) { // definite length, long form
+ // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
+ // support it up to four.
+ $length&= 0x7F;
+ $temp = $this->_string_shift($encoded, $length);
+ $start+= $length;
+ extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
+ }
+
+ // End-of-content, see 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
+ if (!$type && !$length) {
+ return $decoded;
+ }
+ $content = $this->_string_shift($encoded, $length);
+
+ /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
+ built-in types. It defines an application-independent data type that must be distinguishable from all other
+ data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
+ have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within
+ a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the
+ alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this
+ data type; the term CONTEXT-SPECIFIC does not appear.
+
+ -- http://www.obj-sys.com/asn1tutorial/node12.html */
+ $class = ($type >> 6) & 3;
+ switch ($class) {
+ case FILE_ASN1_CLASS_APPLICATION:
+ case FILE_ASN1_CLASS_PRIVATE:
+ case FILE_ASN1_CLASS_CONTEXT_SPECIFIC:
+ $decoded[] = array(
+ 'type' => $class,
+ 'constant' => $tag,
+ 'content' => $constructed ? $this->_decode_ber($content, $start) : $content,
+ 'length' => $length + $start - $current['start']
+ ) + $current;
+ continue 2;
+ }
+
+ $current+= array('type' => $tag);
+
+ // decode UNIVERSAL tags
+ switch ($tag) {
+ case FILE_ASN1_TYPE_BOOLEAN:
+ // "The contents octets shall consist of a single octet." -- 8.2.1
+ //if (strlen($content) != 1) {
+ // return false;
+ //}
+ $current['content'] = (bool) ord($content[0]);
+ break;
+ case FILE_ASN1_TYPE_INTEGER:
+ case FILE_ASN1_TYPE_ENUMERATED:
+ $current['content'] = new Math_BigInteger($content, -256);
+ break;
+ case FILE_ASN1_TYPE_REAL: // not currently supported
+ return false;
+ case FILE_ASN1_TYPE_BIT_STRING:
+ // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
+ // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
+ // seven.
+ if (!$constructed) {
+ $current['content'] = $content;
+ } else {
+ $temp = $this->_decode_ber($content, $start);
+ $length-= strlen($content);
+ $last = count($temp) - 1;
+ for ($i = 0; $i < $last; $i++) {
+ // all subtags should be bit strings
+ //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
+ // return false;
+ //}
+ $current['content'].= substr($temp[$i]['content'], 1);
+ }
+ // all subtags should be bit strings
+ //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
+ // return false;
+ //}
+ $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
+ }
+ break;
+ case FILE_ASN1_TYPE_OCTET_STRING:
+ if (!$constructed) {
+ $current['content'] = $content;
+ } else {
+ $temp = $this->_decode_ber($content, $start);
+ $length-= strlen($content);
+ for ($i = 0, $size = count($temp); $i < $size; $i++) {
+ // all subtags should be octet strings
+ //if ($temp[$i]['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
+ // return false;
+ //}
+ $current['content'].= $temp[$i]['content'];
+ }
+ // $length =
+ }
+ break;
+ case FILE_ASN1_TYPE_NULL:
+ // "The contents octets shall not contain any octets." -- 8.8.2
+ //if (strlen($content)) {
+ // return false;
+ //}
+ break;
+ case FILE_ASN1_TYPE_SEQUENCE:
+ case FILE_ASN1_TYPE_SET:
+ $current['content'] = $this->_decode_ber($content, $start);
+ break;
+ case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
+ $temp = ord($this->_string_shift($content));
+ $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
+ $valuen = 0;
+ // process septets
+ while (strlen($content)) {
+ $temp = ord($this->_string_shift($content));
+ $valuen <<= 7;
+ $valuen |= $temp & 0x7F;
+ if (~$temp & 0x80) {
+ $current['content'].= ".$valuen";
+ $valuen = 0;
+ }
+ }
+ // the eighth bit of the last byte should not be 1
+ //if ($temp >> 7) {
+ // return false;
+ //}
+ break;
+ /* Each character string type shall be encoded as if it had been declared:
+ [UNIVERSAL x] IMPLICIT OCTET STRING
+
+ -- X.690-0207.pdf#page=23 ( 8.21.3)
+
+ Per that, we're not going to do any validation. If there are any illegal characters in the string,
+ we don't really care */
+ case FILE_ASN1_TYPE_NUMERIC_STRING:
+ // 0,1,2,3,4,5,6,7,8,9, and space
+ case FILE_ASN1_TYPE_PRINTABLE_STRING:
+ // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma,
+ // hyphen, full stop, solidus, colon, equal sign, question mark
+ case FILE_ASN1_TYPE_TELETEX_STRING:
+ // The Teletex character set in CCITT's T61, space, and delete
+ // see http://en.wikipedia.org/wiki/Teletex#Character_sets
+ case FILE_ASN1_TYPE_VIDEOTEX_STRING:
+ // The Videotex character set in CCITT's T.100 and T.101, space, and delete
+ case FILE_ASN1_TYPE_VISIBLE_STRING:
+ // Printing character sets of international ASCII, and space
+ case FILE_ASN1_TYPE_IA5_STRING:
+ // International Alphabet 5 (International ASCII)
+ case FILE_ASN1_TYPE_GRAPHIC_STRING:
+ // All registered G sets, and space
+ case FILE_ASN1_TYPE_GENERAL_STRING:
+ // All registered C and G sets, space and delete
+ case FILE_ASN1_TYPE_UTF8_STRING:
+ // ????
+ case FILE_ASN1_TYPE_BMP_STRING:
+ $current['content'] = $content;
+ break;
+ case FILE_ASN1_TYPE_UTC_TIME:
+ case FILE_ASN1_TYPE_GENERALIZED_TIME:
+ $current['content'] = $this->_decodeTime($content, $tag);
+ default:
+
+ }
+
+ $start+= $length;
+ $decoded[] = $current + array('length' => $start - $current['start']);
+ }
+
+ return $decoded;
+ }
+
+ /**
+ * ASN.1 Decode
+ *
+ * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
+ *
+ * @param Array $decoded
+ * @param Array $mapping
+ * @return Array
+ * @access public
+ */
+ function asn1map($decoded, $mapping)
+ {
+ if (isset($mapping['explicit'])) {
+ $decoded = $decoded['content'][0];
+ }
+
+ switch (true) {
+ case $mapping['type'] == FILE_ASN1_TYPE_ANY:
+ $intype = $decoded['type'];
+ if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) {
+ return new File_ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length']));
+ }
+ $inmap = $this->ANYmap[$intype];
+ if (is_string($inmap)) {
+ return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping));
+ }
+ break;
+ case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
+ foreach ($mapping['children'] as $key => $option) {
+ switch (true) {
+ case isset($option['constant']) && $option['constant'] == $decoded['constant']:
+ case !isset($option['constant']) && $option['type'] == $decoded['type']:
+ $value = $this->asn1map($decoded, $option);
+ break;
+ case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
+ $v = $this->asn1map($decoded, $option);
+ if (isset($v)) {
+ $value = $v;
+ }
+ }
+ if (isset($value)) {
+ return array($key => $value);
+ }
+ }
+ return NULL;
+ case isset($mapping['implicit']):
+ case isset($mapping['explicit']):
+ case $decoded['type'] == $mapping['type']:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (isset($mapping['implicit'])) {
+ $decoded['type'] = $mapping['type'];
+ }
+
+ switch ($decoded['type']) {
+ case FILE_ASN1_TYPE_SEQUENCE:
+ $map = array();
+
+ // ignore the min and max
+ if (isset($mapping['min']) && isset($mapping['max'])) {
+ $child = $mapping['children'];
+ foreach ($decoded['content'] as $content) {
+ if (($map[] = $this->asn1map($content, $child)) === NULL) {
+ return NULL;
+ }
+ }
+
+ return $map;
+ }
+
+ $n = count($decoded['content']);
+ $i = 0;
+
+ foreach ($mapping['children'] as $key => $child) {
+ $maymatch = $i < $n; // Match only existing input.
+ if ($maymatch) {
+ $temp = $decoded['content'][$i];
+
+ if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
+ // Get the mapping and input class & constant.
+ $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
+ $constant = NULL;
+ if (isset($temp['constant'])) {
+ $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
+ }
+ if (isset($child['class'])) {
+ $childClass = $child['class'];
+ $constant = $child['cast'];
+ }
+ elseif (isset($child['constant'])) {
+ $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
+ $constant = $child['constant'];
+ }
+
+ if (isset($constant) && isset($temp['constant'])) {
+ // Can only match if constants and class match.
+ $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
+ } else {
+ // Can only match if no constant expected and type matches or is generic.
+ $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
+ }
+ }
+ }
+
+ if ($maymatch) {
+ // Attempt submapping.
+ $candidate = $this->asn1map($temp, $child);
+ $maymatch = $candidate !== NULL;
+ }
+
+ if ($maymatch) {
+ // Got the match: use it.
+ $map[$key] = $candidate;
+ $i++;
+ } elseif (isset($child['default'])) {
+ $map[$key] = $child['default']; // Use default.
+ } elseif (!isset($child['optional'])) {
+ return NULL; // Syntax error.
+ }
+ }
+
+ // Fail mapping if all input items have not been consumed.
+ return $i < $n? NULL: $map;
+
+ // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
+ case FILE_ASN1_TYPE_SET:
+ $map = array();
+
+ // ignore the min and max
+ if (isset($mapping['min']) && isset($mapping['max'])) {
+ $child = $mapping['children'];
+ foreach ($decoded['content'] as $content) {
+ if (($map[] = $this->asn1map($content, $child)) === NULL) {
+ return NULL;
+ }
+ }
+
+ return $map;
+ }
+
+ for ($i = 0; $i < count($decoded['content']); $i++) {
+ $temp = $decoded['content'][$i];
+ $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
+ if (isset($temp['constant'])) {
+ $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
+ }
+
+ foreach ($mapping['children'] as $key => $child) {
+ if (isset($map[$key])) {
+ continue;
+ }
+ $maymatch = true;
+ if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
+ $childClass = FILE_ASN1_CLASS_UNIVERSAL;
+ $constant = NULL;
+ if (isset($child['class'])) {
+ $childClass = $child['class'];
+ $constant = $child['cast'];
+ }
+ elseif (isset($child['constant'])) {
+ $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
+ $constant = $child['constant'];
+ }
+
+ if (isset($constant) && isset($temp['constant'])) {
+ // Can only match if constants and class match.
+ $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
+ } else {
+ // Can only match if no constant expected and type matches or is generic.
+ $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
+ }
+ }
+
+ if ($maymatch) {
+ // Attempt submapping.
+ $candidate = $this->asn1map($temp, $child);
+ $maymatch = $candidate !== NULL;
+ }
+
+ if (!$maymatch) {
+ break;
+ }
+
+ // Got the match: use it.
+ $map[$key] = $candidate;
+ break;
+ }
+ }
+
+ foreach ($mapping['children'] as $key => $child) {
+ if (!isset($map[$key])) {
+ if (isset($child['default'])) {
+ $map[$key] = $child['default'];
+ } elseif (!isset($child['optional'])) {
+ return NULL;
+ }
+ }
+ }
+ return $map;
+ case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
+ return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
+ case FILE_ASN1_TYPE_UTC_TIME:
+ case FILE_ASN1_TYPE_GENERALIZED_TIME:
+ if (isset($mapping['implicit'])) {
+ $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
+ }
+ return @date($this->format, $decoded['content']);
+ case FILE_ASN1_TYPE_BIT_STRING:
+ if (isset($mapping['mapping'])) {
+ $offset = ord($decoded['content'][0]);
+ $size = (strlen($decoded['content']) - 1) * 8 - $offset;
+ /*
+ From X.680-0207.pdf#page=46 (21.7):
+
+ "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove)
+ arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should
+ therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
+ 0 bits."
+ */
+ $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
+ for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
+ $current = ord($decoded['content'][$i]);
+ for ($j = $offset; $j < 8; $j++) {
+ $bits[] = (bool) ($current & (1 << $j));
+ }
+ $offset = 0;
+ }
+ $values = array();
+ $map = array_reverse($mapping['mapping']);
+ foreach ($map as $i => $value) {
+ if ($bits[$i]) {
+ $values[] = $value;
+ }
+ }
+ return $values;
+ }
+ case FILE_ASN1_TYPE_OCTET_STRING:
+ return base64_encode($decoded['content']);
+ case FILE_ASN1_TYPE_NULL:
+ return '';
+ case FILE_ASN1_TYPE_BOOLEAN:
+ return $decoded['content'];
+ case FILE_ASN1_TYPE_NUMERIC_STRING:
+ case FILE_ASN1_TYPE_PRINTABLE_STRING:
+ case FILE_ASN1_TYPE_TELETEX_STRING:
+ case FILE_ASN1_TYPE_VIDEOTEX_STRING:
+ case FILE_ASN1_TYPE_IA5_STRING:
+ case FILE_ASN1_TYPE_GRAPHIC_STRING:
+ case FILE_ASN1_TYPE_VISIBLE_STRING:
+ case FILE_ASN1_TYPE_GENERAL_STRING:
+ case FILE_ASN1_TYPE_UNIVERSAL_STRING:
+ case FILE_ASN1_TYPE_UTF8_STRING:
+ case FILE_ASN1_TYPE_BMP_STRING:
+ return $decoded['content'];
+ case FILE_ASN1_TYPE_INTEGER:
+ case FILE_ASN1_TYPE_ENUMERATED:
+ $temp = $decoded['content'];
+ if (isset($mapping['implicit'])) {
+ $temp = new Math_BigInteger($decoded['content'], -256);
+ }
+ if (isset($mapping['mapping'])) {
+ $temp = (int) $temp->toString();
+ return isset($mapping['mapping'][$temp]) ?
+ $mapping['mapping'][$temp] :
+ false;
+ }
+ return $temp;
+ }
+ }
+
+ /**
+ * ASN.1 Encode
+ *
+ * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
+ * an ASN.1 compiler.
+ *
+ * @param String $source
+ * @param String $mapping
+ * @param Integer $idx
+ * @return String
+ * @access public
+ */
+ function encodeDER($source, $mapping)
+ {
+ $this->location = array();
+ return $this->_encode_der($source, $mapping);
+ }
+
+ /**
+ * ASN.1 Encode (Helper function)
+ *
+ * @param String $source
+ * @param String $mapping
+ * @param Integer $idx
+ * @return String
+ * @access private
+ */
+ function _encode_der($source, $mapping, $idx = NULL)
+ {
+ if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') {
+ return $source->element;
+ }
+
+ // do not encode (implicitly optional) fields with value set to default
+ if (isset($mapping['default']) && $source === $mapping['default']) {
+ return '';
+ }
+
+ if (isset($idx)) {
+ $this->location[] = $idx;
+ }
+
+ $tag = $mapping['type'];
+
+ switch ($tag) {
+ case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence.
+ case FILE_ASN1_TYPE_SEQUENCE:
+ $tag|= 0x20; // set the constructed bit
+ $value = '';
+
+ // ignore the min and max
+ if (isset($mapping['min']) && isset($mapping['max'])) {
+ $child = $mapping['children'];
+
+ foreach ($source as $content) {
+ $temp = $this->_encode_der($content, $child);
+ if ($temp === false) {
+ return false;
+ }
+ $value.= $temp;
+ }
+ break;
+ }
+
+ foreach ($mapping['children'] as $key => $child) {
+ if (!isset($source[$key])) {
+ if (!isset($child['optional'])) {
+ return false;
+ }
+ continue;
+ }
+
+ $temp = $this->_encode_der($source[$key], $child, $key);
+ if ($temp === false) {
+ return false;
+ }
+
+ // An empty child encoding means it has been optimized out.
+ // Else we should have at least one tag byte.
+ if ($temp === '') {
+ continue;
+ }
+
+ // if isset($child['constant']) is true then isset($child['optional']) should be true as well
+ if (isset($child['constant'])) {
+ /*
+ From X.680-0207.pdf#page=58 (30.6):
+
+ "The tagging construction specifies explicit tagging if any of the following holds:
+ ...
+ c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
+ AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
+ an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
+ */
+ if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
+ $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
+ $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
+ } else {
+ $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
+ $temp = $subtag . substr($temp, 1);
+ }
+ }
+ $value.= $temp;
+ }
+ break;
+ case FILE_ASN1_TYPE_CHOICE:
+ $temp = false;
+
+ foreach ($mapping['children'] as $key => $child) {
+ if (!isset($source[$key])) {
+ continue;
+ }
+
+ $temp = $this->_encode_der($source[$key], $child, $key);
+ if ($temp === false) {
+ return false;
+ }
+
+ // An empty child encoding means it has been optimized out.
+ // Else we should have at least one tag byte.
+ if ($temp === '') {
+ continue;
+ }
+
+ $tag = ord($temp[0]);
+
+ // if isset($child['constant']) is true then isset($child['optional']) should be true as well
+ if (isset($child['constant'])) {
+ if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
+ $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
+ $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
+ } else {
+ $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
+ $temp = $subtag . substr($temp, 1);
+ }
+ }
+ }
+
+ if (isset($idx)) {
+ array_pop($this->location);
+ }
+
+ if ($temp && isset($mapping['cast'])) {
+ $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
+ }
+
+ return $temp;
+ case FILE_ASN1_TYPE_INTEGER:
+ case FILE_ASN1_TYPE_ENUMERATED:
+ if (!isset($mapping['mapping'])) {
+ $value = $source->toBytes(true);
+ } else {
+ $value = array_search($source, $mapping['mapping']);
+ if ($value === false) {
+ return false;
+ }
+ $value = new Math_BigInteger($value);
+ $value = $value->toBytes(true);
+ }
+ break;
+ case FILE_ASN1_TYPE_UTC_TIME:
+ case FILE_ASN1_TYPE_GENERALIZED_TIME:
+ $format = $mapping['type'] == FILE_ASN1_TYPE_UTC_TIME ? 'y' : 'Y';
+ $format.= 'mdHis';
+ $value = @gmdate($format, strtotime($source)) . 'Z';
+ break;
+ case FILE_ASN1_TYPE_BIT_STRING:
+ if (isset($mapping['mapping'])) {
+ $bits = array_fill(0, count($mapping['mapping']), 0);
+ $size = 0;
+ for ($i = 0; $i < count($mapping['mapping']); $i++) {
+ if (in_array($mapping['mapping'][$i], $source)) {
+ $bits[$i] = 1;
+ $size = $i;
+ }
+ }
+
+ $offset = 8 - (($size + 1) & 7);
+ $offset = $offset !== 8 ? $offset : 0;
+
+ $value = chr($offset);
+
+ for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
+ unset($bits[$i]);
+ }
+
+ $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
+ $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
+ foreach ($bytes as $byte) {
+ $value.= chr(bindec($byte));
+ }
+
+ break;
+ }
+ case FILE_ASN1_TYPE_OCTET_STRING:
+ /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
+ the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.
+
+ -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
+ $value = base64_decode($source);
+ break;
+ case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
+ $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
+ if ($oid === false) {
+ user_error('Invalid OID');
+ return false;
+ }
+ $value = '';
+ $parts = explode('.', $oid);
+ $value = chr(40 * $parts[0] + $parts[1]);
+ for ($i = 2; $i < count($parts); $i++) {
+ $temp = '';
+ if (!$parts[$i]) {
+ $temp = "\0";
+ } else {
+ while ($parts[$i]) {
+ $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
+ $parts[$i] >>= 7;
+ }
+ $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
+ }
+ $value.= $temp;
+ }
+ break;
+ case FILE_ASN1_TYPE_ANY:
+ $loc = $this->location;
+ if (isset($idx)) {
+ array_pop($this->location);
+ }
+
+ switch (true) {
+ case !isset($source):
+ return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping);
+ case is_int($source):
+ case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
+ return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping);
+ case is_float($source):
+ return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping);
+ case is_bool($source):
+ return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping);
+ case is_array($source) && count($source) == 1:
+ $typename = implode('', array_keys($source));
+ $outtype = array_search($typename, $this->ANYmap, true);
+ if ($outtype !== false) {
+ return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping);
+ }
+ }
+
+ $filters = $this->filters;
+ foreach ($loc as $part) {
+ if (!isset($filters[$part])) {
+ $filters = false;
+ break;
+ }
+ $filters = $filters[$part];
+ }
+ if ($filters === false) {
+ user_error('No filters defined for ' . implode('/', $loc));
+ return false;
+ }
+ return $this->_encode_der($source, $filters + $mapping);
+ case FILE_ASN1_TYPE_NULL:
+ $value = '';
+ break;
+ case FILE_ASN1_TYPE_NUMERIC_STRING:
+ case FILE_ASN1_TYPE_TELETEX_STRING:
+ case FILE_ASN1_TYPE_PRINTABLE_STRING:
+ case FILE_ASN1_TYPE_UNIVERSAL_STRING:
+ case FILE_ASN1_TYPE_UTF8_STRING:
+ case FILE_ASN1_TYPE_BMP_STRING:
+ case FILE_ASN1_TYPE_IA5_STRING:
+ case FILE_ASN1_TYPE_VISIBLE_STRING:
+ case FILE_ASN1_TYPE_VIDEOTEX_STRING:
+ case FILE_ASN1_TYPE_GRAPHIC_STRING:
+ case FILE_ASN1_TYPE_GENERAL_STRING:
+ $value = $source;
+ break;
+ case FILE_ASN1_TYPE_BOOLEAN:
+ $value = $source ? "\xFF" : "\x00";
+ break;
+ default:
+ user_error('Mapping provides no type definition for ' . implode('/', $this->location));
+ return false;
+ }
+
+ if (isset($idx)) {
+ array_pop($this->location);
+ }
+
+ if (isset($mapping['cast'])) {
+ $tag = ($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast'];
+ }
+
+ return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
+ }
+
+ /**
+ * DER-encode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information.
+ *
+ * @access private
+ * @param Integer $length
+ * @return String
+ */
+ function _encodeLength($length)
+ {
+ if ($length <= 0x7F) {
+ return chr($length);
+ }
+
+ $temp = ltrim(pack('N', $length), chr(0));
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
+ }
+
+ /**
+ * BER-decode the time
+ *
+ * Called by _decode_ber() and in the case of implicit tags asn1map().
+ *
+ * @access private
+ * @param String $content
+ * @param Integer $tag
+ * @return String
+ */
+ function _decodeTime($content, $tag)
+ {
+ /* UTCTime:
+ http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
+ http://www.obj-sys.com/asn1tutorial/node15.html
+
+ GeneralizedTime:
+ http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
+ http://www.obj-sys.com/asn1tutorial/node14.html */
+
+ $pattern = $tag == FILE_ASN1_TYPE_UTC_TIME ?
+ '#(..)(..)(..)(..)(..)(..)(.*)#' :
+ '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
+
+ preg_match($pattern, $content, $matches);
+
+ list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
+
+ if ($tag == FILE_ASN1_TYPE_UTC_TIME) {
+ $year = $year >= 50 ? "19$year" : "20$year";
+ }
+
+ if ($timezone == 'Z') {
+ $mktime = 'gmmktime';
+ $timezone = 0;
+ } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
+ $mktime = 'gmmktime';
+ $timezone = 60 * $matches[3] + 3600 * $matches[2];
+ if ($matches[1] == '-') {
+ $timezone = -$timezone;
+ }
+ } else {
+ $mktime = 'mktime';
+ $timezone = 0;
+ }
+
+ return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
+ }
+
+ /**
+ * Set the time format
+ *
+ * Sets the time / date format for asn1map().
+ *
+ * @access public
+ * @param String $format
+ */
+ function setTimeFormat($format)
+ {
+ $this->format = $format;
+ }
+
+ /**
+ * Load OIDs
+ *
+ * Load the relevant OIDs for a particular ASN.1 semantic mapping.
+ *
+ * @access public
+ * @param Array $oids
+ */
+ function loadOIDs($oids)
+ {
+ $this->oids = $oids;
+ }
+
+ /**
+ * Load filters
+ *
+ * See File_X509, etc, for an example.
+ *
+ * @access public
+ * @param Array $filters
+ */
+ function loadFilters($filters)
+ {
+ $this->filters = $filters;
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+
+ /**
+ * String type conversion
+ *
+ * This is a lazy conversion, dealing only with character size.
+ * No real conversion table is used.
+ *
+ * @param String $in
+ * @param optional Integer $from
+ * @param optional Integer $to
+ * @return String
+ * @access public
+ */
+ function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING)
+ {
+ if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
+ return false;
+ }
+ $insize = $this->stringTypeSize[$from];
+ $outsize = $this->stringTypeSize[$to];
+ $inlength = strlen($in);
+ $out = '';
+
+ for ($i = 0; $i < $inlength;) {
+ if ($inlength - $i < $insize) {
+ return false;
+ }
+
+ // Get an input character as a 32-bit value.
+ $c = ord($in[$i++]);
+ switch (true) {
+ case $insize == 4:
+ $c = ($c << 8) | ord($in[$i++]);
+ $c = ($c << 8) | ord($in[$i++]);
+ case $insize == 2:
+ $c = ($c << 8) | ord($in[$i++]);
+ case $insize == 1:
+ break;
+ case ($c & 0x80) == 0x00:
+ break;
+ case ($c & 0x40) == 0x00:
+ return false;
+ default:
+ $bit = 6;
+ do {
+ if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
+ return false;
+ }
+ $c = ($c << 6) | (ord($in[$i++]) & 0x3F);
+ $bit += 5;
+ $mask = 1 << $bit;
+ } while ($c & $bit);
+ $c &= $mask - 1;
+ break;
+ }
+
+ // Convert and append the character to output string.
+ $v = '';
+ switch (true) {
+ case $outsize == 4:
+ $v .= chr($c & 0xFF);
+ $c >>= 8;
+ $v .= chr($c & 0xFF);
+ $c >>= 8;
+ case $outsize == 2:
+ $v .= chr($c & 0xFF);
+ $c >>= 8;
+ case $outsize == 1:
+ $v .= chr($c & 0xFF);
+ $c >>= 8;
+ if ($c) {
+ return false;
+ }
+ break;
+ case ($c & 0x80000000) != 0:
+ return false;
+ case $c >= 0x04000000:
+ $v .= chr(0x80 | ($c & 0x3F));
+ $c = ($c >> 6) | 0x04000000;
+ case $c >= 0x00200000:
+ $v .= chr(0x80 | ($c & 0x3F));
+ $c = ($c >> 6) | 0x00200000;
+ case $c >= 0x00010000:
+ $v .= chr(0x80 | ($c & 0x3F));
+ $c = ($c >> 6) | 0x00010000;
+ case $c >= 0x00000800:
+ $v .= chr(0x80 | ($c & 0x3F));
+ $c = ($c >> 6) | 0x00000800;
+ case $c >= 0x00000080:
+ $v .= chr(0x80 | ($c & 0x3F));
+ $c = ($c >> 6) | 0x000000C0;
+ default:
+ $v .= chr($c);
+ break;
+ }
+ $out .= strrev($v);
+ }
+ return $out;
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php
new file mode 100644
index 00000000000..278da62e262
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php
@@ -0,0 +1,4323 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP X.509 Parser
+ *
+ * PHP versions 4 and 5
+ *
+ * Encode and decode X.509 certificates.
+ *
+ * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
+ * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
+ *
+ * Note that loading an X.509 certificate and resaving it may invalidate the signature. The reason being that the signature is based on a
+ * portion of the certificate that contains optional parameters with default values. ie. if the parameter isn't there the default value is
+ * used. Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
+ * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the
+ * the certificate all together unless the certificate is re-signed.
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category File
+ * @package File_X509
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMXII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id$
+ * @link htp://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include File_ASN1
+ */
+if (!class_exists('File_ASN1')) {
+ require_once('File/ASN1.php');
+}
+
+/**
+ * Flag to only accept signatures signed by certificate authorities
+ *
+ * @access public
+ * @see File_X509::validateSignature()
+ */
+define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1);
+
+/**#@+
+ * @access public
+ * @see File_X509::getDN()
+ */
+/**
+ * Return internal array representation
+ */
+define('FILE_X509_DN_ARRAY', 0);
+/**
+ * Return string
+ */
+define('FILE_X509_DN_STRING', 1);
+/**
+ * Return ASN.1 name string
+ */
+define('FILE_X509_DN_ASN1', 2);
+/**
+ * Return OpenSSL compatible array
+ */
+define('FILE_X509_DN_OPENSSL', 3);
+/**
+ * Return canonical ASN.1 RDNs string
+ */
+define('FILE_X509_DN_CANON', 4);
+/**
+ * Return name hash for file indexing
+ */
+define('FILE_X509_DN_HASH', 5);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see File_X509::saveX509()
+ * @see File_X509::saveCSR()
+ * @see File_X509::saveCRL()
+ */
+/**
+ * Save as PEM
+ *
+ * ie. a base64-encoded PEM with a header and a footer
+ */
+define('FILE_X509_FORMAT_PEM', 0);
+/**
+ * Save as DER
+ */
+define('FILE_X509_FORMAT_DER', 1);
+/**
+ * Save as a SPKAC
+ *
+ * Only works on CSRs. Not currently supported.
+ */
+define('FILE_X509_FORMAT_SPKAC', 2);
+/**#@-*/
+
+/**
+ * Attribute value disposition.
+ * If disposition is >= 0, this is the index of the target value.
+ */
+define('FILE_X509_ATTR_ALL', -1); // All attribute values (array).
+define('FILE_X509_ATTR_APPEND', -2); // Add a value.
+define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value.
+
+/**
+ * Pure-PHP X.509 Parser
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.3.1
+ * @access public
+ * @package File_X509
+ */
+class File_X509 {
+ /**
+ * ASN.1 syntax for X.509 certificates
+ *
+ * @var Array
+ * @access private
+ */
+ var $Certificate;
+
+ /**#@+
+ * ASN.1 syntax for various extensions
+ *
+ * @access private
+ */
+ var $DirectoryString;
+ var $PKCS9String;
+ var $AttributeValue;
+ var $Extensions;
+ var $KeyUsage;
+ var $ExtKeyUsageSyntax;
+ var $BasicConstraints;
+ var $KeyIdentifier;
+ var $CRLDistributionPoints;
+ var $AuthorityKeyIdentifier;
+ var $CertificatePolicies;
+ var $AuthorityInfoAccessSyntax;
+ var $SubjectAltName;
+ var $PrivateKeyUsagePeriod;
+ var $IssuerAltName;
+ var $PolicyMappings;
+ var $NameConstraints;
+
+ var $CPSuri;
+ var $UserNotice;
+
+ var $netscape_cert_type;
+ var $netscape_comment;
+ var $netscape_ca_policy_url;
+
+ var $Name;
+ var $RelativeDistinguishedName;
+ var $CRLNumber;
+ var $CRLReason;
+ var $IssuingDistributionPoint;
+ var $InvalidityDate;
+ var $CertificateIssuer;
+ var $HoldInstructionCode;
+ var $SignedPublicKeyAndChallenge;
+ /**#@-*/
+
+ /**
+ * ASN.1 syntax for Certificate Signing Requests (RFC2986)
+ *
+ * @var Array
+ * @access private
+ */
+ var $CertificationRequest;
+
+ /**
+ * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
+ *
+ * @var Array
+ * @access private
+ */
+ var $CertificateList;
+
+ /**
+ * Distinguished Name
+ *
+ * @var Array
+ * @access private
+ */
+ var $dn;
+
+ /**
+ * Public key
+ *
+ * @var String
+ * @access private
+ */
+ var $publicKey;
+
+ /**
+ * Private key
+ *
+ * @var String
+ * @access private
+ */
+ var $privateKey;
+
+ /**
+ * Object identifiers for X.509 certificates
+ *
+ * @var Array
+ * @access private
+ * @link http://en.wikipedia.org/wiki/Object_identifier
+ */
+ var $oids;
+
+ /**
+ * The certificate authorities
+ *
+ * @var Array
+ * @access private
+ */
+ var $CAs;
+
+ /**
+ * The currently loaded certificate
+ *
+ * @var Array
+ * @access private
+ */
+ var $currentCert;
+
+ /**
+ * The signature subject
+ *
+ * There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally
+ * encoded so we take save the portion of the original cert that the signature would have made for.
+ *
+ * @var String
+ * @access private
+ */
+ var $signatureSubject;
+
+ /**
+ * Certificate Start Date
+ *
+ * @var String
+ * @access private
+ */
+ var $startDate;
+
+ /**
+ * Certificate End Date
+ *
+ * @var String
+ * @access private
+ */
+ var $endDate;
+
+ /**
+ * Serial Number
+ *
+ * @var String
+ * @access private
+ */
+ var $serialNumber;
+
+ /**
+ * Key Identifier
+ *
+ * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
+ * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
+ *
+ * @var String
+ * @access private
+ */
+ var $currentKeyIdentifier;
+
+ /**
+ * CA Flag
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $caFlag = false;
+
+ /**
+ * Default Constructor.
+ *
+ * @return File_X509
+ * @access public
+ */
+ function File_X509()
+ {
+ // Explicitly Tagged Module, 1988 Syntax
+ // http://tools.ietf.org/html/rfc5280#appendix-A.1
+
+ $this->DirectoryString = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'teletexString' => array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
+ 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
+ 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
+ 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING),
+ 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING)
+ )
+ );
+
+ $this->PKCS9String = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING),
+ 'directoryString' => $this->DirectoryString
+ )
+ );
+
+ $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);
+
+ $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
+
+ $AttributeTypeAndValue = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'type' => $AttributeType,
+ 'value'=> $this->AttributeValue
+ )
+ );
+
+ /*
+ In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
+ but they can be useful at times when either there is no unique attribute in the entry or you
+ want to ensure that the entry's DN contains some useful identifying information.
+
+ - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
+ */
+ $this->RelativeDistinguishedName = array(
+ 'type' => FILE_ASN1_TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $AttributeTypeAndValue
+ );
+
+ // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
+ $RDNSequence = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ // RDNSequence does not define a min or a max, which means it doesn't have one
+ 'min' => 0,
+ 'max' => -1,
+ 'children' => $this->RelativeDistinguishedName
+ );
+
+ $this->Name = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'rdnSequence' => $RDNSequence
+ )
+ );
+
+ // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
+ $AlgorithmIdentifier = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
+ 'parameters' => array(
+ 'type' => FILE_ASN1_TYPE_ANY,
+ 'optional' => true
+ )
+ )
+ );
+
+ /*
+ A certificate using system MUST reject the certificate if it encounters
+ a critical extension it does not recognize; however, a non-critical
+ extension may be ignored if it is not recognized.
+
+ http://tools.ietf.org/html/rfc5280#section-4.2
+ */
+ $Extension = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
+ 'critical' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'optional' => true,
+ 'default' => false
+ ),
+ 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
+ )
+ );
+
+ $this->Extensions = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ // technically, it's MAX, but we'll assume anything < 0 is MAX
+ 'max' => -1,
+ // if 'children' isn't an array then 'min' and 'max' must be defined
+ 'children' => $Extension
+ );
+
+ $SubjectPublicKeyInfo = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'algorithm' => $AlgorithmIdentifier,
+ 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
+ )
+ );
+
+ $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);
+
+ $Time = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME),
+ 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
+ )
+ );
+
+ // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
+ $Validity = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'notBefore' => $Time,
+ 'notAfter' => $Time
+ )
+ );
+
+ $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
+
+ $Version = array(
+ 'type' => FILE_ASN1_TYPE_INTEGER,
+ 'mapping' => array('v1', 'v2', 'v3')
+ );
+
+ // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
+ $TBSCertificate = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
+ // reenforce that fact
+ 'version' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true,
+ 'default' => 'v1'
+ ) + $Version,
+ 'serialNumber' => $CertificateSerialNumber,
+ 'signature' => $AlgorithmIdentifier,
+ 'issuer' => $this->Name,
+ 'validity' => $Validity,
+ 'subject' => $this->Name,
+ 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
+ // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
+ 'issuerUniqueID' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $UniqueIdentifier,
+ 'subjectUniqueID' => array(
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $UniqueIdentifier,
+ // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
+ // it's not IMPLICIT, it's EXPLICIT
+ 'extensions' => array(
+ 'constant' => 3,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $this->Extensions
+ )
+ );
+
+ $this->Certificate = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'tbsCertificate' => $TBSCertificate,
+ 'signatureAlgorithm' => $AlgorithmIdentifier,
+ 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
+ )
+ );
+
+ $this->KeyUsage = array(
+ 'type' => FILE_ASN1_TYPE_BIT_STRING,
+ 'mapping' => array(
+ 'digitalSignature',
+ 'nonRepudiation',
+ 'keyEncipherment',
+ 'dataEncipherment',
+ 'keyAgreement',
+ 'keyCertSign',
+ 'cRLSign',
+ 'encipherOnly',
+ 'decipherOnly'
+ )
+ );
+
+ $this->BasicConstraints = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'cA' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'optional' => true,
+ 'default' => false
+ ),
+ 'pathLenConstraint' => array(
+ 'type' => FILE_ASN1_TYPE_INTEGER,
+ 'optional' => true
+ )
+ )
+ );
+
+ $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);
+
+ $OrganizationalUnitNames = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 4, // ub-organizational-units
+ 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
+ );
+
+ $PersonalName = array(
+ 'type' => FILE_ASN1_TYPE_SET,
+ 'children' => array(
+ 'surname' => array(
+ 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'given-name' => array(
+ 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'initials' => array(
+ 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'generation-qualifier' => array(
+ 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ )
+ )
+ );
+
+ $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
+
+ $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
+
+ $PrivateDomainName = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
+ 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
+ )
+ );
+
+ $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
+
+ $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
+
+ $AdministrationDomainName = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
+ // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
+ 'class' => FILE_ASN1_CLASS_APPLICATION,
+ 'cast' => 2,
+ 'children' => array(
+ 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
+ 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
+ )
+ );
+
+ $CountryName = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
+ // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
+ 'class' => FILE_ASN1_CLASS_APPLICATION,
+ 'cast' => 1,
+ 'children' => array(
+ 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
+ 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
+ )
+ );
+
+ $AnotherName = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
+ 'value' => array(
+ 'type' => FILE_ASN1_TYPE_ANY,
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ )
+ )
+ );
+
+ $ExtensionAttribute = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'extension-attribute-type' => array(
+ 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'extension-attribute-value' => array(
+ 'type' => FILE_ASN1_TYPE_ANY,
+ 'constant' => 1,
+ 'optional' => true,
+ 'explicit' => true
+ )
+ )
+ );
+
+ $ExtensionAttributes = array(
+ 'type' => FILE_ASN1_TYPE_SET,
+ 'min' => 1,
+ 'max' => 256, // ub-extension-attributes
+ 'children' => $ExtensionAttribute
+ );
+
+ $BuiltInDomainDefinedAttribute = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
+ 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
+ )
+ );
+
+ $BuiltInDomainDefinedAttributes = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 4, // ub-domain-defined-attributes
+ 'children' => $BuiltInDomainDefinedAttribute
+ );
+
+ $BuiltInStandardAttributes = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'country-name' => array('optional' => true) + $CountryName,
+ 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
+ 'network-address' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $NetworkAddress,
+ 'terminal-identifier' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $TerminalIdentifier,
+ 'private-domain-name' => array(
+ 'constant' => 2,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $PrivateDomainName,
+ 'organization-name' => array(
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $OrganizationName,
+ 'numeric-user-identifier' => array(
+ 'constant' => 4,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $NumericUserIdentifier,
+ 'personal-name' => array(
+ 'constant' => 5,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $PersonalName,
+ 'organizational-unit-names' => array(
+ 'constant' => 6,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $OrganizationalUnitNames
+ )
+ );
+
+ $ORAddress = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'built-in-standard-attributes' => $BuiltInStandardAttributes,
+ 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
+ 'extension-attributes' => array('optional' => true) + $ExtensionAttributes
+ )
+ );
+
+ $EDIPartyName = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'nameAssigner' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $this->DirectoryString,
+ // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
+ // setting it to optional gets the job done in any event.
+ 'partyName' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $this->DirectoryString
+ )
+ );
+
+ $GeneralName = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'otherName' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $AnotherName,
+ 'rfc822Name' => array(
+ 'type' => FILE_ASN1_TYPE_IA5_STRING,
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'dNSName' => array(
+ 'type' => FILE_ASN1_TYPE_IA5_STRING,
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'x400Address' => array(
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $ORAddress,
+ 'directoryName' => array(
+ 'constant' => 4,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $this->Name,
+ 'ediPartyName' => array(
+ 'constant' => 5,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $EDIPartyName,
+ 'uniformResourceIdentifier' => array(
+ 'type' => FILE_ASN1_TYPE_IA5_STRING,
+ 'constant' => 6,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'iPAddress' => array(
+ 'type' => FILE_ASN1_TYPE_OCTET_STRING,
+ 'constant' => 7,
+ 'optional' => true,
+ 'implicit' => true
+ ),
+ 'registeredID' => array(
+ 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
+ 'constant' => 8,
+ 'optional' => true,
+ 'implicit' => true
+ )
+ )
+ );
+
+ $GeneralNames = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $GeneralName
+ );
+
+ $this->IssuerAltName = $GeneralNames;
+
+ $ReasonFlags = array(
+ 'type' => FILE_ASN1_TYPE_BIT_STRING,
+ 'mapping' => array(
+ 'unused',
+ 'keyCompromise',
+ 'cACompromise',
+ 'affiliationChanged',
+ 'superseded',
+ 'cessationOfOperation',
+ 'certificateHold',
+ 'privilegeWithdrawn',
+ 'aACompromise'
+ )
+ );
+
+ $DistributionPointName = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'fullName' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $GeneralNames,
+ 'nameRelativeToCRLIssuer' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $this->RelativeDistinguishedName
+ )
+ );
+
+ $DistributionPoint = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'distributionPoint' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $DistributionPointName,
+ 'reasons' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $ReasonFlags,
+ 'cRLIssuer' => array(
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $GeneralNames
+ )
+ );
+
+ $this->CRLDistributionPoints = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $DistributionPoint
+ );
+
+ $this->AuthorityKeyIdentifier = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'keyIdentifier' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $this->KeyIdentifier,
+ 'authorityCertIssuer' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $GeneralNames,
+ 'authorityCertSerialNumber' => array(
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $CertificateSerialNumber
+ )
+ );
+
+ $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
+
+ $PolicyQualifierInfo = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'policyQualifierId' => $PolicyQualifierId,
+ 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY)
+ )
+ );
+
+ $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
+
+ $PolicyInformation = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'policyIdentifier' => $CertPolicyId,
+ 'policyQualifiers' => array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 0,
+ 'max' => -1,
+ 'optional' => true,
+ 'children' => $PolicyQualifierInfo
+ )
+ )
+ );
+
+ $this->CertificatePolicies = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $PolicyInformation
+ );
+
+ $this->PolicyMappings = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'issuerDomainPolicy' => $CertPolicyId,
+ 'subjectDomainPolicy' => $CertPolicyId
+ )
+ )
+ );
+
+ $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
+
+ $this->ExtKeyUsageSyntax = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $KeyPurposeId
+ );
+
+ $AccessDescription = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
+ 'accessLocation' => $GeneralName
+ )
+ );
+
+ $this->AuthorityInfoAccessSyntax = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $AccessDescription
+ );
+
+ $this->SubjectAltName = $GeneralNames;
+
+ $this->PrivateKeyUsagePeriod = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'notBefore' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true,
+ 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
+ 'notAfter' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true,
+ 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
+ )
+ );
+
+ $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);
+
+ $GeneralSubtree = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'base' => $GeneralName,
+ 'minimum' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true,
+ 'default' => new Math_BigInteger(0)
+ ) + $BaseDistance,
+ 'maximum' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true,
+ ) + $BaseDistance
+ )
+ );
+
+ $GeneralSubtrees = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $GeneralSubtree
+ );
+
+ $this->NameConstraints = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'permittedSubtrees' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $GeneralSubtrees,
+ 'excludedSubtrees' => array(
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $GeneralSubtrees
+ )
+ );
+
+ $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);
+
+ $DisplayText = array(
+ 'type' => FILE_ASN1_TYPE_CHOICE,
+ 'children' => array(
+ 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING),
+ 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
+ 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING),
+ 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
+ )
+ );
+
+ $NoticeReference = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'organization' => $DisplayText,
+ 'noticeNumbers' => array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 200,
+ 'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
+ )
+ )
+ );
+
+ $this->UserNotice = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'noticeRef' => array(
+ 'optional' => true,
+ 'implicit' => true
+ ) + $NoticeReference,
+ 'explicitText' => array(
+ 'optional' => true,
+ 'implicit' => true
+ ) + $DisplayText
+ )
+ );
+
+ // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
+ $this->netscape_cert_type = array(
+ 'type' => FILE_ASN1_TYPE_BIT_STRING,
+ 'mapping' => array(
+ 'SSLClient',
+ 'SSLServer',
+ 'Email',
+ 'ObjectSigning',
+ 'Reserved',
+ 'SSLCA',
+ 'EmailCA',
+ 'ObjectSigningCA'
+ )
+ );
+
+ $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
+ $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING);
+
+ // attribute is used in RFC2986 but we're using the RFC5280 definition
+
+ $Attribute = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'type' => $AttributeType,
+ 'value'=> array(
+ 'type' => FILE_ASN1_TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $this->AttributeValue
+ )
+ )
+ );
+
+ // adapted from <http://tools.ietf.org/html/rfc2986>
+
+ $Attributes = array(
+ 'type' => FILE_ASN1_TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $Attribute
+ );
+
+ $CertificationRequestInfo = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'version' => array(
+ 'type' => FILE_ASN1_TYPE_INTEGER,
+ 'mapping' => array('v1')
+ ),
+ 'subject' => $this->Name,
+ 'subjectPKInfo' => $SubjectPublicKeyInfo,
+ 'attributes' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $Attributes,
+ )
+ );
+
+ $this->CertificationRequest = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'certificationRequestInfo' => $CertificationRequestInfo,
+ 'signatureAlgorithm' => $AlgorithmIdentifier,
+ 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
+ )
+ );
+
+ $RevokedCertificate = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'userCertificate' => $CertificateSerialNumber,
+ 'revocationDate' => $Time,
+ 'crlEntryExtensions' => array(
+ 'optional' => true
+ ) + $this->Extensions
+ )
+ );
+
+ $TBSCertList = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'version' => array(
+ 'optional' => true,
+ 'default' => 'v1'
+ ) + $Version,
+ 'signature' => $AlgorithmIdentifier,
+ 'issuer' => $this->Name,
+ 'thisUpdate' => $Time,
+ 'nextUpdate' => array(
+ 'optional' => true
+ ) + $Time,
+ 'revokedCertificates' => array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'optional' => true,
+ 'min' => 0,
+ 'max' => -1,
+ 'children' => $RevokedCertificate
+ ),
+ 'crlExtensions' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $this->Extensions
+ )
+ );
+
+ $this->CertificateList = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'tbsCertList' => $TBSCertList,
+ 'signatureAlgorithm' => $AlgorithmIdentifier,
+ 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
+ )
+ );
+
+ $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
+
+ $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED,
+ 'mapping' => array(
+ 'unspecified',
+ 'keyCompromise',
+ 'cACompromise',
+ 'affiliationChanged',
+ 'superseded',
+ 'cessationOfOperation',
+ 'certificateHold',
+ // Value 7 is not used.
+ 8 => 'removeFromCRL',
+ 'privilegeWithdrawn',
+ 'aACompromise'
+ )
+ );
+
+ $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'distributionPoint' => array(
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ) + $DistributionPointName,
+ 'onlyContainsUserCerts' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'constant' => 1,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ),
+ 'onlyContainsCACerts' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'constant' => 2,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ),
+ 'onlySomeReasons' => array(
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ) + $ReasonFlags,
+ 'indirectCRL' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'constant' => 4,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ),
+ 'onlyContainsAttributeCerts' => array(
+ 'type' => FILE_ASN1_TYPE_BOOLEAN,
+ 'constant' => 5,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ )
+ )
+ );
+
+ $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME);
+
+ $this->CertificateIssuer = $GeneralNames;
+
+ $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
+
+ $PublicKeyAndChallenge = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'spki' => $SubjectPublicKeyInfo,
+ 'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING)
+ )
+ );
+
+ $this->SignedPublicKeyAndChallenge = array(
+ 'type' => FILE_ASN1_TYPE_SEQUENCE,
+ 'children' => array(
+ 'publicKeyAndChallenge' => $PublicKeyAndChallenge,
+ 'signatureAlgorithm' => $AlgorithmIdentifier,
+ 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
+ )
+ );
+
+ // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
+ $this->oids = array(
+ '1.3.6.1.5.5.7' => 'id-pkix',
+ '1.3.6.1.5.5.7.1' => 'id-pe',
+ '1.3.6.1.5.5.7.2' => 'id-qt',
+ '1.3.6.1.5.5.7.3' => 'id-kp',
+ '1.3.6.1.5.5.7.48' => 'id-ad',
+ '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
+ '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
+ '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
+ '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
+ '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
+ '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
+ '2.5.4' => 'id-at',
+ '2.5.4.41' => 'id-at-name',
+ '2.5.4.4' => 'id-at-surname',
+ '2.5.4.42' => 'id-at-givenName',
+ '2.5.4.43' => 'id-at-initials',
+ '2.5.4.44' => 'id-at-generationQualifier',
+ '2.5.4.3' => 'id-at-commonName',
+ '2.5.4.7' => 'id-at-localityName',
+ '2.5.4.8' => 'id-at-stateOrProvinceName',
+ '2.5.4.10' => 'id-at-organizationName',
+ '2.5.4.11' => 'id-at-organizationalUnitName',
+ '2.5.4.12' => 'id-at-title',
+ '2.5.4.13' => 'id-at-description',
+ '2.5.4.46' => 'id-at-dnQualifier',
+ '2.5.4.6' => 'id-at-countryName',
+ '2.5.4.5' => 'id-at-serialNumber',
+ '2.5.4.65' => 'id-at-pseudonym',
+ '2.5.4.17' => 'id-at-postalCode',
+ '2.5.4.9' => 'id-at-streetAddress',
+ '2.5.4.45' => 'id-at-uniqueIdentifier',
+ '2.5.4.72' => 'id-at-role',
+
+ '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
+ '1.2.840.113549.1.9' => 'pkcs-9',
+ '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
+ '2.5.29' => 'id-ce',
+ '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
+ '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
+ '2.5.29.15' => 'id-ce-keyUsage',
+ '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
+ '2.5.29.32' => 'id-ce-certificatePolicies',
+ '2.5.29.32.0' => 'anyPolicy',
+
+ '2.5.29.33' => 'id-ce-policyMappings',
+ '2.5.29.17' => 'id-ce-subjectAltName',
+ '2.5.29.18' => 'id-ce-issuerAltName',
+ '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
+ '2.5.29.19' => 'id-ce-basicConstraints',
+ '2.5.29.30' => 'id-ce-nameConstraints',
+ '2.5.29.36' => 'id-ce-policyConstraints',
+ '2.5.29.31' => 'id-ce-cRLDistributionPoints',
+ '2.5.29.37' => 'id-ce-extKeyUsage',
+ '2.5.29.37.0' => 'anyExtendedKeyUsage',
+ '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
+ '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
+ '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
+ '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
+ '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
+ '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
+ '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
+ '2.5.29.46' => 'id-ce-freshestCRL',
+ '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
+ '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
+ '2.5.29.20' => 'id-ce-cRLNumber',
+ '2.5.29.28' => 'id-ce-issuingDistributionPoint',
+ '2.5.29.27' => 'id-ce-deltaCRLIndicator',
+ '2.5.29.21' => 'id-ce-cRLReasons',
+ '2.5.29.29' => 'id-ce-certificateIssuer',
+ '2.5.29.23' => 'id-ce-holdInstructionCode',
+ '1.2.840.10040.2' => 'holdInstruction',
+ '1.2.840.10040.2.1' => 'id-holdinstruction-none',
+ '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
+ '1.2.840.10040.2.3' => 'id-holdinstruction-reject',
+ '2.5.29.24' => 'id-ce-invalidityDate',
+
+ '1.2.840.113549.2.2' => 'md2',
+ '1.2.840.113549.2.5' => 'md5',
+ '1.3.14.3.2.26' => 'id-sha1',
+ '1.2.840.10040.4.1' => 'id-dsa',
+ '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
+ '1.2.840.113549.1.1' => 'pkcs-1',
+ '1.2.840.113549.1.1.1' => 'rsaEncryption',
+ '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
+ '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
+ '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
+ '1.2.840.10046.2.1' => 'dhpublicnumber',
+ '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
+ '1.2.840.10045' => 'ansi-X9-62',
+ '1.2.840.10045.4' => 'id-ecSigType',
+ '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
+ '1.2.840.10045.1' => 'id-fieldType',
+ '1.2.840.10045.1.1' => 'prime-field',
+ '1.2.840.10045.1.2' => 'characteristic-two-field',
+ '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
+ '1.2.840.10045.1.2.3.1' => 'gnBasis',
+ '1.2.840.10045.1.2.3.2' => 'tpBasis',
+ '1.2.840.10045.1.2.3.3' => 'ppBasis',
+ '1.2.840.10045.2' => 'id-publicKeyType',
+ '1.2.840.10045.2.1' => 'id-ecPublicKey',
+ '1.2.840.10045.3' => 'ellipticCurve',
+ '1.2.840.10045.3.0' => 'c-TwoCurve',
+ '1.2.840.10045.3.0.1' => 'c2pnb163v1',
+ '1.2.840.10045.3.0.2' => 'c2pnb163v2',
+ '1.2.840.10045.3.0.3' => 'c2pnb163v3',
+ '1.2.840.10045.3.0.4' => 'c2pnb176w1',
+ '1.2.840.10045.3.0.5' => 'c2pnb191v1',
+ '1.2.840.10045.3.0.6' => 'c2pnb191v2',
+ '1.2.840.10045.3.0.7' => 'c2pnb191v3',
+ '1.2.840.10045.3.0.8' => 'c2pnb191v4',
+ '1.2.840.10045.3.0.9' => 'c2pnb191v5',
+ '1.2.840.10045.3.0.10' => 'c2pnb208w1',
+ '1.2.840.10045.3.0.11' => 'c2pnb239v1',
+ '1.2.840.10045.3.0.12' => 'c2pnb239v2',
+ '1.2.840.10045.3.0.13' => 'c2pnb239v3',
+ '1.2.840.10045.3.0.14' => 'c2pnb239v4',
+ '1.2.840.10045.3.0.15' => 'c2pnb239v5',
+ '1.2.840.10045.3.0.16' => 'c2pnb272w1',
+ '1.2.840.10045.3.0.17' => 'c2pnb304w1',
+ '1.2.840.10045.3.0.18' => 'c2pnb359v1',
+ '1.2.840.10045.3.0.19' => 'c2pnb368w1',
+ '1.2.840.10045.3.0.20' => 'c2pnb431r1',
+ '1.2.840.10045.3.1' => 'primeCurve',
+ '1.2.840.10045.3.1.1' => 'prime192v1',
+ '1.2.840.10045.3.1.2' => 'prime192v2',
+ '1.2.840.10045.3.1.3' => 'prime192v3',
+ '1.2.840.10045.3.1.4' => 'prime239v1',
+ '1.2.840.10045.3.1.5' => 'prime239v2',
+ '1.2.840.10045.3.1.6' => 'prime239v3',
+ '1.2.840.10045.3.1.7' => 'prime256v1',
+ '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
+ '1.2.840.113549.1.1.9' => 'id-pSpecified',
+ '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
+ '1.2.840.113549.1.1.8' => 'id-mgf1',
+ '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
+ '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
+ '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
+ '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
+ '2.16.840.1.101.3.4.2.4' => 'id-sha224',
+ '2.16.840.1.101.3.4.2.1' => 'id-sha256',
+ '2.16.840.1.101.3.4.2.2' => 'id-sha384',
+ '2.16.840.1.101.3.4.2.3' => 'id-sha512',
+ '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
+ '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
+ '1.2.643.2.2.20' => 'id-GostR3410-2001',
+ '1.2.643.2.2.19' => 'id-GostR3410-94',
+ // Netscape Object Identifiers from "Netscape Certificate Extensions"
+ '2.16.840.1.113730' => 'netscape',
+ '2.16.840.1.113730.1' => 'netscape-cert-extension',
+ '2.16.840.1.113730.1.1' => 'netscape-cert-type',
+ '2.16.840.1.113730.1.13' => 'netscape-comment',
+ '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
+ // the following are X.509 extensions not supported by phpseclib
+ '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
+ '1.2.840.113533.7.65.0' => 'entrustVersInfo',
+ '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
+ // for Certificate Signing Requests
+ // see http://tools.ietf.org/html/rfc2985
+ '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name
+ '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', // Challenge password for certificate revocations
+ '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request
+ );
+ }
+
+ /**
+ * Load X.509 certificate
+ *
+ * Returns an associative array describing the X.509 cert or a false if the cert failed to load
+ *
+ * @param String $cert
+ * @access public
+ * @return Mixed
+ */
+ function loadX509($cert)
+ {
+ if (is_array($cert) && isset($cert['tbsCertificate'])) {
+ unset($this->currentCert);
+ unset($this->currentKeyIdentifier);
+ $this->dn = $cert['tbsCertificate']['subject'];
+ if (!isset($this->dn)) {
+ return false;
+ }
+ $this->currentCert = $cert;
+
+ $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
+ $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL;
+
+ unset($this->signatureSubject);
+
+ return $cert;
+ }
+
+ $asn1 = new File_ASN1();
+
+ /*
+ X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie.
+ some may have the following preceeding the -----BEGIN CERTIFICATE----- line:
+
+ subject=/O=organization/OU=org unit/CN=common name
+ issuer=/O=organization/CN=common name
+ */
+ $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $cert);
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
+ if ($temp != false) {
+ $cert = $temp;
+ }
+
+ if ($cert === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $asn1->loadOIDs($this->oids);
+ $decoded = $asn1->decodeBER($cert);
+
+ if (!empty($decoded)) {
+ $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
+ }
+ if (!isset($x509) || $x509 === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
+
+ $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
+
+ $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
+ $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
+
+ $this->currentCert = $x509;
+ $this->dn = $x509['tbsCertificate']['subject'];
+
+ $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
+ $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL;
+
+ return $x509;
+ }
+
+ /**
+ * Save X.509 certificate
+ *
+ * @param Array $cert
+ * @param Integer $format optional
+ * @access public
+ * @return String
+ */
+ function saveX509($cert, $format = FILE_X509_FORMAT_PEM)
+ {
+ if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
+ return false;
+ }
+
+ switch (true) {
+ // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()"
+ case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
+ case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
+ break;
+ default:
+ switch ($algorithm) {
+ case 'rsaEncryption':
+ $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
+ base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
+ }
+ }
+
+ $asn1 = new File_ASN1();
+
+ $asn1->loadOIDs($this->oids);
+
+ $filters = array();
+ $filters['tbsCertificate']['signature']['parameters'] =
+ $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] =
+ $filters['tbsCertificate']['issuer']['rdnSequence']['value'] =
+ $filters['tbsCertificate']['subject']['rdnSequence']['value'] =
+ $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] =
+ $filters['signatureAlgorithm']['parameters'] =
+ $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] =
+ //$filters['policyQualifiers']['qualifier'] =
+ $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] =
+ $filters['directoryName']['rdnSequence']['value'] =
+ array('type' => FILE_ASN1_TYPE_UTF8_STRING);
+ /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
+ FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
+ characters.
+ */
+ $filters['policyQualifiers']['qualifier'] =
+ array('type' => FILE_ASN1_TYPE_IA5_STRING);
+
+ $asn1->loadFilters($filters);
+
+ $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1);
+
+ $cert = $asn1->encodeDER($cert, $this->Certificate);
+
+ switch ($format) {
+ case FILE_X509_FORMAT_DER:
+ return $cert;
+ // case FILE_X509_FORMAT_PEM:
+ default:
+ return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
+ }
+ }
+
+ /**
+ * Map extension values from octet string to extension-specific internal
+ * format.
+ *
+ * @param Array ref $root
+ * @param String $path
+ * @param Object $asn1
+ * @access private
+ */
+ function _mapInExtensions(&$root, $path, $asn1)
+ {
+ $extensions = &$this->_subArray($root, $path);
+
+ if (is_array($extensions)) {
+ for ($i = 0; $i < count($extensions); $i++) {
+ $id = $extensions[$i]['extnId'];
+ $value = &$extensions[$i]['extnValue'];
+ $value = base64_decode($value);
+ $decoded = $asn1->decodeBER($value);
+ /* [extnValue] contains the DER encoding of an ASN.1 value
+ corresponding to the extension type identified by extnID */
+ $map = $this->_getMapping($id);
+ if (!is_bool($map)) {
+ $mapped = $asn1->asn1map($decoded[0], $map);
+ $value = $mapped === false ? $decoded[0] : $mapped;
+
+ if ($id == 'id-ce-certificatePolicies') {
+ for ($j = 0; $j < count($value); $j++) {
+ if (!isset($value[$j]['policyQualifiers'])) {
+ continue;
+ }
+ for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
+ $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
+ $map = $this->_getMapping($subid);
+ $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
+ if ($map !== false) {
+ $decoded = $asn1->decodeBER($subvalue);
+ $mapped = $asn1->asn1map($decoded[0], $map);
+ $subvalue = $mapped === false ? $decoded[0] : $mapped;
+ }
+ }
+ }
+ }
+ } elseif ($map) {
+ $value = base64_encode($value);
+ }
+ }
+ }
+ }
+
+ /**
+ * Map extension values from extension-specific internal format to
+ * octet string.
+ *
+ * @param Array ref $root
+ * @param String $path
+ * @param Object $asn1
+ * @access private
+ */
+ function _mapOutExtensions(&$root, $path, $asn1)
+ {
+ $extensions = &$this->_subArray($root, $path);
+
+ if (is_array($extensions)) {
+ $size = count($extensions);
+ for ($i = 0; $i < $size; $i++) {
+ $id = $extensions[$i]['extnId'];
+ $value = &$extensions[$i]['extnValue'];
+
+ switch ($id) {
+ case 'id-ce-certificatePolicies':
+ for ($j = 0; $j < count($value); $j++) {
+ if (!isset($value[$j]['policyQualifiers'])) {
+ continue;
+ }
+ for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
+ $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
+ $map = $this->_getMapping($subid);
+ $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
+ if ($map !== false) {
+ // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
+ // actual type is FILE_ASN1_TYPE_ANY
+ $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue, $map));
+ }
+ }
+ }
+ break;
+ case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string
+ if (isset($value['authorityCertSerialNumber'])) {
+ if ($value['authorityCertSerialNumber']->toBytes() == '') {
+ $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
+ $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
+ }
+ }
+ }
+
+ /* [extnValue] contains the DER encoding of an ASN.1 value
+ corresponding to the extension type identified by extnID */
+ $map = $this->_getMapping($id);
+ if (is_bool($map)) {
+ if (!$map) {
+ user_error($id . ' is not a currently supported extension');
+ unset($extensions[$i]);
+ }
+ } else {
+ $temp = $asn1->encodeDER($value, $map);
+ $value = base64_encode($temp);
+ }
+ }
+ }
+ }
+
+ /**
+ * Map attribute values from ANY type to attribute-specific internal
+ * format.
+ *
+ * @param Array ref $root
+ * @param String $path
+ * @param Object $asn1
+ * @access private
+ */
+ function _mapInAttributes(&$root, $path, $asn1)
+ {
+ $attributes = &$this->_subArray($root, $path);
+
+ if (is_array($attributes)) {
+ for ($i = 0; $i < count($attributes); $i++) {
+ $id = $attributes[$i]['type'];
+ /* $value contains the DER encoding of an ASN.1 value
+ corresponding to the attribute type identified by type */
+ $map = $this->_getMapping($id);
+ if (is_array($attributes[$i]['value'])) {
+ $values = &$attributes[$i]['value'];
+ for ($j = 0; $j < count($values); $j++) {
+ $value = $asn1->encodeDER($values[$j], $this->AttributeValue);
+ $decoded = $asn1->decodeBER($value);
+ if (!is_bool($map)) {
+ $mapped = $asn1->asn1map($decoded[0], $map);
+ if ($mapped !== false) {
+ $values[$j] = $mapped;
+ }
+ if ($id == 'pkcs-9-at-extensionRequest') {
+ $this->_mapInExtensions($values, $j, $asn1);
+ }
+ } elseif ($map) {
+ $values[$j] = base64_encode($value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Map attribute values from attribute-specific internal format to
+ * ANY type.
+ *
+ * @param Array ref $root
+ * @param String $path
+ * @param Object $asn1
+ * @access private
+ */
+ function _mapOutAttributes(&$root, $path, $asn1)
+ {
+ $attributes = &$this->_subArray($root, $path);
+
+ if (is_array($attributes)) {
+ $size = count($attributes);
+ for ($i = 0; $i < $size; $i++) {
+ /* [value] contains the DER encoding of an ASN.1 value
+ corresponding to the attribute type identified by type */
+ $id = $attributes[$i]['type'];
+ $map = $this->_getMapping($id);
+ if ($map === false) {
+ user_error($id . ' is not a currently supported attribute', E_USER_NOTICE);
+ unset($attributes[$i]);
+ }
+ elseif (is_array($attributes[$i]['value'])) {
+ $values = &$attributes[$i]['value'];
+ for ($j = 0; $j < count($values); $j++) {
+ switch ($id) {
+ case 'pkcs-9-at-extensionRequest':
+ $this->_mapOutExtensions($values, $j, $asn1);
+ break;
+ }
+
+ if (!is_bool($map)) {
+ $temp = $asn1->encodeDER($values[$j], $map);
+ $decoded = $asn1->decodeBER($temp);
+ $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Associate an extension ID to an extension mapping
+ *
+ * @param String $extnId
+ * @access private
+ * @return Mixed
+ */
+ function _getMapping($extnId)
+ {
+ if (!is_string($extnId)) { // eg. if it's a File_ASN1_Element object
+ return true;
+ }
+
+ switch ($extnId) {
+ case 'id-ce-keyUsage':
+ return $this->KeyUsage;
+ case 'id-ce-basicConstraints':
+ return $this->BasicConstraints;
+ case 'id-ce-subjectKeyIdentifier':
+ return $this->KeyIdentifier;
+ case 'id-ce-cRLDistributionPoints':
+ return $this->CRLDistributionPoints;
+ case 'id-ce-authorityKeyIdentifier':
+ return $this->AuthorityKeyIdentifier;
+ case 'id-ce-certificatePolicies':
+ return $this->CertificatePolicies;
+ case 'id-ce-extKeyUsage':
+ return $this->ExtKeyUsageSyntax;
+ case 'id-pe-authorityInfoAccess':
+ return $this->AuthorityInfoAccessSyntax;
+ case 'id-ce-subjectAltName':
+ return $this->SubjectAltName;
+ case 'id-ce-privateKeyUsagePeriod':
+ return $this->PrivateKeyUsagePeriod;
+ case 'id-ce-issuerAltName':
+ return $this->IssuerAltName;
+ case 'id-ce-policyMappings':
+ return $this->PolicyMappings;
+ case 'id-ce-nameConstraints':
+ return $this->NameConstraints;
+
+ case 'netscape-cert-type':
+ return $this->netscape_cert_type;
+ case 'netscape-comment':
+ return $this->netscape_comment;
+ case 'netscape-ca-policy-url':
+ return $this->netscape_ca_policy_url;
+
+ // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
+ // back around to asn1map() and we don't want it decoded again.
+ //case 'id-qt-cps':
+ // return $this->CPSuri;
+ case 'id-qt-unotice':
+ return $this->UserNotice;
+
+ // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
+ case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt
+ case 'entrustVersInfo':
+ // http://support.microsoft.com/kb/287547
+ case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION
+ case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION
+ // "SET Secure Electronic Transaction Specification"
+ // http://www.maithean.com/docs/set_bk3.pdf
+ case '2.23.42.7.0': // id-set-hashedRootKey
+ return true;
+
+ // CSR attributes
+ case 'pkcs-9-at-unstructuredName':
+ return $this->PKCS9String;
+ case 'pkcs-9-at-challengePassword':
+ return $this->DirectoryString;
+ case 'pkcs-9-at-extensionRequest':
+ return $this->Extensions;
+
+ // CRL extensions.
+ case 'id-ce-cRLNumber':
+ return $this->CRLNumber;
+ case 'id-ce-deltaCRLIndicator':
+ return $this->CRLNumber;
+ case 'id-ce-issuingDistributionPoint':
+ return $this->IssuingDistributionPoint;
+ case 'id-ce-freshestCRL':
+ return $this->CRLDistributionPoints;
+ case 'id-ce-cRLReasons':
+ return $this->CRLReason;
+ case 'id-ce-invalidityDate':
+ return $this->InvalidityDate;
+ case 'id-ce-certificateIssuer':
+ return $this->CertificateIssuer;
+ case 'id-ce-holdInstructionCode':
+ return $this->HoldInstructionCode;
+ }
+
+ return false;
+ }
+
+ /**
+ * Load an X.509 certificate as a certificate authority
+ *
+ * @param String $cert
+ * @access public
+ * @return Boolean
+ */
+ function loadCA($cert)
+ {
+ $olddn = $this->dn;
+ $oldcert = $this->currentCert;
+ $oldsigsubj = $this->signatureSubject;
+ $oldkeyid = $this->currentKeyIdentifier;
+
+ $cert = $this->loadX509($cert);
+ if (!$cert) {
+ $this->dn = $olddn;
+ $this->currentCert = $oldcert;
+ $this->signatureSubject = $oldsigsubj;
+ $this->currentKeyIdentifier = $oldkeyid;
+
+ return false;
+ }
+
+ /* From RFC5280 "PKIX Certificate and CRL Profile":
+
+ If the keyUsage extension is present, then the subject public key
+ MUST NOT be used to verify signatures on certificates or CRLs unless
+ the corresponding keyCertSign or cRLSign bit is set. */
+ //$keyUsage = $this->getExtension('id-ce-keyUsage');
+ //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
+ // return false;
+ //}
+
+ /* From RFC5280 "PKIX Certificate and CRL Profile":
+
+ The cA boolean indicates whether the certified public key may be used
+ to verify certificate signatures. If the cA boolean is not asserted,
+ then the keyCertSign bit in the key usage extension MUST NOT be
+ asserted. If the basic constraints extension is not present in a
+ version 3 certificate, or the extension is present but the cA boolean
+ is not asserted, then the certified public key MUST NOT be used to
+ verify certificate signatures. */
+ //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
+ //if (!$basicConstraints || !$basicConstraints['cA']) {
+ // return false;
+ //}
+
+ $this->CAs[] = $cert;
+
+ $this->dn = $olddn;
+ $this->currentCert = $oldcert;
+ $this->signatureSubject = $oldsigsubj;
+
+ return true;
+ }
+
+ /**
+ * Validate an X.509 certificate against a URL
+ *
+ * From RFC2818 "HTTP over TLS":
+ *
+ * Matching is performed using the matching rules specified by
+ * [RFC2459]. If more than one identity of a given type is present in
+ * the certificate (e.g., more than one dNSName name, a match in any one
+ * of the set is considered acceptable.) Names may contain the wildcard
+ * character * which is considered to match any single domain name
+ * component or component fragment. E.g., *.a.com matches foo.a.com but
+ * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+ *
+ * @param String $url
+ * @access public
+ * @return Boolean
+ */
+ function validateURL($url)
+ {
+ if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
+ return false;
+ }
+
+ $components = parse_url($url);
+ if (!isset($components['host'])) {
+ return false;
+ }
+
+ if ($names = $this->getExtension('id-ce-subjectAltName')) {
+ foreach ($names as $key => $value) {
+ $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
+ switch ($key) {
+ case 'dNSName':
+ /* From RFC2818 "HTTP over TLS":
+
+ If a subjectAltName extension of type dNSName is present, that MUST
+ be used as the identity. Otherwise, the (most specific) Common Name
+ field in the Subject field of the certificate MUST be used. Although
+ the use of the Common Name is existing practice, it is deprecated and
+ Certification Authorities are encouraged to use the dNSName instead. */
+ if (preg_match('#^' . $value . '$#', $components['host'])) {
+ return true;
+ }
+ break;
+ case 'iPAddress':
+ /* From RFC2818 "HTTP over TLS":
+
+ In some cases, the URI is specified as an IP address rather than a
+ hostname. In this case, the iPAddress subjectAltName must be present
+ in the certificate and must exactly match the IP in the URI. */
+ if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ if ($value = $this->getDNProp('id-at-commonName')) {
+ $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
+ return preg_match('#^' . $value . '$#', $components['host']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate a date
+ *
+ * If $date isn't defined it is assumed to be the current date.
+ *
+ * @param Integer $date optional
+ * @access public
+ */
+ function validateDate($date = NULL)
+ {
+ if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
+ return false;
+ }
+
+ if (!isset($date)) {
+ $date = time();
+ }
+
+ $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
+ $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];
+
+ $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
+ $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
+
+ switch (true) {
+ case $date < @strtotime($notBefore):
+ case $date > @strtotime($notAfter):
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate a signature
+ *
+ * Works on X.509 certs, CSR's and CRL's.
+ * Returns true if the signature is verified, false if it is not correct or NULL on error
+ *
+ * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
+ *
+ * @param Integer $options optional
+ * @access public
+ * @return Mixed
+ */
+ function validateSignature($options = 0)
+ {
+ if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
+ return 0;
+ }
+
+ /* TODO:
+ "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
+ -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
+
+ implement pathLenConstraint in the id-ce-basicConstraints extension */
+
+ switch (true) {
+ case isset($this->currentCert['tbsCertificate']):
+ // self-signed cert
+ if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) {
+ $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
+ $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
+ switch (true) {
+ case !is_array($authorityKey):
+ case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
+ $signingCert = $this->currentCert; // working cert
+ }
+ }
+
+ if (!empty($this->CAs)) {
+ for ($i = 0; $i < count($this->CAs); $i++) {
+ // even if the cert is a self-signed one we still want to see if it's a CA;
+ // if not, we'll conditionally return an error
+ $ca = $this->CAs[$i];
+ if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
+ $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
+ $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
+ switch (true) {
+ case !is_array($authorityKey):
+ case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
+ $signingCert = $ca; // working cert
+ break 2;
+ }
+ }
+ }
+ if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
+ return false;
+ }
+ } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
+ return false;
+ }
+ return $this->_validateSignature(
+ $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
+ $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
+ $this->currentCert['signatureAlgorithm']['algorithm'],
+ substr(base64_decode($this->currentCert['signature']), 1),
+ $this->signatureSubject
+ );
+ case isset($this->currentCert['certificationRequestInfo']):
+ return $this->_validateSignature(
+ $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
+ $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
+ $this->currentCert['signatureAlgorithm']['algorithm'],
+ substr(base64_decode($this->currentCert['signature']), 1),
+ $this->signatureSubject
+ );
+ case isset($this->currentCert['publicKeyAndChallenge']):
+ return $this->_validateSignature(
+ $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
+ $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
+ $this->currentCert['signatureAlgorithm']['algorithm'],
+ substr(base64_decode($this->currentCert['signature']), 1),
+ $this->signatureSubject
+ );
+ case isset($this->currentCert['tbsCertList']):
+ if (!empty($this->CAs)) {
+ for ($i = 0; $i < count($this->CAs); $i++) {
+ $ca = $this->CAs[$i];
+ if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) {
+ $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
+ $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
+ switch (true) {
+ case !is_array($authorityKey):
+ case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
+ $signingCert = $ca; // working cert
+ break 2;
+ }
+ }
+ }
+ }
+ if (!isset($signingCert)) {
+ return false;
+ }
+ return $this->_validateSignature(
+ $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
+ $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
+ $this->currentCert['signatureAlgorithm']['algorithm'],
+ substr(base64_decode($this->currentCert['signature']), 1),
+ $this->signatureSubject
+ );
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Validates a signature
+ *
+ * Returns true if the signature is verified, false if it is not correct or NULL on error
+ *
+ * @param String $publicKeyAlgorithm
+ * @param String $publicKey
+ * @param String $signatureAlgorithm
+ * @param String $signature
+ * @param String $signatureSubject
+ * @access private
+ * @return Integer
+ */
+ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
+ {
+ switch ($publicKeyAlgorithm) {
+ case 'rsaEncryption':
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+ $rsa = new Crypt_RSA();
+ $rsa->loadKey($publicKey);
+
+ switch ($signatureAlgorithm) {
+ case 'md2WithRSAEncryption':
+ case 'md5WithRSAEncryption':
+ case 'sha1WithRSAEncryption':
+ case 'sha224WithRSAEncryption':
+ case 'sha256WithRSAEncryption':
+ case 'sha384WithRSAEncryption':
+ case 'sha512WithRSAEncryption':
+ $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
+ $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
+ if (!@$rsa->verify($signatureSubject, $signature)) {
+ return false;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ return true;
+ }
+
+ /**
+ * Reformat public keys
+ *
+ * Reformats a public key to a format supported by phpseclib (if applicable)
+ *
+ * @param String $algorithm
+ * @param String $key
+ * @access private
+ * @return String
+ */
+ function _reformatKey($algorithm, $key)
+ {
+ switch ($algorithm) {
+ case 'rsaEncryption':
+ return
+ "-----BEGIN PUBLIC KEY-----\r\n" .
+ // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits
+ // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox
+ // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
+ chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
+ '-----END PUBLIC KEY-----';
+ default:
+ return $key;
+ }
+ }
+
+ /**
+ * "Normalizes" a Distinguished Name property
+ *
+ * @param String $propName
+ * @access private
+ * @return Mixed
+ */
+ function _translateDNProp($propName)
+ {
+ switch (strtolower($propName)) {
+ case 'id-at-countryname':
+ case 'countryname':
+ case 'c':
+ return 'id-at-countryName';
+ case 'id-at-organizationname':
+ case 'organizationname':
+ case 'o':
+ return 'id-at-organizationName';
+ case 'id-at-dnqualifier':
+ case 'dnqualifier':
+ return 'id-at-dnQualifier';
+ case 'id-at-commonname':
+ case 'commonname':
+ case 'cn':
+ return 'id-at-commonName';
+ case 'id-at-stateorprovinceName':
+ case 'stateorprovincename':
+ case 'state':
+ case 'province':
+ case 'provincename':
+ case 'st':
+ return 'id-at-stateOrProvinceName';
+ case 'id-at-localityname':
+ case 'localityname':
+ case 'l':
+ return 'id-at-localityName';
+ case 'id-emailaddress':
+ case 'emailaddress':
+ return 'pkcs-9-at-emailAddress';
+ case 'id-at-serialnumber':
+ case 'serialnumber':
+ return 'id-at-serialNumber';
+ case 'id-at-postalcode':
+ case 'postalcode':
+ return 'id-at-postalCode';
+ case 'id-at-streetaddress':
+ case 'streetaddress':
+ return 'id-at-streetAddress';
+ case 'id-at-name':
+ case 'name':
+ return 'id-at-name';
+ case 'id-at-givenname':
+ case 'givenname':
+ return 'id-at-givenName';
+ case 'id-at-surname':
+ case 'surname':
+ case 'sn':
+ return 'id-at-surname';
+ case 'id-at-initials':
+ case 'initials':
+ return 'id-at-initials';
+ case 'id-at-generationqualifier':
+ case 'generationqualifier':
+ return 'id-at-generationQualifier';
+ case 'id-at-organizationalunitname':
+ case 'organizationalunitname':
+ case 'ou':
+ return 'id-at-organizationalUnitName';
+ case 'id-at-pseudonym':
+ case 'pseudonym':
+ return 'id-at-pseudonym';
+ case 'id-at-title':
+ case 'title':
+ return 'id-at-title';
+ case 'id-at-description':
+ case 'description':
+ return 'id-at-description';
+ case 'id-at-role':
+ case 'role':
+ return 'id-at-role';
+ case 'id-at-uniqueidentifier':
+ case 'uniqueidentifier':
+ case 'x500uniqueidentifier':
+ return 'id-at-uniqueIdentifier';
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Set a Distinguished Name property
+ *
+ * @param String $propName
+ * @param Mixed $propValue
+ * @param String $type optional
+ * @access public
+ * @return Boolean
+ */
+ function setDNProp($propName, $propValue, $type = 'utf8String')
+ {
+ if (empty($this->dn)) {
+ $this->dn = array('rdnSequence' => array());
+ }
+
+ if (($propName = $this->_translateDNProp($propName)) === false) {
+ return false;
+ }
+
+ foreach ((array) $propValue as $v) {
+ if (!is_array($v) && isset($type)) {
+ $v = array($type => $v);
+ }
+ $this->dn['rdnSequence'][] = array(
+ array(
+ 'type' => $propName,
+ 'value'=> $v
+ )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove Distinguished Name properties
+ *
+ * @param String $propName
+ * @access public
+ */
+ function removeDNProp($propName)
+ {
+ if (empty($this->dn)) {
+ return;
+ }
+
+ if (($propName = $this->_translateDNProp($propName)) === false) {
+ return;
+ }
+
+ $dn = &$this->dn['rdnSequence'];
+ $size = count($dn);
+ for ($i = 0; $i < $size; $i++) {
+ if ($dn[$i][0]['type'] == $propName) {
+ unset($dn[$i]);
+ }
+ }
+
+ $dn = array_values($dn);
+ }
+
+ /**
+ * Get Distinguished Name properties
+ *
+ * @param String $propName
+ * @param Array $dn optional
+ * @param Boolean $withType optional
+ * @return Mixed
+ * @access public
+ */
+ function getDNProp($propName, $dn = NULL, $withType = false)
+ {
+ if (!isset($dn)) {
+ $dn = $this->dn;
+ }
+
+ if (empty($dn)) {
+ return false;
+ }
+
+ if (($propName = $this->_translateDNProp($propName)) === false) {
+ return false;
+ }
+
+ $dn = $dn['rdnSequence'];
+ $result = array();
+ $asn1 = new File_ASN1();
+ for ($i = 0; $i < count($dn); $i++) {
+ if ($dn[$i][0]['type'] == $propName) {
+ $v = $dn[$i][0]['value'];
+ if (!$withType && is_array($v)) {
+ foreach ($v as $type => $s) {
+ $type = array_search($type, $asn1->ANYmap, true);
+ if ($type !== false && isset($asn1->stringTypeSize[$type])) {
+ $s = $asn1->convert($s, $type);
+ if ($s !== false) {
+ $v = $s;
+ break;
+ }
+ }
+ }
+ if (is_array($v)) {
+ $v = array_pop($v); // Always strip data type.
+ }
+ }
+ $result[] = $v;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Set a Distinguished Name
+ *
+ * @param Mixed $dn
+ * @param Boolean $merge optional
+ * @param String $type optional
+ * @access public
+ * @return Boolean
+ */
+ function setDN($dn, $merge = false, $type = 'utf8String')
+ {
+ if (!$merge) {
+ $this->dn = NULL;
+ }
+
+ if (is_array($dn)) {
+ if (isset($dn['rdnSequence'])) {
+ $this->dn = $dn; // No merge here.
+ return true;
+ }
+
+ // handles stuff generated by openssl_x509_parse()
+ foreach ($dn as $prop => $value) {
+ if (!$this->setDNProp($prop, $value, $type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // handles everything else
+ $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
+ for ($i = 1; $i < count($results); $i+=2) {
+ $prop = trim($results[$i], ', =/');
+ $value = $results[$i + 1];
+ if (!$this->setDNProp($prop, $value, $type)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the Distinguished Name for a certificates subject
+ *
+ * @param Mixed $format optional
+ * @param Array $dn optional
+ * @access public
+ * @return Boolean
+ */
+ function getDN($format = FILE_X509_DN_ARRAY, $dn = NULL)
+ {
+ if (!isset($dn)) {
+ $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
+ }
+
+ switch ((int) $format) {
+ case FILE_X509_DN_ARRAY:
+ return $dn;
+ case FILE_X509_DN_ASN1:
+ $asn1 = new File_ASN1();
+ $asn1->loadOIDs($this->oids);
+ $filters = array();
+ $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
+ $asn1->loadFilters($filters);
+ return $asn1->encodeDER($dn, $this->Name);
+ case FILE_X509_DN_OPENSSL:
+ $dn = $this->getDN(FILE_X509_DN_STRING, $dn);
+ if ($dn === false) {
+ return false;
+ }
+ $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $dn = array();
+ for ($i = 1; $i < count($attrs); $i += 2) {
+ $prop = trim($attrs[$i], ', =/');
+ $value = $attrs[$i + 1];
+ if (!isset($dn[$prop])) {
+ $dn[$prop] = $value;
+ } else {
+ $dn[$prop] = array_merge((array) $dn[$prop], array($value));
+ }
+ }
+ return $dn;
+ case FILE_X509_DN_CANON:
+ // No SEQUENCE around RDNs and all string values normalized as
+ // trimmed lowercase UTF-8 with all spacing as one blank.
+ $asn1 = new File_ASN1();
+ $asn1->loadOIDs($this->oids);
+ $filters = array();
+ $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
+ $asn1->loadFilters($filters);
+ $result = '';
+ foreach ($dn['rdnSequence'] as $rdn) {
+ foreach ($rdn as &$attr) {
+ if (is_array($attr['value'])) {
+ foreach ($attr['value'] as $type => $v) {
+ $type = array_search($type, $asn1->ANYmap, true);
+ if ($type !== false && isset($asn1->stringTypeSize[$type])) {
+ $v = $asn1->convert($v, $type);
+ if ($v !== false) {
+ $v = preg_replace('/\s+/', ' ', $v);
+ $attr['value'] = strtolower(trim($v));
+ break;
+ }
+ }
+ }
+ }
+ }
+ $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
+ }
+ return $result;
+ case FILE_X509_DN_HASH:
+ $dn = $this->getDN(FILE_X509_DN_CANON, $dn);
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+ $hash = new Crypt_Hash('sha1');
+ $hash = $hash->hash($dn);
+ extract(unpack('Vhash', $hash));
+ return strtolower(bin2hex(pack('N', $hash)));
+ }
+
+ // Defaut is to return a string.
+ $start = true;
+ $output = '';
+ $asn1 = new File_ASN1();
+ foreach ($dn['rdnSequence'] as $field) {
+ $prop = $field[0]['type'];
+ $value = $field[0]['value'];
+
+ $delim = ', ';
+ switch ($prop) {
+ case 'id-at-countryName':
+ $desc = 'C=';
+ break;
+ case 'id-at-stateOrProvinceName':
+ $desc = 'ST=';
+ break;
+ case 'id-at-organizationName':
+ $desc = 'O=';
+ break;
+ case 'id-at-organizationalUnitName':
+ $desc = 'OU=';
+ break;
+ case 'id-at-commonName':
+ $desc = 'CN=';
+ break;
+ case 'id-at-localityName':
+ $desc = 'L=';
+ break;
+ case 'id-at-surname':
+ $desc = 'SN=';
+ break;
+ case 'id-at-uniqueIdentifier':
+ $delim = '/';
+ $desc = 'x500UniqueIdentifier=';
+ break;
+ default:
+ $delim = '/';
+ $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '=';
+ }
+
+ if (!$start) {
+ $output.= $delim;
+ }
+ if (is_array($value)) {
+ foreach ($value as $type => $v) {
+ $type = array_search($type, $asn1->ANYmap, true);
+ if ($type !== false && isset($asn1->stringTypeSize[$type])) {
+ $v = $asn1->convert($v, $type);
+ if ($v !== false) {
+ $value = $v;
+ break;
+ }
+ }
+ }
+ if (is_array($value)) {
+ $value = array_pop($value); // Always strip data type.
+ }
+ }
+ $output.= $desc . $value;
+ $start = false;
+ }
+
+ return $output;
+ }
+
+ /**
+ * Get the Distinguished Name for a certificate/crl issuer
+ *
+ * @param Integer $format optional
+ * @access public
+ * @return Mixed
+ */
+ function getIssuerDN($format = FILE_X509_DN_ARRAY)
+ {
+ switch (true) {
+ case !isset($this->currentCert) || !is_array($this->currentCert):
+ break;
+ case isset($this->currentCert['tbsCertificate']):
+ return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']);
+ case isset($this->currentCert['tbsCertList']):
+ return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the Distinguished Name for a certificate/csr subject
+ * Alias of getDN()
+ *
+ * @param Integer $format optional
+ * @access public
+ * @return Mixed
+ */
+ function getSubjectDN($format = FILE_X509_DN_ARRAY)
+ {
+ switch (true) {
+ case !empty($this->dn):
+ return $this->getDN($format);
+ case !isset($this->currentCert) || !is_array($this->currentCert):
+ break;
+ case isset($this->currentCert['tbsCertificate']):
+ return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']);
+ case isset($this->currentCert['certificationRequestInfo']):
+ return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an individual Distinguished Name property for a certificate/crl issuer
+ *
+ * @param String $propName
+ * @param Boolean $withType optional
+ * @access public
+ * @return Mixed
+ */
+ function getIssuerDNProp($propName, $withType = false)
+ {
+ switch (true) {
+ case !isset($this->currentCert) || !is_array($this->currentCert):
+ break;
+ case isset($this->currentCert['tbsCertificate']):
+ return $this->getDNProp($propname, $this->currentCert['tbsCertificate']['issuer'], $withType);
+ case isset($this->currentCert['tbsCertList']):
+ return $this->getDNProp($propname, $this->currentCert['tbsCertList']['issuer'], $withType);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an individual Distinguished Name property for a certificate/csr subject
+ *
+ * @param String $propName
+ * @param Boolean $withType optional
+ * @access public
+ * @return Mixed
+ */
+ function getSubjectDNProp($propName, $withType = false)
+ {
+ switch (true) {
+ case !empty($this->dn):
+ return $this->getDNProp($propName, NULL, $withType);
+ case !isset($this->currentCert) || !is_array($this->currentCert):
+ break;
+ case isset($this->currentCert['tbsCertificate']):
+ return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType);
+ case isset($this->currentCert['certificationRequestInfo']):
+ return $this->getDNProp($propname, $this->currentCert['certificationRequestInfo']['subject'], $withType);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the certificate chain for the current cert
+ *
+ * @access public
+ * @return Mixed
+ */
+ function getChain()
+ {
+ $chain = array($this->currentCert);
+
+ if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
+ return false;
+ }
+ if (empty($this->CAs)) {
+ return $chain;
+ }
+ while (true) {
+ $currentCert = $chain[count($chain) - 1];
+ for ($i = 0; $i < count($this->CAs); $i++) {
+ $ca = $this->CAs[$i];
+ if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
+ $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert);
+ $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
+ switch (true) {
+ case !is_array($authorityKey):
+ case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
+ if ($currentCert === $ca) {
+ break 3;
+ }
+ $chain[] = $ca;
+ break 2;
+ }
+ }
+ }
+ if ($i == count($this->CAs)) {
+ break;
+ }
+ }
+ foreach ($chain as $key=>$value) {
+ $chain[$key] = new File_X509();
+ $chain[$key]->loadX509($value);
+ }
+ return $chain;
+ }
+
+ /**
+ * Set public key
+ *
+ * Key needs to be a Crypt_RSA object
+ *
+ * @param Object $key
+ * @access public
+ * @return Boolean
+ */
+ function setPublicKey($key)
+ {
+ $this->publicKey = $key;
+ }
+
+ /**
+ * Set private key
+ *
+ * Key needs to be a Crypt_RSA object
+ *
+ * @param Object $key
+ * @access public
+ */
+ function setPrivateKey($key)
+ {
+ $this->privateKey = $key;
+ }
+
+ /**
+ * Gets the public key
+ *
+ * Returns a Crypt_RSA object or a false.
+ *
+ * @access public
+ * @return Mixed
+ */
+ function getPublicKey()
+ {
+ if (isset($this->publicKey)) {
+ return $this->publicKey;
+ }
+
+ if (isset($this->currentCert) && is_array($this->currentCert)) {
+ foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) {
+ $keyinfo = $this->_subArray($this->currentCert, $path);
+ if (!empty($keyinfo)) {
+ break;
+ }
+ }
+ }
+ if (empty($keyinfo)) {
+ return false;
+ }
+
+ $key = $keyinfo['subjectPublicKey'];
+
+ switch ($keyinfo['algorithm']['algorithm']) {
+ case 'rsaEncryption':
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+ $publicKey = new Crypt_RSA();
+ $publicKey->loadKey($key);
+ $publicKey->setPublicKey();
+ break;
+ default:
+ return false;
+ }
+
+ return $publicKey;
+ }
+
+ /**
+ * Load a Certificate Signing Request
+ *
+ * @param String $csr
+ * @access public
+ * @return Mixed
+ */
+ function loadCSR($csr)
+ {
+ if (is_array($csr) && isset($csr['certificationRequestInfo'])) {
+ unset($this->currentCert);
+ unset($this->currentKeyIdentifier);
+ unset($this->signatureSubject);
+ $this->dn = $csr['certificationRequestInfo']['subject'];
+ if (!isset($this->dn)) {
+ return false;
+ }
+
+ $this->currentCert = $csr;
+ return $csr;
+ }
+
+ // see http://tools.ietf.org/html/rfc2986
+
+ $asn1 = new File_ASN1();
+
+ $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $csr);
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
+ if ($temp != false) {
+ $csr = $temp;
+ }
+ $orig = $csr;
+
+ if ($csr === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $asn1->loadOIDs($this->oids);
+ $decoded = $asn1->decodeBER($csr);
+
+ if (empty($decoded)) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
+ if (!isset($csr) || $csr === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $this->dn = $csr['certificationRequestInfo']['subject'];
+ $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
+
+ $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
+
+ $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
+ $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
+ $key = $this->_reformatKey($algorithm, $key);
+
+ switch ($algorithm) {
+ case 'rsaEncryption':
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+ $this->publicKey = new Crypt_RSA();
+ $this->publicKey->loadKey($key);
+ $this->publicKey->setPublicKey();
+ break;
+ default:
+ $this->publicKey = NULL;
+ }
+
+ $this->currentKeyIdentifier = NULL;
+ $this->currentCert = $csr;
+
+ return $csr;
+ }
+
+ /**
+ * Save CSR request
+ *
+ * @param Array $csr
+ * @param Integer $format optional
+ * @access public
+ * @return String
+ */
+ function saveCSR($csr, $format = FILE_X509_FORMAT_PEM)
+ {
+ if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
+ return false;
+ }
+
+ switch (true) {
+ case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
+ case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']);
+ break;
+ default:
+ switch ($algorithm) {
+ case 'rsaEncryption':
+ $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
+ base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
+ }
+ }
+
+ $asn1 = new File_ASN1();
+
+ $asn1->loadOIDs($this->oids);
+
+ $filters = array();
+ $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] =
+ array('type' => FILE_ASN1_TYPE_UTF8_STRING);
+
+ $asn1->loadFilters($filters);
+
+ $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
+ $csr = $asn1->encodeDER($csr, $this->CertificationRequest);
+
+ switch ($format) {
+ case FILE_X509_FORMAT_DER:
+ return $csr;
+ // case FILE_X509_FORMAT_PEM:
+ default:
+ return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
+ }
+ }
+
+ /**
+ * Load a SPKAC CSR
+ *
+ * SPKAC's are produced by the HTML5 keygen element:
+ *
+ * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
+ *
+ * @param String $csr
+ * @access public
+ * @return Mixed
+ */
+ function loadSPKAC($csr)
+ {
+ if (is_array($csr) && isset($csr['publicKeyAndChallenge'])) {
+ unset($this->currentCert);
+ unset($this->currentKeyIdentifier);
+ unset($this->signatureSubject);
+ $this->currentCert = $csr;
+ return $csr;
+ }
+
+ // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge
+
+ $asn1 = new File_ASN1();
+
+ $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $csr);
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
+ if ($temp != false) {
+ $csr = $temp;
+ }
+ $orig = $csr;
+
+ if ($csr === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $asn1->loadOIDs($this->oids);
+ $decoded = $asn1->decodeBER($csr);
+
+ if (empty($decoded)) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $csr = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);
+
+ if (!isset($csr) || $csr === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
+
+ $algorithm = &$csr['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
+ $key = &$csr['publicKeyAndChallenge']['spki']['subjectPublicKey'];
+ $key = $this->_reformatKey($algorithm, $key);
+
+ switch ($algorithm) {
+ case 'rsaEncryption':
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+ $this->publicKey = new Crypt_RSA();
+ $this->publicKey->loadKey($key);
+ $this->publicKey->setPublicKey();
+ break;
+ default:
+ $this->publicKey = NULL;
+ }
+
+ $this->currentKeyIdentifier = NULL;
+ $this->currentCert = $csr;
+
+ return $csr;
+ }
+
+ /**
+ * Load a Certificate Revocation List
+ *
+ * @param String $crl
+ * @access public
+ * @return Mixed
+ */
+ function loadCRL($crl)
+ {
+ if (is_array($crl) && isset($crl['tbsCertList'])) {
+ $this->currentCert = $crl;
+ unset($this->signatureSubject);
+ return $crl;
+ }
+
+ $asn1 = new File_ASN1();
+
+ $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $crl);
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
+ if ($temp != false) {
+ $crl = $temp;
+ }
+ $orig = $crl;
+
+ if ($crl === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $asn1->loadOIDs($this->oids);
+ $decoded = $asn1->decodeBER($crl);
+
+ if (empty($decoded)) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $crl = $asn1->asn1map($decoded[0], $this->CertificateList);
+ if (!isset($crl) || $crl === false) {
+ $this->currentCert = false;
+ return false;
+ }
+
+ $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
+
+ $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
+ $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates');
+ if (is_array($rclist)) {
+ foreach ($rclist as $i => $extension) {
+ $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1);
+ }
+ }
+
+ $this->currentKeyIdentifier = NULL;
+ $this->currentCert = $crl;
+
+ return $crl;
+ }
+
+ /**
+ * Save Certificate Revocation List.
+ *
+ * @param Array $crl
+ * @param Integer $format optional
+ * @access public
+ * @return String
+ */
+ function saveCRL($crl, $format = FILE_X509_FORMAT_PEM)
+ {
+ if (!is_array($crl) || !isset($crl['tbsCertList'])) {
+ return false;
+ }
+
+ $asn1 = new File_ASN1();
+
+ $asn1->loadOIDs($this->oids);
+
+ $filters = array();
+ $filters['tbsCertList']['issuer']['rdnSequence']['value'] =
+ $filters['tbsCertList']['signature']['parameters'] =
+ $filters['signatureAlgorithm']['parameters'] =
+ array('type' => FILE_ASN1_TYPE_UTF8_STRING);
+
+ if (empty($crl['tbsCertList']['signature']['parameters'])) {
+ $filters['tbsCertList']['signature']['parameters'] =
+ array('type' => FILE_ASN1_TYPE_NULL);
+ }
+
+ if (empty($crl['signatureAlgorithm']['parameters'])) {
+ $filters['signatureAlgorithm']['parameters'] =
+ array('type' => FILE_ASN1_TYPE_NULL);
+ }
+
+ $asn1->loadFilters($filters);
+
+ $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
+ $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates');
+ if (is_array($rclist)) {
+ foreach ($rclist as $i => $extension) {
+ $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1);
+ }
+ }
+
+ $crl = $asn1->encodeDER($crl, $this->CertificateList);
+
+ switch ($format) {
+ case FILE_X509_FORMAT_DER:
+ return $crl;
+ // case FILE_X509_FORMAT_PEM:
+ default:
+ return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
+ }
+ }
+
+ /**
+ * Sign an X.509 certificate
+ *
+ * $issuer's private key needs to be loaded.
+ * $subject can be either an existing X.509 cert (if you want to resign it),
+ * a CSR or something with the DN and public key explicitly set.
+ *
+ * @param File_X509 $issuer
+ * @param File_X509 $subject
+ * @param String $signatureAlgorithm optional
+ * @access public
+ * @return Mixed
+ */
+ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
+ {
+ if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
+ return false;
+ }
+
+ if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
+ return false;
+ }
+
+ $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL;
+
+ if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
+ $this->currentCert = $subject->currentCert;
+ $this->currentCert['tbsCertificate']['signature']['algorithm'] =
+ $this->currentCert['signatureAlgorithm']['algorithm'] =
+ $signatureAlgorithm;
+ if (!empty($this->startDate)) {
+ $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate;
+ unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']);
+ }
+ if (!empty($this->endDate)) {
+ $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate;
+ unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']);
+ }
+ if (!empty($this->serialNumber)) {
+ $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
+ }
+ if (!empty($subject->dn)) {
+ $this->currentCert['tbsCertificate']['subject'] = $subject->dn;
+ }
+ if (!empty($subject->publicKey)) {
+ $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
+ }
+ $this->removeExtension('id-ce-authorityKeyIdentifier');
+ if (isset($subject->domains)) {
+ $this->removeExtension('id-ce-subjectAltName');
+ }
+ } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
+ return false;
+ } else {
+ if (!isset($subject->publicKey)) {
+ return false;
+ }
+
+ $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O');
+ $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M y H:i:s O', strtotime('+1 year'));
+ $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger();
+
+ $this->currentCert = array(
+ 'tbsCertificate' =>
+ array(
+ 'version' => 'v3',
+ 'serialNumber' => $serialNumber, // $this->setserialNumber()
+ 'signature' => array('algorithm' => $signatureAlgorithm),
+ 'issuer' => false, // this is going to be overwritten later
+ 'validity' => array(
+ 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate()
+ 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate()
+ ),
+ 'subject' => $subject->dn,
+ 'subjectPublicKeyInfo' => $subjectPublicKey
+ ),
+ 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ 'signature' => false // this is going to be overwritten later
+ );
+
+ // Copy extensions from CSR.
+ $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0);
+
+ if (!empty($csrexts)) {
+ $this->currentCert['tbsCertificate']['extensions'] = $csrexts;
+ }
+ }
+
+ $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
+
+ if (isset($issuer->currentKeyIdentifier)) {
+ $this->setExtension('id-ce-authorityKeyIdentifier', array(
+ //'authorityCertIssuer' => array(
+ // array(
+ // 'directoryName' => $issuer->dn
+ // )
+ //),
+ 'keyIdentifier' => $issuer->currentKeyIdentifier
+ )
+ );
+ //$extensions = &$this->currentCert['tbsCertificate']['extensions'];
+ //if (isset($issuer->serialNumber)) {
+ // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
+ //}
+ //unset($extensions);
+ }
+
+ if (isset($subject->currentKeyIdentifier)) {
+ $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier);
+ }
+
+ if (isset($subject->domains) && count($subject->domains) > 1) {
+ $this->setExtension('id-ce-subjectAltName',
+ array_map(array('File_X509', '_dnsName'), $subject->domains));
+ }
+
+ if ($this->caFlag) {
+ $keyUsage = $this->getExtension('id-ce-keyUsage');
+ if (!$keyUsage) {
+ $keyUsage = array();
+ }
+
+ $this->setExtension('id-ce-keyUsage',
+ array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
+ );
+
+ $basicConstraints = $this->getExtension('id-ce-basicConstraints');
+ if (!$basicConstraints) {
+ $basicConstraints = array();
+ }
+
+ $this->setExtension('id-ce-basicConstraints',
+ array_unique(array_merge(array('cA' => true), $basicConstraints)), true);
+
+ if (!isset($subject->currentKeyIdentifier)) {
+ $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false);
+ }
+ }
+
+ // resync $this->signatureSubject
+ // save $tbsCertificate in case there are any File_ASN1_Element objects in it
+ $tbsCertificate = $this->currentCert['tbsCertificate'];
+ $this->loadX509($this->saveX509($this->currentCert));
+
+ $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
+ $result['tbsCertificate'] = $tbsCertificate;
+
+ $this->currentCert = $currentCert;
+ $this->signatureSubject = $signatureSubject;
+
+ return $result;
+ }
+
+ /**
+ * Sign a CSR
+ *
+ * @access public
+ * @return Mixed
+ */
+ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
+ {
+ if (!is_object($this->privateKey) || empty($this->dn)) {
+ return false;
+ }
+
+ $origPublicKey = $this->publicKey;
+ $class = get_class($this->privateKey);
+ $this->publicKey = new $class();
+ $this->publicKey->loadKey($this->privateKey->getPublicKey());
+ $this->publicKey->setPublicKey();
+ if (!($publicKey = $this->_formatSubjectPublicKey())) {
+ return false;
+ }
+ $this->publicKey = $origPublicKey;
+
+ $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL;
+
+ if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
+ $this->currentCert['signatureAlgorithm']['algorithm'] =
+ $signatureAlgorithm;
+ if (!empty($this->dn)) {
+ $this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
+ }
+ $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
+ } else {
+ $this->currentCert = array(
+ 'certificationRequestInfo' =>
+ array(
+ 'version' => 'v1',
+ 'subject' => $this->dn,
+ 'subjectPKInfo' => $publicKey
+ ),
+ 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ 'signature' => false // this is going to be overwritten later
+ );
+ }
+
+ // resync $this->signatureSubject
+ // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
+ $certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
+ $this->loadCSR($this->saveCSR($this->currentCert));
+
+ $result = $this->_sign($this->privateKey, $signatureAlgorithm);
+ $result['certificationRequestInfo'] = $certificationRequestInfo;
+
+ $this->currentCert = $currentCert;
+ $this->signatureSubject = $signatureSubject;
+
+ return $result;
+ }
+
+ /**
+ * Sign a CRL
+ *
+ * $issuer's private key needs to be loaded.
+ *
+ * @param File_X509 $issuer
+ * @param File_X509 $crl
+ * @param String $signatureAlgorithm optional
+ * @access public
+ * @return Mixed
+ */
+ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption')
+ {
+ if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
+ return false;
+ }
+
+ $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : NULL;
+ $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O');
+
+ if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
+ $this->currentCert = $crl->currentCert;
+ $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
+ $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
+ } else {
+ $this->currentCert = array(
+ 'tbsCertList' =>
+ array(
+ 'version' => 'v2',
+ 'signature' => array('algorithm' => $signatureAlgorithm),
+ 'issuer' => false, // this is going to be overwritten later
+ 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate()
+ ),
+ 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ 'signature' => false // this is going to be overwritten later
+ );
+ }
+
+ $tbsCertList = &$this->currentCert['tbsCertList'];
+ $tbsCertList['issuer'] = $issuer->dn;
+ $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate);
+
+ if (!empty($this->endDate)) {
+ $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate()
+ } else {
+ unset($tbsCertList['nextUpdate']);
+ }
+
+ if (!empty($this->serialNumber)) {
+ $crlNumber = $this->serialNumber;
+ }
+ else {
+ $crlNumber = $this->getExtension('id-ce-cRLNumber');
+ $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : NULL;
+ }
+
+ $this->removeExtension('id-ce-authorityKeyIdentifier');
+ $this->removeExtension('id-ce-issuerAltName');
+
+ // Be sure version >= v2 if some extension found.
+ $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
+ if (!$version) {
+ if (!empty($tbsCertList['crlExtensions'])) {
+ $version = 1; // v2.
+ }
+ elseif (!empty($tbsCertList['revokedCertificates'])) {
+ foreach ($tbsCertList['revokedCertificates'] as $cert) {
+ if (!empty($cert['crlEntryExtensions'])) {
+ $version = 1; // v2.
+ }
+ }
+ }
+
+ if ($version) {
+ $tbsCertList['version'] = $version;
+ }
+ }
+
+ // Store additional extensions.
+ if (!empty($tbsCertList['version'])) { // At least v2.
+ if (!empty($crlNumber)) {
+ $this->setExtension('id-ce-cRLNumber', $crlNumber);
+ }
+
+ if (isset($issuer->currentKeyIdentifier)) {
+ $this->setExtension('id-ce-authorityKeyIdentifier', array(
+ //'authorityCertIssuer' => array(
+ // array(
+ // 'directoryName' => $issuer->dn
+ // )
+ //),
+ 'keyIdentifier' => $issuer->currentKeyIdentifier
+ )
+ );
+ //$extensions = &$tbsCertList['crlExtensions'];
+ //if (isset($issuer->serialNumber)) {
+ // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
+ //}
+ //unset($extensions);
+ }
+
+ $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert);
+
+ if ($issuerAltName !== false) {
+ $this->setExtension('id-ce-issuerAltName', $issuerAltName);
+ }
+ }
+
+ if (empty($tbsCertList['revokedCertificates'])) {
+ unset($tbsCertList['revokedCertificates']);
+ }
+
+ unset($tbsCertList);
+
+ // resync $this->signatureSubject
+ // save $tbsCertList in case there are any File_ASN1_Element objects in it
+ $tbsCertList = $this->currentCert['tbsCertList'];
+ $this->loadCRL($this->saveCRL($this->currentCert));
+
+ $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
+ $result['tbsCertList'] = $tbsCertList;
+
+ $this->currentCert = $currentCert;
+ $this->signatureSubject = $signatureSubject;
+
+ return $result;
+ }
+
+ /**
+ * X.509 certificate signing helper function.
+ *
+ * @param Object $key
+ * @param File_X509 $subject
+ * @param String $signatureAlgorithm
+ * @access public
+ * @return Mixed
+ */
+ function _sign($key, $signatureAlgorithm)
+ {
+ switch (strtolower(get_class($key))) {
+ case 'crypt_rsa':
+ switch ($signatureAlgorithm) {
+ case 'md2WithRSAEncryption':
+ case 'md5WithRSAEncryption':
+ case 'sha1WithRSAEncryption':
+ case 'sha224WithRSAEncryption':
+ case 'sha256WithRSAEncryption':
+ case 'sha384WithRSAEncryption':
+ case 'sha512WithRSAEncryption':
+ $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
+ $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
+
+ $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
+ return $this->currentCert;
+ }
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Set certificate start date
+ *
+ * @param String $date
+ * @access public
+ */
+ function setStartDate($date)
+ {
+ $this->startDate = @date('D, d M y H:i:s O', @strtotime($date));
+ }
+
+ /**
+ * Set certificate end date
+ *
+ * @param String $date
+ * @access public
+ */
+ function setEndDate($date)
+ {
+ /*
+ To indicate that a certificate has no well-defined expiration date,
+ the notAfter SHOULD be assigned the GeneralizedTime value of
+ 99991231235959Z.
+
+ -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
+ */
+ if (strtolower($date) == 'lifetime') {
+ $temp = '99991231235959Z';
+ $asn1 = new File_ASN1();
+ $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
+ $this->endDate = new File_ASN1_Element($temp);
+ } else {
+ $this->endDate = @date('D, d M y H:i:s O', @strtotime($date));
+ }
+ }
+
+ /**
+ * Set Serial Number
+ *
+ * @param String $serial
+ * @param $base optional
+ * @access public
+ */
+ function setSerialNumber($serial, $base = -256)
+ {
+ $this->serialNumber = new Math_BigInteger($serial, $base);
+ }
+
+ /**
+ * Turns the certificate into a certificate authority
+ *
+ * @access public
+ */
+ function makeCA()
+ {
+ $this->caFlag = true;
+ }
+
+ /**
+ * Get a reference to a subarray
+ *
+ * @param array $root
+ * @param String $path absolute path with / as component separator
+ * @param Boolean $create optional
+ * @access private
+ * @return array item ref or false
+ */
+ function &_subArray(&$root, $path, $create = false)
+ {
+ $false = false;
+
+ if (!is_array($root)) {
+ return $false;
+ }
+
+ foreach (explode('/', $path) as $i) {
+ if (!is_array($root)) {
+ return $false;
+ }
+
+ if (!isset($root[$i])) {
+ if (!$create) {
+ return $false;
+ }
+
+ $root[$i] = array();
+ }
+
+ $root = &$root[$i];
+ }
+
+ return $root;
+ }
+
+ /**
+ * Get a reference to an extension subarray
+ *
+ * @param array $root
+ * @param String $path optional absolute path with / as component separator
+ * @param Boolean $create optional
+ * @access private
+ * @return array ref or false
+ */
+ function &_extensions(&$root, $path = NULL, $create = false)
+ {
+ if (!isset($root)) {
+ $root = $this->currentCert;
+ }
+
+ switch (true) {
+ case !empty($path):
+ case !is_array($root):
+ break;
+ case isset($root['tbsCertificate']):
+ $path = 'tbsCertificate/extensions';
+ break;
+ case isset($root['tbsCertList']):
+ $path = 'tbsCertList/crlExtensions';
+ break;
+ case isset($root['certificationRequestInfo']):
+ $pth = 'certificationRequestInfo/attributes';
+ $attributes = &$this->_subArray($root, $pth, $create);
+
+ if (is_array($attributes)) {
+ foreach ($attributes as $key => $value) {
+ if ($value['type'] == 'pkcs-9-at-extensionRequest') {
+ $path = "$pth/$key/value/0";
+ break 2;
+ }
+ }
+ if ($create) {
+ $key = count($attributes);
+ $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array());
+ $path = "$pth/$key/value/0";
+ }
+ }
+ break;
+ }
+
+ $extensions = &$this->_subArray($root, $path, $create);
+
+ if (!is_array($extensions)) {
+ $false = false;
+ return $false;
+ }
+
+ return $extensions;
+ }
+
+ /**
+ * Remove an Extension
+ *
+ * @param String $id
+ * @param String $path optional
+ * @access private
+ * @return Boolean
+ */
+ function _removeExtension($id, $path = NULL)
+ {
+ $extensions = &$this->_extensions($this->currentCert, $path);
+
+ if (!is_array($extensions)) {
+ return false;
+ }
+
+ $result = false;
+ foreach ($extensions as $key => $value) {
+ if ($value['extnId'] == $id) {
+ unset($extensions[$key]);
+ $result = true;
+ }
+ }
+
+ $extensions = array_values($extensions);
+ return $result;
+ }
+
+ /**
+ * Get an Extension
+ *
+ * Returns the extension if it exists and false if not
+ *
+ * @param String $id
+ * @param Array $cert optional
+ * @param String $path optional
+ * @access private
+ * @return Mixed
+ */
+ function _getExtension($id, $cert = NULL, $path = NULL)
+ {
+ $extensions = $this->_extensions($cert, $path);
+
+ if (!is_array($extensions)) {
+ return false;
+ }
+
+ foreach ($extensions as $key => $value) {
+ if ($value['extnId'] == $id) {
+ return $value['extnValue'];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a list of all extensions in use
+ *
+ * @param array $cert optional
+ * @param String $path optional
+ * @access private
+ * @return Array
+ */
+ function _getExtensions($cert = NULL, $path = NULL)
+ {
+ $exts = $this->_extensions($cert, $path);
+ $extensions = array();
+
+ if (is_array($exts)) {
+ foreach ($exts as $extension) {
+ $extensions[] = $extension['extnId'];
+ }
+ }
+
+ return $extensions;
+ }
+
+ /**
+ * Set an Extension
+ *
+ * @param String $id
+ * @param Mixed $value
+ * @param Boolean $critical optional
+ * @param Boolean $replace optional
+ * @param String $path optional
+ * @access private
+ * @return Boolean
+ */
+ function _setExtension($id, $value, $critical = false, $replace = true, $path = NULL)
+ {
+ $extensions = &$this->_extensions($this->currentCert, $path, true);
+
+ if (!is_array($extensions)) {
+ return false;
+ }
+
+ $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value);
+
+ foreach ($extensions as $key => $value) {
+ if ($value['extnId'] == $id) {
+ if (!$replace) {
+ return false;
+ }
+
+ $extensions[$key] = $newext;
+ return true;
+ }
+ }
+
+ $extensions[] = $newext;
+ return true;
+ }
+
+ /**
+ * Remove a certificate, CSR or CRL Extension
+ *
+ * @param String $id
+ * @access public
+ * @return Boolean
+ */
+ function removeExtension($id)
+ {
+ return $this->_removeExtension($id);
+ }
+
+ /**
+ * Get a certificate, CSR or CRL Extension
+ *
+ * Returns the extension if it exists and false if not
+ *
+ * @param String $id
+ * @param Array $cert optional
+ * @access public
+ * @return Mixed
+ */
+ function getExtension($id, $cert = NULL)
+ {
+ return $this->_getExtension($id, $cert);
+ }
+
+ /**
+ * Returns a list of all extensions in use in certificate, CSR or CRL
+ *
+ * @param array $cert optional
+ * @access public
+ * @return Array
+ */
+ function getExtensions($cert = NULL)
+ {
+ return $this->_getExtensions($cert);
+ }
+
+ /**
+ * Set a certificate, CSR or CRL Extension
+ *
+ * @param String $id
+ * @param Mixed $value
+ * @param Boolean $critical optional
+ * @param Boolean $replace optional
+ * @access public
+ * @return Boolean
+ */
+ function setExtension($id, $value, $critical = false, $replace = true)
+ {
+ return $this->_setExtension($id, $value, $critical, $replace);
+ }
+
+ /**
+ * Remove a CSR attribute.
+ *
+ * @param String $id
+ * @param Integer $disposition optional
+ * @access public
+ * @return Boolean
+ */
+ function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL)
+ {
+ $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes');
+
+ if (!is_array($attributes)) {
+ return false;
+ }
+
+ $result = false;
+ foreach ($attributes as $key => $attribute) {
+ if ($attribute['type'] == $id) {
+ $n = count($attribute['value']);
+ switch (true) {
+ case $disposition == FILE_X509_ATTR_APPEND:
+ case $disposition == FILE_X509_ATTR_REPLACE:
+ return false;
+ case $disposition >= $n:
+ $disposition -= $n;
+ break;
+ case $disposition == FILE_X509_ATTR_ALL:
+ case $n == 1:
+ unset($attributes[$key]);
+ $result = true;
+ break;
+ default:
+ unset($attributes[$key]['value'][$disposition]);
+ $attributes[$key]['value'] = array_values($attributes[$key]['value']);
+ $result = true;
+ break;
+ }
+ if ($result && $disposition != FILE_X509_ATTR_ALL) {
+ break;
+ }
+ }
+ }
+
+ $attributes = array_values($attributes);
+ return $result;
+ }
+
+ /**
+ * Get a CSR attribute
+ *
+ * Returns the attribute if it exists and false if not
+ *
+ * @param String $id
+ * @param Integer $disposition optional
+ * @param Array $csr optional
+ * @access public
+ * @return Mixed
+ */
+ function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = NULL)
+ {
+ if (empty($csr)) {
+ $csr = $this->currentCert;
+ }
+
+ $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
+
+ if (!is_array($attributes)) {
+ return false;
+ }
+
+ foreach ($attributes as $key => $attribute) {
+ if ($attribute['type'] == $id) {
+ $n = count($attribute['value']);
+ switch (true) {
+ case $disposition == FILE_X509_ATTR_APPEND:
+ case $disposition == FILE_X509_ATTR_REPLACE:
+ return false;
+ case $disposition == FILE_X509_ATTR_ALL:
+ return $attribute['value'];
+ case $disposition >= $n:
+ $disposition -= $n;
+ break;
+ default:
+ return $attribute['value'][$disposition];
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a list of all CSR attributes in use
+ *
+ * @param array $csr optional
+ * @access public
+ * @return Array
+ */
+ function getAttributes($csr = NULL)
+ {
+ if (empty($csr)) {
+ $csr = $this->currentCert;
+ }
+
+ $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
+ $attrs = array();
+
+ if (is_array($attributes)) {
+ foreach ($attributes as $attribute) {
+ $attrs[] = $attribute['type'];
+ }
+ }
+
+ return $attrs;
+ }
+
+ /**
+ * Set a CSR attribute
+ *
+ * @param String $id
+ * @param Mixed $value
+ * @param Boolean $disposition optional
+ * @access public
+ * @return Boolean
+ */
+ function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL)
+ {
+ $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true);
+
+ if (!is_array($attributes)) {
+ return false;
+ }
+
+ switch ($disposition) {
+ case FILE_X509_ATTR_REPLACE:
+ $disposition = FILE_X509_ATTR_APPEND;
+ case FILE_X509_ATTR_ALL:
+ $this->removeAttribute($id);
+ break;
+ }
+
+ foreach ($attributes as $key => $attribute) {
+ if ($attribute['type'] == $id) {
+ $n = count($attribute['value']);
+ switch (true) {
+ case $disposition == FILE_X509_ATTR_APPEND:
+ $last = $key;
+ break;
+ case $disposition >= $n;
+ $disposition -= $n;
+ break;
+ default:
+ $attributes[$key]['value'][$disposition] = $value;
+ return true;
+ }
+ }
+ }
+
+ switch (true) {
+ case $disposition >= 0:
+ return false;
+ case isset($last):
+ $attributes[$last]['value'][] = $value;
+ break;
+ default:
+ $attributes[] = array('type' => $id, 'value' => $disposition == FILE_X509_ATTR_ALL ? $value: array($value));
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets the subject key identifier
+ *
+ * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
+ *
+ * @param String $value
+ * @access public
+ */
+ function setKeyIdentifier($value)
+ {
+ if (empty($value)) {
+ unset($this->currentKeyIdentifier);
+ } else {
+ $this->currentKeyIdentifier = base64_encode($value);
+ }
+ }
+
+ /**
+ * Compute a public key identifier.
+ *
+ * Although key identifiers may be set to any unique value, this function
+ * computes key identifiers from public key according to the two
+ * recommended methods (4.2.1.2 RFC 3280).
+ * Highly polymorphic: try to accept all possible forms of key:
+ * - Key object
+ * - File_X509 object with public or private key defined
+ * - Certificate or CSR array
+ * - File_ASN1_Element object
+ * - PEM or DER string
+ *
+ * @param Mixed $key optional
+ * @param Integer $method optional
+ * @access public
+ * @return String binary key identifier
+ */
+ function computeKeyIdentifier($key = NULL, $method = 1)
+ {
+ if (is_null($key)) {
+ $key = $this;
+ }
+
+ switch (true) {
+ case is_string($key):
+ break;
+ case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
+ return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
+ case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
+ return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
+ case !is_object($key):
+ return false;
+ case strtolower(get_class($key)) == 'file_asn1_element':
+ $asn1 = new File_ASN1();
+ $decoded = $asn1->decodeBER($cert);
+ if (empty($decoded)) {
+ return false;
+ }
+ $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING));
+ break;
+ case strtolower(get_class($key)) == 'file_x509':
+ if (isset($key->publicKey)) {
+ return $this->computeKeyIdentifier($key->publicKey, $method);
+ }
+ if (isset($key->privateKey)) {
+ return $this->computeKeyIdentifier($key->privateKey, $method);
+ }
+ if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
+ return $this->computeKeyIdentifier($key->currentCert, $method);
+ }
+ return false;
+ default: // Should be a key object (i.e.: Crypt_RSA).
+ $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW);
+ break;
+ }
+
+ // If in PEM format, convert to binary.
+ if (preg_match('#^-----BEGIN #', $key)) {
+ $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key));
+ }
+
+ // Now we have the key string: compute its sha-1 sum.
+ if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+ }
+ $hash = new Crypt_Hash('sha1');
+ $hash = $hash->hash($key);
+
+ if ($method == 2) {
+ $hash = substr($hash, -8);
+ $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
+ }
+
+ return $hash;
+ }
+
+ /**
+ * Format a public key as appropriate
+ *
+ * @access private
+ * @return Array
+ */
+ function _formatSubjectPublicKey()
+ {
+ if (!isset($this->publicKey) || !is_object($this->publicKey)) {
+ return false;
+ }
+
+ switch (strtolower(get_class($this->publicKey))) {
+ case 'crypt_rsa':
+ // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
+ // the former is a good example of how to do fuzzing on the public key
+ //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey())));
+ return array(
+ 'algorithm' => array('algorithm' => 'rsaEncryption'),
+ 'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW)
+ );
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Set the domain name's which the cert is to be valid for
+ *
+ * @access public
+ * @return Array
+ */
+ function setDomain()
+ {
+ $this->domains = func_get_args();
+ $this->removeDNProp('id-at-commonName');
+ $this->setDNProp('id-at-commonName', $this->domains[0]);
+ }
+
+ /**
+ * Helper function to build domain array
+ *
+ * @access private
+ * @param String $domain
+ * @return Array
+ */
+ function _dnsName($domain)
+ {
+ return array('dNSName' => $domain);
+ }
+
+ /**
+ * Get the index of a revoked certificate.
+ *
+ * @param array $rclist
+ * @param String $serial
+ * @param Boolean $create optional
+ * @access private
+ * @return Integer or false
+ */
+ function _revokedCertificate(&$rclist, $serial, $create = false)
+ {
+ $serial = new Math_BigInteger($serial);
+
+ foreach ($rclist as $i => $rc) {
+ if (!($serial->compare($rc['userCertificate']))) {
+ return $i;
+ }
+ }
+
+ if (!$create) {
+ return false;
+ }
+
+ $i = count($rclist);
+ $rclist[] = array('userCertificate' => $serial,
+ 'revocationDate' => array('generalTime' => @date('D, d M y H:i:s O')));
+ return $i;
+ }
+
+ /**
+ * Revoke a certificate.
+ *
+ * @param String $serial
+ * @param String $date optional
+ * @access public
+ * @return Boolean
+ */
+ function revoke($serial, $date = NULL)
+ {
+ if (isset($this->currentCert['tbsCertList'])) {
+ if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
+ if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked
+ if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
+
+ if (!empty($date)) {
+ $rclist[$i]['revocationDate'] = array('generalTime' => $date);
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Unrevoke a certificate.
+ *
+ * @param String $serial
+ * @access public
+ * @return Boolean
+ */
+ function unrevoke($serial)
+ {
+ if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ unset($rclist[$i]);
+ $rclist = array_values($rclist);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get a revoked certificate.
+ *
+ * @param String $serial
+ * @access public
+ * @return Mixed
+ */
+ function getRevoked($serial)
+ {
+ if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ return $rclist[$i];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * List revoked certificates
+ *
+ * @param array $crl optional
+ * @access public
+ * @return array
+ */
+ function listRevoked($crl = NULL)
+ {
+ if (!isset($crl)) {
+ $crl = $this->currentCert;
+ }
+
+ if (!isset($crl['tbsCertList'])) {
+ return false;
+ }
+
+ $result = array();
+
+ if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ foreach ($rclist as $rc) {
+ $result[] = $rc['userCertificate']->toString();
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove a Revoked Certificate Extension
+ *
+ * @param String $serial
+ * @param String $id
+ * @access public
+ * @return Boolean
+ */
+ function removeRevokedCertificateExtension($serial, $id)
+ {
+ if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get a Revoked Certificate Extension
+ *
+ * Returns the extension if it exists and false if not
+ *
+ * @param String $serial
+ * @param String $id
+ * @param Array $crl optional
+ * @access public
+ * @return Mixed
+ */
+ function getRevokedCertificateExtension($serial, $id, $crl = NULL)
+ {
+ if (!isset($crl)) {
+ $crl = $this->currentCert;
+ }
+
+ if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a list of all extensions in use for a given revoked certificate
+ *
+ * @param String $serial
+ * @param array $crl optional
+ * @access public
+ * @return Array
+ */
+ function getRevokedCertificateExtensions($serial, $crl = NULL)
+ {
+ if (!isset($crl)) {
+ $crl = $this->currentCert;
+ }
+
+ if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Set a Revoked Certificate Extension
+ *
+ * @param String $serial
+ * @param String $id
+ * @param Mixed $value
+ * @param Boolean $critical optional
+ * @param Boolean $replace optional
+ * @access public
+ * @return Boolean
+ */
+ function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
+ {
+ if (isset($this->currentCert['tbsCertList'])) {
+ if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
+ if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
+ return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php
new file mode 100644
index 00000000000..d048cb032c5
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php
@@ -0,0 +1,3633 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP arbitrary precision integer arithmetic library.
+ *
+ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
+ * and an internal implementation, otherwise.
+ *
+ * PHP versions 4 and 5
+ *
+ * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
+ * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode)
+ *
+ * Math_BigInteger uses base-2**26 to perform operations such as multiplication and division and
+ * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
+ * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
+ * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
+ * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
+ * which only supports integers. Although this fact will slow this library down, the fact that such a high
+ * base is being used should more than compensate.
+ *
+ * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again,
+ * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition /
+ * subtraction).
+ *
+ * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
+ * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
+ * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
+ * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
+ *
+ * Here's an example of how to use this library:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger(2);
+ * $b = new Math_BigInteger(3);
+ *
+ * $c = $a->add($b);
+ *
+ * echo $c->toString(); // outputs 5
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Math
+ * @package Math_BigInteger
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVI Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+/**#@+
+ * Reduction constants
+ *
+ * @access private
+ * @see Math_BigInteger::_reduce()
+ */
+/**
+ * @see Math_BigInteger::_montgomery()
+ * @see Math_BigInteger::_prepMontgomery()
+ */
+define('MATH_BIGINTEGER_MONTGOMERY', 0);
+/**
+ * @see Math_BigInteger::_barrett()
+ */
+define('MATH_BIGINTEGER_BARRETT', 1);
+/**
+ * @see Math_BigInteger::_mod2()
+ */
+define('MATH_BIGINTEGER_POWEROF2', 2);
+/**
+ * @see Math_BigInteger::_remainder()
+ */
+define('MATH_BIGINTEGER_CLASSIC', 3);
+/**
+ * @see Math_BigInteger::__clone()
+ */
+define('MATH_BIGINTEGER_NONE', 4);
+/**#@-*/
+
+/**#@+
+ * Array constants
+ *
+ * Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
+ *
+ * @access private
+ */
+/**
+ * $result[MATH_BIGINTEGER_VALUE] contains the value.
+ */
+define('MATH_BIGINTEGER_VALUE', 0);
+/**
+ * $result[MATH_BIGINTEGER_SIGN] contains the sign.
+ */
+define('MATH_BIGINTEGER_SIGN', 1);
+/**#@-*/
+
+/**#@+
+ * @access private
+ * @see Math_BigInteger::_montgomery()
+ * @see Math_BigInteger::_barrett()
+ */
+/**
+ * Cache constants
+ *
+ * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid.
+ */
+define('MATH_BIGINTEGER_VARIABLE', 0);
+/**
+ * $cache[MATH_BIGINTEGER_DATA] contains the cached data.
+ */
+define('MATH_BIGINTEGER_DATA', 1);
+/**#@-*/
+
+/**#@+
+ * Mode constants.
+ *
+ * @access private
+ * @see Math_BigInteger::Math_BigInteger()
+ */
+/**
+ * To use the pure-PHP implementation
+ */
+define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
+/**
+ * To use the BCMath library
+ *
+ * (if enabled; otherwise, the internal implementation will be used)
+ */
+define('MATH_BIGINTEGER_MODE_BCMATH', 2);
+/**
+ * To use the GMP library
+ *
+ * (if present; otherwise, either the BCMath or the internal implementation will be used)
+ */
+define('MATH_BIGINTEGER_MODE_GMP', 3);
+/**#@-*/
+
+/**
+ * The largest digit that may be used in addition / subtraction
+ *
+ * (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations
+ * will truncate 4503599627370496)
+ *
+ * @access private
+ */
+define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52));
+
+/**
+ * Karatsuba Cutoff
+ *
+ * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
+ *
+ * @access private
+ */
+define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
+
+/**
+ * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
+ * numbers.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 1.0.0RC4
+ * @access public
+ * @package Math_BigInteger
+ */
+class Math_BigInteger {
+ /**
+ * Holds the BigInteger's value.
+ *
+ * @var Array
+ * @access private
+ */
+ var $value;
+
+ /**
+ * Holds the BigInteger's magnitude.
+ *
+ * @var Boolean
+ * @access private
+ */
+ var $is_negative = false;
+
+ /**
+ * Random number generator function
+ *
+ * @see setRandomGenerator()
+ * @access private
+ */
+ var $generator = 'mt_rand';
+
+ /**
+ * Precision
+ *
+ * @see setPrecision()
+ * @access private
+ */
+ var $precision = -1;
+
+ /**
+ * Precision Bitmask
+ *
+ * @see setPrecision()
+ * @access private
+ */
+ var $bitmask = false;
+
+ /**
+ * Mode independant value used for serialization.
+ *
+ * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
+ * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
+ * however, $this->hex is only calculated when $this->__sleep() is called.
+ *
+ * @see __sleep()
+ * @see __wakeup()
+ * @var String
+ * @access private
+ */
+ var $hex;
+
+ /**
+ * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers.
+ *
+ * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
+ * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('0x32', 16); // 50 in base-16
+ *
+ * echo $a->toString(); // outputs 50
+ * ?>
+ * </code>
+ *
+ * @param optional $x base-10 number or base-$base number if $base set.
+ * @param optional integer $base
+ * @return Math_BigInteger
+ * @access public
+ */
+ function Math_BigInteger($x = 0, $base = 10)
+ {
+ if ( !defined('MATH_BIGINTEGER_MODE') ) {
+ switch (true) {
+ case extension_loaded('gmp'):
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
+ break;
+ case extension_loaded('bcmath'):
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
+ break;
+ default:
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
+ }
+ }
+
+ if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
+ define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
+ }
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ if (is_resource($x) && get_resource_type($x) == 'GMP integer') {
+ $this->value = $x;
+ return;
+ }
+ $this->value = gmp_init(0);
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $this->value = '0';
+ break;
+ default:
+ $this->value = array();
+ }
+
+ // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
+ // '0' is the only value like this per http://php.net/empty
+ if (empty($x) && (abs($base) != 256 || $x !== '0')) {
+ return;
+ }
+
+ switch ($base) {
+ case -256:
+ if (ord($x[0]) & 0x80) {
+ $x = ~$x;
+ $this->is_negative = true;
+ }
+ case 256:
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $sign = $this->is_negative ? '-' : '';
+ $this->value = gmp_init($sign . '0x' . bin2hex($x));
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ // round $len to the nearest 4 (thanks, DavidMJ!)
+ $len = (strlen($x) + 3) & 0xFFFFFFFC;
+
+ $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
+
+ for ($i = 0; $i < $len; $i+= 4) {
+ $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
+ $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
+ }
+
+ if ($this->is_negative) {
+ $this->value = '-' . $this->value;
+ }
+
+ break;
+ // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
+ default:
+ while (strlen($x)) {
+ $this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26));
+ }
+ }
+
+ if ($this->is_negative) {
+ if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
+ $this->is_negative = false;
+ }
+ $temp = $this->add(new Math_BigInteger('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case 16:
+ case -16:
+ if ($base > 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
+
+ $is_negative = false;
+ if ($base < 0 && hexdec($x[0]) >= 8) {
+ $this->is_negative = $is_negative = true;
+ $x = bin2hex(~pack('H*', $x));
+ }
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
+ $this->value = gmp_init($temp);
+ $this->is_negative = false;
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
+ $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
+ $this->is_negative = false;
+ break;
+ default:
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
+ $this->value = $temp->value;
+ }
+
+ if ($is_negative) {
+ $temp = $this->add(new Math_BigInteger('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case 10:
+ case -10:
+ $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x);
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $this->value = gmp_init($x);
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
+ // results then doing it on '-1' does (modInverse does $x[0])
+ $this->value = (string) $x;
+ break;
+ default:
+ $temp = new Math_BigInteger();
+
+ // array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it.
+ $multiplier = new Math_BigInteger();
+ $multiplier->value = array(10000000);
+
+ if ($x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT);
+
+ while (strlen($x)) {
+ $temp = $temp->multiply($multiplier);
+ $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256));
+ $x = substr($x, 7);
+ }
+
+ $this->value = $temp->value;
+ }
+ break;
+ case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
+ case -2:
+ if ($base > 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^([01]*).*#', '$1', $x);
+ $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
+
+ $str = '0x';
+ while (strlen($x)) {
+ $part = substr($x, 0, 4);
+ $str.= dechex(bindec($part));
+ $x = substr($x, 4);
+ }
+
+ if ($this->is_negative) {
+ $str = '-' . $str;
+ }
+
+ $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
+ $this->value = $temp->value;
+ $this->is_negative = $temp->is_negative;
+
+ break;
+ default:
+ // base not supported, so we'll let $this == 0
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('65');
+ *
+ * echo $a->toBytes(); // outputs chr(65)
+ * ?>
+ * </code>
+ *
+ * @param Boolean $twos_compliment
+ * @return String
+ * @access public
+ * @internal Converts a base-2**26 number to base-2**8
+ */
+ function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ $comparison = $this->compare(new Math_BigInteger());
+ if ($comparison == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
+ $bytes = $temp->toBytes();
+
+ if (empty($bytes)) { // eg. if the number we're trying to convert is -1
+ $bytes = chr(0);
+ }
+
+ if (ord($bytes[0]) & 0x80) {
+ $bytes = chr(0) . $bytes;
+ }
+
+ return $comparison < 0 ? ~$bytes : $bytes;
+ }
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ if (gmp_cmp($this->value, gmp_init(0)) == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = gmp_strval(gmp_abs($this->value), 16);
+ $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp;
+ $temp = pack('H*', $temp);
+
+ return $this->precision > 0 ?
+ substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($temp, chr(0));
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ if ($this->value === '0') {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $value = '';
+ $current = $this->value;
+
+ if ($current[0] == '-') {
+ $current = substr($current, 1);
+ }
+
+ while (bccomp($current, '0', 0) > 0) {
+ $temp = bcmod($current, '16777216');
+ $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
+ $current = bcdiv($current, '16777216', 0);
+ }
+
+ return $this->precision > 0 ?
+ substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($value, chr(0));
+ }
+
+ if (!count($this->value)) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+ $result = $this->_int2bytes($this->value[count($this->value) - 1]);
+
+ $temp = $this->copy();
+
+ for ($i = count($temp->value) - 2; $i >= 0; --$i) {
+ $temp->_base256_lshift($result, 26);
+ $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
+ }
+
+ return $this->precision > 0 ?
+ str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
+ $result;
+ }
+
+ /**
+ * Converts a BigInteger to a hex string (eg. base-16)).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('65');
+ *
+ * echo $a->toHex(); // outputs '41'
+ * ?>
+ * </code>
+ *
+ * @param Boolean $twos_compliment
+ * @return String
+ * @access public
+ * @internal Converts a base-2**26 number to base-2**8
+ */
+ function toHex($twos_compliment = false)
+ {
+ return bin2hex($this->toBytes($twos_compliment));
+ }
+
+ /**
+ * Converts a BigInteger to a bit string (eg. base-2).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('65');
+ *
+ * echo $a->toBits(); // outputs '1000001'
+ * ?>
+ * </code>
+ *
+ * @param Boolean $twos_compliment
+ * @return String
+ * @access public
+ * @internal Converts a base-2**26 number to base-2**2
+ */
+ function toBits($twos_compliment = false)
+ {
+ $hex = $this->toHex($twos_compliment);
+ $bits = '';
+ for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
+ $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
+ }
+ if ($start) { // hexdec('') == 0
+ $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
+ }
+ $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
+
+ if ($twos_compliment && $this->compare(new Math_BigInteger()) > 0 && $this->precision <= 0) {
+ return '0' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('50');
+ *
+ * echo $a->toString(); // outputs 50
+ * ?>
+ * </code>
+ *
+ * @return String
+ * @access public
+ * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
+ */
+ function toString()
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ return gmp_strval($this->value);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ if ($this->value === '0') {
+ return '0';
+ }
+
+ return ltrim($this->value, '0');
+ }
+
+ if (!count($this->value)) {
+ return '0';
+ }
+
+ $temp = $this->copy();
+ $temp->is_negative = false;
+
+ $divisor = new Math_BigInteger();
+ $divisor->value = array(10000000); // eg. 10**7
+ $result = '';
+ while (count($temp->value)) {
+ list($temp, $mod) = $temp->divide($divisor);
+ $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result;
+ }
+ $result = ltrim($result, '0');
+ if (empty($result)) {
+ $result = '0';
+ }
+
+ if ($this->is_negative) {
+ $result = '-' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Copy an object
+ *
+ * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
+ * that all objects are passed by value, when appropriate. More information can be found here:
+ *
+ * {@link http://php.net/language.oop5.basic#51624}
+ *
+ * @access public
+ * @see __clone()
+ * @return Math_BigInteger
+ */
+ function copy()
+ {
+ $temp = new Math_BigInteger();
+ $temp->value = $this->value;
+ $temp->is_negative = $this->is_negative;
+ $temp->generator = $this->generator;
+ $temp->precision = $this->precision;
+ $temp->bitmask = $this->bitmask;
+ return $temp;
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
+ * toString().
+ *
+ * @access public
+ * @internal Implemented per a suggestion by Techie-Michael - thanks!
+ */
+ function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * __clone() magic method
+ *
+ * Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone()
+ * directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
+ * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5,
+ * call Math_BigInteger::copy(), instead.
+ *
+ * @access public
+ * @see copy()
+ * @return Math_BigInteger
+ */
+ function __clone()
+ {
+ return $this->copy();
+ }
+
+ /**
+ * __sleep() magic method
+ *
+ * Will be called, automatically, when serialize() is called on a Math_BigInteger object.
+ *
+ * @see __wakeup()
+ * @access public
+ */
+ function __sleep()
+ {
+ $this->hex = $this->toHex(true);
+ $vars = array('hex');
+ if ($this->generator != 'mt_rand') {
+ $vars[] = 'generator';
+ }
+ if ($this->precision > 0) {
+ $vars[] = 'precision';
+ }
+ return $vars;
+
+ }
+
+ /**
+ * __wakeup() magic method
+ *
+ * Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
+ *
+ * @see __sleep()
+ * @access public
+ */
+ function __wakeup()
+ {
+ $temp = new Math_BigInteger($this->hex, -16);
+ $this->value = $temp->value;
+ $this->is_negative = $temp->is_negative;
+ $this->setRandomGenerator($this->generator);
+ if ($this->precision > 0) {
+ // recalculate $this->bitmask
+ $this->setPrecision($this->precision);
+ }
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('10');
+ * $b = new Math_BigInteger('20');
+ *
+ * $c = $a->add($b);
+ *
+ * echo $c->toString(); // outputs 30
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $y
+ * @return Math_BigInteger
+ * @access public
+ * @internal Performs base-2**52 addition
+ */
+ function add($y)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_add($this->value, $y->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp = new Math_BigInteger();
+ $temp->value = bcadd($this->value, $y->value, 0);
+
+ return $this->_normalize($temp);
+ }
+
+ $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ $result = new Math_BigInteger();
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Performs addition.
+ *
+ * @param Array $x_value
+ * @param Boolean $x_negative
+ * @param Array $y_value
+ * @param Boolean $y_negative
+ * @return Array
+ * @access private
+ */
+ function _add($x_value, $x_negative, $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return array(
+ MATH_BIGINTEGER_VALUE => $y_value,
+ MATH_BIGINTEGER_SIGN => $y_negative
+ );
+ } else if ($y_size == 0) {
+ return array(
+ MATH_BIGINTEGER_VALUE => $x_value,
+ MATH_BIGINTEGER_SIGN => $x_negative
+ );
+ }
+
+ // subtract, if appropriate
+ if ( $x_negative != $y_negative ) {
+ if ( $x_value == $y_value ) {
+ return array(
+ MATH_BIGINTEGER_VALUE => array(),
+ MATH_BIGINTEGER_SIGN => false
+ );
+ }
+
+ $temp = $this->_subtract($x_value, false, $y_value, false);
+ $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
+ $x_negative : $y_negative;
+
+ return $temp;
+ }
+
+ if ($x_size < $y_size) {
+ $size = $x_size;
+ $value = $y_value;
+ } else {
+ $size = $y_size;
+ $value = $x_value;
+ }
+
+ $value[] = 0; // just in case the carry adds an extra digit
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry;
+ $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
+
+ $temp = (int) ($sum / 0x4000000);
+
+ $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
+ $value[$j] = $temp;
+ }
+
+ if ($j == $size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] + $y_value[$i] + $carry;
+ $carry = $sum >= 0x4000000;
+ $value[$i] = $carry ? $sum - 0x4000000 : $sum;
+ ++$i; // ie. let $i = $j since we've just done $value[$i]
+ }
+
+ if ($carry) {
+ for (; $value[$i] == 0x3FFFFFF; ++$i) {
+ $value[$i] = 0;
+ }
+ ++$value[$i];
+ }
+
+ return array(
+ MATH_BIGINTEGER_VALUE => $this->_trim($value),
+ MATH_BIGINTEGER_SIGN => $x_negative
+ );
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('10');
+ * $b = new Math_BigInteger('20');
+ *
+ * $c = $a->subtract($b);
+ *
+ * echo $c->toString(); // outputs -10
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $y
+ * @return Math_BigInteger
+ * @access public
+ * @internal Performs base-2**52 subtraction
+ */
+ function subtract($y)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_sub($this->value, $y->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp = new Math_BigInteger();
+ $temp->value = bcsub($this->value, $y->value, 0);
+
+ return $this->_normalize($temp);
+ }
+
+ $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ $result = new Math_BigInteger();
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Performs subtraction.
+ *
+ * @param Array $x_value
+ * @param Boolean $x_negative
+ * @param Array $y_value
+ * @param Boolean $y_negative
+ * @return Array
+ * @access private
+ */
+ function _subtract($x_value, $x_negative, $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return array(
+ MATH_BIGINTEGER_VALUE => $y_value,
+ MATH_BIGINTEGER_SIGN => !$y_negative
+ );
+ } else if ($y_size == 0) {
+ return array(
+ MATH_BIGINTEGER_VALUE => $x_value,
+ MATH_BIGINTEGER_SIGN => $x_negative
+ );
+ }
+
+ // add, if appropriate (ie. -$x - +$y or +$x - -$y)
+ if ( $x_negative != $y_negative ) {
+ $temp = $this->_add($x_value, false, $y_value, false);
+ $temp[MATH_BIGINTEGER_SIGN] = $x_negative;
+
+ return $temp;
+ }
+
+ $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
+
+ if ( !$diff ) {
+ return array(
+ MATH_BIGINTEGER_VALUE => array(),
+ MATH_BIGINTEGER_SIGN => false
+ );
+ }
+
+ // switch $x and $y around, if appropriate.
+ if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_negative = !$x_negative;
+
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+ }
+
+ // at this point, $x_value should be at least as big as - if not bigger than - $y_value
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry;
+ $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
+
+ $temp = (int) ($sum / 0x4000000);
+
+ $x_value[$i] = (int) ($sum - 0x4000000 * $temp);
+ $x_value[$j] = $temp;
+ }
+
+ if ($j == $y_size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] - $y_value[$i] - $carry;
+ $carry = $sum < 0;
+ $x_value[$i] = $carry ? $sum + 0x4000000 : $sum;
+ ++$i;
+ }
+
+ if ($carry) {
+ for (; !$x_value[$i]; ++$i) {
+ $x_value[$i] = 0x3FFFFFF;
+ }
+ --$x_value[$i];
+ }
+
+ return array(
+ MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
+ MATH_BIGINTEGER_SIGN => $x_negative
+ );
+ }
+
+ /**
+ * Multiplies two BigIntegers
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('10');
+ * $b = new Math_BigInteger('20');
+ *
+ * $c = $a->multiply($b);
+ *
+ * echo $c->toString(); // outputs 200
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $x
+ * @return Math_BigInteger
+ * @access public
+ */
+ function multiply($x)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_mul($this->value, $x->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp = new Math_BigInteger();
+ $temp->value = bcmul($this->value, $x->value, 0);
+
+ return $this->_normalize($temp);
+ }
+
+ $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
+
+ $product = new Math_BigInteger();
+ $product->value = $temp[MATH_BIGINTEGER_VALUE];
+ $product->is_negative = $temp[MATH_BIGINTEGER_SIGN];
+
+ return $this->_normalize($product);
+ }
+
+ /**
+ * Performs multiplication.
+ *
+ * @param Array $x_value
+ * @param Boolean $x_negative
+ * @param Array $y_value
+ * @param Boolean $y_negative
+ * @return Array
+ * @access private
+ */
+ function _multiply($x_value, $x_negative, $y_value, $y_negative)
+ {
+ //if ( $x_value == $y_value ) {
+ // return array(
+ // MATH_BIGINTEGER_VALUE => $this->_square($x_value),
+ // MATH_BIGINTEGER_SIGN => $x_sign != $y_value
+ // );
+ //}
+
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
+ return array(
+ MATH_BIGINTEGER_VALUE => array(),
+ MATH_BIGINTEGER_SIGN => false
+ );
+ }
+
+ return array(
+ MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
+ $this->_trim($this->_regularMultiply($x_value, $y_value)) :
+ $this->_trim($this->_karatsuba($x_value, $y_value)),
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
+ );
+ }
+
+ /**
+ * Performs long multiplication on two BigIntegers
+ *
+ * Modeled after 'multiply' in MutableBigInteger.java.
+ *
+ * @param Array $x_value
+ * @param Array $y_value
+ * @return Array
+ * @access private
+ */
+ function _regularMultiply($x_value, $y_value)
+ {
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
+ return array();
+ }
+
+ if ( $x_length < $y_length ) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+ }
+
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
+
+ // the following for loop could be removed if the for loop following it
+ // (the one with nested for loops) initially set $i to 0, but
+ // doing so would also make the result in one set of unnecessary adds,
+ // since on the outermost loops first pass, $product->value[$k] is going
+ // to always be 0
+
+ $carry = 0;
+
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
+ $carry = (int) ($temp / 0x4000000);
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
+ }
+
+ $product_value[$j] = $carry;
+
+ // the above for loop is what the previous comment was talking about. the
+ // following for loop is the "one with nested for loops"
+ for ($i = 1; $i < $y_length; ++$i) {
+ $carry = 0;
+
+ for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
+ $carry = (int) ($temp / 0x4000000);
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
+ }
+
+ $product_value[$k] = $carry;
+ }
+
+ return $product_value;
+ }
+
+ /**
+ * Performs Karatsuba multiplication on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
+ *
+ * @param Array $x_value
+ * @param Array $y_value
+ * @return Array
+ * @access private
+ */
+ function _karatsuba($x_value, $y_value)
+ {
+ $m = min(count($x_value) >> 1, count($y_value) >> 1);
+
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
+ return $this->_regularMultiply($x_value, $y_value);
+ }
+
+ $x1 = array_slice($x_value, $m);
+ $x0 = array_slice($x_value, 0, $m);
+ $y1 = array_slice($y_value, $m);
+ $y0 = array_slice($y_value, 0, $m);
+
+ $z2 = $this->_karatsuba($x1, $y1);
+ $z0 = $this->_karatsuba($x0, $y0);
+
+ $z1 = $this->_add($x1, false, $x0, false);
+ $temp = $this->_add($y1, false, $y0, false);
+ $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
+ $temp = $this->_add($z2, false, $z0, false);
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
+
+ $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
+ $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);
+
+ return $xy[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * Performs squaring
+ *
+ * @param Array $x
+ * @return Array
+ * @access private
+ */
+ function _square($x = false)
+ {
+ return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
+ $this->_trim($this->_baseSquare($x)) :
+ $this->_trim($this->_karatsubaSquare($x));
+ }
+
+ /**
+ * Performs traditional squaring on two BigIntegers
+ *
+ * Squaring can be done faster than multiplying a number by itself can be. See
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
+ *
+ * @param Array $value
+ * @return Array
+ * @access private
+ */
+ function _baseSquare($value)
+ {
+ if ( empty($value) ) {
+ return array();
+ }
+ $square_value = $this->_array_repeat(0, 2 * count($value));
+
+ for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
+ $i2 = $i << 1;
+
+ $temp = $square_value[$i2] + $value[$i] * $value[$i];
+ $carry = (int) ($temp / 0x4000000);
+ $square_value[$i2] = (int) ($temp - 0x4000000 * $carry);
+
+ // note how we start from $i+1 instead of 0 as we do in multiplication.
+ for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
+ $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
+ $carry = (int) ($temp / 0x4000000);
+ $square_value[$k] = (int) ($temp - 0x4000000 * $carry);
+ }
+
+ // the following line can yield values larger 2**15. at this point, PHP should switch
+ // over to floats.
+ $square_value[$i + $max_index + 1] = $carry;
+ }
+
+ return $square_value;
+ }
+
+ /**
+ * Performs Karatsuba "squaring" on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
+ *
+ * @param Array $value
+ * @return Array
+ * @access private
+ */
+ function _karatsubaSquare($value)
+ {
+ $m = count($value) >> 1;
+
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
+ return $this->_baseSquare($value);
+ }
+
+ $x1 = array_slice($value, $m);
+ $x0 = array_slice($value, 0, $m);
+
+ $z2 = $this->_karatsubaSquare($x1);
+ $z0 = $this->_karatsubaSquare($x0);
+
+ $z1 = $this->_add($x1, false, $x0, false);
+ $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
+ $temp = $this->_add($z2, false, $z0, false);
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
+
+ $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
+ $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);
+
+ return $xx[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('10');
+ * $b = new Math_BigInteger('20');
+ *
+ * list($quotient, $remainder) = $a->divide($b);
+ *
+ * echo $quotient->toString(); // outputs 0
+ * echo "\r\n";
+ * echo $remainder->toString(); // outputs 10
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $y
+ * @return Array
+ * @access public
+ * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
+ */
+ function divide($y)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $quotient = new Math_BigInteger();
+ $remainder = new Math_BigInteger();
+
+ list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
+
+ if (gmp_sign($remainder->value) < 0) {
+ $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
+ }
+
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $quotient = new Math_BigInteger();
+ $remainder = new Math_BigInteger();
+
+ $quotient->value = bcdiv($this->value, $y->value, 0);
+ $remainder->value = bcmod($this->value, $y->value);
+
+ if ($remainder->value[0] == '-') {
+ $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
+ }
+
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
+ }
+
+ if (count($y->value) == 1) {
+ list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
+ $quotient = new Math_BigInteger();
+ $remainder = new Math_BigInteger();
+ $quotient->value = $q;
+ $remainder->value = array($r);
+ $quotient->is_negative = $this->is_negative != $y->is_negative;
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
+ }
+
+ static $zero;
+ if ( !isset($zero) ) {
+ $zero = new Math_BigInteger();
+ }
+
+ $x = $this->copy();
+ $y = $y->copy();
+
+ $x_sign = $x->is_negative;
+ $y_sign = $y->is_negative;
+
+ $x->is_negative = $y->is_negative = false;
+
+ $diff = $x->compare($y);
+
+ if ( !$diff ) {
+ $temp = new Math_BigInteger();
+ $temp->value = array(1);
+ $temp->is_negative = $x_sign != $y_sign;
+ return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
+ }
+
+ if ( $diff < 0 ) {
+ // if $x is negative, "add" $y.
+ if ( $x_sign ) {
+ $x = $y->subtract($x);
+ }
+ return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
+ }
+
+ // normalize $x and $y as described in HAC 14.23 / 14.24
+ $msb = $y->value[count($y->value) - 1];
+ for ($shift = 0; !($msb & 0x2000000); ++$shift) {
+ $msb <<= 1;
+ }
+ $x->_lshift($shift);
+ $y->_lshift($shift);
+ $y_value = &$y->value;
+
+ $x_max = count($x->value) - 1;
+ $y_max = count($y->value) - 1;
+
+ $quotient = new Math_BigInteger();
+ $quotient_value = &$quotient->value;
+ $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
+
+ static $temp, $lhs, $rhs;
+ if (!isset($temp)) {
+ $temp = new Math_BigInteger();
+ $lhs = new Math_BigInteger();
+ $rhs = new Math_BigInteger();
+ }
+ $temp_value = &$temp->value;
+ $rhs_value = &$rhs->value;
+
+ // $temp = $y << ($x_max - $y_max-1) in base 2**26
+ $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
+
+ while ( $x->compare($temp) >= 0 ) {
+ // calculate the "common residue"
+ ++$quotient_value[$x_max - $y_max];
+ $x = $x->subtract($temp);
+ $x_max = count($x->value) - 1;
+ }
+
+ for ($i = $x_max; $i >= $y_max + 1; --$i) {
+ $x_value = &$x->value;
+ $x_window = array(
+ isset($x_value[$i]) ? $x_value[$i] : 0,
+ isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
+ isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
+ );
+ $y_window = array(
+ $y_value[$y_max],
+ ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0
+ );
+
+ $q_index = $i - $y_max - 1;
+ if ($x_window[0] == $y_window[0]) {
+ $quotient_value[$q_index] = 0x3FFFFFF;
+ } else {
+ $quotient_value[$q_index] = (int) (
+ ($x_window[0] * 0x4000000 + $x_window[1])
+ /
+ $y_window[0]
+ );
+ }
+
+ $temp_value = array($y_window[1], $y_window[0]);
+
+ $lhs->value = array($quotient_value[$q_index]);
+ $lhs = $lhs->multiply($temp);
+
+ $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
+
+ while ( $lhs->compare($rhs) > 0 ) {
+ --$quotient_value[$q_index];
+
+ $lhs->value = array($quotient_value[$q_index]);
+ $lhs = $lhs->multiply($temp);
+ }
+
+ $adjust = $this->_array_repeat(0, $q_index);
+ $temp_value = array($quotient_value[$q_index]);
+ $temp = $temp->multiply($y);
+ $temp_value = &$temp->value;
+ $temp_value = array_merge($adjust, $temp_value);
+
+ $x = $x->subtract($temp);
+
+ if ($x->compare($zero) < 0) {
+ $temp_value = array_merge($adjust, $y_value);
+ $x = $x->add($temp);
+
+ --$quotient_value[$q_index];
+ }
+
+ $x_max = count($x_value) - 1;
+ }
+
+ // unnormalize the remainder
+ $x->_rshift($shift);
+
+ $quotient->is_negative = $x_sign != $y_sign;
+
+ // calculate the "common residue", if appropriate
+ if ( $x_sign ) {
+ $y->_rshift($shift);
+ $x = $y->subtract($x);
+ }
+
+ return array($this->_normalize($quotient), $this->_normalize($x));
+ }
+
+ /**
+ * Divides a BigInteger by a regular integer
+ *
+ * abc / x = a00 / x + b0 / x + c / x
+ *
+ * @param Array $dividend
+ * @param Array $divisor
+ * @return Array
+ * @access private
+ */
+ function _divide_digit($dividend, $divisor)
+ {
+ $carry = 0;
+ $result = array();
+
+ for ($i = count($dividend) - 1; $i >= 0; --$i) {
+ $temp = 0x4000000 * $carry + $dividend[$i];
+ $result[$i] = (int) ($temp / $divisor);
+ $carry = (int) ($temp - $divisor * $result[$i]);
+ }
+
+ return array($result, $carry);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger('10');
+ * $b = new Math_BigInteger('20');
+ * $c = new Math_BigInteger('30');
+ *
+ * $c = $a->modPow($b, $c);
+ *
+ * echo $c->toString(); // outputs 10
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $e
+ * @param Math_BigInteger $n
+ * @return Math_BigInteger
+ * @access public
+ * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
+ * and although the approach involving repeated squaring does vastly better, it, too, is impractical
+ * for our purposes. The reason being that division - by far the most complicated and time-consuming
+ * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
+ *
+ * Modular reductions resolve this issue. Although an individual modular reduction takes more time
+ * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
+ *
+ * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
+ * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
+ * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
+ * the product of two odd numbers is odd), but what about when RSA isn't used?
+ *
+ * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
+ * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
+ * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
+ * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
+ * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
+ * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
+ */
+ function modPow($e, $n)
+ {
+ $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
+
+ if ($e->compare(new Math_BigInteger()) < 0) {
+ $e = $e->abs();
+
+ $temp = $this->modInverse($n);
+ if ($temp === false) {
+ return false;
+ }
+
+ return $this->_normalize($temp->modPow($e, $n));
+ }
+
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP ) {
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_powm($this->value, $e->value, $n->value);
+
+ return $this->_normalize($temp);
+ }
+
+ if ($this->compare(new Math_BigInteger()) < 0 || $this->compare($n) > 0) {
+ list(, $temp) = $this->divide($n);
+ return $temp->modPow($e, $n);
+ }
+
+ if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
+ $components = array(
+ 'modulus' => $n->toBytes(true),
+ 'publicExponent' => $e->toBytes(true)
+ );
+
+ $components = array(
+ 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
+ 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
+ );
+
+ $RSAPublicKey = pack('Ca*a*a*',
+ 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
+ $components['modulus'], $components['publicExponent']
+ );
+
+ $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
+ $RSAPublicKey = chr(0) . $RSAPublicKey;
+ $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
+
+ $encapsulated = pack('Ca*a*',
+ 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
+ );
+
+ $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(base64_encode($encapsulated)) .
+ '-----END PUBLIC KEY-----';
+
+ $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
+
+ if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
+ return new Math_BigInteger($result, 256);
+ }
+ }
+
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
+ $temp = new Math_BigInteger();
+ $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
+
+ return $this->_normalize($temp);
+ }
+
+ if ( empty($e->value) ) {
+ $temp = new Math_BigInteger();
+ $temp->value = array(1);
+ return $this->_normalize($temp);
+ }
+
+ if ( $e->value == array(1) ) {
+ list(, $temp) = $this->divide($n);
+ return $this->_normalize($temp);
+ }
+
+ if ( $e->value == array(2) ) {
+ $temp = new Math_BigInteger();
+ $temp->value = $this->_square($this->value);
+ list(, $temp) = $temp->divide($n);
+ return $this->_normalize($temp);
+ }
+
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
+
+ // is the modulo odd?
+ if ( $n->value[0] & 1 ) {
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
+ }
+ // if it's not, it's even
+
+ // find the lowest set bit (eg. the max pow of 2 that divides $n)
+ for ($i = 0; $i < count($n->value); ++$i) {
+ if ( $n->value[$i] ) {
+ $temp = decbin($n->value[$i]);
+ $j = strlen($temp) - strrpos($temp, '1') - 1;
+ $j+= 26 * $i;
+ break;
+ }
+ }
+ // at this point, 2^$j * $n/(2^$j) == $n
+
+ $mod1 = $n->copy();
+ $mod1->_rshift($j);
+ $mod2 = new Math_BigInteger();
+ $mod2->value = array(1);
+ $mod2->_lshift($j);
+
+ $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
+ $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
+
+ $y1 = $mod2->modInverse($mod1);
+ $y2 = $mod1->modInverse($mod2);
+
+ $result = $part1->multiply($mod2);
+ $result = $result->multiply($y1);
+
+ $temp = $part2->multiply($mod1);
+ $temp = $temp->multiply($y2);
+
+ $result = $result->add($temp);
+ list(, $result) = $result->divide($n);
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for Math_BigInteger::modPow()
+ *
+ * @param Math_BigInteger $e
+ * @param Math_BigInteger $n
+ * @return Math_BigInteger
+ * @access public
+ */
+ function powMod($e, $n)
+ {
+ return $this->modPow($e, $n);
+ }
+
+ /**
+ * Sliding Window k-ary Modular Exponentiation
+ *
+ * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
+ * however, this function performs a modular reduction after every multiplication and squaring operation.
+ * As such, this function has the same preconditions that the reductions being used do.
+ *
+ * @param Math_BigInteger $e
+ * @param Math_BigInteger $n
+ * @param Integer $mode
+ * @return Math_BigInteger
+ * @access private
+ */
+ function _slidingWindow($e, $n, $mode)
+ {
+ static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
+ //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
+
+ $e_value = $e->value;
+ $e_length = count($e_value) - 1;
+ $e_bits = decbin($e_value[$e_length]);
+ for ($i = $e_length - 1; $i >= 0; --$i) {
+ $e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT);
+ }
+
+ $e_length = strlen($e_bits);
+
+ // calculate the appropriate window size.
+ // $window_size == 3 if $window_ranges is between 25 and 81, for example.
+ for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i);
+
+ $n_value = $n->value;
+
+ // precompute $this^0 through $this^$window_size
+ $powers = array();
+ $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
+ $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
+
+ // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
+ // in a 1. ie. it's supposed to be odd.
+ $temp = 1 << ($window_size - 1);
+ for ($i = 1; $i < $temp; ++$i) {
+ $i2 = $i << 1;
+ $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
+ }
+
+ $result = array(1);
+ $result = $this->_prepareReduce($result, $n_value, $mode);
+
+ for ($i = 0; $i < $e_length; ) {
+ if ( !$e_bits[$i] ) {
+ $result = $this->_squareReduce($result, $n_value, $mode);
+ ++$i;
+ } else {
+ for ($j = $window_size - 1; $j > 0; --$j) {
+ if ( !empty($e_bits[$i + $j]) ) {
+ break;
+ }
+ }
+
+ for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1)
+ $result = $this->_squareReduce($result, $n_value, $mode);
+ }
+
+ $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
+
+ $i+=$j + 1;
+ }
+ }
+
+ $temp = new Math_BigInteger();
+ $temp->value = $this->_reduce($result, $n_value, $mode);
+
+ return $temp;
+ }
+
+ /**
+ * Modular reduction
+ *
+ * For most $modes this will return the remainder.
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @param Integer $mode
+ * @return Array
+ */
+ function _reduce($x, $n, $mode)
+ {
+ switch ($mode) {
+ case MATH_BIGINTEGER_MONTGOMERY:
+ return $this->_montgomery($x, $n);
+ case MATH_BIGINTEGER_BARRETT:
+ return $this->_barrett($x, $n);
+ case MATH_BIGINTEGER_POWEROF2:
+ $lhs = new Math_BigInteger();
+ $lhs->value = $x;
+ $rhs = new Math_BigInteger();
+ $rhs->value = $n;
+ return $x->_mod2($n);
+ case MATH_BIGINTEGER_CLASSIC:
+ $lhs = new Math_BigInteger();
+ $lhs->value = $x;
+ $rhs = new Math_BigInteger();
+ $rhs->value = $n;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ case MATH_BIGINTEGER_NONE:
+ return $x;
+ default:
+ // an invalid $mode was provided
+ }
+ }
+
+ /**
+ * Modular reduction preperation
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @param Integer $mode
+ * @return Array
+ */
+ function _prepareReduce($x, $n, $mode)
+ {
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
+ return $this->_prepMontgomery($x, $n);
+ }
+ return $this->_reduce($x, $n, $mode);
+ }
+
+ /**
+ * Modular multiply
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $y
+ * @param Array $n
+ * @param Integer $mode
+ * @return Array
+ */
+ function _multiplyReduce($x, $y, $n, $mode)
+ {
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
+ return $this->_montgomeryMultiply($x, $y, $n);
+ }
+ $temp = $this->_multiply($x, false, $y, false);
+ return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
+ }
+
+ /**
+ * Modular square
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @param Integer $mode
+ * @return Array
+ */
+ function _squareReduce($x, $n, $mode)
+ {
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
+ return $this->_montgomeryMultiply($x, $x, $n);
+ }
+ return $this->_reduce($this->_square($x), $n, $mode);
+ }
+
+ /**
+ * Modulos for Powers of Two
+ *
+ * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
+ * we'll just use this function as a wrapper for doing that.
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Math_BigInteger
+ * @return Math_BigInteger
+ */
+ function _mod2($n)
+ {
+ $temp = new Math_BigInteger();
+ $temp->value = array(1);
+ return $this->bitwise_and($n->subtract($temp));
+ }
+
+ /**
+ * Barrett Modular Reduction
+ *
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
+ * so as not to require negative numbers (initially, this script didn't support negative numbers).
+ *
+ * Employs "folding", as described at
+ * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
+ * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
+ *
+ * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
+ * usable on account of (1) its not using reasonable radix points as discussed in
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
+ * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
+ * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
+ * comments for details.
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $n
+ * @param Array $m
+ * @return Array
+ */
+ function _barrett($n, $m)
+ {
+ static $cache = array(
+ MATH_BIGINTEGER_VARIABLE => array(),
+ MATH_BIGINTEGER_DATA => array()
+ );
+
+ $m_length = count($m);
+
+ // if ($this->_compare($n, $this->_square($m)) >= 0) {
+ if (count($n) > 2 * $m_length) {
+ $lhs = new Math_BigInteger();
+ $rhs = new Math_BigInteger();
+ $lhs->value = $n;
+ $rhs->value = $m;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
+ if ($m_length < 5) {
+ return $this->_regularBarrett($n, $m);
+ }
+
+ // n = 2 * m.length
+
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
+
+ $lhs = new Math_BigInteger();
+ $lhs_value = &$lhs->value;
+ $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
+ $lhs_value[] = 1;
+ $rhs = new Math_BigInteger();
+ $rhs->value = $m;
+
+ list($u, $m1) = $lhs->divide($rhs);
+ $u = $u->value;
+ $m1 = $m1->value;
+
+ $cache[MATH_BIGINTEGER_DATA][] = array(
+ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
+ 'm1'=> $m1 // m.length
+ );
+ } else {
+ extract($cache[MATH_BIGINTEGER_DATA][$key]);
+ }
+
+ $cutoff = $m_length + ($m_length >> 1);
+ $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
+ $msd = array_slice($n, $cutoff); // m.length >> 1
+ $lsd = $this->_trim($lsd);
+ $temp = $this->_multiply($msd, false, $m1, false);
+ $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1
+
+ if ($m_length & 1) {
+ return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
+ }
+
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
+ $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
+ // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
+ // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
+ $temp = $this->_multiply($temp, false, $u, false);
+ // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
+ // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
+ // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
+ // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
+ $temp = $this->_multiply($temp, false, $m, false);
+
+ // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
+ // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
+ // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
+
+ $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
+
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
+ }
+
+ return $result[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * (Regular) Barrett Modular Reduction
+ *
+ * For numbers with more than four digits Math_BigInteger::_barrett() is faster. The difference between that and this
+ * is that this function does not fold the denominator into a smaller form.
+ *
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @return Array
+ */
+ function _regularBarrett($x, $n)
+ {
+ static $cache = array(
+ MATH_BIGINTEGER_VARIABLE => array(),
+ MATH_BIGINTEGER_DATA => array()
+ );
+
+ $n_length = count($n);
+
+ if (count($x) > 2 * $n_length) {
+ $lhs = new Math_BigInteger();
+ $rhs = new Math_BigInteger();
+ $lhs->value = $x;
+ $rhs->value = $n;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $n;
+ $lhs = new Math_BigInteger();
+ $lhs_value = &$lhs->value;
+ $lhs_value = $this->_array_repeat(0, 2 * $n_length);
+ $lhs_value[] = 1;
+ $rhs = new Math_BigInteger();
+ $rhs->value = $n;
+ list($temp, ) = $lhs->divide($rhs); // m.length
+ $cache[MATH_BIGINTEGER_DATA][] = $temp->value;
+ }
+
+ // 2 * m.length - (m.length - 1) = m.length + 1
+ $temp = array_slice($x, $n_length - 1);
+ // (m.length + 1) + m.length = 2 * m.length + 1
+ $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
+ // (2 * m.length + 1) - (m.length - 1) = m.length + 2
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);
+
+ // m.length + 1
+ $result = array_slice($x, 0, $n_length + 1);
+ // m.length + 1
+ $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
+ // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
+
+ if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
+ $corrector_value = $this->_array_repeat(0, $n_length + 1);
+ $corrector_value[] = 1;
+ $result = $this->_add($result, false, $corrector, false);
+ $result = $result[MATH_BIGINTEGER_VALUE];
+ }
+
+ // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
+ $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
+ }
+
+ return $result[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * Performs long multiplication up to $stop digits
+ *
+ * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
+ *
+ * @see _regularBarrett()
+ * @param Array $x_value
+ * @param Boolean $x_negative
+ * @param Array $y_value
+ * @param Boolean $y_negative
+ * @return Array
+ * @access private
+ */
+ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
+ {
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
+ return array(
+ MATH_BIGINTEGER_VALUE => array(),
+ MATH_BIGINTEGER_SIGN => false
+ );
+ }
+
+ if ( $x_length < $y_length ) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+ }
+
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
+
+ // the following for loop could be removed if the for loop following it
+ // (the one with nested for loops) initially set $i to 0, but
+ // doing so would also make the result in one set of unnecessary adds,
+ // since on the outermost loops first pass, $product->value[$k] is going
+ // to always be 0
+
+ $carry = 0;
+
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
+ $carry = (int) ($temp / 0x4000000);
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
+ }
+
+ if ($j < $stop) {
+ $product_value[$j] = $carry;
+ }
+
+ // the above for loop is what the previous comment was talking about. the
+ // following for loop is the "one with nested for loops"
+
+ for ($i = 1; $i < $y_length; ++$i) {
+ $carry = 0;
+
+ for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
+ $carry = (int) ($temp / 0x4000000);
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
+ }
+
+ if ($k < $stop) {
+ $product_value[$k] = $carry;
+ }
+ }
+
+ return array(
+ MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
+ );
+ }
+
+ /**
+ * Montgomery Modular Reduction
+ *
+ * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
+ * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
+ * to work correctly.
+ *
+ * @see _prepMontgomery()
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @return Array
+ */
+ function _montgomery($x, $n)
+ {
+ static $cache = array(
+ MATH_BIGINTEGER_VARIABLE => array(),
+ MATH_BIGINTEGER_DATA => array()
+ );
+
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $x;
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
+ }
+
+ $k = count($n);
+
+ $result = array(MATH_BIGINTEGER_VALUE => $x);
+
+ for ($i = 0; $i < $k; ++$i) {
+ $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
+ $temp = $this->_regularMultiply(array($temp), $n);
+ $temp = array_merge($this->_array_repeat(0, $i), $temp);
+ $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
+ }
+
+ $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);
+
+ if ($this->_compare($result, false, $n, false) >= 0) {
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
+ }
+
+ return $result[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * Montgomery Multiply
+ *
+ * Interleaves the montgomery reduction and long multiplication algorithms together as described in
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
+ *
+ * @see _prepMontgomery()
+ * @see _montgomery()
+ * @access private
+ * @param Array $x
+ * @param Array $y
+ * @param Array $m
+ * @return Array
+ */
+ function _montgomeryMultiply($x, $y, $m)
+ {
+ $temp = $this->_multiply($x, false, $y, false);
+ return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
+
+ static $cache = array(
+ MATH_BIGINTEGER_VARIABLE => array(),
+ MATH_BIGINTEGER_DATA => array()
+ );
+
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
+ }
+
+ $n = max(count($x), count($y), count($m));
+ $x = array_pad($x, $n, 0);
+ $y = array_pad($y, $n, 0);
+ $m = array_pad($m, $n, 0);
+ $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
+ for ($i = 0; $i < $n; ++$i) {
+ $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
+ $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
+ $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
+ $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
+ $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
+ }
+ if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
+ $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
+ }
+ return $a[MATH_BIGINTEGER_VALUE];
+ }
+
+ /**
+ * Prepare a number for use in Montgomery Modular Reductions
+ *
+ * @see _montgomery()
+ * @see _slidingWindow()
+ * @access private
+ * @param Array $x
+ * @param Array $n
+ * @return Array
+ */
+ function _prepMontgomery($x, $n)
+ {
+ $lhs = new Math_BigInteger();
+ $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
+ $rhs = new Math_BigInteger();
+ $rhs->value = $n;
+
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ /**
+ * Modular Inverse of a number mod 2**26 (eg. 67108864)
+ *
+ * Based off of the bnpInvDigit function implemented and justified in the following URL:
+ *
+ * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
+ *
+ * The following URL provides more info:
+ *
+ * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
+ *
+ * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
+ * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
+ * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
+ * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
+ * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
+ * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
+ * 40 bits, which only 64-bit floating points will support.
+ *
+ * Thanks to Pedro Gimeno Fortea for input!
+ *
+ * @see _montgomery()
+ * @access private
+ * @param Array $x
+ * @return Integer
+ */
+ function _modInverse67108864($x) // 2**26 == 67108864
+ {
+ $x = -$x[0];
+ $result = $x & 0x3; // x**-1 mod 2**2
+ $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
+ $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
+ $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
+ $result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26
+ return $result & 0x3FFFFFF;
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger(30);
+ * $b = new Math_BigInteger(17);
+ *
+ * $c = $a->modInverse($b);
+ * echo $c->toString(); // outputs 4
+ *
+ * echo "\r\n";
+ *
+ * $d = $a->multiply($c);
+ * list(, $d) = $d->divide($b);
+ * echo $d; // outputs 1 (as per the definition of modular inverse)
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $n
+ * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise.
+ * @access public
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
+ */
+ function modInverse($n)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_invert($this->value, $n->value);
+
+ return ( $temp->value === false ) ? false : $this->_normalize($temp);
+ }
+
+ static $zero, $one;
+ if (!isset($zero)) {
+ $zero = new Math_BigInteger();
+ $one = new Math_BigInteger(1);
+ }
+
+ // $x mod -$n == $x mod $n.
+ $n = $n->abs();
+
+ if ($this->compare($zero) < 0) {
+ $temp = $this->abs();
+ $temp = $temp->modInverse($n);
+ return $this->_normalize($n->subtract($temp));
+ }
+
+ extract($this->extendedGCD($n));
+
+ if (!$gcd->equals($one)) {
+ return false;
+ }
+
+ $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
+
+ return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bzout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bzout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependant upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bzout's identity - Wikipedia} for more information.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger(693);
+ * $b = new Math_BigInteger(609);
+ *
+ * extract($a->extendedGCD($b));
+ *
+ * echo $gcd->toString() . "\r\n"; // outputs 21
+ * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $n
+ * @return Math_BigInteger
+ * @access public
+ * @internal Calculates the GCD using the binary xGCD algorithim described in
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
+ * the more traditional algorithim requires "relatively costly multiple-precision divisions".
+ */
+ function extendedGCD($n)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ extract(gmp_gcdext($this->value, $n->value));
+
+ return array(
+ 'gcd' => $this->_normalize(new Math_BigInteger($g)),
+ 'x' => $this->_normalize(new Math_BigInteger($s)),
+ 'y' => $this->_normalize(new Math_BigInteger($t))
+ );
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
+ // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
+ // the basic extended euclidean algorithim is what we're using.
+
+ $u = $this->value;
+ $v = $n->value;
+
+ $a = '1';
+ $b = '0';
+ $c = '0';
+ $d = '1';
+
+ while (bccomp($v, '0', 0) != 0) {
+ $q = bcdiv($u, $v, 0);
+
+ $temp = $u;
+ $u = $v;
+ $v = bcsub($temp, bcmul($v, $q, 0), 0);
+
+ $temp = $a;
+ $a = $c;
+ $c = bcsub($temp, bcmul($a, $q, 0), 0);
+
+ $temp = $b;
+ $b = $d;
+ $d = bcsub($temp, bcmul($b, $q, 0), 0);
+ }
+
+ return array(
+ 'gcd' => $this->_normalize(new Math_BigInteger($u)),
+ 'x' => $this->_normalize(new Math_BigInteger($a)),
+ 'y' => $this->_normalize(new Math_BigInteger($b))
+ );
+ }
+
+ $y = $n->copy();
+ $x = $this->copy();
+ $g = new Math_BigInteger();
+ $g->value = array(1);
+
+ while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) {
+ $x->_rshift(1);
+ $y->_rshift(1);
+ $g->_lshift(1);
+ }
+
+ $u = $x->copy();
+ $v = $y->copy();
+
+ $a = new Math_BigInteger();
+ $b = new Math_BigInteger();
+ $c = new Math_BigInteger();
+ $d = new Math_BigInteger();
+
+ $a->value = $d->value = $g->value = array(1);
+ $b->value = $c->value = array();
+
+ while ( !empty($u->value) ) {
+ while ( !($u->value[0] & 1) ) {
+ $u->_rshift(1);
+ if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) {
+ $a = $a->add($y);
+ $b = $b->subtract($x);
+ }
+ $a->_rshift(1);
+ $b->_rshift(1);
+ }
+
+ while ( !($v->value[0] & 1) ) {
+ $v->_rshift(1);
+ if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) {
+ $c = $c->add($y);
+ $d = $d->subtract($x);
+ }
+ $c->_rshift(1);
+ $d->_rshift(1);
+ }
+
+ if ($u->compare($v) >= 0) {
+ $u = $u->subtract($v);
+ $a = $a->subtract($c);
+ $b = $b->subtract($d);
+ } else {
+ $v = $v->subtract($u);
+ $c = $c->subtract($a);
+ $d = $d->subtract($b);
+ }
+ }
+
+ return array(
+ 'gcd' => $this->_normalize($g->multiply($v)),
+ 'x' => $this->_normalize($c),
+ 'y' => $this->_normalize($d)
+ );
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * include('Math/BigInteger.php');
+ *
+ * $a = new Math_BigInteger(693);
+ * $b = new Math_BigInteger(609);
+ *
+ * $gcd = a->extendedGCD($b);
+ *
+ * echo $gcd->toString() . "\r\n"; // outputs 21
+ * ?>
+ * </code>
+ *
+ * @param Math_BigInteger $n
+ * @return Math_BigInteger
+ * @access public
+ */
+ function gcd($n)
+ {
+ extract($this->extendedGCD($n));
+ return $gcd;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return Math_BigInteger
+ * @access public
+ */
+ function abs()
+ {
+ $temp = new Math_BigInteger();
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp->value = gmp_abs($this->value);
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
+ break;
+ default:
+ $temp->value = $this->value;
+ }
+
+ return $temp;
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * @param Math_BigInteger $x
+ * @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal.
+ * @access public
+ * @see equals()
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
+ */
+ function compare($y)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ return gmp_cmp($this->value, $y->value);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ return bccomp($this->value, $y->value, 0);
+ }
+
+ return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * @param Array $x_value
+ * @param Boolean $x_negative
+ * @param Array $y_value
+ * @param Boolean $y_negative
+ * @return Integer
+ * @see compare()
+ * @access private
+ */
+ function _compare($x_value, $x_negative, $y_value, $y_negative)
+ {
+ if ( $x_negative != $y_negative ) {
+ return ( !$x_negative && $y_negative ) ? 1 : -1;
+ }
+
+ $result = $x_negative ? -1 : 1;
+
+ if ( count($x_value) != count($y_value) ) {
+ return ( count($x_value) > count($y_value) ) ? $result : -$result;
+ }
+ $size = max(count($x_value), count($y_value));
+
+ $x_value = array_pad($x_value, $size, 0);
+ $y_value = array_pad($y_value, $size, 0);
+
+ for ($i = count($x_value) - 1; $i >= 0; --$i) {
+ if ($x_value[$i] != $y_value[$i]) {
+ return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare()
+ *
+ * @param Math_BigInteger $x
+ * @return Boolean
+ * @access public
+ * @see compare()
+ */
+ function equals($x)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ return gmp_cmp($this->value, $x->value) == 0;
+ default:
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
+ }
+ }
+
+ /**
+ * Set Precision
+ *
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
+ * shift, not, and rotates.
+ *
+ * @param Math_BigInteger $x
+ * @access public
+ * @return Math_BigInteger
+ */
+ function setPrecision($bits)
+ {
+ $this->precision = $bits;
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) {
+ $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
+ } else {
+ $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
+ }
+
+ $temp = $this->_normalize($this);
+ $this->value = $temp->value;
+ }
+
+ /**
+ * Logical And
+ *
+ * @param Math_BigInteger $x
+ * @access public
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
+ * @return Math_BigInteger
+ */
+ function bitwise_and($x)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_and($this->value, $x->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->_normalize(new Math_BigInteger($left & $right, 256));
+ }
+
+ $result = $this->copy();
+
+ $length = min(count($x->value), count($this->value));
+
+ $result->value = array_slice($result->value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i] = $result->value[$i] & $x->value[$i];
+ }
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param Math_BigInteger $x
+ * @access public
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
+ * @return Math_BigInteger
+ */
+ function bitwise_or($x)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_or($this->value, $x->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->_normalize(new Math_BigInteger($left | $right, 256));
+ }
+
+ $length = max(count($this->value), count($x->value));
+ $result = $this->copy();
+ $result->value = array_pad($result->value, 0, $length);
+ $x->value = array_pad($x->value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i] = $this->value[$i] | $x->value[$i];
+ }
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Logical Exclusive-Or
+ *
+ * @param Math_BigInteger $x
+ * @access public
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
+ * @return Math_BigInteger
+ */
+ function bitwise_xor($x)
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ $temp = new Math_BigInteger();
+ $temp->value = gmp_xor($this->value, $x->value);
+
+ return $this->_normalize($temp);
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->_normalize(new Math_BigInteger($left ^ $right, 256));
+ }
+
+ $length = max(count($this->value), count($x->value));
+ $result = $this->copy();
+ $result->value = array_pad($result->value, 0, $length);
+ $x->value = array_pad($x->value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i] = $this->value[$i] ^ $x->value[$i];
+ }
+
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Logical Not
+ *
+ * @access public
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
+ * @return Math_BigInteger
+ */
+ function bitwise_not()
+ {
+ // calculuate "not" without regard to $this->precision
+ // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
+ $temp = $this->toBytes();
+ $pre_msb = decbin(ord($temp[0]));
+ $temp = ~$temp;
+ $msb = decbin(ord($temp[0]));
+ if (strlen($msb) == 8) {
+ $msb = substr($msb, strpos($msb, '0'));
+ }
+ $temp[0] = chr(bindec($msb));
+
+ // see if we need to add extra leading 1's
+ $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
+ $new_bits = $this->precision - $current_bits;
+ if ($new_bits <= 0) {
+ return $this->_normalize(new Math_BigInteger($temp, 256));
+ }
+
+ // generate as many leading 1's as we need to.
+ $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
+ $this->_base256_lshift($leading_ones, $current_bits);
+
+ $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT);
+
+ return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param Integer $shift
+ * @return Math_BigInteger
+ * @access public
+ * @internal The only version that yields any speed increases is the internal version.
+ */
+ function bitwise_rightShift($shift)
+ {
+ $temp = new Math_BigInteger();
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ static $two;
+
+ if (!isset($two)) {
+ $two = gmp_init('2');
+ }
+
+ $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
+
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
+
+ break;
+ default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->_rshift($shift);
+ }
+
+ return $this->_normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param Integer $shift
+ * @return Math_BigInteger
+ * @access public
+ * @internal The only version that yields any speed increases is the internal version.
+ */
+ function bitwise_leftShift($shift)
+ {
+ $temp = new Math_BigInteger();
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ static $two;
+
+ if (!isset($two)) {
+ $two = gmp_init('2');
+ }
+
+ $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
+
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
+
+ break;
+ default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->_lshift($shift);
+ }
+
+ return $this->_normalize($temp);
+ }
+
+ /**
+ * Logical Left Rotate
+ *
+ * Instead of the top x bits being dropped they're appended to the shifted bit string.
+ *
+ * @param Integer $shift
+ * @return Math_BigInteger
+ * @access public
+ */
+ function bitwise_leftRotate($shift)
+ {
+ $bits = $this->toBytes();
+
+ if ($this->precision > 0) {
+ $precision = $this->precision;
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
+ $mask = $this->bitmask->subtract(new Math_BigInteger(1));
+ $mask = $mask->toBytes();
+ } else {
+ $mask = $this->bitmask->toBytes();
+ }
+ } else {
+ $temp = ord($bits[0]);
+ for ($i = 0; $temp >> $i; ++$i);
+ $precision = 8 * strlen($bits) - 8 + $i;
+ $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
+ }
+
+ if ($shift < 0) {
+ $shift+= $precision;
+ }
+ $shift%= $precision;
+
+ if (!$shift) {
+ return $this->copy();
+ }
+
+ $left = $this->bitwise_leftShift($shift);
+ $left = $left->bitwise_and(new Math_BigInteger($mask, 256));
+ $right = $this->bitwise_rightShift($precision - $shift);
+ $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
+ return $this->_normalize($result);
+ }
+
+ /**
+ * Logical Right Rotate
+ *
+ * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
+ *
+ * @param Integer $shift
+ * @return Math_BigInteger
+ * @access public
+ */
+ function bitwise_rightRotate($shift)
+ {
+ return $this->bitwise_leftRotate(-$shift);
+ }
+
+ /**
+ * Set random number generator function
+ *
+ * This function is deprecated.
+ *
+ * @param String $generator
+ * @access public
+ */
+ function setRandomGenerator($generator)
+ {
+ }
+
+ /**
+ * Generate a random number
+ *
+ * @param optional Integer $min
+ * @param optional Integer $max
+ * @return Math_BigInteger
+ * @access public
+ */
+ function random($min = false, $max = false)
+ {
+ if ($min === false) {
+ $min = new Math_BigInteger(0);
+ }
+
+ if ($max === false) {
+ $max = new Math_BigInteger(0x7FFFFFFF);
+ }
+
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $this->_normalize($min);
+ } else if ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ $generator = $this->generator;
+
+ $max = $max->subtract($min);
+ $max = ltrim($max->toBytes(), chr(0));
+ $size = strlen($max) - 1;
+
+ $crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string'));
+ if ($crypt_random) {
+ $random = crypt_random_string($size);
+ } else {
+ $random = '';
+
+ if ($size & 1) {
+ $random.= chr(mt_rand(0, 255));
+ }
+
+ $blocks = $size >> 1;
+ for ($i = 0; $i < $blocks; ++$i) {
+ // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
+ $random.= pack('n', mt_rand(0, 0xFFFF));
+ }
+ }
+
+ $fragment = new Math_BigInteger($random, 256);
+ $leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ?
+ ord($max[0]) - 1 : ord($max[0]);
+
+ if (!$crypt_random) {
+ $msb = chr(mt_rand(0, $leading));
+ } else {
+ $cutoff = floor(0xFF / $leading) * $leading;
+ while (true) {
+ $msb = ord(crypt_random_string(1));
+ if ($msb <= $cutoff) {
+ $msb%= $leading;
+ break;
+ }
+ }
+ $msb = chr($msb);
+ }
+
+ $random = new Math_BigInteger($msb . $random, 256);
+
+ return $this->_normalize($random->add($min));
+ }
+
+ /**
+ * Generate a random prime number.
+ *
+ * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed,
+ * give up and return false.
+ *
+ * @param optional Integer $min
+ * @param optional Integer $max
+ * @param optional Integer $timeout
+ * @return Math_BigInteger
+ * @access public
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
+ */
+ function randomPrime($min = false, $max = false, $timeout = false)
+ {
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $min;
+ } else if ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) {
+ // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function
+ // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however,
+ // the same $max / $min checks are not performed.
+ if ($min === false) {
+ $min = new Math_BigInteger(0);
+ }
+
+ if ($max === false) {
+ $max = new Math_BigInteger(0x7FFFFFFF);
+ }
+
+ $x = $this->random($min, $max);
+
+ $x->value = gmp_nextprime($x->value);
+
+ if ($x->compare($max) <= 0) {
+ return $x;
+ }
+
+ $x->value = gmp_nextprime($min->value);
+
+ if ($x->compare($max) <= 0) {
+ return $x;
+ }
+
+ return false;
+ }
+
+ static $one, $two;
+ if (!isset($one)) {
+ $one = new Math_BigInteger(1);
+ $two = new Math_BigInteger(2);
+ }
+
+ $start = time();
+
+ $x = $this->random($min, $max);
+ if ($x->equals($two)) {
+ return $x;
+ }
+
+ $x->_make_odd();
+ if ($x->compare($max) > 0) {
+ // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
+ if ($min->equals($max)) {
+ return false;
+ }
+ $x = $min->copy();
+ $x->_make_odd();
+ }
+
+ $initial_x = $x->copy();
+
+ while (true) {
+ if ($timeout !== false && time() - $start > $timeout) {
+ return false;
+ }
+
+ if ($x->isPrime()) {
+ return $x;
+ }
+
+ $x = $x->add($two);
+
+ if ($x->compare($max) > 0) {
+ $x = $min->copy();
+ if ($x->equals($two)) {
+ return $x;
+ }
+ $x->_make_odd();
+ }
+
+ if ($x->equals($initial_x)) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see randomPrime()
+ * @access private
+ */
+ function _make_odd()
+ {
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ gmp_setbit($this->value, 0);
+ break;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
+ $this->value = bcadd($this->value, '1');
+ }
+ break;
+ default:
+ $this->value[0] |= 1;
+ }
+ }
+
+ /**
+ * Checks a numer to see if it's prime
+ *
+ * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
+ * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads
+ * on a website instead of just one.
+ *
+ * @param optional Integer $t
+ * @return Boolean
+ * @access public
+ * @internal Uses the
+ * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
+ */
+ function isPrime($t = false)
+ {
+ $length = strlen($this->toBytes());
+
+ if (!$t) {
+ // see HAC 4.49 "Note (controlling the error probability)"
+ if ($length >= 163) { $t = 2; } // floor(1300 / 8)
+ else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
+ else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
+ else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
+ else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
+ else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
+ else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
+ else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
+ else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
+ else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
+ else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
+ else { $t = 27; }
+ }
+
+ // ie. gmp_testbit($this, 0)
+ // ie. isEven() or !isOdd()
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ return gmp_prob_prime($this->value, $t) != 0;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ if ($this->value === '2') {
+ return true;
+ }
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
+ return false;
+ }
+ break;
+ default:
+ if ($this->value == array(2)) {
+ return true;
+ }
+ if (~$this->value[0] & 1) {
+ return false;
+ }
+ }
+
+ static $primes, $zero, $one, $two;
+
+ if (!isset($primes)) {
+ $primes = array(
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
+ 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
+ 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
+ 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
+ 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
+ 953, 967, 971, 977, 983, 991, 997
+ );
+
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
+ for ($i = 0; $i < count($primes); ++$i) {
+ $primes[$i] = new Math_BigInteger($primes[$i]);
+ }
+ }
+
+ $zero = new Math_BigInteger();
+ $one = new Math_BigInteger(1);
+ $two = new Math_BigInteger(2);
+ }
+
+ if ($this->equals($one)) {
+ return false;
+ }
+
+ // see HAC 4.4.1 "Random search for probable primes"
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
+ foreach ($primes as $prime) {
+ list(, $r) = $this->divide($prime);
+ if ($r->equals($zero)) {
+ return $this->equals($prime);
+ }
+ }
+ } else {
+ $value = $this->value;
+ foreach ($primes as $prime) {
+ list(, $r) = $this->_divide_digit($value, $prime);
+ if (!$r) {
+ return count($value) == 1 && $value[0] == $prime;
+ }
+ }
+ }
+
+ $n = $this->copy();
+ $n_1 = $n->subtract($one);
+ $n_2 = $n->subtract($two);
+
+ $r = $n_1->copy();
+ $r_value = $r->value;
+ // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
+ $s = 0;
+ // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
+ while ($r->value[strlen($r->value) - 1] % 2 == 0) {
+ $r->value = bcdiv($r->value, '2', 0);
+ ++$s;
+ }
+ } else {
+ for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
+ $temp = ~$r_value[$i] & 0xFFFFFF;
+ for ($j = 1; ($temp >> $j) & 1; ++$j);
+ if ($j != 25) {
+ break;
+ }
+ }
+ $s = 26 * $i + $j - 1;
+ $r->_rshift($s);
+ }
+
+ for ($i = 0; $i < $t; ++$i) {
+ $a = $this->random($two, $n_2);
+ $y = $a->modPow($r, $n);
+
+ if (!$y->equals($one) && !$y->equals($n_1)) {
+ for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
+ $y = $y->modPow($two, $n);
+ if ($y->equals($one)) {
+ return false;
+ }
+ }
+
+ if (!$y->equals($n_1)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param Integer $shift
+ * @access private
+ */
+ function _lshift($shift)
+ {
+ if ( $shift == 0 ) {
+ return;
+ }
+
+ $num_digits = (int) ($shift / 26);
+ $shift %= 26;
+ $shift = 1 << $shift;
+
+ $carry = 0;
+
+ for ($i = 0; $i < count($this->value); ++$i) {
+ $temp = $this->value[$i] * $shift + $carry;
+ $carry = (int) ($temp / 0x4000000);
+ $this->value[$i] = (int) ($temp - $carry * 0x4000000);
+ }
+
+ if ( $carry ) {
+ $this->value[] = $carry;
+ }
+
+ while ($num_digits--) {
+ array_unshift($this->value, 0);
+ }
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param Integer $shift
+ * @access private
+ */
+ function _rshift($shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_digits = (int) ($shift / 26);
+ $shift %= 26;
+ $carry_shift = 26 - $shift;
+ $carry_mask = (1 << $shift) - 1;
+
+ if ( $num_digits ) {
+ $this->value = array_slice($this->value, $num_digits);
+ }
+
+ $carry = 0;
+
+ for ($i = count($this->value) - 1; $i >= 0; --$i) {
+ $temp = $this->value[$i] >> $shift | $carry;
+ $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
+ $this->value[$i] = $temp;
+ }
+
+ $this->value = $this->_trim($this->value);
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @param Math_BigInteger
+ * @return Math_BigInteger
+ * @see _trim()
+ * @access private
+ */
+ function _normalize($result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ switch ( MATH_BIGINTEGER_MODE ) {
+ case MATH_BIGINTEGER_MODE_GMP:
+ if (!empty($result->bitmask->value)) {
+ $result->value = gmp_and($result->value, $result->bitmask->value);
+ }
+
+ return $result;
+ case MATH_BIGINTEGER_MODE_BCMATH:
+ if (!empty($result->bitmask->value)) {
+ $result->value = bcmod($result->value, $result->bitmask->value);
+ }
+
+ return $result;
+ }
+
+ $value = &$result->value;
+
+ if ( !count($value) ) {
+ return $result;
+ }
+
+ $value = $this->_trim($value);
+
+ if (!empty($result->bitmask->value)) {
+ $length = min(count($value), count($this->bitmask->value));
+ $value = array_slice($value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $value[$i] = $value[$i] & $this->bitmask->value[$i];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Trim
+ *
+ * Removes leading zeros
+ *
+ * @return Math_BigInteger
+ * @access private
+ */
+ function _trim($value)
+ {
+ for ($i = count($value) - 1; $i >= 0; --$i) {
+ if ( $value[$i] ) {
+ break;
+ }
+ unset($value[$i]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Array Repeat
+ *
+ * @param $input Array
+ * @param $multiplier mixed
+ * @return Array
+ * @access private
+ */
+ function _array_repeat($input, $multiplier)
+ {
+ return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
+ *
+ * @param $x String
+ * @param $shift Integer
+ * @return String
+ * @access private
+ */
+ function _base256_lshift(&$x, $shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
+ $shift &= 7; // eg. $shift % 8
+
+ $carry = 0;
+ for ($i = strlen($x) - 1; $i >= 0; --$i) {
+ $temp = ord($x[$i]) << $shift | $carry;
+ $x[$i] = chr($temp);
+ $carry = $temp >> 8;
+ }
+ $carry = ($carry != 0) ? chr($carry) : '';
+ $x = $carry . $x . str_repeat(chr(0), $num_bytes);
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
+ *
+ * @param $x String
+ * @param $shift Integer
+ * @return String
+ * @access private
+ */
+ function _base256_rshift(&$x, $shift)
+ {
+ if ($shift == 0) {
+ $x = ltrim($x, chr(0));
+ return '';
+ }
+
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
+ $shift &= 7; // eg. $shift % 8
+
+ $remainder = '';
+ if ($num_bytes) {
+ $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
+ $remainder = substr($x, $start);
+ $x = substr($x, 0, -$num_bytes);
+ }
+
+ $carry = 0;
+ $carry_shift = 8 - $shift;
+ for ($i = 0; $i < strlen($x); ++$i) {
+ $temp = (ord($x[$i]) >> $shift) | $carry;
+ $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
+ $x[$i] = chr($temp);
+ }
+ $x = ltrim($x, chr(0));
+
+ $remainder = chr($carry >> $carry_shift) . $remainder;
+
+ return ltrim($remainder, chr(0));
+ }
+
+ // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
+ // at 32-bits, while java's longs are 64-bits.
+
+ /**
+ * Converts 32-bit integers to bytes.
+ *
+ * @param Integer $x
+ * @return String
+ * @access private
+ */
+ function _int2bytes($x)
+ {
+ return ltrim(pack('N', $x), chr(0));
+ }
+
+ /**
+ * Converts bytes to 32-bit integers
+ *
+ * @param String $x
+ * @return Integer
+ * @access private
+ */
+ function _bytes2int($x)
+ {
+ $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
+ return $temp['int'];
+ }
+
+ /**
+ * DER-encode an integer
+ *
+ * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
+ *
+ * @see modPow()
+ * @access private
+ * @param Integer $length
+ * @return String
+ */
+ function _encodeASN1Length($length)
+ {
+ if ($length <= 0x7F) {
+ return chr($length);
+ }
+
+ $temp = ltrim(pack('N', $length), chr(0));
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
+ }
+} \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php
new file mode 100644
index 00000000000..8db087d3d99
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php
@@ -0,0 +1,2029 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of SFTP.
+ *
+ * PHP versions 4 and 5
+ *
+ * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
+ * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
+ * to an SFTPv4/5/6 server.
+ *
+ * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Net/SFTP.php');
+ *
+ * $sftp = new Net_SFTP('www.domain.tld');
+ * if (!$sftp->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $sftp->pwd() . "\r\n";
+ * $sftp->put('filename.ext', 'hello, world!');
+ * print_r($sftp->nlist());
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Net
+ * @package Net_SFTP
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMIX Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Net_SSH2
+ */
+if (!class_exists('Net_SSH2')) {
+ require_once('Net/SSH2.php');
+}
+
+/**#@+
+ * @access public
+ * @see Net_SFTP::getLog()
+ */
+/**
+ * Returns the message numbers
+ */
+define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE);
+/**
+ * Returns the message content
+ */
+define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
+/**
+ * Outputs the message content in real-time.
+ */
+define('NET_SFTP_LOG_REALTIME', 3);
+/**#@-*/
+
+/**
+ * SFTP channel constant
+ *
+ * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
+ *
+ * @see Net_SSH2::_send_channel_packet()
+ * @see Net_SSH2::_get_channel_packet()
+ * @access private
+ */
+define('NET_SFTP_CHANNEL', 2);
+
+/**#@+
+ * @access public
+ * @see Net_SFTP::put()
+ */
+/**
+ * Reads data from a local file.
+ */
+define('NET_SFTP_LOCAL_FILE', 1);
+/**
+ * Reads data from a string.
+ */
+// this value isn't really used anymore but i'm keeping it reserved for historical reasons
+define('NET_SFTP_STRING', 2);
+/**
+ * Resumes an upload
+ */
+define('NET_SFTP_RESUME', 4);
+/**#@-*/
+
+/**
+ * Pure-PHP implementations of SFTP.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Net_SFTP
+ */
+class Net_SFTP extends Net_SSH2 {
+ /**
+ * Packet Types
+ *
+ * @see Net_SFTP::Net_SFTP()
+ * @var Array
+ * @access private
+ */
+ var $packet_types = array();
+
+ /**
+ * Status Codes
+ *
+ * @see Net_SFTP::Net_SFTP()
+ * @var Array
+ * @access private
+ */
+ var $status_codes = array();
+
+ /**
+ * The Request ID
+ *
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
+ * concurrent actions, so it's somewhat academic, here.
+ *
+ * @var Integer
+ * @see Net_SFTP::_send_sftp_packet()
+ * @access private
+ */
+ var $request_id = false;
+
+ /**
+ * The Packet Type
+ *
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
+ * concurrent actions, so it's somewhat academic, here.
+ *
+ * @var Integer
+ * @see Net_SFTP::_get_sftp_packet()
+ * @access private
+ */
+ var $packet_type = -1;
+
+ /**
+ * Packet Buffer
+ *
+ * @var String
+ * @see Net_SFTP::_get_sftp_packet()
+ * @access private
+ */
+ var $packet_buffer = '';
+
+ /**
+ * Extensions supported by the server
+ *
+ * @var Array
+ * @see Net_SFTP::_initChannel()
+ * @access private
+ */
+ var $extensions = array();
+
+ /**
+ * Server SFTP version
+ *
+ * @var Integer
+ * @see Net_SFTP::_initChannel()
+ * @access private
+ */
+ var $version;
+
+ /**
+ * Current working directory
+ *
+ * @var String
+ * @see Net_SFTP::_realpath()
+ * @see Net_SFTP::chdir()
+ * @access private
+ */
+ var $pwd = false;
+
+ /**
+ * Packet Type Log
+ *
+ * @see Net_SFTP::getLog()
+ * @var Array
+ * @access private
+ */
+ var $packet_type_log = array();
+
+ /**
+ * Packet Log
+ *
+ * @see Net_SFTP::getLog()
+ * @var Array
+ * @access private
+ */
+ var $packet_log = array();
+
+ /**
+ * Error information
+ *
+ * @see Net_SFTP::getSFTPErrors()
+ * @see Net_SFTP::getLastSFTPError()
+ * @var String
+ * @access private
+ */
+ var $sftp_errors = array();
+
+ /**
+ * File Type
+ *
+ * @see Net_SFTP::_parseLongname()
+ * @var Integer
+ * @access private
+ */
+ var $fileType = 0;
+
+ /**
+ * Directory Cache
+ *
+ * Rather than always having to open a directory and close it immediately there after to see if a file is a directory or
+ * rather than always
+ *
+ * @see Net_SFTP::_save_dir()
+ * @see Net_SFTP::_remove_dir()
+ * @see Net_SFTP::_is_dir()
+ * @var Array
+ * @access private
+ */
+ var $dirs = array();
+
+ /**
+ * Default Constructor.
+ *
+ * Connects to an SFTP server
+ *
+ * @param String $host
+ * @param optional Integer $port
+ * @param optional Integer $timeout
+ * @return Net_SFTP
+ * @access public
+ */
+ function Net_SFTP($host, $port = 22, $timeout = 10)
+ {
+ parent::Net_SSH2($host, $port, $timeout);
+ $this->packet_types = array(
+ 1 => 'NET_SFTP_INIT',
+ 2 => 'NET_SFTP_VERSION',
+ /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
+ 3 => 'NET_SFTP_OPEN',
+ 4 => 'NET_SFTP_CLOSE',
+ 5 => 'NET_SFTP_READ',
+ 6 => 'NET_SFTP_WRITE',
+ 7 => 'NET_SFTP_LSTAT',
+ 9 => 'NET_SFTP_SETSTAT',
+ 11 => 'NET_SFTP_OPENDIR',
+ 12 => 'NET_SFTP_READDIR',
+ 13 => 'NET_SFTP_REMOVE',
+ 14 => 'NET_SFTP_MKDIR',
+ 15 => 'NET_SFTP_RMDIR',
+ 16 => 'NET_SFTP_REALPATH',
+ 17 => 'NET_SFTP_STAT',
+ /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
+ 18 => 'NET_SFTP_RENAME',
+
+ 101=> 'NET_SFTP_STATUS',
+ 102=> 'NET_SFTP_HANDLE',
+ /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
+ SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
+ pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
+ 103=> 'NET_SFTP_DATA',
+ 104=> 'NET_SFTP_NAME',
+ 105=> 'NET_SFTP_ATTRS',
+
+ 200=> 'NET_SFTP_EXTENDED'
+ );
+ $this->status_codes = array(
+ 0 => 'NET_SFTP_STATUS_OK',
+ 1 => 'NET_SFTP_STATUS_EOF',
+ 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
+ 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
+ 4 => 'NET_SFTP_STATUS_FAILURE',
+ 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
+ 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
+ 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
+ 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED'
+ );
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
+ // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
+ $this->attributes = array(
+ 0x00000001 => 'NET_SFTP_ATTR_SIZE',
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
+ 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
+ // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
+ // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
+ // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
+ // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
+ -1 << 31 => 'NET_SFTP_ATTR_EXTENDED'
+ );
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
+ // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
+ // the array for that $this->open5_flags and similarily alter the constant names.
+ $this->open_flags = array(
+ 0x00000001 => 'NET_SFTP_OPEN_READ',
+ 0x00000002 => 'NET_SFTP_OPEN_WRITE',
+ 0x00000004 => 'NET_SFTP_OPEN_APPEND',
+ 0x00000008 => 'NET_SFTP_OPEN_CREATE',
+ 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE'
+ );
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
+ // see Net_SFTP::_parseLongname() for an explanation
+ $this->file_types = array(
+ 1 => 'NET_SFTP_TYPE_REGULAR',
+ 2 => 'NET_SFTP_TYPE_DIRECTORY',
+ 3 => 'NET_SFTP_TYPE_SYMLINK',
+ 4 => 'NET_SFTP_TYPE_SPECIAL'
+ );
+ $this->_define_array(
+ $this->packet_types,
+ $this->status_codes,
+ $this->attributes,
+ $this->open_flags,
+ $this->file_types
+ );
+ }
+
+ /**
+ * Login
+ *
+ * @param String $username
+ * @param optional String $password
+ * @return Boolean
+ * @access public
+ */
+ function login($username, $password = '')
+ {
+ if (!parent::login($username, $password)) {
+ return false;
+ }
+
+ $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size;
+
+ $packet = pack('CNa*N3',
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000);
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
+
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
+ if ($response === false) {
+ return false;
+ }
+
+ $packet = pack('CNNa*CNa*',
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp');
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
+
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
+ if ($response === false) {
+ return false;
+ }
+
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
+
+ if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_VERSION) {
+ user_error('Expected SSH_FXP_VERSION');
+ return false;
+ }
+
+ extract(unpack('Nversion', $this->_string_shift($response, 4)));
+ $this->version = $version;
+ while (!empty($response)) {
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $key = $this->_string_shift($response, $length);
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $value = $this->_string_shift($response, $length);
+ $this->extensions[$key] = $value;
+ }
+
+ /*
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
+ 'newline@vandyke.com' would.
+ */
+ /*
+ if (isset($this->extensions['newline@vandyke.com'])) {
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
+ unset($this->extensions['newline@vandyke.com']);
+ }
+ */
+
+ $this->request_id = 1;
+
+ /*
+ A Note on SFTPv4/5/6 support:
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
+
+ "If the client wishes to interoperate with servers that support noncontiguous version
+ numbers it SHOULD send '3'"
+
+ Given that the server only sends its version number after the client has already done so, the above
+ seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the
+ most popular.
+
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
+
+ "If the server did not send the "versions" extension, or the version-from-list was not included, the
+ server MAY send a status response describing the failure, but MUST then close the channel without
+ processing any further requests."
+
+ So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
+ a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
+ v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
+ in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
+ channel and reopen it with a new and updated SSH_FXP_INIT packet.
+ */
+ switch ($this->version) {
+ case 2:
+ case 3:
+ break;
+ default:
+ return false;
+ }
+
+ $this->pwd = $this->_realpath('.', false);
+
+ $this->_save_dir($this->pwd);
+
+ return true;
+ }
+
+ /**
+ * Returns the current directory name
+ *
+ * @return Mixed
+ * @access public
+ */
+ function pwd()
+ {
+ return $this->pwd;
+ }
+
+ /**
+ * Logs errors
+ *
+ * @param String $response
+ * @param optional Integer $status
+ * @access public
+ */
+ function _logError($response, $status = -1) {
+ if ($status == -1) {
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ }
+
+ $error = $this->status_codes[$status];
+
+ if ($this->version > 2) {
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
+ } else {
+ $this->sftp_errors[] = $error;
+ }
+ }
+
+ /**
+ * Canonicalize the Server-Side Path Name
+ *
+ * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
+ * the absolute (canonicalized) path.
+ *
+ * @see Net_SFTP::chdir()
+ * @param String $dir
+ * @return Mixed
+ * @access private
+ */
+ function _realpath($dir, $check_dir = true)
+ {
+ if ($check_dir && $this->_is_dir($dir)) {
+ return true;
+ }
+
+ /*
+ "This protocol represents file names as strings. File names are
+ assumed to use the slash ('/') character as a directory separator.
+
+ File names starting with a slash are "absolute", and are relative to
+ the root of the file system. Names starting with any other character
+ are relative to the user's default directory (home directory). Note
+ that identifying the user is assumed to take place outside of this
+ protocol."
+
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6
+ */
+ $file = '';
+ if ($this->pwd !== false) {
+ // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary
+ // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on
+ // the subject, we'll go ahead and work around it with the following.
+ if (empty($dir) || $dir[strlen($dir) - 1] != '/') {
+ $file = basename($dir);
+ $dir = dirname($dir);
+ }
+
+ $dir = $dir[0] == '/' ? '/' . rtrim(substr($dir, 1), '/') : rtrim($dir, '/');
+
+ if ($dir == '.' || $dir == $this->pwd) {
+ $temp = $this->pwd;
+ if (!empty($file)) {
+ $temp.= '/' . $file;
+ }
+ return $temp;
+ }
+
+ if ($dir[0] != '/') {
+ $dir = $this->pwd . '/' . $dir;
+ }
+ // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths
+ // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let
+ // the server do it. we'll do the latter.
+ }
+
+ /*
+ that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the
+ specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet
+ regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename
+ field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely
+ not be the case, but for this one, it is.
+ */
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
+ if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_NAME:
+ // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
+ // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
+ // at is the first part and that part is defined the same in SFTP versions 3 through 6.
+ $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $realpath = $this->_string_shift($response, $length);
+ // the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information.
+ // per the above comment, this is a shot in the dark that, on most servers, won't help us in determining
+ // the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot.
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->fileType = $this->_parseLongname($this->_string_shift($response, $length));
+ break;
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ return false;
+ default:
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
+ return false;
+ }
+
+ // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to
+ // be a bonafide directory
+ if (!empty($file)) {
+ $realpath.= '/' . $file;
+ }
+
+ return $realpath;
+ }
+
+ /**
+ * Changes the current directory
+ *
+ * @param String $dir
+ * @return Boolean
+ * @access public
+ */
+ function chdir($dir)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ if ($dir[strlen($dir) - 1] != '/') {
+ $dir.= '/';
+ }
+
+ // confirm that $dir is, in fact, a valid directory
+ if ($this->_is_dir($dir)) {
+ $this->pwd = $dir;
+ return true;
+ }
+
+ $dir = $this->_realpath($dir, false);
+
+ if ($this->_is_dir($dir)) {
+ $this->pwd = $dir;
+ return true;
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
+ return false;
+ }
+
+ // see Net_SFTP::nlist() for a more thorough explanation of the following
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_HANDLE:
+ $handle = substr($response, 4);
+ break;
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ return false;
+ default:
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
+ return false;
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ $this->_save_dir($dir);
+
+ $this->pwd = $dir;
+ return true;
+ }
+
+ /**
+ * Returns a list of files in the given directory
+ *
+ * @param optional String $dir
+ * @return Mixed
+ * @access public
+ */
+ function nlist($dir = '.')
+ {
+ return $this->_list($dir, false);
+ }
+
+ /**
+ * Returns a detailed list of files in the given directory
+ *
+ * @param optional String $dir
+ * @return Mixed
+ * @access public
+ */
+ function rawlist($dir = '.')
+ {
+ return $this->_list($dir, true);
+ }
+
+ /**
+ * Reads a list, be it detailed or not, of files in the given directory
+ *
+ * $realpath exists because, in the case of the recursive deletes and recursive chmod's $realpath has already
+ * been calculated.
+ *
+ * @param String $dir
+ * @param optional Boolean $raw
+ * @param optional Boolean $realpath
+ * @return Mixed
+ * @access private
+ */
+ function _list($dir, $raw = true, $realpath = true)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $dir = $this->_realpath($dir . '/');
+ if ($dir === false) {
+ return false;
+ }
+
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_HANDLE:
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
+ // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
+ // represent the length of the string and leave it at that
+ $handle = substr($response, 4);
+ break;
+ case NET_SFTP_STATUS:
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
+ $this->_logError($response);
+ return false;
+ default:
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
+ return false;
+ }
+
+ $this->_save_dir($dir);
+
+ $contents = array();
+ while (true) {
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
+ // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
+ // SSH_MSG_CHANNEL_DATA messages is not known to me.
+ if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_NAME:
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ for ($i = 0; $i < $count; $i++) {
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $shortname = $this->_string_shift($response, $length);
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $longname = $this->_string_shift($response, $length);
+ $attributes = $this->_parseAttributes($response); // we also don't care about the attributes
+ if (!$raw) {
+ $contents[] = $shortname;
+ } else {
+ $contents[$shortname] = $attributes;
+ $fileType = $this->_parseLongname($longname);
+ if ($fileType) {
+ if ($fileType == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
+ $this->_save_dir($dir . '/' . $shortname);
+ }
+ $contents[$shortname]['type'] = $fileType;
+ }
+ }
+ // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
+ // final SSH_FXP_STATUS packet should tell us that, already.
+ }
+ break;
+ case NET_SFTP_STATUS:
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_EOF) {
+ $this->_logError($response, $status);
+ return false;
+ }
+ break 2;
+ default:
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
+ return false;
+ }
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
+ return false;
+ }
+
+ // "The client MUST release all resources associated with the handle regardless of the status."
+ // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ return $contents;
+ }
+
+ /**
+ * Returns the file size, in bytes, or false, on failure
+ *
+ * Files larger than 4GB will show up as being exactly 4GB.
+ *
+ * @param String $filename
+ * @return Mixed
+ * @access public
+ */
+ function size($filename)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $filename = $this->_realpath($filename);
+ if ($filename === false) {
+ return false;
+ }
+
+ return $this->_size($filename);
+ }
+
+ /**
+ * Save directories to cache
+ *
+ * @param String $dir
+ * @access private
+ */
+ function _save_dir($dir)
+ {
+ // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($dir, '/'))
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
+
+ $temp = &$this->dirs;
+ foreach ($dirs as $dir) {
+ if (!isset($temp[$dir])) {
+ $temp[$dir] = array();
+ }
+ $temp = &$temp[$dir];
+ }
+ }
+
+ /**
+ * Remove directories from cache
+ *
+ * @param String $dir
+ * @access private
+ */
+ function _remove_dir($dir)
+ {
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
+
+ $temp = &$this->dirs;
+ foreach ($dirs as $dir) {
+ if ($dir == end($dirs)) {
+ unset($temp[$dir]);
+ return true;
+ }
+ if (!isset($temp[$dir])) {
+ return false;
+ }
+ $temp = &$temp[$dir];
+ }
+ }
+
+ /**
+ * Checks cache for directory
+ *
+ * @param String $dir
+ * @access private
+ */
+ function _is_dir($dir)
+ {
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
+
+ $temp = &$this->dirs;
+ foreach ($dirs as $dir) {
+ if (!isset($temp[$dir])) {
+ return false;
+ }
+ $temp = &$temp[$dir];
+ }
+ }
+
+ /**
+ * Returns general information about a file.
+ *
+ * Returns an array on success and false otherwise.
+ *
+ * @param String $filename
+ * @return Mixed
+ * @access public
+ */
+ function stat($filename)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $filename = $this->_realpath($filename);
+ if ($filename === false) {
+ return false;
+ }
+
+ $stat = $this->_stat($filename, NET_SFTP_STAT);
+ if ($stat === false) {
+ return false;
+ }
+
+ $pwd = $this->pwd;
+ $stat['type'] = $this->chdir($filename) ?
+ NET_SFTP_TYPE_DIRECTORY :
+ NET_SFTP_TYPE_REGULAR;
+ $this->pwd = $pwd;
+
+ return $stat;
+ }
+
+ /**
+ * Returns general information about a file or symbolic link.
+ *
+ * Returns an array on success and false otherwise.
+ *
+ * @param String $filename
+ * @return Mixed
+ * @access public
+ */
+ function lstat($filename)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $filename = $this->_realpath($filename);
+ if ($filename === false) {
+ return false;
+ }
+
+ $lstat = $this->_stat($filename, NET_SFTP_LSTAT);
+ $stat = $this->_stat($filename, NET_SFTP_STAT);
+ if ($stat === false) {
+ return false;
+ }
+
+ if ($lstat != $stat) {
+ return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
+ }
+
+ $pwd = $this->pwd;
+ $lstat['type'] = $this->chdir($filename) ?
+ NET_SFTP_TYPE_DIRECTORY :
+ NET_SFTP_TYPE_REGULAR;
+ $this->pwd = $pwd;
+
+ return $lstat;
+ }
+
+ /**
+ * Returns general information about a file or symbolic link
+ *
+ * Determines information without calling Net_SFTP::_realpath().
+ * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
+ *
+ * @param String $filename
+ * @param Integer $type
+ * @return Mixed
+ * @access private
+ */
+ function _stat($filename, $type)
+ {
+ // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
+ $packet = pack('Na*', strlen($filename), $filename);
+ if (!$this->_send_sftp_packet($type, $packet)) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_ATTRS:
+ $attributes = $this->_parseAttributes($response);
+ if ($this->fileType) {
+ $attributes['type'] = $this->fileType;
+ }
+ return $attributes;
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ return false;
+ }
+
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
+ return false;
+ }
+
+ /**
+ * Attempt to identify the file type
+ *
+ * @param String $path
+ * @param Array $stat
+ * @param Array $lstat
+ * @return Integer
+ * @access private
+ */
+ function _identify_type($path, $stat1, $stat2)
+ {
+ $stat1 = $this->_stat($path, $stat1);
+ $stat2 = $this->_stat($path, $stat2);
+
+ if ($stat1 != $stat2) {
+ return array_merge($stat1, array('type' => NET_SFTP_TYPE_SYMLINK));
+ }
+
+ $pwd = $this->pwd;
+ $stat1['type'] = $this->chdir($path) ?
+ NET_SFTP_TYPE_DIRECTORY :
+ NET_SFTP_TYPE_REGULAR;
+ $this->pwd = $pwd;
+
+ return $stat1;
+ }
+
+ /**
+ * Returns the file size, in bytes, or false, on failure
+ *
+ * Determines the size without calling Net_SFTP::_realpath()
+ *
+ * @param String $filename
+ * @return Mixed
+ * @access private
+ */
+ function _size($filename)
+ {
+ $result = $this->_stat($filename, NET_SFTP_LSTAT);
+ if ($result === false) {
+ return false;
+ }
+ return isset($result['size']) ? $result['size'] : -1;
+ }
+
+ /**
+ * Set permissions on a file.
+ *
+ * Returns the new file permissions on success or FALSE on error.
+ *
+ * @param Integer $mode
+ * @param String $filename
+ * @return Mixed
+ * @access public
+ */
+ function chmod($mode, $filename, $recursive = false)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $filename = $this->_realpath($filename);
+ if ($filename === false) {
+ return false;
+ }
+
+ if ($recursive) {
+ $i = 0;
+ $result = $this->_chmod_recursive($mode, $filename, $i);
+ $this->_read_put_responses($i);
+ return $result;
+ }
+
+ // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
+ // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
+ return false;
+ }
+
+ /*
+ "Because some systems must use separate system calls to set various attributes, it is possible that a failure
+ response will be returned, but yet some of the attributes may be have been successfully modified. If possible,
+ servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
+
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
+ */
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ }
+
+ // rather than return what the permissions *should* be, we'll return what they actually are. this will also
+ // tell us if the file actually exists.
+ // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
+ $packet = pack('Na*', strlen($filename), $filename);
+ if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_ATTRS:
+ $attrs = $this->_parseAttributes($response);
+ return $attrs['permissions'];
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ return false;
+ }
+
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
+ return false;
+ }
+
+ /**
+ * Recursively chmods directories on the SFTP server
+ *
+ * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
+ *
+ * @param Integer $mode
+ * @param String $filename
+ * @return Boolean
+ * @access private
+ */
+ function _chmod_recursive($mode, $path, &$i)
+ {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ $entries = $this->_list($path, true, false);
+
+ if ($entries === false) {
+ return $this->chmod($mode, $path);
+ }
+
+ // normally $entries would have at least . and .. but it might not if the directories
+ // permissions didn't allow reading
+ if (empty($entries)) {
+ return false;
+ }
+
+ foreach ($entries as $filename=>$props) {
+ if ($filename == '.' || $filename == '..') {
+ continue;
+ }
+
+ if (!isset($props['type'])) {
+ return false;
+ }
+
+ $temp = $path . '/' . $filename;
+ if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
+ if (!$this->_chmod_recursive($mode, $temp, $i)) {
+ return false;
+ }
+ } else {
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
+ return false;
+ }
+
+ $i++;
+
+ if ($i >= 50) {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ }
+ }
+ }
+
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
+ return false;
+ }
+
+ $i++;
+
+ if ($i >= 50) {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates a directory.
+ *
+ * @param String $dir
+ * @return Boolean
+ * @access public
+ */
+ function mkdir($dir)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ if ($dir[0] != '/') {
+ $dir = $this->_realpath(rtrim($dir, '/'));
+ if ($dir === false) {
+ return false;
+ }
+ if (!$this->_mkdir_helper($dir)) {
+ return false;
+ }
+ } else {
+ $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
+ $temp = '';
+ foreach ($dirs as $dir) {
+ $temp.= '/' . $dir;
+ $result = $this->_mkdir_helper($temp);
+ }
+ if (!$result) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper function for directory creation
+ *
+ * @param String $dir
+ * @return Boolean
+ * @access private
+ */
+ function _mkdir_helper($dir)
+ {
+ // by not providing any permissions, hopefully the server will use the logged in users umask - their
+ // default permissions.
+ if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ $this->_save_dir($dir);
+
+ return true;
+ }
+
+ /**
+ * Removes a directory.
+ *
+ * @param String $dir
+ * @return Boolean
+ * @access public
+ */
+ function rmdir($dir)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $dir = $this->_realpath($dir);
+ if ($dir === false) {
+ return false;
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ $this->_remove_dir($dir);
+
+ return true;
+ }
+
+ /**
+ * Uploads a file to the SFTP server.
+ *
+ * By default, Net_SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
+ * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes
+ * long, containing 'filename.ext' as its contents.
+ *
+ * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
+ * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
+ * large $remote_file will be, as well.
+ *
+ * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
+ * care of that, yourself.
+ *
+ * @param String $remote_file
+ * @param String $data
+ * @param optional Integer $mode
+ * @return Boolean
+ * @access public
+ * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
+ */
+ function put($remote_file, $data, $mode = NET_SFTP_STRING)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $remote_file = $this->_realpath($remote_file);
+ if ($remote_file === false) {
+ return false;
+ }
+
+ $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
+ // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
+ // in practice, it doesn't seem to do that.
+ //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
+
+ // if NET_SFTP_OPEN_APPEND worked as it should the following (up until the -----------) wouldn't be necessary
+ $offset = 0;
+ if ($mode & NET_SFTP_RESUME) {
+ $size = $this->_size($remote_file);
+ $offset = $size !== false ? $size : 0;
+ } else {
+ $flags|= NET_SFTP_OPEN_TRUNCATE;
+ }
+ // --------------
+
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_HANDLE:
+ $handle = substr($response, 4);
+ break;
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ return false;
+ default:
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
+ return false;
+ }
+
+ $initialize = true;
+
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
+ if ($mode & NET_SFTP_LOCAL_FILE) {
+ if (!is_file($data)) {
+ user_error("$data is not a valid file");
+ return false;
+ }
+ $fp = @fopen($data, 'rb');
+ if (!$fp) {
+ return false;
+ }
+ $size = filesize($data);
+ } else {
+ $size = strlen($data);
+ }
+
+ $sent = 0;
+ $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
+
+ $sftp_packet_size = 4096; // PuTTY uses 4096
+ $i = 0;
+ while ($sent < $size) {
+ $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size);
+ $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $offset + $sent, strlen($temp), $temp);
+ if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
+ fclose($fp);
+ return false;
+ }
+ $sent+= strlen($temp);
+
+ $i++;
+
+ if ($i == 50) {
+ if (!$this->_read_put_responses($i)) {
+ $i = 0;
+ break;
+ }
+ $i = 0;
+ }
+ }
+
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+
+ if ($mode & NET_SFTP_LOCAL_FILE) {
+ fclose($fp);
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Reads multiple successive SSH_FXP_WRITE responses
+ *
+ * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
+ * SSH_FXP_WRITEs, in succession, and then reading $i responses.
+ *
+ * @param Integer $i
+ * @return Boolean
+ * @access private
+ */
+ function _read_put_responses($i)
+ {
+ while ($i--) {
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ break;
+ }
+ }
+
+ return $i < 0;
+ }
+
+ /**
+ * Downloads a file from the SFTP server.
+ *
+ * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
+ * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
+ * operation
+ *
+ * @param String $remote_file
+ * @param optional String $local_file
+ * @return Mixed
+ * @access public
+ */
+ function get($remote_file, $local_file = false, $offset = 0, $length = -1)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $remote_file = $this->_realpath($remote_file);
+ if ($remote_file === false) {
+ return false;
+ }
+
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_HANDLE:
+ $handle = substr($response, 4);
+ break;
+ case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
+ $this->_logError($response);
+ return false;
+ default:
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
+ return false;
+ }
+
+ if ($local_file !== false) {
+ $fp = fopen($local_file, 'wb');
+ if (!$fp) {
+ return false;
+ }
+ } else {
+ $content = '';
+ }
+
+ $size = (1 << 20) < $length || $length < 0 ? 1 << 20 : $length;
+ $start = $offset;
+ while (true) {
+ $packet = pack('Na*N3', strlen($handle), $handle, 0, $offset, $size);
+ if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
+ if ($local_file !== false) {
+ fclose($fp);
+ }
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ switch ($this->packet_type) {
+ case NET_SFTP_DATA:
+ $temp = substr($response, 4);
+ $offset+= strlen($temp);
+ if ($local_file === false) {
+ $content.= $temp;
+ } else {
+ fputs($fp, $temp);
+ }
+ break;
+ case NET_SFTP_STATUS:
+ $this->_logError($response);
+ break 2;
+ default:
+ user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS');
+ if ($local_file !== false) {
+ fclose($fp);
+ }
+ return false;
+ }
+
+ if ($length > 0 && $length <= $offset - $size) {
+ if ($local_file === false) {
+ $content = substr($content, 0, $length);
+ } else {
+ ftruncate($fp, $length);
+ }
+ break;
+ }
+ }
+
+ if ($local_file !== false) {
+ fclose($fp);
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ if (isset($content)) {
+ return $content;
+ }
+
+ return true;
+ }
+
+ /**
+ * Deletes a file on the SFTP server.
+ *
+ * @param String $path
+ * @param Boolean $recursive
+ * @return Boolean
+ * @access public
+ */
+ function delete($path, $recursive = true)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $path = $this->_realpath($path);
+ if ($path === false) {
+ return false;
+ }
+
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
+ if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ if (!$recursive) {
+ return false;
+ }
+ $i = 0;
+ $result = $this->_delete_recursive($path, $i);
+ $this->_read_put_responses($i);
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Recursively deletes directories on the SFTP server
+ *
+ * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
+ *
+ * @param String $path
+ * @param Integer $i
+ * @return Boolean
+ * @access private
+ */
+ function _delete_recursive($path, &$i)
+ {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ $entries = $this->_list($path, true, false);
+
+ // normally $entries would have at least . and .. but it might not if the directories
+ // permissions didn't allow reading
+ if (empty($entries)) {
+ return false;
+ }
+
+ foreach ($entries as $filename=>$props) {
+ if ($filename == '.' || $filename == '..') {
+ continue;
+ }
+
+ if (!isset($props['type'])) {
+ return false;
+ }
+
+ $temp = $path . '/' . $filename;
+ if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
+ if (!$this->_delete_recursive($temp, $i)) {
+ return false;
+ }
+ } else {
+ if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
+ return false;
+ }
+
+ $i++;
+
+ if ($i >= 50) {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ }
+ }
+ }
+
+ if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
+ return false;
+ }
+ $this->_remove_dir($path);
+
+ $i++;
+
+ if ($i >= 50) {
+ if (!$this->_read_put_responses($i)) {
+ return false;
+ }
+ $i = 0;
+ }
+
+ return true;
+ }
+
+ /**
+ * Renames a file or a directory on the SFTP server
+ *
+ * @param String $oldname
+ * @param String $newname
+ * @return Boolean
+ * @access public
+ */
+ function rename($oldname, $newname)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ $oldname = $this->_realpath($oldname);
+ $newname = $this->_realpath($newname);
+ if ($oldname === false || $newname === false) {
+ return false;
+ }
+
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
+ $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
+ if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
+ return false;
+ }
+
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Parse Attributes
+ *
+ * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
+ *
+ * @param String $response
+ * @return Array
+ * @access private
+ */
+ function _parseAttributes(&$response)
+ {
+ $attr = array();
+ extract(unpack('Nflags', $this->_string_shift($response, 4)));
+ // SFTPv4+ have a type field (a byte) that follows the above flag field
+ foreach ($this->attributes as $key => $value) {
+ switch ($flags & $key) {
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
+ // size is represented by a 64-bit integer, so we perhaps ought to be doing the following:
+ // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256);
+ // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB
+ // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than
+ // 4GB as being 4GB.
+ extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8)));
+ if ($upper) {
+ $attr['size'] = 0xFFFFFFFF;
+ } else {
+ $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
+ }
+ break;
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
+ $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
+ break;
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
+ $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
+ break;
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
+ $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
+ break;
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ for ($i = 0; $i < $count; $i++) {
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $key = $this->_string_shift($response, $length);
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $attr[$key] = $this->_string_shift($response, $length);
+ }
+ }
+ }
+ return $attr;
+ }
+
+ /**
+ * Parse Longname
+ *
+ * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open
+ * a file as a directory and see if an error is returned or you could try to parse the
+ * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does.
+ * The result is returned using the
+ * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
+ *
+ * If the longname is in an unrecognized format bool(false) is returned.
+ *
+ * @param String $longname
+ * @return Mixed
+ * @access private
+ */
+ function _parseLongname($longname)
+ {
+ // http://en.wikipedia.org/wiki/Unix_file_types
+ // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
+ if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
+ switch ($longname[0]) {
+ case '-':
+ return NET_SFTP_TYPE_REGULAR;
+ case 'd':
+ return NET_SFTP_TYPE_DIRECTORY;
+ case 'l':
+ return NET_SFTP_TYPE_SYMLINK;
+ default:
+ return NET_SFTP_TYPE_SPECIAL;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sends SFTP Packets
+ *
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
+ *
+ * @param Integer $type
+ * @param String $data
+ * @see Net_SFTP::_get_sftp_packet()
+ * @see Net_SSH2::_send_channel_packet()
+ * @return Boolean
+ * @access private
+ */
+ function _send_sftp_packet($type, $data)
+ {
+ $packet = $this->request_id !== false ?
+ pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
+ pack('NCa*', strlen($data) + 1, $type, $data);
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet);
+ $stop = strtok(microtime(), ' ') + strtok('');
+
+ if (defined('NET_SFTP_LOGGING')) {
+ $packet_type = '-> ' . $this->packet_types[$type] .
+ ' (' . round($stop - $start, 4) . 's)';
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
+ echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
+ flush();
+ ob_flush();
+ } else {
+ $this->packet_type_log[] = $packet_type;
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
+ $this->packet_log[] = $data;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Receives SFTP Packets
+ *
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
+ *
+ * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
+ * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
+ * messages containing one SFTP packet.
+ *
+ * @see Net_SFTP::_send_sftp_packet()
+ * @return String
+ * @access private
+ */
+ function _get_sftp_packet()
+ {
+ $this->curTimeout = false;
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+
+ // SFTP packet length
+ while (strlen($this->packet_buffer) < 4) {
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
+ if (is_bool($temp)) {
+ $this->packet_type = false;
+ $this->packet_buffer = '';
+ return false;
+ }
+ $this->packet_buffer.= $temp;
+ }
+ extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
+ $tempLength = $length;
+ $tempLength-= strlen($this->packet_buffer);
+
+ // SFTP packet type and data payload
+ while ($tempLength > 0) {
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
+ if (is_bool($temp)) {
+ $this->packet_type = false;
+ $this->packet_buffer = '';
+ return false;
+ }
+ $this->packet_buffer.= $temp;
+ $tempLength-= strlen($temp);
+ }
+
+ $stop = strtok(microtime(), ' ') + strtok('');
+
+ $this->packet_type = ord($this->_string_shift($this->packet_buffer));
+
+ if ($this->request_id !== false) {
+ $this->_string_shift($this->packet_buffer, 4); // remove the request id
+ $length-= 5; // account for the request id and the packet type
+ } else {
+ $length-= 1; // account for the packet type
+ }
+
+ $packet = $this->_string_shift($this->packet_buffer, $length);
+
+ if (defined('NET_SFTP_LOGGING')) {
+ $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
+ ' (' . round($stop - $start, 4) . 's)';
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
+ echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
+ flush();
+ ob_flush();
+ } else {
+ $this->packet_type_log[] = $packet_type;
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
+ $this->packet_log[] = $packet;
+ }
+ }
+ }
+
+ return $packet;
+ }
+
+ /**
+ * Returns a log of the packets that have been sent and received.
+ *
+ * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
+ *
+ * @access public
+ * @return String or Array
+ */
+ function getSFTPLog()
+ {
+ if (!defined('NET_SFTP_LOGGING')) {
+ return false;
+ }
+
+ switch (NET_SFTP_LOGGING) {
+ case NET_SFTP_LOG_COMPLEX:
+ return $this->_format_log($this->packet_log, $this->packet_type_log);
+ break;
+ //case NET_SFTP_LOG_SIMPLE:
+ default:
+ return $this->packet_type_log;
+ }
+ }
+
+ /**
+ * Returns all errors
+ *
+ * @return String
+ * @access public
+ */
+ function getSFTPErrors()
+ {
+ return $this->sftp_errors;
+ }
+
+ /**
+ * Returns the last error
+ *
+ * @return String
+ * @access public
+ */
+ function getLastSFTPError()
+ {
+ return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
+ }
+
+ /**
+ * Get supported SFTP versions
+ *
+ * @return Array
+ * @access public
+ */
+ function getSupportedVersions()
+ {
+ $temp = array('version' => $this->version);
+ if (isset($this->extensions['versions'])) {
+ $temp['extensions'] = $this->extensions['versions'];
+ }
+ return $temp;
+ }
+
+ /**
+ * Disconnect
+ *
+ * @param Integer $reason
+ * @return Boolean
+ * @access private
+ */
+ function _disconnect($reason)
+ {
+ $this->pwd = false;
+ parent::_disconnect($reason);
+ }
+} \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php
new file mode 100644
index 00000000000..8f5c79938e4
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php
@@ -0,0 +1,1577 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of SSHv1.
+ *
+ * PHP versions 4 and 5
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include('Net/SSH1.php');
+ *
+ * $ssh = new Net_SSH1('www.domain.tld');
+ * if (!$ssh->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->exec('ls -la');
+ * ?>
+ * </code>
+ *
+ * Here's another short example:
+ * <code>
+ * <?php
+ * include('Net/SSH1.php');
+ *
+ * $ssh = new Net_SSH1('www.domain.tld');
+ * if (!$ssh->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->read('username@username:~$');
+ * $ssh->write("ls -la\n");
+ * echo $ssh->read('username@username:~$');
+ * ?>
+ * </code>
+ *
+ * More information on the SSHv1 specification can be found by reading
+ * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Net
+ * @package Net_SSH1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Math_BigInteger
+ *
+ * Used to do RSA encryption.
+ */
+if (!class_exists('Math_BigInteger')) {
+ require_once('Math/BigInteger.php');
+}
+
+/**
+ * Include Crypt_Null
+ */
+//require_once('Crypt/Null.php');
+
+/**
+ * Include Crypt_DES
+ */
+if (!class_exists('Crypt_DES')) {
+ require_once('Crypt/DES.php');
+}
+
+/**
+ * Include Crypt_TripleDES
+ */
+if (!class_exists('Crypt_TripleDES')) {
+ require_once('Crypt/TripleDES.php');
+}
+
+/**
+ * Include Crypt_RC4
+ */
+if (!class_exists('Crypt_RC4')) {
+ require_once('Crypt/RC4.php');
+}
+
+/**
+ * Include Crypt_Random
+ */
+// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
+// will trigger a call to __autoload() if you're wanting to auto-load classes
+// call function_exists() a second time to stop the require_once from being called outside
+// of the auto loader
+if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) {
+ require_once('Crypt/Random.php');
+}
+
+/**#@+
+ * Encryption Methods
+ *
+ * @see Net_SSH1::getSupportedCiphers()
+ * @access public
+ */
+/**
+ * No encryption
+ *
+ * Not supported.
+ */
+define('NET_SSH1_CIPHER_NONE', 0);
+/**
+ * IDEA in CFB mode
+ *
+ * Not supported.
+ */
+define('NET_SSH1_CIPHER_IDEA', 1);
+/**
+ * DES in CBC mode
+ */
+define('NET_SSH1_CIPHER_DES', 2);
+/**
+ * Triple-DES in CBC mode
+ *
+ * All implementations are required to support this
+ */
+define('NET_SSH1_CIPHER_3DES', 3);
+/**
+ * TRI's Simple Stream encryption CBC
+ *
+ * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h),
+ * although it doesn't use it (see cipher.c)
+ */
+define('NET_SSH1_CIPHER_BROKEN_TSS', 4);
+/**
+ * RC4
+ *
+ * Not supported.
+ *
+ * @internal According to the SSH1 specs:
+ *
+ * "The first 16 bytes of the session key are used as the key for
+ * the server to client direction. The remaining 16 bytes are used
+ * as the key for the client to server direction. This gives
+ * independent 128-bit keys for each direction."
+ *
+ * This library currently only supports encryption when the same key is being used for both directions. This is
+ * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps).
+ */
+define('NET_SSH1_CIPHER_RC4', 5);
+/**
+ * Blowfish
+ *
+ * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and
+ * uses it (see cipher.c)
+ */
+define('NET_SSH1_CIPHER_BLOWFISH', 6);
+/**#@-*/
+
+/**#@+
+ * Authentication Methods
+ *
+ * @see Net_SSH1::getSupportedAuthentications()
+ * @access public
+ */
+/**
+ * .rhosts or /etc/hosts.equiv
+ */
+define('NET_SSH1_AUTH_RHOSTS', 1);
+/**
+ * pure RSA authentication
+ */
+define('NET_SSH1_AUTH_RSA', 2);
+/**
+ * password authentication
+ *
+ * This is the only method that is supported by this library.
+ */
+define('NET_SSH1_AUTH_PASSWORD', 3);
+/**
+ * .rhosts with RSA host authentication
+ */
+define('NET_SSH1_AUTH_RHOSTS_RSA', 4);
+/**#@-*/
+
+/**#@+
+ * Terminal Modes
+ *
+ * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
+ * @access private
+ */
+define('NET_SSH1_TTY_OP_END', 0);
+/**#@-*/
+
+/**
+ * The Response Type
+ *
+ * @see Net_SSH1::_get_binary_packet()
+ * @access private
+ */
+define('NET_SSH1_RESPONSE_TYPE', 1);
+
+/**
+ * The Response Data
+ *
+ * @see Net_SSH1::_get_binary_packet()
+ * @access private
+ */
+define('NET_SSH1_RESPONSE_DATA', 2);
+
+/**#@+
+ * Execution Bitmap Masks
+ *
+ * @see Net_SSH1::bitmap
+ * @access private
+ */
+define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001);
+define('NET_SSH1_MASK_LOGIN', 0x00000002);
+define('NET_SSH1_MASK_SHELL', 0x00000004);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Net_SSH1::getLog()
+ */
+/**
+ * Returns the message numbers
+ */
+define('NET_SSH1_LOG_SIMPLE', 1);
+/**
+ * Returns the message content
+ */
+define('NET_SSH1_LOG_COMPLEX', 2);
+/**
+ * Outputs the content real-time
+ */
+define('NET_SSH2_LOG_REALTIME', 3);
+/**
+ * Dumps the content real-time to a file
+ */
+define('NET_SSH2_LOG_REALTIME_FILE', 4);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Net_SSH1::read()
+ */
+/**
+ * Returns when a string matching $expect exactly is found
+ */
+define('NET_SSH1_READ_SIMPLE', 1);
+/**
+ * Returns when a string matching the regular expression $expect is found
+ */
+define('NET_SSH1_READ_REGEX', 2);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of SSHv1.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Net_SSH1
+ */
+class Net_SSH1 {
+ /**
+ * The SSH identifier
+ *
+ * @var String
+ * @access private
+ */
+ var $identifier = 'SSH-1.5-phpseclib';
+
+ /**
+ * The Socket Object
+ *
+ * @var Object
+ * @access private
+ */
+ var $fsock;
+
+ /**
+ * The cryptography object
+ *
+ * @var Object
+ * @access private
+ */
+ var $crypto = false;
+
+ /**
+ * Execution Bitmap
+ *
+ * The bits that are set represent functions that have been called already. This is used to determine
+ * if a requisite function has been successfully executed. If not, an error should be thrown.
+ *
+ * @var Integer
+ * @access private
+ */
+ var $bitmap = 0;
+
+ /**
+ * The Server Key Public Exponent
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getServerKeyPublicExponent()
+ * @var String
+ * @access private
+ */
+ var $server_key_public_exponent;
+
+ /**
+ * The Server Key Public Modulus
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getServerKeyPublicModulus()
+ * @var String
+ * @access private
+ */
+ var $server_key_public_modulus;
+
+ /**
+ * The Host Key Public Exponent
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getHostKeyPublicExponent()
+ * @var String
+ * @access private
+ */
+ var $host_key_public_exponent;
+
+ /**
+ * The Host Key Public Modulus
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getHostKeyPublicModulus()
+ * @var String
+ * @access private
+ */
+ var $host_key_public_modulus;
+
+ /**
+ * Supported Ciphers
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getSupportedCiphers()
+ * @var Array
+ * @access private
+ */
+ var $supported_ciphers = array(
+ NET_SSH1_CIPHER_NONE => 'No encryption',
+ NET_SSH1_CIPHER_IDEA => 'IDEA in CFB mode',
+ NET_SSH1_CIPHER_DES => 'DES in CBC mode',
+ NET_SSH1_CIPHER_3DES => 'Triple-DES in CBC mode',
+ NET_SSH1_CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
+ NET_SSH1_CIPHER_RC4 => 'RC4',
+ NET_SSH1_CIPHER_BLOWFISH => 'Blowfish'
+ );
+
+ /**
+ * Supported Authentications
+ *
+ * Logged for debug purposes
+ *
+ * @see Net_SSH1::getSupportedAuthentications()
+ * @var Array
+ * @access private
+ */
+ var $supported_authentications = array(
+ NET_SSH1_AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv',
+ NET_SSH1_AUTH_RSA => 'pure RSA authentication',
+ NET_SSH1_AUTH_PASSWORD => 'password authentication',
+ NET_SSH1_AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
+ );
+
+ /**
+ * Server Identification
+ *
+ * @see Net_SSH1::getServerIdentification()
+ * @var String
+ * @access private
+ */
+ var $server_identification = '';
+
+ /**
+ * Protocol Flags
+ *
+ * @see Net_SSH1::Net_SSH1()
+ * @var Array
+ * @access private
+ */
+ var $protocol_flags = array();
+
+ /**
+ * Protocol Flag Log
+ *
+ * @see Net_SSH1::getLog()
+ * @var Array
+ * @access private
+ */
+ var $protocol_flag_log = array();
+
+ /**
+ * Message Log
+ *
+ * @see Net_SSH1::getLog()
+ * @var Array
+ * @access private
+ */
+ var $message_log = array();
+
+ /**
+ * Real-time log file pointer
+ *
+ * @see Net_SSH1::_append_log()
+ * @var Resource
+ * @access private
+ */
+ var $realtime_log_file;
+
+ /**
+ * Real-time log file size
+ *
+ * @see Net_SSH1::_append_log()
+ * @var Integer
+ * @access private
+ */
+ var $realtime_log_size;
+
+ /**
+ * Real-time log file wrap boolean
+ *
+ * @see Net_SSH1::_append_log()
+ * @var Boolean
+ * @access private
+ */
+ var $realtime_log_wrap;
+
+ /**
+ * Interactive Buffer
+ *
+ * @see Net_SSH1::read()
+ * @var Array
+ * @access private
+ */
+ var $interactiveBuffer = '';
+
+ /**
+ * Timeout
+ *
+ * @see Net_SSH1::setTimeout()
+ * @access private
+ */
+ var $timeout;
+
+ /**
+ * Current Timeout
+ *
+ * @see Net_SSH2::_get_channel_packet()
+ * @access private
+ */
+ var $curTimeout;
+
+ /**
+ * Default Constructor.
+ *
+ * Connects to an SSHv1 server
+ *
+ * @param String $host
+ * @param optional Integer $port
+ * @param optional Integer $timeout
+ * @param optional Integer $cipher
+ * @return Net_SSH1
+ * @access public
+ */
+ function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
+ {
+ $this->protocol_flags = array(
+ 1 => 'NET_SSH1_MSG_DISCONNECT',
+ 2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
+ 3 => 'NET_SSH1_CMSG_SESSION_KEY',
+ 4 => 'NET_SSH1_CMSG_USER',
+ 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
+ 10 => 'NET_SSH1_CMSG_REQUEST_PTY',
+ 12 => 'NET_SSH1_CMSG_EXEC_SHELL',
+ 13 => 'NET_SSH1_CMSG_EXEC_CMD',
+ 14 => 'NET_SSH1_SMSG_SUCCESS',
+ 15 => 'NET_SSH1_SMSG_FAILURE',
+ 16 => 'NET_SSH1_CMSG_STDIN_DATA',
+ 17 => 'NET_SSH1_SMSG_STDOUT_DATA',
+ 18 => 'NET_SSH1_SMSG_STDERR_DATA',
+ 19 => 'NET_SSH1_CMSG_EOF',
+ 20 => 'NET_SSH1_SMSG_EXITSTATUS',
+ 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
+ );
+
+ $this->_define_array($this->protocol_flags);
+
+ $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
+ if (!$this->fsock) {
+ user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
+ return;
+ }
+
+ $this->server_identification = $init_line = fgets($this->fsock, 255);
+
+ if (defined('NET_SSH1_LOGGING')) {
+ $this->_append_log('<-', $this->server_identification);
+ $this->_append_log('->', $this->identifier . "\r\n");
+ }
+
+ if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
+ user_error('Can only connect to SSH servers');
+ return;
+ }
+ if ($parts[1][0] != 1) {
+ user_error("Cannot connect to SSH $parts[1] servers");
+ return;
+ }
+
+ fputs($this->fsock, $this->identifier."\r\n");
+
+ $response = $this->_get_binary_packet();
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
+ user_error('Expected SSH_SMSG_PUBLIC_KEY');
+ return;
+ }
+
+ $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8);
+
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
+
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
+ $server_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
+ $this->server_key_public_exponent = $server_key_public_exponent;
+
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
+ $server_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
+ $this->server_key_public_modulus = $server_key_public_modulus;
+
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
+
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
+ $host_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
+ $this->host_key_public_exponent = $host_key_public_exponent;
+
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
+ $host_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
+ $this->host_key_public_modulus = $host_key_public_modulus;
+
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
+
+ // get a list of the supported ciphers
+ extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
+ foreach ($this->supported_ciphers as $mask=>$name) {
+ if (($supported_ciphers_mask & (1 << $mask)) == 0) {
+ unset($this->supported_ciphers[$mask]);
+ }
+ }
+
+ // get a list of the supported authentications
+ extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
+ foreach ($this->supported_authentications as $mask=>$name) {
+ if (($supported_authentications_mask & (1 << $mask)) == 0) {
+ unset($this->supported_authentications[$mask]);
+ }
+ }
+
+ $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
+
+ $session_key = crypt_random_string(32);
+ $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
+
+ if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
+ $double_encrypted_session_key = $this->_rsa_crypt(
+ $double_encrypted_session_key,
+ array(
+ $server_key_public_exponent,
+ $server_key_public_modulus
+ )
+ );
+ $double_encrypted_session_key = $this->_rsa_crypt(
+ $double_encrypted_session_key,
+ array(
+ $host_key_public_exponent,
+ $host_key_public_modulus
+ )
+ );
+ } else {
+ $double_encrypted_session_key = $this->_rsa_crypt(
+ $double_encrypted_session_key,
+ array(
+ $host_key_public_exponent,
+ $host_key_public_modulus
+ )
+ );
+ $double_encrypted_session_key = $this->_rsa_crypt(
+ $double_encrypted_session_key,
+ array(
+ $server_key_public_exponent,
+ $server_key_public_modulus
+ )
+ );
+ }
+
+ $cipher = isset($this->supported_ciphers[$cipher]) ? $cipher : NET_SSH1_CIPHER_3DES;
+ $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_SESSION_KEY');
+ return;
+ }
+
+ switch ($cipher) {
+ //case NET_SSH1_CIPHER_NONE:
+ // $this->crypto = new Crypt_Null();
+ // break;
+ case NET_SSH1_CIPHER_DES:
+ $this->crypto = new Crypt_DES();
+ $this->crypto->disablePadding();
+ $this->crypto->enableContinuousBuffer();
+ $this->crypto->setKey(substr($session_key, 0, 8));
+ break;
+ case NET_SSH1_CIPHER_3DES:
+ $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC);
+ $this->crypto->disablePadding();
+ $this->crypto->enableContinuousBuffer();
+ $this->crypto->setKey(substr($session_key, 0, 24));
+ break;
+ //case NET_SSH1_CIPHER_RC4:
+ // $this->crypto = new Crypt_RC4();
+ // $this->crypto->enableContinuousBuffer();
+ // $this->crypto->setKey(substr($session_key, 0, 16));
+ // break;
+ }
+
+ $response = $this->_get_binary_packet();
+
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
+ user_error('Expected SSH_SMSG_SUCCESS');
+ return;
+ }
+
+ $this->bitmap = NET_SSH1_MASK_CONSTRUCTOR;
+ }
+
+ /**
+ * Login
+ *
+ * @param String $username
+ * @param optional String $password
+ * @return Boolean
+ * @access public
+ */
+ function login($username, $password = '')
+ {
+ if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) {
+ return false;
+ }
+
+ $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_USER');
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+
+ if ($response === true) {
+ return false;
+ }
+ if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
+ $this->bitmap |= NET_SSH1_MASK_LOGIN;
+ return true;
+ } else if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
+ user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
+ return false;
+ }
+
+ $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
+ return false;
+ }
+
+ // remove the username and password from the last logged packet
+ if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
+ $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
+ $this->message_log[count($this->message_log) - 1] = $data;
+ }
+
+ $response = $this->_get_binary_packet();
+
+ if ($response === true) {
+ return false;
+ }
+ if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
+ $this->bitmap |= NET_SSH1_MASK_LOGIN;
+ return true;
+ } else if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
+ return false;
+ } else {
+ user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
+ return false;
+ }
+ }
+
+ /**
+ * Set Timeout
+ *
+ * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
+ * Setting $timeout to false or 0 will mean there is no timeout.
+ *
+ * @param Mixed $timeout
+ */
+ function setTimeout($timeout)
+ {
+ $this->timeout = $this->curTimeout = $timeout;
+ }
+
+ /**
+ * Executes a command on a non-interactive shell, returns the output, and quits.
+ *
+ * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2
+ * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a
+ * shell with the -s option, as discussed in the following links:
+ *
+ * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
+ * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
+ *
+ * To execute further commands, a new Net_SSH1 object will need to be created.
+ *
+ * Returns false on failure and the output, otherwise.
+ *
+ * @see Net_SSH1::interactiveRead()
+ * @see Net_SSH1::interactiveWrite()
+ * @param String $cmd
+ * @return mixed
+ * @access public
+ */
+ function exec($cmd, $block = true)
+ {
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_EXEC_CMD');
+ return false;
+ }
+
+ if (!$block) {
+ return true;
+ }
+
+ $output = '';
+ $response = $this->_get_binary_packet();
+
+ if ($response !== false) {
+ do {
+ $output.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
+ $response = $this->_get_binary_packet();
+ } while (is_array($response) && $response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
+ }
+
+ $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
+
+ // i don't think it's really all that important if this packet gets sent or not.
+ $this->_send_binary_packet($data);
+
+ fclose($this->fsock);
+
+ // reset the execution bitmap - a new Net_SSH1 object needs to be created.
+ $this->bitmap = 0;
+
+ return $output;
+ }
+
+ /**
+ * Creates an interactive shell
+ *
+ * @see Net_SSH1::interactiveRead()
+ * @see Net_SSH1::interactiveWrite()
+ * @return Boolean
+ * @access private
+ */
+ function _initShell()
+ {
+ // connect using the sample parameters in protocol-1.5.txt.
+ // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
+ // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
+ $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_REQUEST_PTY');
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+
+ if ($response === true) {
+ return false;
+ }
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
+ user_error('Expected SSH_SMSG_SUCCESS');
+ return false;
+ }
+
+ $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_EXEC_SHELL');
+ return false;
+ }
+
+ $this->bitmap |= NET_SSH1_MASK_SHELL;
+
+ //stream_set_blocking($this->fsock, 0);
+
+ return true;
+ }
+
+ /**
+ * Inputs a command into an interactive shell.
+ *
+ * @see Net_SSH1::interactiveWrite()
+ * @param String $cmd
+ * @return Boolean
+ * @access public
+ */
+ function write($cmd)
+ {
+ return $this->interactiveWrite($cmd);
+ }
+
+ /**
+ * Returns the output of an interactive shell when there's a match for $expect
+ *
+ * $expect can take the form of a string literal or, if $mode == NET_SSH1_READ_REGEX,
+ * a regular expression.
+ *
+ * @see Net_SSH1::write()
+ * @param String $expect
+ * @param Integer $mode
+ * @return Boolean
+ * @access public
+ */
+ function read($expect, $mode = NET_SSH1_READ_SIMPLE)
+ {
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
+ user_error('Unable to initiate an interactive shell session');
+ return false;
+ }
+
+ $match = $expect;
+ while (true) {
+ if ($mode == NET_SSH1_READ_REGEX) {
+ preg_match($expect, $this->interactiveBuffer, $matches);
+ $match = isset($matches[0]) ? $matches[0] : '';
+ }
+ $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
+ if ($pos !== false) {
+ return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
+ }
+ $response = $this->_get_binary_packet();
+
+ if ($response === true) {
+ return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
+ }
+ $this->interactiveBuffer.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
+ }
+ }
+
+ /**
+ * Inputs a command into an interactive shell.
+ *
+ * @see Net_SSH1::interactiveRead()
+ * @param String $cmd
+ * @return Boolean
+ * @access public
+ */
+ function interactiveWrite($cmd)
+ {
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
+ user_error('Unable to initiate an interactive shell session');
+ return false;
+ }
+
+ $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Error sending SSH_CMSG_STDIN');
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the output of an interactive shell when no more output is available.
+ *
+ * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
+ * "", you're seeing ANSI escape codes. According to
+ * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
+ * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
+ * there's not going to be much recourse.
+ *
+ * @see Net_SSH1::interactiveRead()
+ * @return String
+ * @access public
+ */
+ function interactiveRead()
+ {
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
+ user_error('Unable to initiate an interactive shell session');
+ return false;
+ }
+
+ $read = array($this->fsock);
+ $write = $except = null;
+ if (stream_select($read, $write, $except, 0)) {
+ $response = $this->_get_binary_packet();
+ return substr($response[NET_SSH1_RESPONSE_DATA], 4);
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Disconnect
+ *
+ * @access public
+ */
+ function disconnect()
+ {
+ $this->_disconnect();
+ }
+
+ /**
+ * Destructor.
+ *
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
+ * disconnect().
+ *
+ * @access public
+ */
+ function __destruct()
+ {
+ $this->_disconnect();
+ }
+
+ /**
+ * Disconnect
+ *
+ * @param String $msg
+ * @access private
+ */
+ function _disconnect($msg = 'Client Quit')
+ {
+ if ($this->bitmap) {
+ $data = pack('C', NET_SSH1_CMSG_EOF);
+ $this->_send_binary_packet($data);
+ /*
+ $response = $this->_get_binary_packet();
+ if ($response === true) {
+ $response = array(NET_SSH1_RESPONSE_TYPE => -1);
+ }
+ switch ($response[NET_SSH1_RESPONSE_TYPE]) {
+ case NET_SSH1_SMSG_EXITSTATUS:
+ $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
+ break;
+ default:
+ $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
+ }
+ */
+ $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
+
+ $this->_send_binary_packet($data);
+ fclose($this->fsock);
+ $this->bitmap = 0;
+ }
+ }
+
+ /**
+ * Gets Binary Packets
+ *
+ * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
+ *
+ * Also, this function could be improved upon by adding detection for the following exploit:
+ * http://www.securiteam.com/securitynews/5LP042K3FY.html
+ *
+ * @see Net_SSH1::_send_binary_packet()
+ * @return Array
+ * @access private
+ */
+ function _get_binary_packet()
+ {
+ if (feof($this->fsock)) {
+ //user_error('connection closed prematurely');
+ return false;
+ }
+
+ if ($this->curTimeout) {
+ $read = array($this->fsock);
+ $write = $except = NULL;
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $sec = floor($this->curTimeout);
+ $usec = 1000000 * ($this->curTimeout - $sec);
+ // on windows this returns a "Warning: Invalid CRT parameters detected" error
+ if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
+ //$this->_disconnect('Timeout');
+ return true;
+ }
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
+ $this->curTimeout-= $elapsed;
+ }
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $temp = unpack('Nlength', fread($this->fsock, 4));
+
+ $padding_length = 8 - ($temp['length'] & 7);
+ $length = $temp['length'] + $padding_length;
+
+ while ($length > 0) {
+ $temp = fread($this->fsock, $length);
+ $raw.= $temp;
+ $length-= strlen($temp);
+ }
+ $stop = strtok(microtime(), ' ') + strtok('');
+
+ if (strlen($raw) && $this->crypto !== false) {
+ $raw = $this->crypto->decrypt($raw);
+ }
+
+ $padding = substr($raw, 0, $padding_length);
+ $type = $raw[$padding_length];
+ $data = substr($raw, $padding_length + 1, -4);
+
+ $temp = unpack('Ncrc', substr($raw, -4));
+
+ //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
+ // user_error('Bad CRC in packet from server');
+ // return false;
+ //}
+
+ $type = ord($type);
+
+ if (defined('NET_SSH1_LOGGING')) {
+ $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
+ $temp = '<- ' . $temp .
+ ' (' . round($stop - $start, 4) . 's)';
+ $this->_append_log($temp, $data);
+ }
+
+ return array(
+ NET_SSH1_RESPONSE_TYPE => $type,
+ NET_SSH1_RESPONSE_DATA => $data
+ );
+ }
+
+ /**
+ * Sends Binary Packets
+ *
+ * Returns true on success, false on failure.
+ *
+ * @see Net_SSH1::_get_binary_packet()
+ * @param String $data
+ * @return Boolean
+ * @access private
+ */
+ function _send_binary_packet($data)
+ {
+ if (feof($this->fsock)) {
+ //user_error('connection closed prematurely');
+ return false;
+ }
+
+ $length = strlen($data) + 4;
+
+ $padding = crypt_random_string(8 - ($length & 7));
+
+ $orig = $data;
+ $data = $padding . $data;
+ $data.= pack('N', $this->_crc($data));
+
+ if ($this->crypto !== false) {
+ $data = $this->crypto->encrypt($data);
+ }
+
+ $packet = pack('Na*', $length, $data);
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $result = strlen($packet) == fputs($this->fsock, $packet);
+ $stop = strtok(microtime(), ' ') + strtok('');
+
+ if (defined('NET_SSH1_LOGGING')) {
+ $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
+ $temp = '-> ' . $temp .
+ ' (' . round($stop - $start, 4) . 's)';
+ $this->_append_log($temp, $orig);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Cyclic Redundancy Check (CRC)
+ *
+ * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
+ * we've reimplemented it. A more detailed discussion of the differences can be found after
+ * $crc_lookup_table's initialization.
+ *
+ * @see Net_SSH1::_get_binary_packet()
+ * @see Net_SSH1::_send_binary_packet()
+ * @param String $data
+ * @return Integer
+ * @access private
+ */
+ function _crc($data)
+ {
+ static $crc_lookup_table = array(
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+ 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+ 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+ 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+ 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+ 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+ 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+ 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+ 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+ 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+ 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+ 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+ 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+ 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+ 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ );
+
+ // For this function to yield the same output as PHP's crc32 function, $crc would have to be
+ // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
+ $crc = 0x00000000;
+ $length = strlen($data);
+
+ for ($i=0;$i<$length;$i++) {
+ // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
+ // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example,
+ // yields 0xFF800000 - not 0x00800000. The following link elaborates:
+ // http://www.php.net/manual/en/language.operators.bitwise.php#57281
+ $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
+ }
+
+ // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
+ // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
+ return $crc;
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+
+ /**
+ * RSA Encrypt
+ *
+ * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
+ * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that
+ * calls this call modexp, instead, but I think this makes things clearer, maybe...
+ *
+ * @see Net_SSH1::Net_SSH1()
+ * @param Math_BigInteger $m
+ * @param Array $key
+ * @return Math_BigInteger
+ * @access private
+ */
+ function _rsa_crypt($m, $key)
+ {
+ /*
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+
+ $rsa = new Crypt_RSA();
+ $rsa->loadKey($key, CRYPT_RSA_PUBLIC_FORMAT_RAW);
+ $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
+ return $rsa->encrypt($m);
+ */
+
+ // To quote from protocol-1.5.txt:
+ // The most significant byte (which is only partial as the value must be
+ // less than the public modulus, which is never a power of two) is zero.
+ //
+ // The next byte contains the value 2 (which stands for public-key
+ // encrypted data in the PKCS standard [PKCS#1]). Then, there are non-
+ // zero random bytes to fill any unused space, a zero byte, and the data
+ // to be encrypted in the least significant bytes, the last byte of the
+ // data in the least significant byte.
+
+ // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
+ // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
+ // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
+ $modulus = $key[1]->toBytes();
+ $length = strlen($modulus) - strlen($m) - 3;
+ $random = '';
+ while (strlen($random) != $length) {
+ $block = crypt_random_string($length - strlen($random));
+ $block = str_replace("\x00", '', $block);
+ $random.= $block;
+ }
+ $temp = chr(0) . chr(2) . $random . chr(0) . $m;
+
+ $m = new Math_BigInteger($temp, 256);
+ $m = $m->modPow($key[0], $key[1]);
+
+ return $m->toBytes();
+ }
+
+ /**
+ * Define Array
+ *
+ * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
+ * named constants from it, using the value as the name of the constant and the index as the value of the constant.
+ * If any of the constants that would be defined already exists, none of the constants will be defined.
+ *
+ * @param Array $array
+ * @access private
+ */
+ function _define_array()
+ {
+ $args = func_get_args();
+ foreach ($args as $arg) {
+ foreach ($arg as $key=>$value) {
+ if (!defined($value)) {
+ define($value, $key);
+ } else {
+ break 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a log of the packets that have been sent and received.
+ *
+ * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
+ *
+ * @access public
+ * @return String or Array
+ */
+ function getLog()
+ {
+ if (!defined('NET_SSH1_LOGGING')) {
+ return false;
+ }
+
+ switch (NET_SSH1_LOGGING) {
+ case NET_SSH1_LOG_SIMPLE:
+ return $this->message_number_log;
+ break;
+ case NET_SSH1_LOG_COMPLEX:
+ return $this->_format_log($this->message_log, $this->protocol_flags_log);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Formats a log for printing
+ *
+ * @param Array $message_log
+ * @param Array $message_number_log
+ * @access private
+ * @return String
+ */
+ function _format_log($message_log, $message_number_log)
+ {
+ static $boundary = ':', $long_width = 65, $short_width = 16;
+
+ $output = '';
+ for ($i = 0; $i < count($message_log); $i++) {
+ $output.= $message_number_log[$i] . "\r\n";
+ $current_log = $message_log[$i];
+ $j = 0;
+ do {
+ if (strlen($current_log)) {
+ $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
+ }
+ $fragment = $this->_string_shift($current_log, $short_width);
+ $hex = substr(
+ preg_replace(
+ '#(.)#es',
+ '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
+ $fragment),
+ strlen($boundary)
+ );
+ // replace non ASCII printable characters with dots
+ // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
+ // also replace < with a . since < messes up the output on web browsers
+ $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
+ $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
+ $j++;
+ } while (strlen($current_log));
+ $output.= "\r\n";
+ }
+
+ return $output;
+ }
+
+ /**
+ * Return the server key public exponent
+ *
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
+ * the raw bytes. This behavior is similar to PHP's md5() function.
+ *
+ * @param optional Boolean $raw_output
+ * @return String
+ * @access public
+ */
+ function getServerKeyPublicExponent($raw_output = false)
+ {
+ return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
+ }
+
+ /**
+ * Return the server key public modulus
+ *
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
+ * the raw bytes. This behavior is similar to PHP's md5() function.
+ *
+ * @param optional Boolean $raw_output
+ * @return String
+ * @access public
+ */
+ function getServerKeyPublicModulus($raw_output = false)
+ {
+ return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
+ }
+
+ /**
+ * Return the host key public exponent
+ *
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
+ * the raw bytes. This behavior is similar to PHP's md5() function.
+ *
+ * @param optional Boolean $raw_output
+ * @return String
+ * @access public
+ */
+ function getHostKeyPublicExponent($raw_output = false)
+ {
+ return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
+ }
+
+ /**
+ * Return the host key public modulus
+ *
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
+ * the raw bytes. This behavior is similar to PHP's md5() function.
+ *
+ * @param optional Boolean $raw_output
+ * @return String
+ * @access public
+ */
+ function getHostKeyPublicModulus($raw_output = false)
+ {
+ return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
+ }
+
+ /**
+ * Return a list of ciphers supported by SSH1 server.
+ *
+ * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
+ * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll
+ * get array(NET_SSH1_CIPHER_3DES).
+ *
+ * @param optional Boolean $raw_output
+ * @return Array
+ * @access public
+ */
+ function getSupportedCiphers($raw_output = false)
+ {
+ return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
+ }
+
+ /**
+ * Return a list of authentications supported by SSH1 server.
+ *
+ * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
+ * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll
+ * get array(NET_SSH1_AUTH_PASSWORD).
+ *
+ * @param optional Boolean $raw_output
+ * @return Array
+ * @access public
+ */
+ function getSupportedAuthentications($raw_output = false)
+ {
+ return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
+ }
+
+ /**
+ * Return the server identification.
+ *
+ * @return String
+ * @access public
+ */
+ function getServerIdentification()
+ {
+ return rtrim($this->server_identification);
+ }
+
+ /**
+ * Logs data packets
+ *
+ * Makes sure that only the last 1MB worth of packets will be logged
+ *
+ * @param String $data
+ * @access private
+ */
+ function _append_log($protocol_flags, $message)
+ {
+ switch (NET_SSH1_LOGGING) {
+ // useful for benchmarks
+ case NET_SSH1_LOG_SIMPLE:
+ $this->protocol_flags_log[] = $protocol_flags;
+ break;
+ // the most useful log for SSH1
+ case NET_SSH1_LOG_COMPLEX:
+ $this->protocol_flags_log[] = $protocol_flags;
+ $this->_string_shift($message);
+ $this->log_size+= strlen($message);
+ $this->message_log[] = $message;
+ while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
+ $this->log_size-= strlen(array_shift($this->message_log));
+ array_shift($this->protocol_flags_log);
+ }
+ break;
+ // dump the output out realtime; packets may be interspersed with non packets,
+ // passwords won't be filtered out and select other packets may not be correctly
+ // identified
+ case NET_SSH1_LOG_REALTIME:
+ echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
+ @flush();
+ @ob_flush();
+ break;
+ // basically the same thing as NET_SSH1_LOG_REALTIME with the caveat that NET_SSH1_LOG_REALTIME_FILE
+ // needs to be defined and that the resultant log file will be capped out at NET_SSH1_LOG_MAX_SIZE.
+ // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
+ // at the beginning of the file
+ case NET_SSH1_LOG_REALTIME_FILE:
+ if (!isset($this->realtime_log_file)) {
+ // PHP doesn't seem to like using constants in fopen()
+ $filename = NET_SSH2_LOG_REALTIME_FILE;
+ $fp = fopen($filename, 'w');
+ $this->realtime_log_file = $fp;
+ }
+ if (!is_resource($this->realtime_log_file)) {
+ break;
+ }
+ $entry = $this->_format_log(array($message), array($protocol_flags));
+ if ($this->realtime_log_wrap) {
+ $temp = "<<< START >>>\r\n";
+ $entry.= $temp;
+ fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
+ }
+ $this->realtime_log_size+= strlen($entry);
+ if ($this->realtime_log_size > NET_SSH1_LOG_MAX_SIZE) {
+ fseek($this->realtime_log_file, 0);
+ $this->realtime_log_size = strlen($entry);
+ $this->realtime_log_wrap = true;
+ }
+ fputs($this->realtime_log_file, $entry);
+ }
+ }
+} \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php
new file mode 100644
index 00000000000..43bfbca2dbe
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php
@@ -0,0 +1,3009 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pure-PHP implementation of SSHv2.
+ *
+ * PHP versions 4 and 5
+ *
+ * Here are some examples of how to use this library:
+ * <code>
+ * <?php
+ * include('Net/SSH2.php');
+ *
+ * $ssh = new Net_SSH2('www.domain.tld');
+ * if (!$ssh->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->exec('pwd');
+ * echo $ssh->exec('ls -la');
+ * ?>
+ * </code>
+ *
+ * <code>
+ * <?php
+ * include('Crypt/RSA.php');
+ * include('Net/SSH2.php');
+ *
+ * $key = new Crypt_RSA();
+ * //$key->setPassword('whatever');
+ * $key->loadKey(file_get_contents('privatekey'));
+ *
+ * $ssh = new Net_SSH2('www.domain.tld');
+ * if (!$ssh->login('username', $key)) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->read('username@username:~$');
+ * $ssh->write("ls -la\n");
+ * echo $ssh->read('username@username:~$');
+ * ?>
+ * </code>
+ *
+ * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @category Net
+ * @package Net_SSH2
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright MMVII Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $
+ * @link http://phpseclib.sourceforge.net
+ */
+
+/**
+ * Include Math_BigInteger
+ *
+ * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
+ */
+if (!class_exists('Math_BigInteger')) {
+ require_once('Math/BigInteger.php');
+}
+
+/**
+ * Include Crypt_Random
+ */
+// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
+// will trigger a call to __autoload() if you're wanting to auto-load classes
+// call function_exists() a second time to stop the require_once from being called outside
+// of the auto loader
+if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) {
+ require_once('Crypt/Random.php');
+}
+
+/**
+ * Include Crypt_Hash
+ */
+if (!class_exists('Crypt_Hash')) {
+ require_once('Crypt/Hash.php');
+}
+
+/**
+ * Include Crypt_TripleDES
+ */
+if (!class_exists('Crypt_TripleDES')) {
+ require_once('Crypt/TripleDES.php');
+}
+
+/**
+ * Include Crypt_RC4
+ */
+if (!class_exists('Crypt_RC4')) {
+ require_once('Crypt/RC4.php');
+}
+
+/**
+ * Include Crypt_AES
+ */
+if (!class_exists('Crypt_AES')) {
+ require_once('Crypt/AES.php');
+}
+
+/**#@+
+ * Execution Bitmap Masks
+ *
+ * @see Net_SSH2::bitmap
+ * @access private
+ */
+define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
+define('NET_SSH2_MASK_LOGIN', 0x00000002);
+define('NET_SSH2_MASK_SHELL', 0x00000004);
+/**#@-*/
+
+/**#@+
+ * Channel constants
+ *
+ * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
+ * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
+ * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
+ * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
+ * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
+ * The 'recipient channel' is the channel number given in the original
+ * open request, and 'sender channel' is the channel number allocated by
+ * the other side.
+ *
+ * @see Net_SSH2::_send_channel_packet()
+ * @see Net_SSH2::_get_channel_packet()
+ * @access private
+ */
+define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
+define('NET_SSH2_CHANNEL_SHELL',1);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Net_SSH2::getLog()
+ */
+/**
+ * Returns the message numbers
+ */
+define('NET_SSH2_LOG_SIMPLE', 1);
+/**
+ * Returns the message content
+ */
+define('NET_SSH2_LOG_COMPLEX', 2);
+/**
+ * Outputs the content real-time
+ */
+define('NET_SSH2_LOG_REALTIME', 3);
+/**
+ * Dumps the content real-time to a file
+ */
+define('NET_SSH2_LOG_REALTIME_FILE', 4);
+/**#@-*/
+
+/**#@+
+ * @access public
+ * @see Net_SSH2::read()
+ */
+/**
+ * Returns when a string matching $expect exactly is found
+ */
+define('NET_SSH2_READ_SIMPLE', 1);
+/**
+ * Returns when a string matching the regular expression $expect is found
+ */
+define('NET_SSH2_READ_REGEX', 2);
+/**
+ * Make sure that the log never gets larger than this
+ */
+define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
+/**#@-*/
+
+/**
+ * Pure-PHP implementation of SSHv2.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @version 0.1.0
+ * @access public
+ * @package Net_SSH2
+ */
+class Net_SSH2 {
+ /**
+ * The SSH identifier
+ *
+ * @var String
+ * @access private
+ */
+ var $identifier = 'SSH-2.0-phpseclib_0.3';
+
+ /**
+ * The Socket Object
+ *
+ * @var Object
+ * @access private
+ */
+ var $fsock;
+
+ /**
+ * Execution Bitmap
+ *
+ * The bits that are set represent functions that have been called already. This is used to determine
+ * if a requisite function has been successfully executed. If not, an error should be thrown.
+ *
+ * @var Integer
+ * @access private
+ */
+ var $bitmap = 0;
+
+ /**
+ * Error information
+ *
+ * @see Net_SSH2::getErrors()
+ * @see Net_SSH2::getLastError()
+ * @var String
+ * @access private
+ */
+ var $errors = array();
+
+ /**
+ * Server Identifier
+ *
+ * @see Net_SSH2::getServerIdentification()
+ * @var String
+ * @access private
+ */
+ var $server_identifier = '';
+
+ /**
+ * Key Exchange Algorithms
+ *
+ * @see Net_SSH2::getKexAlgorithims()
+ * @var Array
+ * @access private
+ */
+ var $kex_algorithms;
+
+ /**
+ * Server Host Key Algorithms
+ *
+ * @see Net_SSH2::getServerHostKeyAlgorithms()
+ * @var Array
+ * @access private
+ */
+ var $server_host_key_algorithms;
+
+ /**
+ * Encryption Algorithms: Client to Server
+ *
+ * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
+ * @var Array
+ * @access private
+ */
+ var $encryption_algorithms_client_to_server;
+
+ /**
+ * Encryption Algorithms: Server to Client
+ *
+ * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
+ * @var Array
+ * @access private
+ */
+ var $encryption_algorithms_server_to_client;
+
+ /**
+ * MAC Algorithms: Client to Server
+ *
+ * @see Net_SSH2::getMACAlgorithmsClient2Server()
+ * @var Array
+ * @access private
+ */
+ var $mac_algorithms_client_to_server;
+
+ /**
+ * MAC Algorithms: Server to Client
+ *
+ * @see Net_SSH2::getMACAlgorithmsServer2Client()
+ * @var Array
+ * @access private
+ */
+ var $mac_algorithms_server_to_client;
+
+ /**
+ * Compression Algorithms: Client to Server
+ *
+ * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
+ * @var Array
+ * @access private
+ */
+ var $compression_algorithms_client_to_server;
+
+ /**
+ * Compression Algorithms: Server to Client
+ *
+ * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
+ * @var Array
+ * @access private
+ */
+ var $compression_algorithms_server_to_client;
+
+ /**
+ * Languages: Server to Client
+ *
+ * @see Net_SSH2::getLanguagesServer2Client()
+ * @var Array
+ * @access private
+ */
+ var $languages_server_to_client;
+
+ /**
+ * Languages: Client to Server
+ *
+ * @see Net_SSH2::getLanguagesClient2Server()
+ * @var Array
+ * @access private
+ */
+ var $languages_client_to_server;
+
+ /**
+ * Block Size for Server to Client Encryption
+ *
+ * "Note that the length of the concatenation of 'packet_length',
+ * 'padding_length', 'payload', and 'random padding' MUST be a multiple
+ * of the cipher block size or 8, whichever is larger. This constraint
+ * MUST be enforced, even when using stream ciphers."
+ *
+ * -- http://tools.ietf.org/html/rfc4253#section-6
+ *
+ * @see Net_SSH2::Net_SSH2()
+ * @see Net_SSH2::_send_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $encrypt_block_size = 8;
+
+ /**
+ * Block Size for Client to Server Encryption
+ *
+ * @see Net_SSH2::Net_SSH2()
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $decrypt_block_size = 8;
+
+ /**
+ * Server to Client Encryption Object
+ *
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Object
+ * @access private
+ */
+ var $decrypt = false;
+
+ /**
+ * Client to Server Encryption Object
+ *
+ * @see Net_SSH2::_send_binary_packet()
+ * @var Object
+ * @access private
+ */
+ var $encrypt = false;
+
+ /**
+ * Client to Server HMAC Object
+ *
+ * @see Net_SSH2::_send_binary_packet()
+ * @var Object
+ * @access private
+ */
+ var $hmac_create = false;
+
+ /**
+ * Server to Client HMAC Object
+ *
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Object
+ * @access private
+ */
+ var $hmac_check = false;
+
+ /**
+ * Size of server to client HMAC
+ *
+ * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
+ * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is
+ * append it.
+ *
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $hmac_size = false;
+
+ /**
+ * Server Public Host Key
+ *
+ * @see Net_SSH2::getServerPublicHostKey()
+ * @var String
+ * @access private
+ */
+ var $server_public_host_key;
+
+ /**
+ * Session identifer
+ *
+ * "The exchange hash H from the first key exchange is additionally
+ * used as the session identifier, which is a unique identifier for
+ * this connection."
+ *
+ * -- http://tools.ietf.org/html/rfc4253#section-7.2
+ *
+ * @see Net_SSH2::_key_exchange()
+ * @var String
+ * @access private
+ */
+ var $session_id = false;
+
+ /**
+ * Exchange hash
+ *
+ * The current exchange hash
+ *
+ * @see Net_SSH2::_key_exchange()
+ * @var String
+ * @access private
+ */
+ var $exchange_hash = false;
+
+ /**
+ * Message Numbers
+ *
+ * @see Net_SSH2::Net_SSH2()
+ * @var Array
+ * @access private
+ */
+ var $message_numbers = array();
+
+ /**
+ * Disconnection Message 'reason codes' defined in RFC4253
+ *
+ * @see Net_SSH2::Net_SSH2()
+ * @var Array
+ * @access private
+ */
+ var $disconnect_reasons = array();
+
+ /**
+ * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
+ *
+ * @see Net_SSH2::Net_SSH2()
+ * @var Array
+ * @access private
+ */
+ var $channel_open_failure_reasons = array();
+
+ /**
+ * Terminal Modes
+ *
+ * @link http://tools.ietf.org/html/rfc4254#section-8
+ * @see Net_SSH2::Net_SSH2()
+ * @var Array
+ * @access private
+ */
+ var $terminal_modes = array();
+
+ /**
+ * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
+ *
+ * @link http://tools.ietf.org/html/rfc4254#section-5.2
+ * @see Net_SSH2::Net_SSH2()
+ * @var Array
+ * @access private
+ */
+ var $channel_extended_data_type_codes = array();
+
+ /**
+ * Send Sequence Number
+ *
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
+ *
+ * @see Net_SSH2::_send_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $send_seq_no = 0;
+
+ /**
+ * Get Sequence Number
+ *
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
+ *
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $get_seq_no = 0;
+
+ /**
+ * Server Channels
+ *
+ * Maps client channels to server channels
+ *
+ * @see Net_SSH2::_get_channel_packet()
+ * @see Net_SSH2::exec()
+ * @var Array
+ * @access private
+ */
+ var $server_channels = array();
+
+ /**
+ * Channel Buffers
+ *
+ * If a client requests a packet from one channel but receives two packets from another those packets should
+ * be placed in a buffer
+ *
+ * @see Net_SSH2::_get_channel_packet()
+ * @see Net_SSH2::exec()
+ * @var Array
+ * @access private
+ */
+ var $channel_buffers = array();
+
+ /**
+ * Channel Status
+ *
+ * Contains the type of the last sent message
+ *
+ * @see Net_SSH2::_get_channel_packet()
+ * @var Array
+ * @access private
+ */
+ var $channel_status = array();
+
+ /**
+ * Packet Size
+ *
+ * Maximum packet size indexed by channel
+ *
+ * @see Net_SSH2::_send_channel_packet()
+ * @var Array
+ * @access private
+ */
+ var $packet_size_client_to_server = array();
+
+ /**
+ * Message Number Log
+ *
+ * @see Net_SSH2::getLog()
+ * @var Array
+ * @access private
+ */
+ var $message_number_log = array();
+
+ /**
+ * Message Log
+ *
+ * @see Net_SSH2::getLog()
+ * @var Array
+ * @access private
+ */
+ var $message_log = array();
+
+ /**
+ * The Window Size
+ *
+ * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
+ *
+ * @var Integer
+ * @see Net_SSH2::_send_channel_packet()
+ * @see Net_SSH2::exec()
+ * @access private
+ */
+ var $window_size = 0x7FFFFFFF;
+
+ /**
+ * Window size
+ *
+ * Window size indexed by channel
+ *
+ * @see Net_SSH2::_send_channel_packet()
+ * @var Array
+ * @access private
+ */
+ var $window_size_client_to_server = array();
+
+ /**
+ * Server signature
+ *
+ * Verified against $this->session_id
+ *
+ * @see Net_SSH2::getServerPublicHostKey()
+ * @var String
+ * @access private
+ */
+ var $signature = '';
+
+ /**
+ * Server signature format
+ *
+ * ssh-rsa or ssh-dss.
+ *
+ * @see Net_SSH2::getServerPublicHostKey()
+ * @var String
+ * @access private
+ */
+ var $signature_format = '';
+
+ /**
+ * Interactive Buffer
+ *
+ * @see Net_SSH2::read()
+ * @var Array
+ * @access private
+ */
+ var $interactiveBuffer = '';
+
+ /**
+ * Current log size
+ *
+ * Should never exceed NET_SSH2_LOG_MAX_SIZE
+ *
+ * @see Net_SSH2::_send_binary_packet()
+ * @see Net_SSH2::_get_binary_packet()
+ * @var Integer
+ * @access private
+ */
+ var $log_size;
+
+ /**
+ * Timeout
+ *
+ * @see Net_SSH2::setTimeout()
+ * @access private
+ */
+ var $timeout;
+
+ /**
+ * Current Timeout
+ *
+ * @see Net_SSH2::_get_channel_packet()
+ * @access private
+ */
+ var $curTimeout;
+
+ /**
+ * Real-time log file pointer
+ *
+ * @see Net_SSH2::_append_log()
+ * @var Resource
+ * @access private
+ */
+ var $realtime_log_file;
+
+ /**
+ * Real-time log file size
+ *
+ * @see Net_SSH2::_append_log()
+ * @var Integer
+ * @access private
+ */
+ var $realtime_log_size;
+
+ /**
+ * Has the signature been validated?
+ *
+ * @see Net_SSH2::getServerPublicHostKey()
+ * @var Boolean
+ * @access private
+ */
+ var $signature_validated = false;
+
+ /**
+ * Real-time log file wrap boolean
+ *
+ * @see Net_SSH2::_append_log()
+ * @access private
+ */
+ var $realtime_log_wrap;
+
+ /**
+ * Flag to suppress stderr from output
+ *
+ * @see Net_SSH2::enableQuietMode()
+ * @access private
+ */
+ var $quiet_mode = false;
+
+ /**
+ * Time of first network activity
+ *
+ * @access private
+ */
+ var $last_packet;
+
+ /**
+ * Exit status returned from ssh if any
+ *
+ * @var Integer
+ * @access private
+ */
+ var $exit_status;
+
+ /**
+ * Default Constructor.
+ *
+ * Connects to an SSHv2 server
+ *
+ * @param String $host
+ * @param optional Integer $port
+ * @param optional Integer $timeout
+ * @return Net_SSH2
+ * @access public
+ */
+ function Net_SSH2($host, $port = 22, $timeout = 10)
+ {
+ $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5
+ $this->message_numbers = array(
+ 1 => 'NET_SSH2_MSG_DISCONNECT',
+ 2 => 'NET_SSH2_MSG_IGNORE',
+ 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
+ 4 => 'NET_SSH2_MSG_DEBUG',
+ 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
+ 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
+ 20 => 'NET_SSH2_MSG_KEXINIT',
+ 21 => 'NET_SSH2_MSG_NEWKEYS',
+ 30 => 'NET_SSH2_MSG_KEXDH_INIT',
+ 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
+ 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
+ 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
+ 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
+ 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
+
+ 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
+ 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
+ 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
+ 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
+ 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
+ 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
+ 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
+ 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
+ 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
+ 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
+ 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
+ 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
+ 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
+ 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
+ );
+ $this->disconnect_reasons = array(
+ 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
+ 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
+ 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
+ 4 => 'NET_SSH2_DISCONNECT_RESERVED',
+ 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
+ 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
+ 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
+ 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
+ 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
+ 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
+ 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
+ 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
+ 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
+ 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
+ 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
+ );
+ $this->channel_open_failure_reasons = array(
+ 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
+ );
+ $this->terminal_modes = array(
+ 0 => 'NET_SSH2_TTY_OP_END'
+ );
+ $this->channel_extended_data_type_codes = array(
+ 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
+ );
+
+ $this->_define_array(
+ $this->message_numbers,
+ $this->disconnect_reasons,
+ $this->channel_open_failure_reasons,
+ $this->terminal_modes,
+ $this->channel_extended_data_type_codes,
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
+ array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
+ 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
+ );
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
+ if (!$this->fsock) {
+ user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
+ return;
+ }
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
+
+ $timeout-= $elapsed;
+
+ if ($timeout <= 0) {
+ user_error(rtrim("Cannot connect to $host. Timeout error"));
+ return;
+ }
+
+ $read = array($this->fsock);
+ $write = $except = NULL;
+
+ $sec = floor($timeout);
+ $usec = 1000000 * ($timeout - $sec);
+
+ // on windows this returns a "Warning: Invalid CRT parameters detected" error
+ // the !count() is done as a workaround for <https://bugs.php.net/42682>
+ if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
+ user_error(rtrim("Cannot connect to $host. Banner timeout"));
+ return;
+ }
+
+ /* According to the SSH2 specs,
+
+ "The server MAY send other lines of data before sending the version
+ string. Each line SHOULD be terminated by a Carriage Return and Line
+ Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
+ in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
+ MUST be able to process such lines." */
+ $temp = '';
+ $extra = '';
+ while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
+ if (substr($temp, -2) == "\r\n") {
+ $extra.= $temp;
+ $temp = '';
+ }
+ $temp.= fgets($this->fsock, 255);
+ }
+
+ if (feof($this->fsock)) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ $ext = array();
+ if (extension_loaded('mcrypt')) {
+ $ext[] = 'mcrypt';
+ }
+ if (extension_loaded('gmp')) {
+ $ext[] = 'gmp';
+ } else if (extension_loaded('bcmath')) {
+ $ext[] = 'bcmath';
+ }
+
+ if (!empty($ext)) {
+ $this->identifier.= ' (' . implode(', ', $ext) . ')';
+ }
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->_append_log('<-', $extra . $temp);
+ $this->_append_log('->', $this->identifier . "\r\n");
+ }
+
+ $this->server_identifier = trim($temp, "\r\n");
+ if (strlen($extra)) {
+ $this->errors[] = utf8_decode($extra);
+ }
+
+ if ($matches[1] != '1.99' && $matches[1] != '2.0') {
+ user_error("Cannot connect to SSH $matches[1] servers");
+ return;
+ }
+
+ fputs($this->fsock, $this->identifier . "\r\n");
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return;
+ }
+
+ if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
+ user_error('Expected SSH_MSG_KEXINIT');
+ return;
+ }
+
+ if (!$this->_key_exchange($response)) {
+ return;
+ }
+
+ $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
+ }
+
+ /**
+ * Key Exchange
+ *
+ * @param String $kexinit_payload_server
+ * @access private
+ */
+ function _key_exchange($kexinit_payload_server)
+ {
+ static $kex_algorithms = array(
+ 'diffie-hellman-group1-sha1', // REQUIRED
+ 'diffie-hellman-group14-sha1' // REQUIRED
+ );
+
+ static $server_host_key_algorithms = array(
+ 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
+ 'ssh-dss' // REQUIRED sign Raw DSS Key
+ );
+
+ static $encryption_algorithms = array(
+ // from <http://tools.ietf.org/html/rfc4345#section-4>:
+ 'arcfour256',
+ 'arcfour128',
+
+ 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
+
+ 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
+ 'aes192-cbc', // OPTIONAL AES with a 192-bit key
+ 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
+
+ // from <http://tools.ietf.org/html/rfc4344#section-4>:
+ 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
+ 'aes192-ctr', // RECOMMENDED AES with 192-bit key
+ 'aes256-ctr', // RECOMMENDED AES with 256-bit key
+ '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
+
+ '3des-cbc', // REQUIRED three-key 3DES in CBC mode
+ 'none' // OPTIONAL no encryption; NOT RECOMMENDED
+ );
+
+ static $mac_algorithms = array(
+ 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
+ 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
+ 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
+ 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
+ 'none' // OPTIONAL no MAC; NOT RECOMMENDED
+ );
+
+ static $compression_algorithms = array(
+ 'none' // REQUIRED no compression
+ //'zlib' // OPTIONAL ZLIB (LZ77) compression
+ );
+
+ // some SSH servers have buggy implementations of some of the above algorithms
+ switch ($this->server_identifier) {
+ case 'SSH-2.0-SSHD':
+ $mac_algorithms = array_values(array_diff(
+ $mac_algorithms,
+ array('hmac-sha1-96', 'hmac-md5-96')
+ ));
+ }
+
+ static $str_kex_algorithms, $str_server_host_key_algorithms,
+ $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
+ $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
+
+ if (empty($str_kex_algorithms)) {
+ $str_kex_algorithms = implode(',', $kex_algorithms);
+ $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
+ $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
+ $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
+ $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
+ }
+
+ $client_cookie = crypt_random_string(16);
+
+ $response = $kexinit_payload_server;
+ $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
+ $server_cookie = $this->_string_shift($response, 16);
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
+
+ extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
+ $first_kex_packet_follows = $first_kex_packet_follows != 0;
+
+ // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
+ $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
+ NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
+ strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
+ $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
+ strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
+ $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
+ strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
+ 0, 0
+ );
+
+ if (!$this->_send_binary_packet($kexinit_payload_client)) {
+ return false;
+ }
+ // here ends the second place.
+
+ // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
+ if ($i == count($encryption_algorithms)) {
+ user_error('No compatible server to client encryption algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
+ // diffie-hellman key exchange as fast as possible
+ $decrypt = $encryption_algorithms[$i];
+ switch ($decrypt) {
+ case '3des-cbc':
+ case '3des-ctr':
+ $decryptKeyLength = 24; // eg. 192 / 8
+ break;
+ case 'aes256-cbc':
+ case 'aes256-ctr':
+ $decryptKeyLength = 32; // eg. 256 / 8
+ break;
+ case 'aes192-cbc':
+ case 'aes192-ctr':
+ $decryptKeyLength = 24; // eg. 192 / 8
+ break;
+ case 'aes128-cbc':
+ case 'aes128-ctr':
+ $decryptKeyLength = 16; // eg. 128 / 8
+ break;
+ case 'arcfour':
+ case 'arcfour128':
+ $decryptKeyLength = 16; // eg. 128 / 8
+ break;
+ case 'arcfour256':
+ $decryptKeyLength = 32; // eg. 128 / 8
+ break;
+ case 'none';
+ $decryptKeyLength = 0;
+ }
+
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
+ if ($i == count($encryption_algorithms)) {
+ user_error('No compatible client to server encryption algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $encrypt = $encryption_algorithms[$i];
+ switch ($encrypt) {
+ case '3des-cbc':
+ case '3des-ctr':
+ $encryptKeyLength = 24;
+ break;
+ case 'aes256-cbc':
+ case 'aes256-ctr':
+ $encryptKeyLength = 32;
+ break;
+ case 'aes192-cbc':
+ case 'aes192-ctr':
+ $encryptKeyLength = 24;
+ break;
+ case 'aes128-cbc':
+ case 'aes128-ctr':
+ $encryptKeyLength = 16;
+ break;
+ case 'arcfour':
+ case 'arcfour128':
+ $encryptKeyLength = 16;
+ break;
+ case 'arcfour256':
+ $encryptKeyLength = 32;
+ break;
+ case 'none';
+ $encryptKeyLength = 0;
+ }
+
+ $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
+
+ // through diffie-hellman key exchange a symmetric key is obtained
+ for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
+ if ($i == count($kex_algorithms)) {
+ user_error('No compatible key exchange algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ switch ($kex_algorithms[$i]) {
+ // see http://tools.ietf.org/html/rfc2409#section-6.2 and
+ // http://tools.ietf.org/html/rfc2412, appendex E
+ case 'diffie-hellman-group1-sha1':
+ $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
+ $hash = 'sha1';
+ break;
+ // see http://tools.ietf.org/html/rfc3526#section-3
+ case 'diffie-hellman-group14-sha1':
+ $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
+ $hash = 'sha1';
+ }
+
+ $p = new Math_BigInteger($p, 256);
+ //$q = $p->bitwise_rightShift(1);
+
+ /* To increase the speed of the key exchange, both client and server may
+ reduce the size of their private exponents. It should be at least
+ twice as long as the key material that is generated from the shared
+ secret. For more details, see the paper by van Oorschot and Wiener
+ [VAN-OORSCHOT].
+
+ -- http://tools.ietf.org/html/rfc4419#section-6.2 */
+ $q = new Math_BigInteger(1);
+ $q = $q->bitwise_leftShift(2 * $keyLength);
+ $q = $q->subtract(new Math_BigInteger(1));
+
+ $g = new Math_BigInteger(2);
+ $x = new Math_BigInteger();
+ $x = $x->random(new Math_BigInteger(1), $q);
+ $e = $g->modPow($x, $p);
+
+ $eBytes = $e->toBytes(true);
+ $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
+
+ if (!$this->_send_binary_packet($data)) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
+ user_error('Expected SSH_MSG_KEXDH_REPLY');
+ return false;
+ }
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
+
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $fBytes = $this->_string_shift($response, $temp['length']);
+ $f = new Math_BigInteger($fBytes, -256);
+
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->signature = $this->_string_shift($response, $temp['length']);
+
+ $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
+ $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
+
+ $key = $f->modPow($x, $p);
+ $keyBytes = $key->toBytes(true);
+
+ $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
+ strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
+ strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
+ $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
+ $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
+ );
+
+ $this->exchange_hash = pack('H*', $hash($this->exchange_hash));
+
+ if ($this->session_id === false) {
+ $this->session_id = $this->exchange_hash;
+ }
+
+ for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
+ if ($i == count($server_host_key_algorithms)) {
+ user_error('No compatible server host key algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
+ user_error('Sever Host Key Algorithm Mismatch');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $packet = pack('C',
+ NET_SSH2_MSG_NEWKEYS
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ if ($type != NET_SSH2_MSG_NEWKEYS) {
+ user_error('Expected SSH_MSG_NEWKEYS');
+ return false;
+ }
+
+ switch ($encrypt) {
+ case '3des-cbc':
+ $this->encrypt = new Crypt_TripleDES();
+ // $this->encrypt_block_size = 64 / 8 == the default
+ break;
+ case '3des-ctr':
+ $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
+ // $this->encrypt_block_size = 64 / 8 == the default
+ break;
+ case 'aes256-cbc':
+ case 'aes192-cbc':
+ case 'aes128-cbc':
+ $this->encrypt = new Crypt_AES();
+ $this->encrypt_block_size = 16; // eg. 128 / 8
+ break;
+ case 'aes256-ctr':
+ case 'aes192-ctr':
+ case 'aes128-ctr':
+ $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
+ $this->encrypt_block_size = 16; // eg. 128 / 8
+ break;
+ case 'arcfour':
+ case 'arcfour128':
+ case 'arcfour256':
+ $this->encrypt = new Crypt_RC4();
+ break;
+ case 'none';
+ //$this->encrypt = new Crypt_Null();
+ }
+
+ switch ($decrypt) {
+ case '3des-cbc':
+ $this->decrypt = new Crypt_TripleDES();
+ break;
+ case '3des-ctr':
+ $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
+ break;
+ case 'aes256-cbc':
+ case 'aes192-cbc':
+ case 'aes128-cbc':
+ $this->decrypt = new Crypt_AES();
+ $this->decrypt_block_size = 16;
+ break;
+ case 'aes256-ctr':
+ case 'aes192-ctr':
+ case 'aes128-ctr':
+ $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
+ $this->decrypt_block_size = 16;
+ break;
+ case 'arcfour':
+ case 'arcfour128':
+ case 'arcfour256':
+ $this->decrypt = new Crypt_RC4();
+ break;
+ case 'none';
+ //$this->decrypt = new Crypt_Null();
+ }
+
+ $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
+
+ if ($this->encrypt) {
+ $this->encrypt->enableContinuousBuffer();
+ $this->encrypt->disablePadding();
+
+ $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id));
+ while ($this->encrypt_block_size > strlen($iv)) {
+ $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
+ }
+ $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
+
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id));
+ while ($encryptKeyLength > strlen($key)) {
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
+ }
+ $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
+ }
+
+ if ($this->decrypt) {
+ $this->decrypt->enableContinuousBuffer();
+ $this->decrypt->disablePadding();
+
+ $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id));
+ while ($this->decrypt_block_size > strlen($iv)) {
+ $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
+ }
+ $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
+
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id));
+ while ($decryptKeyLength > strlen($key)) {
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
+ }
+ $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
+ }
+
+ /* The "arcfour128" algorithm is the RC4 cipher, as described in
+ [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
+ generated by the cipher MUST be discarded, and the first byte of the
+ first encrypted packet MUST be encrypted using the 1537th byte of
+ keystream.
+
+ -- http://tools.ietf.org/html/rfc4345#section-4 */
+ if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
+ $this->encrypt->encrypt(str_repeat("\0", 1536));
+ }
+ if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
+ $this->decrypt->decrypt(str_repeat("\0", 1536));
+ }
+
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
+ if ($i == count($mac_algorithms)) {
+ user_error('No compatible client to server message authentication algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
+ switch ($mac_algorithms[$i]) {
+ case 'hmac-sha1':
+ $this->hmac_create = new Crypt_Hash('sha1');
+ $createKeyLength = 20;
+ break;
+ case 'hmac-sha1-96':
+ $this->hmac_create = new Crypt_Hash('sha1-96');
+ $createKeyLength = 20;
+ break;
+ case 'hmac-md5':
+ $this->hmac_create = new Crypt_Hash('md5');
+ $createKeyLength = 16;
+ break;
+ case 'hmac-md5-96':
+ $this->hmac_create = new Crypt_Hash('md5-96');
+ $createKeyLength = 16;
+ }
+
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
+ if ($i == count($mac_algorithms)) {
+ user_error('No compatible server to client message authentication algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $checkKeyLength = 0;
+ $this->hmac_size = 0;
+ switch ($mac_algorithms[$i]) {
+ case 'hmac-sha1':
+ $this->hmac_check = new Crypt_Hash('sha1');
+ $checkKeyLength = 20;
+ $this->hmac_size = 20;
+ break;
+ case 'hmac-sha1-96':
+ $this->hmac_check = new Crypt_Hash('sha1-96');
+ $checkKeyLength = 20;
+ $this->hmac_size = 12;
+ break;
+ case 'hmac-md5':
+ $this->hmac_check = new Crypt_Hash('md5');
+ $checkKeyLength = 16;
+ $this->hmac_size = 16;
+ break;
+ case 'hmac-md5-96':
+ $this->hmac_check = new Crypt_Hash('md5-96');
+ $checkKeyLength = 16;
+ $this->hmac_size = 12;
+ }
+
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id));
+ while ($createKeyLength > strlen($key)) {
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
+ }
+ $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
+
+ $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id));
+ while ($checkKeyLength > strlen($key)) {
+ $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
+ }
+ $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
+
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
+ if ($i == count($compression_algorithms)) {
+ user_error('No compatible server to client compression algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+ $this->decompress = $compression_algorithms[$i] == 'zlib';
+
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
+ if ($i == count($compression_algorithms)) {
+ user_error('No compatible client to server compression algorithms found');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+ $this->compress = $compression_algorithms[$i] == 'zlib';
+
+ return true;
+ }
+
+ /**
+ * Login
+ *
+ * The $password parameter can be a plaintext password or a Crypt_RSA object.
+ *
+ * @param String $username
+ * @param optional String $password
+ * @return Boolean
+ * @access public
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
+ * by sending dummy SSH_MSG_IGNORE messages.
+ */
+ function login($username, $password = null)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
+ return false;
+ }
+
+ $packet = pack('CNa*',
+ NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
+ user_error('Expected SSH_MSG_SERVICE_ACCEPT');
+ return false;
+ }
+
+ // although PHP5's get_class() preserves the case, PHP4's does not
+ if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
+ return $this->_privatekey_login($username, $password);
+ }
+
+ if (!isset($password)) {
+ $packet = pack('CNa*Na*Na*',
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
+ strlen('none'), 'none'
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ switch ($type) {
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
+ return true;
+ //case NET_SSH2_MSG_USERAUTH_FAILURE:
+ default:
+ return false;
+ }
+ }
+
+ $packet = pack('CNa*Na*Na*CNa*',
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
+ strlen('password'), 'password', 0, strlen($password), $password
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ // remove the username and password from the last logged packet
+ if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
+ $packet = pack('CNa*Na*Na*CNa*',
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
+ strlen('password'), 'password', 0, strlen('password'), 'password'
+ );
+ $this->message_log[count($this->message_log) - 1] = $packet;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ switch ($type) {
+ case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
+ // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
+ // multi-factor authentication
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $auth_methods = explode(',', $this->_string_shift($response, $length));
+ if (in_array('keyboard-interactive', $auth_methods)) {
+ if ($this->_keyboard_interactive_login($username, $password)) {
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
+ return true;
+ }
+ return false;
+ }
+ return false;
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Login via keyboard-interactive authentication
+ *
+ * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
+ *
+ * @param String $username
+ * @param String $password
+ * @return Boolean
+ * @access private
+ */
+ function _keyboard_interactive_login($username, $password)
+ {
+ $packet = pack('CNa*Na*Na*Na*Na*',
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
+ strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ return $this->_keyboard_interactive_process($password);
+ }
+
+ /**
+ * Handle the keyboard-interactive requests / responses.
+ *
+ * @param String $responses...
+ * @return Boolean
+ * @access private
+ */
+ function _keyboard_interactive_process()
+ {
+ $responses = func_get_args();
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ switch ($type) {
+ case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
+ // see http://tools.ietf.org/html/rfc4256#section-3.2
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
+ 'UNKNOWN',
+ 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
+ $this->message_number_log[count($this->message_number_log) - 1]
+ );
+ }
+
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->_string_shift($response, $length); // name; may be empty
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->_string_shift($response, $length); // instruction; may be empty
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->_string_shift($response, $length); // language tag; may be empty
+ extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
+ /*
+ for ($i = 0; $i < $num_prompts; $i++) {
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ // prompt - ie. "Password: "; must not be empty
+ $this->_string_shift($response, $length);
+ $echo = $this->_string_shift($response) != chr(0);
+ }
+ */
+
+ /*
+ After obtaining the requested information from the user, the client
+ MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
+ */
+ // see http://tools.ietf.org/html/rfc4256#section-3.4
+ $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
+ for ($i = 0; $i < count($responses); $i++) {
+ $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
+ $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
+ }
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
+ 'UNKNOWN',
+ 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
+ $this->message_number_log[count($this->message_number_log) - 1]
+ );
+ $this->message_log[count($this->message_log) - 1] = $logged;
+ }
+
+ /*
+ After receiving the response, the server MUST send either an
+ SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
+ SSH_MSG_USERAUTH_INFO_REQUEST message.
+ */
+ // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
+ // there could be an infinite loop of request / responses.
+ return $this->_keyboard_interactive_process();
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
+ return true;
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Login with an RSA private key
+ *
+ * @param String $username
+ * @param Crypt_RSA $password
+ * @return Boolean
+ * @access private
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
+ * by sending dummy SSH_MSG_IGNORE messages.
+ */
+ function _privatekey_login($username, $privatekey)
+ {
+ // see http://tools.ietf.org/html/rfc4253#page-15
+ $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
+ if ($publickey === false) {
+ return false;
+ }
+
+ $publickey = array(
+ 'e' => $publickey['e']->toBytes(true),
+ 'n' => $publickey['n']->toBytes(true)
+ );
+ $publickey = pack('Na*Na*Na*',
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
+ );
+
+ $part1 = pack('CNa*Na*Na*',
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
+ strlen('publickey'), 'publickey'
+ );
+ $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
+
+ $packet = $part1 . chr(0) . $part2;
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ switch ($type) {
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
+ case NET_SSH2_MSG_USERAUTH_PK_OK:
+ // we'll just take it on faith that the public key blob and the public key algorithm name are as
+ // they should be
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
+ 'UNKNOWN',
+ 'NET_SSH2_MSG_USERAUTH_PK_OK',
+ $this->message_number_log[count($this->message_number_log) - 1]
+ );
+ }
+ }
+
+ $packet = $part1 . chr(1) . $part2;
+ $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
+ $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
+ $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
+ $packet.= pack('Na*', strlen($signature), $signature);
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+
+ switch ($type) {
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
+ // either the login is bad or the server employs multi-factor authentication
+ return false;
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Set Timeout
+ *
+ * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
+ * Setting $timeout to false or 0 will mean there is no timeout.
+ *
+ * @param Mixed $timeout
+ */
+ function setTimeout($timeout)
+ {
+ $this->timeout = $this->curTimeout = $timeout;
+ }
+
+ /**
+ * Execute Command
+ *
+ * If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
+ * In all likelihood, this is not a feature you want to be taking advantage of.
+ *
+ * @param String $command
+ * @param optional Boolean $block
+ * @return String
+ * @access public
+ */
+ function exec($command, $block = true)
+ {
+ $this->curTimeout = $this->timeout;
+
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ return false;
+ }
+
+ // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
+ // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
+ // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
+ // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
+ $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
+ // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
+ // uses 0x4000, that's what will be used here, as well.
+ $packet_size = 0x4000;
+
+ $packet = pack('CNa*N3',
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
+
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
+ if ($response === false) {
+ return false;
+ }
+
+ // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
+ // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
+ // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
+ // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
+ // neither will your script.
+
+ // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
+ // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
+ // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
+ $packet = pack('CNNa*CNa*',
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
+
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
+ if ($response === false) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
+
+ if (!$block) {
+ return true;
+ }
+
+ $output = '';
+ while (true) {
+ $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
+ switch (true) {
+ case $temp === true:
+ return $output;
+ case $temp === false:
+ return false;
+ default:
+ $output.= $temp;
+ }
+ }
+ }
+
+ /**
+ * Creates an interactive shell
+ *
+ * @see Net_SSH2::read()
+ * @see Net_SSH2::write()
+ * @return Boolean
+ * @access private
+ */
+ function _initShell()
+ {
+ $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
+ $packet_size = 0x4000;
+
+ $packet = pack('CNa*N3',
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
+
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
+ if ($response === false) {
+ return false;
+ }
+
+ $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
+ $packet = pack('CNNa*CNa*N5a*',
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
+ 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ list(, $type) = unpack('C', $this->_string_shift($response, 1));
+
+ switch ($type) {
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
+ break;
+ case NET_SSH2_MSG_CHANNEL_FAILURE:
+ default:
+ user_error('Unable to request pseudo-terminal');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+
+ $packet = pack('CNNa*C',
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
+
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
+ if ($response === false) {
+ return false;
+ }
+
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
+
+ $this->bitmap |= NET_SSH2_MASK_SHELL;
+
+ return true;
+ }
+
+ /**
+ * Returns the output of an interactive shell
+ *
+ * Returns when there's a match for $expect, which can take the form of a string literal or,
+ * if $mode == NET_SSH2_READ_REGEX, a regular expression.
+ *
+ * @see Net_SSH2::read()
+ * @param String $expect
+ * @param Integer $mode
+ * @return String
+ * @access public
+ */
+ function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
+ {
+ $this->curTimeout = $this->timeout;
+
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
+ user_error('Unable to initiate an interactive shell session');
+ return false;
+ }
+
+ $match = $expect;
+ while (true) {
+ if ($mode == NET_SSH2_READ_REGEX) {
+ preg_match($expect, $this->interactiveBuffer, $matches);
+ $match = isset($matches[0]) ? $matches[0] : '';
+ }
+ $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
+ if ($pos !== false) {
+ return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
+ }
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
+ if (is_bool($response)) {
+ return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
+ }
+
+ $this->interactiveBuffer.= $response;
+ }
+ }
+
+ /**
+ * Inputs a command into an interactive shell.
+ *
+ * @see Net_SSH1::interactiveWrite()
+ * @param String $cmd
+ * @return Boolean
+ * @access public
+ */
+ function write($cmd)
+ {
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ user_error('Operation disallowed prior to login()');
+ return false;
+ }
+
+ if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
+ user_error('Unable to initiate an interactive shell session');
+ return false;
+ }
+
+ return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
+ }
+
+ /**
+ * Disconnect
+ *
+ * @access public
+ */
+ function disconnect()
+ {
+ $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
+ fclose($this->realtime_log_file);
+ }
+ }
+
+ /**
+ * Destructor.
+ *
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
+ * disconnect().
+ *
+ * @access public
+ */
+ function __destruct()
+ {
+ $this->disconnect();
+ }
+
+ /**
+ * Gets Binary Packets
+ *
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
+ *
+ * @see Net_SSH2::_send_binary_packet()
+ * @return String
+ * @access private
+ */
+ function _get_binary_packet()
+ {
+ if (!is_resource($this->fsock) || feof($this->fsock)) {
+ user_error('Connection closed prematurely');
+ $this->bitmask = 0;
+ return false;
+ }
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $raw = fread($this->fsock, $this->decrypt_block_size);
+
+ if (!strlen($raw)) {
+ return '';
+ }
+
+ if ($this->decrypt !== false) {
+ $raw = $this->decrypt->decrypt($raw);
+ }
+ if ($raw === false) {
+ user_error('Unable to decrypt content');
+ return false;
+ }
+
+ extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
+
+ $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
+
+ // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
+ // "implementations SHOULD check that the packet length is reasonable"
+ // PuTTY uses 0x9000 as the actual max packet size and so to shall we
+ if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
+ user_error('Invalid size');
+ return false;
+ }
+
+ $buffer = '';
+ while ($remaining_length > 0) {
+ $temp = fread($this->fsock, $remaining_length);
+ $buffer.= $temp;
+ $remaining_length-= strlen($temp);
+ }
+ $stop = strtok(microtime(), ' ') + strtok('');
+ if (strlen($buffer)) {
+ $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
+ }
+
+ $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
+ $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
+
+ if ($this->hmac_check !== false) {
+ $hmac = fread($this->fsock, $this->hmac_size);
+ if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
+ user_error('Invalid HMAC');
+ return false;
+ }
+ }
+
+ //if ($this->decompress) {
+ // $payload = gzinflate(substr($payload, 2));
+ //}
+
+ $this->get_seq_no++;
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $current = strtok(microtime(), ' ') + strtok('');
+ $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
+ $message_number = '<- ' . $message_number .
+ ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
+ $this->_append_log($message_number, $payload);
+ $this->last_packet = $current;
+ }
+
+ return $this->_filter($payload);
+ }
+
+ /**
+ * Filter Binary Packets
+ *
+ * Because some binary packets need to be ignored...
+ *
+ * @see Net_SSH2::_get_binary_packet()
+ * @return String
+ * @access private
+ */
+ function _filter($payload)
+ {
+ switch (ord($payload[0])) {
+ case NET_SSH2_MSG_DISCONNECT:
+ $this->_string_shift($payload, 1);
+ extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
+ $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
+ $this->bitmask = 0;
+ return false;
+ case NET_SSH2_MSG_IGNORE:
+ $payload = $this->_get_binary_packet();
+ break;
+ case NET_SSH2_MSG_DEBUG:
+ $this->_string_shift($payload, 2);
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
+ $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
+ $payload = $this->_get_binary_packet();
+ break;
+ case NET_SSH2_MSG_UNIMPLEMENTED:
+ return false;
+ case NET_SSH2_MSG_KEXINIT:
+ if ($this->session_id !== false) {
+ if (!$this->_key_exchange($payload)) {
+ $this->bitmask = 0;
+ return false;
+ }
+ $payload = $this->_get_binary_packet();
+ }
+ }
+
+ // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
+ $this->_string_shift($payload, 1);
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
+ $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
+ $payload = $this->_get_binary_packet();
+ }
+
+ // only called when we've already logged in
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
+ switch (ord($payload[0])) {
+ case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
+ $this->_string_shift($payload, 1);
+ extract(unpack('Nlength', $this->_string_shift($payload)));
+ $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
+
+ if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+
+ $payload = $this->_get_binary_packet();
+ break;
+ case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
+ $this->_string_shift($payload, 1);
+ extract(unpack('N', $this->_string_shift($payload, 4)));
+ $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
+
+ $this->_string_shift($payload, 4); // skip over client channel
+ extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
+
+ $packet = pack('CN3a*Na*',
+ NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
+
+ if (!$this->_send_binary_packet($packet)) {
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+
+ $payload = $this->_get_binary_packet();
+ break;
+ case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ $payload = $this->_get_binary_packet();
+ }
+ }
+
+ return $payload;
+ }
+
+ /**
+ * Enable Quiet Mode
+ *
+ * Suppress stderr from output
+ *
+ * @access public
+ */
+ function enableQuietMode()
+ {
+ $this->quiet_mode = true;
+ }
+
+ /**
+ * Disable Quiet Mode
+ *
+ * Show stderr in output
+ *
+ * @access public
+ */
+ function disableQuietMode()
+ {
+ $this->quiet_mode = false;
+ }
+
+ /**
+ * Gets channel data
+ *
+ * Returns the data as a string if it's available and false if not.
+ *
+ * @param $client_channel
+ * @return Mixed
+ * @access private
+ */
+ function _get_channel_packet($client_channel, $skip_extended = false)
+ {
+ if (!empty($this->channel_buffers[$client_channel])) {
+ return array_shift($this->channel_buffers[$client_channel]);
+ }
+
+ while (true) {
+ if ($this->curTimeout) {
+ $read = array($this->fsock);
+ $write = $except = NULL;
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $sec = floor($this->curTimeout);
+ $usec = 1000000 * ($this->curTimeout - $sec);
+ // on windows this returns a "Warning: Invalid CRT parameters detected" error
+ if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
+ $this->_close_channel($client_channel);
+ return true;
+ }
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
+ $this->curTimeout-= $elapsed;
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ user_error('Connection closed by server');
+ return false;
+ }
+
+ if (!strlen($response)) {
+ return '';
+ }
+
+ extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
+
+ switch ($this->channel_status[$channel]) {
+ case NET_SSH2_MSG_CHANNEL_OPEN:
+ switch ($type) {
+ case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
+ extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
+ $this->server_channels[$channel] = $server_channel;
+ $this->_string_shift($response, 4); // skip over (server) window size
+ $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
+ $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
+ return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
+ //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
+ default:
+ user_error('Unable to open channel');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+ break;
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
+ switch ($type) {
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
+ return true;
+ //case NET_SSH2_MSG_CHANNEL_FAILURE:
+ default:
+ user_error('Unable to request pseudo-terminal');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+ case NET_SSH2_MSG_CHANNEL_CLOSE:
+ return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
+ }
+
+ switch ($type) {
+ case NET_SSH2_MSG_CHANNEL_DATA:
+ /*
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
+ // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
+ // this actually seems to make things twice as fast. more to the point, the message right after
+ // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
+ // in OpenSSH it slows things down but only by a couple thousandths of a second.
+ $this->_send_channel_packet($client_channel, chr(0));
+ }
+ */
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $data = $this->_string_shift($response, $length);
+ if ($client_channel == $channel) {
+ return $data;
+ }
+ if (!isset($this->channel_buffers[$client_channel])) {
+ $this->channel_buffers[$client_channel] = array();
+ }
+ $this->channel_buffers[$client_channel][] = $data;
+ break;
+ case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ if ($skip_extended || $this->quiet_mode) {
+ break;
+ }
+ /*
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
+ $this->_send_channel_packet($client_channel, chr(0));
+ }
+ */
+ // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
+ extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
+ $data = $this->_string_shift($response, $length);
+ if ($client_channel == $channel) {
+ return $data;
+ }
+ if (!isset($this->channel_buffers[$client_channel])) {
+ $this->channel_buffers[$client_channel] = array();
+ }
+ $this->channel_buffers[$client_channel][] = $data;
+ break;
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $value = $this->_string_shift($response, $length);
+ switch ($value) {
+ case 'exit-signal':
+ $this->_string_shift($response, 1);
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
+ $this->_string_shift($response, 1);
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ if ($length) {
+ $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
+ }
+ case 'exit-status':
+ extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
+ $this->exit_status = $exit_status;
+ // "The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this message."
+ // -- http://tools.ietf.org/html/rfc4254#section-6.10
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
+
+ $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
+ default:
+ // "Some systems may not implement signals, in which case they SHOULD ignore this message."
+ // -- http://tools.ietf.org/html/rfc4254#section-6.9
+ break;
+ }
+ break;
+ case NET_SSH2_MSG_CHANNEL_CLOSE:
+ $this->curTimeout = 0;
+
+ if ($this->bitmap & NET_SSH2_MASK_SHELL) {
+ $this->bitmap&= ~NET_SSH2_MASK_SHELL;
+ }
+ if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
+ }
+
+ $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
+ return true;
+ case NET_SSH2_MSG_CHANNEL_EOF:
+ break;
+ default:
+ user_error('Error reading channel data');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+ }
+ }
+
+ /**
+ * Sends Binary Packets
+ *
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
+ *
+ * @param String $data
+ * @see Net_SSH2::_get_binary_packet()
+ * @return Boolean
+ * @access private
+ */
+ function _send_binary_packet($data)
+ {
+ if (!is_resource($this->fsock) || feof($this->fsock)) {
+ user_error('Connection closed prematurely');
+ $this->bitmask = 0;
+ return false;
+ }
+
+ //if ($this->compress) {
+ // // the -4 removes the checksum:
+ // // http://php.net/function.gzcompress#57710
+ // $data = substr(gzcompress($data), 0, -4);
+ //}
+
+ // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
+ $packet_length = strlen($data) + 9;
+ // round up to the nearest $this->encrypt_block_size
+ $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
+ // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
+ $padding_length = $packet_length - strlen($data) - 5;
+ $padding = crypt_random_string($padding_length);
+
+ // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
+ $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
+
+ $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
+ $this->send_seq_no++;
+
+ if ($this->encrypt !== false) {
+ $packet = $this->encrypt->encrypt($packet);
+ }
+
+ $packet.= $hmac;
+
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $result = strlen($packet) == fputs($this->fsock, $packet);
+ $stop = strtok(microtime(), ' ') + strtok('');
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $current = strtok(microtime(), ' ') + strtok('');
+ $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
+ $message_number = '-> ' . $message_number .
+ ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
+ $this->_append_log($message_number, $data);
+ $this->last_packet = $current;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Logs data packets
+ *
+ * Makes sure that only the last 1MB worth of packets will be logged
+ *
+ * @param String $data
+ * @access private
+ */
+ function _append_log($message_number, $message)
+ {
+ switch (NET_SSH2_LOGGING) {
+ // useful for benchmarks
+ case NET_SSH2_LOG_SIMPLE:
+ $this->message_number_log[] = $message_number;
+ break;
+ // the most useful log for SSH2
+ case NET_SSH2_LOG_COMPLEX:
+ $this->message_number_log[] = $message_number;
+ $this->_string_shift($message);
+ $this->log_size+= strlen($message);
+ $this->message_log[] = $message;
+ while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
+ $this->log_size-= strlen(array_shift($this->message_log));
+ array_shift($this->message_number_log);
+ }
+ break;
+ // dump the output out realtime; packets may be interspersed with non packets,
+ // passwords won't be filtered out and select other packets may not be correctly
+ // identified
+ case NET_SSH2_LOG_REALTIME:
+ echo "<pre>\r\n" . $this->_format_log(array($message), array($message_number)) . "\r\n</pre>\r\n";
+ @flush();
+ @ob_flush();
+ break;
+ // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
+ // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
+ // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
+ // at the beginning of the file
+ case NET_SSH2_LOG_REALTIME_FILE:
+ if (!isset($this->realtime_log_file)) {
+ // PHP doesn't seem to like using constants in fopen()
+ $filename = NET_SSH2_LOG_REALTIME_FILE;
+ $fp = fopen($filename, 'w');
+ $this->realtime_log_file = $fp;
+ }
+ if (!is_resource($this->realtime_log_file)) {
+ break;
+ }
+ $entry = $this->_format_log(array($message), array($message_number));
+ if ($this->realtime_log_wrap) {
+ $temp = "<<< START >>>\r\n";
+ $entry.= $temp;
+ fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
+ }
+ $this->realtime_log_size+= strlen($entry);
+ if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) {
+ fseek($this->realtime_log_file, 0);
+ $this->realtime_log_size = strlen($entry);
+ $this->realtime_log_wrap = true;
+ }
+ fputs($this->realtime_log_file, $entry);
+ }
+ }
+
+ /**
+ * Sends channel data
+ *
+ * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
+ *
+ * @param Integer $client_channel
+ * @param String $data
+ * @return Boolean
+ * @access private
+ */
+ function _send_channel_packet($client_channel, $data)
+ {
+ while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
+ // resize the window, if appropriate
+ $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
+ }
+
+ $packet = pack('CN2a*',
+ NET_SSH2_MSG_CHANNEL_DATA,
+ $this->server_channels[$client_channel],
+ $this->packet_size_client_to_server[$client_channel],
+ $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
+ );
+
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+ }
+
+ // resize the window, if appropriate
+ $this->window_size_client_to_server[$client_channel]-= strlen($data);
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
+ if (!$this->_send_binary_packet($packet)) {
+ return false;
+ }
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
+ }
+
+ return $this->_send_binary_packet(pack('CN2a*',
+ NET_SSH2_MSG_CHANNEL_DATA,
+ $this->server_channels[$client_channel],
+ strlen($data),
+ $data));
+ }
+
+ /**
+ * Closes and flushes a channel
+ *
+ * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
+ * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
+ * for SCP more than anything.
+ *
+ * @param Integer $client_channel
+ * @return Boolean
+ * @access private
+ */
+ function _close_channel($client_channel)
+ {
+ // see http://tools.ietf.org/html/rfc4254#section-5.3
+
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
+
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
+
+ $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
+
+ $this->curTimeout = 0;
+
+ while (!is_bool($this->_get_channel_packet($client_channel)));
+
+ if ($this->bitmap & NET_SSH2_MASK_SHELL) {
+ $this->bitmap&= ~NET_SSH2_MASK_SHELL;
+ }
+ }
+
+ /**
+ * Disconnect
+ *
+ * @param Integer $reason
+ * @return Boolean
+ * @access private
+ */
+ function _disconnect($reason)
+ {
+ if ($this->bitmap) {
+ $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
+ $this->_send_binary_packet($data);
+ $this->bitmap = 0;
+ fclose($this->fsock);
+ return false;
+ }
+ }
+
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param String $string
+ * @param optional Integer $index
+ * @return String
+ * @access private
+ */
+ function _string_shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+
+ /**
+ * Define Array
+ *
+ * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
+ * named constants from it, using the value as the name of the constant and the index as the value of the constant.
+ * If any of the constants that would be defined already exists, none of the constants will be defined.
+ *
+ * @param Array $array
+ * @access private
+ */
+ function _define_array()
+ {
+ $args = func_get_args();
+ foreach ($args as $arg) {
+ foreach ($arg as $key=>$value) {
+ if (!defined($value)) {
+ define($value, $key);
+ } else {
+ break 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a log of the packets that have been sent and received.
+ *
+ * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
+ *
+ * @access public
+ * @return String or Array
+ */
+ function getLog()
+ {
+ if (!defined('NET_SSH2_LOGGING')) {
+ return false;
+ }
+
+ switch (NET_SSH2_LOGGING) {
+ case NET_SSH2_LOG_SIMPLE:
+ return $this->message_number_log;
+ break;
+ case NET_SSH2_LOG_COMPLEX:
+ return $this->_format_log($this->message_log, $this->message_number_log);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Formats a log for printing
+ *
+ * @param Array $message_log
+ * @param Array $message_number_log
+ * @access private
+ * @return String
+ */
+ function _format_log($message_log, $message_number_log)
+ {
+ static $boundary = ':', $long_width = 65, $short_width = 16;
+
+ $output = '';
+ for ($i = 0; $i < count($message_log); $i++) {
+ $output.= $message_number_log[$i] . "\r\n";
+ $current_log = $message_log[$i];
+ $j = 0;
+ do {
+ if (strlen($current_log)) {
+ $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
+ }
+ $fragment = $this->_string_shift($current_log, $short_width);
+ $hex = substr(
+ preg_replace(
+ '#(.)#es',
+ '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
+ $fragment),
+ strlen($boundary)
+ );
+ // replace non ASCII printable characters with dots
+ // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
+ // also replace < with a . since < messes up the output on web browsers
+ $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
+ $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
+ $j++;
+ } while (strlen($current_log));
+ $output.= "\r\n";
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns all errors
+ *
+ * @return String
+ * @access public
+ */
+ function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Returns the last error
+ *
+ * @return String
+ * @access public
+ */
+ function getLastError()
+ {
+ return $this->errors[count($this->errors) - 1];
+ }
+
+ /**
+ * Return the server identification.
+ *
+ * @return String
+ * @access public
+ */
+ function getServerIdentification()
+ {
+ return $this->server_identifier;
+ }
+
+ /**
+ * Return a list of the key exchange algorithms the server supports.
+ *
+ * @return Array
+ * @access public
+ */
+ function getKexAlgorithms()
+ {
+ return $this->kex_algorithms;
+ }
+
+ /**
+ * Return a list of the host key (public key) algorithms the server supports.
+ *
+ * @return Array
+ * @access public
+ */
+ function getServerHostKeyAlgorithms()
+ {
+ return $this->server_host_key_algorithms;
+ }
+
+ /**
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getEncryptionAlgorithmsClient2Server()
+ {
+ return $this->encryption_algorithms_client_to_server;
+ }
+
+ /**
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getEncryptionAlgorithmsServer2Client()
+ {
+ return $this->encryption_algorithms_server_to_client;
+ }
+
+ /**
+ * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getMACAlgorithmsClient2Server()
+ {
+ return $this->mac_algorithms_client_to_server;
+ }
+
+ /**
+ * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getMACAlgorithmsServer2Client()
+ {
+ return $this->mac_algorithms_server_to_client;
+ }
+
+ /**
+ * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getCompressionAlgorithmsClient2Server()
+ {
+ return $this->compression_algorithms_client_to_server;
+ }
+
+ /**
+ * Return a list of the compression algorithms the server supports, when sending stuff to the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getCompressionAlgorithmsServer2Client()
+ {
+ return $this->compression_algorithms_server_to_client;
+ }
+
+ /**
+ * Return a list of the languages the server supports, when sending stuff to the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getLanguagesServer2Client()
+ {
+ return $this->languages_server_to_client;
+ }
+
+ /**
+ * Return a list of the languages the server supports, when receiving stuff from the client.
+ *
+ * @return Array
+ * @access public
+ */
+ function getLanguagesClient2Server()
+ {
+ return $this->languages_client_to_server;
+ }
+
+ /**
+ * Returns the server public host key.
+ *
+ * Caching this the first time you connect to a server and checking the result on subsequent connections
+ * is recommended. Returns false if the server signature is not signed correctly with the public host key.
+ *
+ * @return Mixed
+ * @access public
+ */
+ function getServerPublicHostKey()
+ {
+ $signature = $this->signature;
+ $server_public_host_key = $this->server_public_host_key;
+
+ extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
+ $this->_string_shift($server_public_host_key, $length);
+
+ if ($this->signature_validated) {
+ return $this->bitmap ?
+ $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
+ false;
+ }
+
+ $this->signature_validated = true;
+
+ switch ($this->signature_format) {
+ case 'ssh-dss':
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+
+ /* The value for 'dss_signature_blob' is encoded as a string containing
+ r, followed by s (which are 160-bit integers, without lengths or
+ padding, unsigned, and in network byte order). */
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
+ if ($temp['length'] != 40) {
+ user_error('Invalid signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
+ $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
+
+ if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
+ user_error('Invalid signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $w = $s->modInverse($q);
+
+ $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
+ list(, $u1) = $u1->divide($q);
+
+ $u2 = $w->multiply($r);
+ list(, $u2) = $u2->divide($q);
+
+ $g = $g->modPow($u1, $p);
+ $y = $y->modPow($u2, $p);
+
+ $v = $g->multiply($y);
+ list(, $v) = $v->divide($p);
+ list(, $v) = $v->divide($q);
+
+ if (!$v->equals($r)) {
+ user_error('Bad server signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ }
+
+ break;
+ case 'ssh-rsa':
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
+ $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
+ $nLength = $temp['length'];
+
+ /*
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
+ $signature = $this->_string_shift($signature, $temp['length']);
+
+ if (!class_exists('Crypt_RSA')) {
+ require_once('Crypt/RSA.php');
+ }
+
+ $rsa = new Crypt_RSA();
+ $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
+ $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
+ if (!$rsa->verify($this->exchange_hash, $signature)) {
+ user_error('Bad server signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ }
+ */
+
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
+ $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
+
+ // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
+ // following URL:
+ // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
+
+ // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
+
+ if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
+ user_error('Invalid signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+
+ $s = $s->modPow($e, $n);
+ $s = $s->toBytes();
+
+ $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
+ $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
+
+ if ($s != $h) {
+ user_error('Bad server signature');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ }
+ break;
+ default:
+ user_error('Unsupported signature format');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ }
+
+ return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
+ }
+
+ /**
+ * Returns the exit status of an SSH command or false.
+ *
+ * @return Integer or false
+ * @access public
+ */
+ function getExitStatus()
+ {
+ if (is_null($this->exit_status)) {
+ return false;
+ }
+ return $this->exit_status;
+ }
+}
diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf b/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf
new file mode 100644
index 00000000000..6baa566102c
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf
@@ -0,0 +1,6 @@
+# minimalist openssl.cnf file for use with phpseclib
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+[ v3_ca ] \ No newline at end of file
diff --git a/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist b/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist
new file mode 100644
index 00000000000..f579ab4fd27
--- /dev/null
+++ b/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit bootstrap="tests/bootstrap.php"
+ colors="true"
+>
+ <testsuites>
+ <testsuite name="phpseclib Test Suite">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <!-- Code Coverage -->
+ <filter>
+ <whitelist>
+ <directory>./phpseclib/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/apps/files_external/ajax/addMountPoint.php b/apps/files_external/ajax/addMountPoint.php
index e08f805942f..fed2ddfcf3d 100644
--- a/apps/files_external/ajax/addMountPoint.php
+++ b/apps/files_external/ajax/addMountPoint.php
@@ -10,4 +10,10 @@ if ($_POST['isPersonal'] == 'true') {
OCP\JSON::checkAdminUser();
$isPersonal = false;
}
-OC_Mount_Config::addMountPoint($_POST['mountPoint'], $_POST['class'], $_POST['classOptions'], $_POST['mountType'], $_POST['applicable'], $isPersonal);
+$status = OC_Mount_Config::addMountPoint($_POST['mountPoint'],
+ $_POST['class'],
+ $_POST['classOptions'],
+ $_POST['mountType'],
+ $_POST['applicable'],
+ $isPersonal);
+OCP\JSON::success(array('data' => array('message' => $status))); \ No newline at end of file
diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php
index e0a0239c954..43fd6752c4a 100644
--- a/apps/files_external/ajax/addRootCertificate.php
+++ b/apps/files_external/ajax/addRootCertificate.php
@@ -1,25 +1,28 @@
<?php
OCP\JSON::checkAppEnabled('files_external');
+OCP\JSON::callCheck();
-if ( !($filename = $_FILES['rootcert_import']['name']) ) {
- header("Location: settings/personal.php");
+if ( ! ($filename = $_FILES['rootcert_import']['name']) ) {
+ header("Location: settings/personal.php");
exit;
}
-$fh = fopen($_FILES['rootcert_import']['tmp_name'], 'r');
-$data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name']));
+$fh = fopen($_FILES['rootcert_import']['tmp_name'], 'r');
+$data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name']));
fclose($fh);
$filename = $_FILES['rootcert_import']['name'];
-
-$view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/files_external/uploads');
-if (!$view->file_exists('')) $view->mkdir('');
+
+$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_external/uploads');
+if (!$view->file_exists('')) {
+ $view->mkdir('');
+}
$isValid = openssl_pkey_get_public($data);
//maybe it was just the wrong file format, try to convert it...
if ($isValid == false) {
- $data = chunk_split(base64_encode($data), 64, "\n");
+ $data = chunk_split(base64_encode($data), 64, "\n");
$data = "-----BEGIN CERTIFICATE-----\n".$data."-----END CERTIFICATE-----\n";
$isValid = openssl_pkey_get_public($data);
}
@@ -29,8 +32,10 @@ if ( $isValid ) {
$view->file_put_contents($filename, $data);
OC_Mount_Config::createCertificateBundle();
} else {
- OCP\Util::writeLog("files_external", "Couldn't import SSL root certificate ($filename), allowed formats: PEM and DER", OCP\Util::WARN);
+ OCP\Util::writeLog('files_external',
+ 'Couldn\'t import SSL root certificate ('.$filename.'), allowed formats: PEM and DER',
+ OCP\Util::WARN);
}
-header("Location: settings/personal.php");
+header('Location:' . OCP\Util::linkToRoute( "settings_personal" ));
exit;
diff --git a/apps/files_external/ajax/dropbox.php b/apps/files_external/ajax/dropbox.php
index f5923940dc9..bc9821c62ec 100644
--- a/apps/files_external/ajax/dropbox.php
+++ b/apps/files_external/ajax/dropbox.php
@@ -4,6 +4,8 @@ require_once 'Dropbox/autoload.php';
OCP\JSON::checkAppEnabled('files_external');
OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
if (isset($_POST['app_key']) && isset($_POST['app_secret'])) {
$oauth = new Dropbox_OAuth_Curl($_POST['app_key'], $_POST['app_secret']);
if (isset($_POST['step'])) {
@@ -16,9 +18,13 @@ if (isset($_POST['app_key']) && isset($_POST['app_secret'])) {
$callback = null;
}
$token = $oauth->getRequestToken();
- OCP\JSON::success(array('data' => array('url' => $oauth->getAuthorizeUrl($callback), 'request_token' => $token['token'], 'request_token_secret' => $token['token_secret'])));
+ OCP\JSON::success(array('data' => array('url' => $oauth->getAuthorizeUrl($callback),
+ 'request_token' => $token['token'],
+ 'request_token_secret' => $token['token_secret'])));
} catch (Exception $exception) {
- OCP\JSON::error(array('data' => array('message' => 'Fetching request tokens failed. Verify that your Dropbox app key and secret are correct.')));
+ OCP\JSON::error(array('data' => array('message' =>
+ 'Fetching request tokens failed. Verify that your Dropbox app key and secret are correct.')
+ ));
}
break;
case 2:
@@ -26,9 +32,12 @@ if (isset($_POST['app_key']) && isset($_POST['app_secret'])) {
try {
$oauth->setToken($_POST['request_token'], $_POST['request_token_secret']);
$token = $oauth->getAccessToken();
- OCP\JSON::success(array('access_token' => $token['token'], 'access_token_secret' => $token['token_secret']));
+ OCP\JSON::success(array('access_token' => $token['token'],
+ 'access_token_secret' => $token['token_secret']));
} catch (Exception $exception) {
- OCP\JSON::error(array('data' => array('message' => 'Fetching access tokens failed. Verify that your Dropbox app key and secret are correct.')));
+ OCP\JSON::error(array('data' => array('message' =>
+ 'Fetching access tokens failed. Verify that your Dropbox app key and secret are correct.')
+ ));
}
}
break;
diff --git a/apps/files_external/ajax/google.php b/apps/files_external/ajax/google.php
index 4cd01c06cc9..70adcb2c2ad 100644
--- a/apps/files_external/ajax/google.php
+++ b/apps/files_external/ajax/google.php
@@ -4,6 +4,8 @@ require_once 'Google/common.inc.php';
OCP\JSON::checkAppEnabled('files_external');
OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
$consumer = new OAuthConsumer('anonymous', 'anonymous');
$sigMethod = new OAuthSignatureMethod_HMAC_SHA1();
if (isset($_POST['step'])) {
@@ -14,7 +16,9 @@ if (isset($_POST['step'])) {
} else {
$callback = null;
}
- $scope = 'https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/';
+ $scope = 'https://docs.google.com/feeds/'
+ .' https://docs.googleusercontent.com/'
+ .' https://spreadsheets.google.com/feeds/';
$url = 'https://www.google.com/accounts/OAuthGetRequestToken?scope='.urlencode($scope);
$params = array('scope' => $scope, 'oauth_callback' => $callback);
$request = OAuthRequest::from_consumer_and_token($consumer, null, 'GET', $url, $params);
@@ -24,24 +28,35 @@ if (isset($_POST['step'])) {
parse_str($response, $token);
if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) {
$authUrl = 'https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token='.$token['oauth_token'];
- OCP\JSON::success(array('data' => array('url' => $authUrl, 'request_token' => $token['oauth_token'], 'request_token_secret' => $token['oauth_token_secret'])));
+ OCP\JSON::success(array('data' => array('url' => $authUrl,
+ 'request_token' => $token['oauth_token'],
+ 'request_token_secret' => $token['oauth_token_secret'])));
} else {
- OCP\JSON::error(array('data' => array('message' => 'Fetching request tokens failed. Error: '.$response)));
+ OCP\JSON::error(array('data' => array(
+ 'message' => 'Fetching request tokens failed. Error: '.$response
+ )));
}
break;
case 2:
- if (isset($_POST['oauth_verifier']) && isset($_POST['request_token']) && isset($_POST['request_token_secret'])) {
+ if (isset($_POST['oauth_verifier'])
+ && isset($_POST['request_token'])
+ && isset($_POST['request_token_secret'])
+ ) {
$token = new OAuthToken($_POST['request_token'], $_POST['request_token_secret']);
$url = 'https://www.google.com/accounts/OAuthGetAccessToken';
- $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url, array('oauth_verifier' => $_POST['oauth_verifier']));
+ $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url,
+ array('oauth_verifier' => $_POST['oauth_verifier']));
$request->sign_request($sigMethod, $consumer, $token);
$response = send_signed_request('GET', $url, array($request->to_header()), null, false);
$token = array();
parse_str($response, $token);
if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) {
- OCP\JSON::success(array('access_token' => $token['oauth_token'], 'access_token_secret' => $token['oauth_token_secret']));
+ OCP\JSON::success(array('access_token' => $token['oauth_token'],
+ 'access_token_secret' => $token['oauth_token_secret']));
} else {
- OCP\JSON::error(array('data' => array('message' => 'Fetching access tokens failed. Error: '.$response)));
+ OCP\JSON::error(array('data' => array(
+ 'message' => 'Fetching access tokens failed. Error: '.$response
+ )));
}
}
break;
diff --git a/apps/files_external/ajax/removeMountPoint.php b/apps/files_external/ajax/removeMountPoint.php
index aa446426202..2f5dbcfdbac 100644
--- a/apps/files_external/ajax/removeMountPoint.php
+++ b/apps/files_external/ajax/removeMountPoint.php
@@ -3,6 +3,15 @@
OCP\JSON::checkAppEnabled('files_external');
OCP\JSON::callCheck();
+if (!isset($_POST['isPersonal']))
+ return;
+if (!isset($_POST['mountPoint']))
+ return;
+if (!isset($_POST['mountType']))
+ return;
+if (!isset($_POST['applicable']))
+ return;
+
if ($_POST['isPersonal'] == 'true') {
OCP\JSON::checkLoggedIn();
$isPersonal = true;
@@ -10,4 +19,5 @@ if ($_POST['isPersonal'] == 'true') {
OCP\JSON::checkAdminUser();
$isPersonal = false;
}
+
OC_Mount_Config::removeMountPoint($_POST['mountPoint'], $_POST['mountType'], $_POST['applicable'], $isPersonal);
diff --git a/apps/files_external/ajax/removeRootCertificate.php b/apps/files_external/ajax/removeRootCertificate.php
index 6871b0fd1d4..664b3937e97 100644
--- a/apps/files_external/ajax/removeRootCertificate.php
+++ b/apps/files_external/ajax/removeRootCertificate.php
@@ -11,4 +11,3 @@ if ( $view->file_exists($file) ) {
$view->unlink($file);
OC_Mount_Config::createCertificateBundle();
}
-
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php
index 837d35c9c63..d786c6c7a2a 100644
--- a/apps/files_external/appinfo/app.php
+++ b/apps/files_external/appinfo/app.php
@@ -6,15 +6,16 @@
* See the COPYING-README file.
*/
-OC::$CLASSPATH['OC_FileStorage_StreamWrapper']='apps/files_external/lib/streamwrapper.php';
-OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php';
-OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php';
-OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php';
-OC::$CLASSPATH['OC_Filestorage_SWIFT']='apps/files_external/lib/swift.php';
-OC::$CLASSPATH['OC_Filestorage_SMB']='apps/files_external/lib/smb.php';
-OC::$CLASSPATH['OC_Filestorage_AmazonS3']='apps/files_external/lib/amazons3.php';
-OC::$CLASSPATH['OC_Filestorage_Dropbox']='apps/files_external/lib/dropbox.php';
-OC::$CLASSPATH['OC_Mount_Config']='apps/files_external/lib/config.php';
+OC::$CLASSPATH['OC\Files\Storage\StreamWrapper'] = 'files_external/lib/streamwrapper.php';
+OC::$CLASSPATH['OC\Files\Storage\FTP'] = 'files_external/lib/ftp.php';
+OC::$CLASSPATH['OC\Files\Storage\DAV'] = 'files_external/lib/webdav.php';
+OC::$CLASSPATH['OC\Files\Storage\Google'] = 'files_external/lib/google.php';
+OC::$CLASSPATH['OC\Files\Storage\SWIFT'] = 'files_external/lib/swift.php';
+OC::$CLASSPATH['OC\Files\Storage\SMB'] = 'files_external/lib/smb.php';
+OC::$CLASSPATH['OC\Files\Storage\AmazonS3'] = 'files_external/lib/amazons3.php';
+OC::$CLASSPATH['OC\Files\Storage\Dropbox'] = 'files_external/lib/dropbox.php';
+OC::$CLASSPATH['OC\Files\Storage\SFTP'] = 'files_external/lib/sftp.php';
+OC::$CLASSPATH['OC_Mount_Config'] = 'files_external/lib/config.php';
OCP\App::registerAdmin('files_external', 'settings');
if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') {
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
index 3da1913c5fc..0542b7b10a7 100644
--- a/apps/files_external/appinfo/info.xml
+++ b/apps/files_external/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>Mount external storage sources</description>
<licence>AGPL</licence>
<author>Robin Appelman, Michael Gapczynski</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
<types>
<filesystem/>
diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css
index ca4b1c3ba89..94b453793b1 100644
--- a/apps/files_external/css/settings.css
+++ b/apps/files_external/css/settings.css
@@ -1,4 +1,7 @@
-.error { color: #FF3B3B; }
+td.status>span { display:inline-block; height:16px; width:16px; }
+span.success { background-image: url('../img/success.png'); background-repeat:no-repeat; }
+span.error { background-image: url('../img/error.png'); background-repeat:no-repeat; }
+span.waiting { background-image: url('../img/waiting.png'); background-repeat:no-repeat; }
td.mountPoint, td.backend { width:10em; }
td.remove>img { visibility:hidden; padding-top:0.8em; }
tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
diff --git a/apps/files_external/img/error.png b/apps/files_external/img/error.png
new file mode 100644
index 00000000000..e8cf45e7a41
--- /dev/null
+++ b/apps/files_external/img/error.png
Binary files differ
diff --git a/apps/files_external/img/success.png b/apps/files_external/img/success.png
new file mode 100644
index 00000000000..6f7022ee7f5
--- /dev/null
+++ b/apps/files_external/img/success.png
Binary files differ
diff --git a/apps/files_external/img/waiting.png b/apps/files_external/img/waiting.png
new file mode 100644
index 00000000000..02a8cbff0da
--- /dev/null
+++ b/apps/files_external/img/waiting.png
Binary files differ
diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js
index c1e38640708..957daeb4d1f 100644
--- a/apps/files_external/js/dropbox.js
+++ b/apps/files_external/js/dropbox.js
@@ -1,6 +1,6 @@
$(document).ready(function() {
- $('#externalStorage tbody tr.OC_Filestorage_Dropbox').each(function() {
+ $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox').each(function() {
var configured = $(this).find('[data-parameter="configured"]');
if ($(configured).val() == 'true') {
$(this).find('.configuration input').attr('disabled', 'disabled');
@@ -15,6 +15,9 @@ $(document).ready(function() {
if (pos != -1 && window.location.search.substr(pos, $(token).val().length) == $(token).val()) {
var token_secret = $(this).find('.configuration [data-parameter="token_secret"]');
var tr = $(this);
+ var statusSpan = $(tr).find('.status span');
+ statusSpan.removeClass();
+ statusSpan.addClass('waiting');
$.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 2, app_key: app_key, app_secret: app_secret, request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) {
if (result && result.status == 'success') {
$(token).val(result.access_token);
@@ -24,23 +27,40 @@ $(document).ready(function() {
$(tr).find('.configuration input').attr('disabled', 'disabled');
$(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
} else {
- OC.dialogs.alert(result.data.message,
- t('files_external', 'Error configuring Dropbox storage')
- );
+ OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage'));
}
});
}
- } else if ($(this).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '' && $(this).find('.dropbox').length == 0) {
- $(this).find('.configuration').append('<a class="button dropbox">'+t('files_external', 'Grant access')+'</a>');
+ } else {
+ onDropboxInputsChange($(this));
}
}
});
- $('#externalStorage tbody tr input').live('keyup', function() {
- var tr = $(this).parent().parent();
- if ($(tr).hasClass('OC_Filestorage_Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') {
+ $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() {
+ var tr = $(this).parent();
+ setTimeout(function() {
+ onDropboxInputsChange(tr);
+ }, 20);
+ });
+
+ $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() {
+ onDropboxInputsChange($(this).parent());
+ });
+
+ $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox .chzn-select', function() {
+ onDropboxInputsChange($(this).parent().parent());
+ });
+
+ function onDropboxInputsChange(tr) {
+ if ($(tr).find('[data-parameter="configured"]').val() != 'true') {
var config = $(tr).find('.configuration');
- if ($(tr).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '') {
+ if ($(tr).find('.mountPoint input').val() != ''
+ && $(config).find('[data-parameter="app_key"]').val() != ''
+ && $(config).find('[data-parameter="app_secret"]').val() != ''
+ && ($(tr).find('.chzn-select').length == 0
+ || $(tr).find('.chzn-select').val() != null))
+ {
if ($(tr).find('.dropbox').length == 0) {
$(config).append('<a class="button dropbox">'+t('files_external', 'Grant access')+'</a>');
} else {
@@ -50,41 +70,37 @@ $(document).ready(function() {
$(tr).find('.dropbox').hide();
}
}
- });
+ }
- $('.dropbox').live('click', function(event) {
+ $('#externalStorage').on('click', '.dropbox', function(event) {
event.preventDefault();
+ var tr = $(this).parent().parent();
var app_key = $(this).parent().find('[data-parameter="app_key"]').val();
var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val();
+ var statusSpan = $(tr).find('.status span');
if (app_key != '' && app_secret != '') {
var tr = $(this).parent().parent();
var configured = $(this).parent().find('[data-parameter="configured"]');
var token = $(this).parent().find('[data-parameter="token"]');
var token_secret = $(this).parent().find('[data-parameter="token_secret"]');
- $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: window.location.href }, function(result) {
+ $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: location.protocol + '//' + location.host + location.pathname }, function(result) {
if (result && result.status == 'success') {
$(configured).val('false');
$(token).val(result.data.request_token);
$(token_secret).val(result.data.request_token_secret);
- if (OC.MountConfig.saveStorage(tr)) {
- window.location = result.data.url;
- } else {
- OC.dialogs.alert(
- t('files_external', 'Fill out all required fields'),
- t('files_external', 'Error configuring Dropbox storage')
- );
- }
+ OC.MountConfig.saveStorage(tr);
+ statusSpan.removeClass();
+ statusSpan.addClass('waiting');
+ window.location = result.data.url;
} else {
- OC.dialogs.alert(result.data.message,
- t('files_external', 'Error configuring Dropbox storage')
- );
+ OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage'));
}
});
} else {
OC.dialogs.alert(
- t('files_external', 'Please provide a valid Dropbox app key and secret.'),
- t('files_external', 'Error configuring Dropbox storage')
- );
+ t('files_external', 'Please provide a valid Dropbox app key and secret.'),
+ t('files_external', 'Error configuring Dropbox storage')
+ );
}
});
diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js
index 0b3c314eb5d..7be1b338e90 100644
--- a/apps/files_external/js/google.js
+++ b/apps/files_external/js/google.js
@@ -1,19 +1,30 @@
$(document).ready(function() {
- $('#externalStorage tbody tr.OC_Filestorage_Google').each(function() {
- var configured = $(this).find('[data-parameter="configured"]');
+ $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function(index, tr) {
+ setupGoogleRow(tr);
+ });
+
+ $('#externalStorage').on('change', '#selectBackend', function() {
+ if ($(this).val() == '\\OC\\Files\\Storage\\Google') {
+ setupGoogleRow($('#externalStorage tbody>tr:last').prev('tr'));
+ }
+ });
+
+ function setupGoogleRow(tr) {
+ var configured = $(tr).find('[data-parameter="configured"]');
if ($(configured).val() == 'true') {
- $(this).find('.configuration')
- .append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
+ $(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
} else {
- var token = $(this).find('[data-parameter="token"]');
- var token_secret = $(this).find('[data-parameter="token_secret"]');
+ var token = $(tr).find('[data-parameter="token"]');
+ var token_secret = $(tr).find('[data-parameter="token_secret"]');
var params = {};
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
params[key] = value;
});
if (params['oauth_token'] !== undefined && params['oauth_verifier'] !== undefined && decodeURIComponent(params['oauth_token']) == $(token).val()) {
- var tr = $(this);
+ var statusSpan = $(tr).find('.status span');
+ statusSpan.removeClass();
+ statusSpan.addClass('waiting');
$.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 2, oauth_verifier: params['oauth_verifier'], request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) {
if (result && result.status == 'success') {
$(token).val(result.access_token);
@@ -22,61 +33,64 @@ $(document).ready(function() {
OC.MountConfig.saveStorage(tr);
$(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
} else {
- OC.dialogs.alert(result.data.message,
- t('files_external', 'Error configuring Google Drive storage')
- );
+ OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Google Drive storage'));
+ onGoogleInputsChange(tr);
}
});
- } else if ($(this).find('.google').length == 0) {
- $(this).find('.configuration').append('<a class="button google">'+t('files_external', 'Grant access')+'</a>');
+ } else {
+ onGoogleInputsChange(tr);
}
}
+ }
+
+ $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', function() {
+ var tr = $(this).parent();
+ setTimeout(function() {
+ onGoogleInputsChange(tr);
+ }, 20);
});
- $('#externalStorage tbody tr').live('change', function() {
- if ($(this).hasClass('OC_Filestorage_Google') && $(this).find('[data-parameter="configured"]').val() != 'true') {
- if ($(this).find('.mountPoint input').val() != '') {
- if ($(this).find('.google').length == 0) {
- $(this).find('.configuration').append('<a class="button google">'+t('files_external', 'Grant access')+'</a>');
- }
- }
- }
+ $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', function() {
+ onGoogleInputsChange($(this).parent());
});
- $('#externalStorage tbody tr .mountPoint input').live('keyup', function() {
- var tr = $(this).parent().parent();
- if ($(tr).hasClass('OC_Filestorage_Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) {
- if ($(this).val() != '') {
- $(tr).find('.google').show();
- } else {
+ $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google .chzn-select', function() {
+ onGoogleInputsChange($(this).parent().parent());
+ });
+
+ function onGoogleInputsChange(tr) {
+ if ($(tr).find('[data-parameter="configured"]').val() != 'true') {
+ var config = $(tr).find('.configuration');
+ if ($(tr).find('.mountPoint input').val() != '' && ($(tr).find('.chzn-select').length == 0 || $(tr).find('.chzn-select').val() != null)) {
+ if ($(tr).find('.google').length == 0) {
+ $(config).append('<a class="button google">'+t('files_external', 'Grant access')+'</a>');
+ } else {
+ $(tr).find('.google').show();
+ }
+ } else if ($(tr).find('.google').length > 0) {
$(tr).find('.google').hide();
}
}
- });
+ }
- $('.google').live('click', function(event) {
+ $('#externalStorage').on('click', '.google', function(event) {
event.preventDefault();
var tr = $(this).parent().parent();
var configured = $(this).parent().find('[data-parameter="configured"]');
var token = $(this).parent().find('[data-parameter="token"]');
var token_secret = $(this).parent().find('[data-parameter="token_secret"]');
- $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: window.location.href }, function(result) {
+ var statusSpan = $(tr).find('.status span');
+ $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: location.protocol + '//' + location.host + location.pathname }, function(result) {
if (result && result.status == 'success') {
$(configured).val('false');
$(token).val(result.data.request_token);
$(token_secret).val(result.data.request_token_secret);
- if (OC.MountConfig.saveStorage(tr)) {
- window.location = result.data.url;
- } else {
- OC.dialogs.alert(
- t('files_external', 'Fill out all required fields'),
- t('files_external', 'Error configuring Google Drive storage')
- );
- }
+ OC.MountConfig.saveStorage(tr);
+ statusSpan.removeClass();
+ statusSpan.addClass('waiting');
+ window.location = result.data.url;
} else {
- OC.dialogs.alert(result.data.message,
- t('files_external', 'Error configuring Google Drive storage')
- );
+ OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Google Drive storage'));
}
});
});
diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js
index 89f346574e2..ac408786ff6 100644
--- a/apps/files_external/js/settings.js
+++ b/apps/files_external/js/settings.js
@@ -4,6 +4,7 @@ OC.MountConfig={
if (mountPoint == '') {
return false;
}
+ var statusSpan = $(tr).find('.status span');
var backendClass = $(tr).find('.backend').data('class');
var configuration = $(tr).find('.configuration input');
var addMountPoint = true;
@@ -26,12 +27,20 @@ OC.MountConfig={
classOptions[$(input).data('parameter')] = $(input).val();
}
});
+ if ($('#externalStorage').data('admin') === true) {
+ var multiselect = $(tr).find('.chzn-select').val();
+ if (multiselect == null) {
+ return false;
+ }
+ }
if (addMountPoint) {
+ var status = false;
if ($('#externalStorage').data('admin') === true) {
var isPersonal = false;
- var multiselect = $(tr).find('.chzn-select').val();
var oldGroups = $(tr).find('.applicable').data('applicable-groups');
var oldUsers = $(tr).find('.applicable').data('applicable-users');
+ var groups = [];
+ var users = [];
$.each(multiselect, function(index, value) {
var pos = value.indexOf('(group)');
if (pos != -1) {
@@ -40,30 +49,96 @@ OC.MountConfig={
if ($.inArray(applicable, oldGroups) != -1) {
oldGroups.splice($.inArray(applicable, oldGroups), 1);
}
+ groups.push(applicable);
} else {
var mountType = 'user';
var applicable = value;
if ($.inArray(applicable, oldUsers) != -1) {
oldUsers.splice($.inArray(applicable, oldUsers), 1);
}
+ users.push(applicable);
}
- $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
+ $.ajax({type: 'POST',
+ url: OC.filePath('files_external', 'ajax', 'addMountPoint.php'),
+ data: {
+ mountPoint: mountPoint,
+ 'class': backendClass,
+ classOptions: classOptions,
+ mountType: mountType,
+ applicable: applicable,
+ isPersonal: isPersonal
+ },
+ async: false,
+ success: function(result) {
+ statusSpan.removeClass();
+ if (result && result.status == 'success' && result.data.message) {
+ status = true;
+ statusSpan.addClass('success');
+ } else {
+ statusSpan.addClass('error');
+ }
+ }
+ });
});
+ $(tr).find('.applicable').data('applicable-groups', groups);
+ $(tr).find('.applicable').data('applicable-users', users);
var mountType = 'group';
$.each(oldGroups, function(index, applicable) {
- $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
+ $.ajax({type: 'POST',
+ url: OC.filePath('files_external', 'ajax', 'removeMountPoint.php'),
+ data: {
+ mountPoint: mountPoint,
+ class: backendClass,
+ classOptions: classOptions,
+ mountType: mountType,
+ applicable: applicable,
+ isPersonal: isPersonal
+ },
+ async: false
+ });
});
var mountType = 'user';
$.each(oldUsers, function(index, applicable) {
- $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
+ $.ajax({type: 'POST',
+ url: OC.filePath('files_external', 'ajax', 'removeMountPoint.php'),
+ data: {
+ mountPoint: mountPoint,
+ class: backendClass,
+ classOptions: classOptions,
+ mountType: mountType,
+ applicable: applicable,
+ isPersonal: isPersonal
+ },
+ async: false
+ });
});
} else {
var isPersonal = true;
var mountType = 'user';
var applicable = OC.currentUser;
- $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
+ $.ajax({type: 'POST',
+ url: OC.filePath('files_external', 'ajax', 'addMountPoint.php'),
+ data: {
+ mountPoint: mountPoint,
+ 'class': backendClass,
+ classOptions: classOptions,
+ mountType: mountType,
+ applicable: applicable,
+ isPersonal: isPersonal
+ },
+ async: false,
+ success: function(result) {
+ statusSpan.removeClass();
+ if (result && result.status == 'success' && result.data.message) {
+ status = true;
+ statusSpan.addClass('success');
+ } else {
+ statusSpan.addClass('error');
+ }
+ }
+ });
}
- return true;
+ return status;
}
}
};
@@ -71,7 +146,7 @@ OC.MountConfig={
$(document).ready(function() {
$('.chzn-select').chosen();
- $('#selectBackend').live('change', function() {
+ $('#externalStorage').on('change', '#selectBackend', function() {
var tr = $(this).parent().parent();
$('#externalStorage tbody').append($(tr).clone());
$('#externalStorage tbody tr').last().find('.mountPoint input').val('');
@@ -79,9 +154,10 @@ $(document).ready(function() {
var backendClass = $(this).val();
$(this).parent().text(selected);
if ($(tr).find('.mountPoint input').val() == '') {
- $(tr).find('.mountPoint input').val(suggestMountPoint(selected.replace(/\s+/g, '')));
+ $(tr).find('.mountPoint input').val(suggestMountPoint(selected));
}
$(tr).addClass(backendClass);
+ $(tr).find('.status').append('<span class="waiting"></span>');
$(tr).find('.backend').data('class', backendClass);
var configurations = $(this).data('configurations');
var td = $(tr).find('td.configuration');
@@ -100,13 +176,17 @@ $(document).ready(function() {
td.append('<input type="text" data-parameter="'+parameter+'" placeholder="'+placeholder+'" />');
}
});
- if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass).length == 1) {
+ if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length == 1) {
OC.addScript('files_external', parameters['custom']);
}
return false;
}
});
- $('.chz-select').chosen();
+ // Reset chosen
+ var chosen = $(tr).find('.applicable select');
+ chosen.parent().find('div').remove();
+ chosen.removeAttr('id').removeClass('chzn-done').css({display:'inline-block'});
+ chosen.chosen();
$(tr).find('td').last().attr('class', 'remove');
$(tr).find('td').last().removeAttr('style');
$(tr).removeAttr('id');
@@ -114,6 +194,11 @@ $(document).ready(function() {
});
function suggestMountPoint(defaultMountPoint) {
+ var pos = defaultMountPoint.indexOf('/');
+ if (pos !== -1) {
+ defaultMountPoint = defaultMountPoint.substring(0, pos);
+ }
+ defaultMountPoint = defaultMountPoint.replace(/\s+/g, '');
var i = 1;
var append = '';
var match = true;
@@ -135,14 +220,37 @@ $(document).ready(function() {
return defaultMountPoint+append;
}
- $('#externalStorage td').live('change', function() {
- OC.MountConfig.saveStorage($(this).parent());
+ $('#externalStorage').on('paste', 'td', function() {
+ var tr = $(this).parent();
+ setTimeout(function() {
+ OC.MountConfig.saveStorage(tr);
+ }, 20);
});
- $('td.remove>img').live('click', function() {
+ var timer;
+
+ $('#externalStorage').on('keyup', 'td input', function() {
+ clearTimeout(timer);
+ var tr = $(this).parent().parent();
+ if ($(this).val) {
+ timer = setTimeout(function() {
+ OC.MountConfig.saveStorage(tr);
+ }, 2000);
+ }
+ });
+
+ $('#externalStorage').on('change', 'td input:checkbox', function() {
+ OC.MountConfig.saveStorage($(this).parent().parent().parent());
+ });
+
+ $('#externalStorage').on('change', '.applicable .chzn-select', function() {
+ OC.MountConfig.saveStorage($(this).parent().parent());
+ });
+
+ $('#externalStorage').on('click', 'td.remove>img', function() {
var tr = $(this).parent().parent();
var mountPoint = $(tr).find('.mountPoint input').val();
- if (!mountPoint) {
+ if ( ! mountPoint) {
var row=this.parentNode.parentNode;
$.post(OC.filePath('files_external', 'ajax', 'removeRootCertificate.php'), { cert: row.id });
$(tr).remove();
@@ -151,23 +259,25 @@ $(document).ready(function() {
if ($('#externalStorage').data('admin') === true) {
var isPersonal = false;
var multiselect = $(tr).find('.chzn-select').val();
- $.each(multiselect, function(index, value) {
- var pos = value.indexOf('(group)');
- if (pos != -1) {
- var mountType = 'group';
- var applicable = value.substr(0, pos);
- } else {
- var mountType = 'user';
- var applicable = value;
- }
- $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
- });
+ if (multiselect != null) {
+ $.each(multiselect, function(index, value) {
+ var pos = value.indexOf('(group)');
+ if (pos != -1) {
+ var mountType = 'group';
+ var applicable = value.substr(0, pos);
+ } else {
+ var mountType = 'user';
+ var applicable = value;
+ }
+ $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
+ });
+ }
} else {
var mountType = 'user';
var applicable = OC.currentUser;
var isPersonal = true;
+ $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
}
- $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
$(tr).remove();
});
diff --git a/apps/files_external/l10n/af_ZA.php b/apps/files_external/l10n/af_ZA.php
new file mode 100644
index 00000000000..cf9b951828d
--- /dev/null
+++ b/apps/files_external/l10n/af_ZA.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Users" => "Gebruikers"
+);
diff --git a/apps/files_external/l10n/ar.php b/apps/files_external/l10n/ar.php
new file mode 100644
index 00000000000..06837d5085c
--- /dev/null
+++ b/apps/files_external/l10n/ar.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "مجموعات",
+"Users" => "المستخدمين",
+"Delete" => "حذف"
+);
diff --git a/apps/files_external/l10n/bg_BG.php b/apps/files_external/l10n/bg_BG.php
new file mode 100644
index 00000000000..66ad4a879d4
--- /dev/null
+++ b/apps/files_external/l10n/bg_BG.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Достъпът е даден",
+"Grant access" => "Даване на достъп",
+"External Storage" => "Външно хранилище",
+"Configuration" => "Конфигурация",
+"Options" => "Опции",
+"Applicable" => "Приложимо",
+"None set" => "Няма избрано",
+"All Users" => "Всички потребители",
+"Groups" => "Групи",
+"Users" => "Потребители",
+"Delete" => "Изтриване",
+"Enable User External Storage" => "Вкл. на поддръжка за външно потр. хранилище",
+"Allow users to mount their own external storage" => "Позволено е на потребителите да ползват тяхно лично външно хранилище",
+"SSL root certificates" => "SSL основни сертификати",
+"Import Root Certificate" => "Импортиране на основен сертификат"
+);
diff --git a/apps/files_external/l10n/bn_BD.php b/apps/files_external/l10n/bn_BD.php
new file mode 100644
index 00000000000..07ccd500746
--- /dev/null
+++ b/apps/files_external/l10n/bn_BD.php
@@ -0,0 +1,20 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "অধিগমনের অনুমতি প্রদান করা হলো",
+"Error configuring Dropbox storage" => "Dropbox সংরক্ষণাগার নির্ধারণ করতে সমস্যা ",
+"Grant access" => "অধিগমনের অনুমতি প্রদান কর",
+"Please provide a valid Dropbox app key and secret." => "দয়া করে সঠিক এবং বৈধ Dropbox app key and secret প্রদান করুন।",
+"Error configuring Google Drive storage" => "Google Drive সংরক্ষণাগার নির্ধারণ করতে সমস্যা ",
+"External Storage" => "বাহ্যিক সংরক্ষণাগার",
+"Configuration" => "কনফিগারেসন",
+"Options" => "বিকল্পসমূহ",
+"Applicable" => "প্রযোজ্য",
+"None set" => "কোনটিই নির্ধারণ করা হয় নি",
+"All Users" => "সমস্ত ব্যবহারকারী",
+"Groups" => "গোষ্ঠীসমূহ",
+"Users" => "ব্যবহারকারী",
+"Delete" => "মুছে ফেল",
+"Enable User External Storage" => "ব্যবহারকারীর বাহ্যিক সংরক্ষণাগার সক্রিয় কর",
+"Allow users to mount their own external storage" => "ব্যবহারকারীদেরকে তাদের নিজস্ব বাহ্যিক সংরক্ষনাগার সাউন্ট করতে অনুমোদন দাও",
+"SSL root certificates" => "SSL রুট সনদপত্র",
+"Import Root Certificate" => "রুট সনদপত্রটি আমদানি করুন"
+);
diff --git a/apps/files_external/l10n/ca.php b/apps/files_external/l10n/ca.php
index fc6706381b7..aa9304d3301 100644
--- a/apps/files_external/l10n/ca.php
+++ b/apps/files_external/l10n/ca.php
@@ -2,16 +2,17 @@
"Access granted" => "S'ha concedit l'accés",
"Error configuring Dropbox storage" => "Error en configurar l'emmagatzemament Dropbox",
"Grant access" => "Concedeix accés",
-"Fill out all required fields" => "Ompliu els camps requerits",
"Please provide a valid Dropbox app key and secret." => "Proporcioneu una clau d'aplicació i secret vàlids per a Dropbox",
"Error configuring Google Drive storage" => "Error en configurar l'emmagatzemament Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Avís:</b> \"smbclient\" no està instal·lat. No es pot muntar la compartició CIFS/SMB. Demaneu a l'administrador del sistema que l'instal·li.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Avís:</b> El suport FTP per PHP no està activat o no està instal·lat. No es pot muntar la compartició FTP. Demaneu a l'administrador del sistema que l'instal·li.",
"External Storage" => "Emmagatzemament extern",
-"Mount point" => "Punt de muntatge",
-"Backend" => "Dorsal",
+"Folder name" => "Nom de la carpeta",
+"External storage" => "Emmagatzemament extern",
"Configuration" => "Configuració",
"Options" => "Options",
"Applicable" => "Aplicable",
-"Add mount point" => "Afegeix punt de muntatge",
+"Add storage" => "Afegeix emmagatzemament",
"None set" => "Cap d'establert",
"All Users" => "Tots els usuaris",
"Groups" => "Grups",
diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php
index 51951c19bfd..20bbe8acbaa 100644
--- a/apps/files_external/l10n/cs_CZ.php
+++ b/apps/files_external/l10n/cs_CZ.php
@@ -2,16 +2,17 @@
"Access granted" => "Přístup povolen",
"Error configuring Dropbox storage" => "Chyba při nastavení úložiště Dropbox",
"Grant access" => "Povolit přístup",
-"Fill out all required fields" => "Vyplňte všechna povinná pole",
"Please provide a valid Dropbox app key and secret." => "Zadejte, prosím, platný klíč a bezpečnostní frázi aplikace Dropbox.",
"Error configuring Google Drive storage" => "Chyba při nastavení úložiště Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Varování:</b> není nainstalován program \"smbclient\". Není možné připojení oddílů CIFS/SMB. Prosím požádejte svého správce systému ať jej nainstaluje.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Varování:</b> není nainstalována, nebo povolena, podpora FTP v PHP. Není možné připojení oddílů FTP. Prosím požádejte svého správce systému ať ji nainstaluje.",
"External Storage" => "Externí úložiště",
-"Mount point" => "Přípojný bod",
-"Backend" => "Podpůrná vrstva",
+"Folder name" => "Název složky",
+"External storage" => "Externí úložiště",
"Configuration" => "Nastavení",
"Options" => "Možnosti",
-"Applicable" => "Platný",
-"Add mount point" => "Přidat bod připojení",
+"Applicable" => "Přístupný pro",
+"Add storage" => "Přidat úložiště",
"None set" => "Nenastaveno",
"All Users" => "Všichni uživatelé",
"Groups" => "Skupiny",
diff --git a/apps/files_external/l10n/da.php b/apps/files_external/l10n/da.php
index 00a81f3da16..0c9c6c39044 100644
--- a/apps/files_external/l10n/da.php
+++ b/apps/files_external/l10n/da.php
@@ -2,16 +2,15 @@
"Access granted" => "Adgang godkendt",
"Error configuring Dropbox storage" => "Fejl ved konfiguration af Dropbox plads",
"Grant access" => "Godkend adgang",
-"Fill out all required fields" => "Udfyld alle nødvendige felter",
"Please provide a valid Dropbox app key and secret." => "Angiv venligst en valid Dropbox app nøgle og hemmelighed",
"Error configuring Google Drive storage" => "Fejl ved konfiguration af Google Drive plads",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b> Advarsel: </ b> \"smbclient\" ikke er installeret. Montering af CIFS / SMB delinger er ikke muligt. Spørg din systemadministrator om at installere det.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b> Advarsel: </ b> FTP-understøttelse i PHP ikke er aktiveret eller installeret. Montering af FTP delinger er ikke muligt. Spørg din systemadministrator om at installere det.",
"External Storage" => "Ekstern opbevaring",
-"Mount point" => "Monteringspunkt",
-"Backend" => "Backend",
+"Folder name" => "Mappenavn",
"Configuration" => "Opsætning",
"Options" => "Valgmuligheder",
"Applicable" => "Kan anvendes",
-"Add mount point" => "Tilføj monteringspunkt",
"None set" => "Ingen sat",
"All Users" => "Alle brugere",
"Groups" => "Grupper",
diff --git a/apps/files_external/l10n/de.php b/apps/files_external/l10n/de.php
index 5d57e5e4598..24183772217 100644
--- a/apps/files_external/l10n/de.php
+++ b/apps/files_external/l10n/de.php
@@ -2,16 +2,17 @@
"Access granted" => "Zugriff gestattet",
"Error configuring Dropbox storage" => "Fehler beim Einrichten von Dropbox",
"Grant access" => "Zugriff gestatten",
-"Fill out all required fields" => "Bitte alle notwendigen Felder füllen",
"Please provide a valid Dropbox app key and secret." => "Bitte trage einen gültigen Dropbox-App-Key mit Secret ein.",
"Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Warnung:</b> \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitte Deinen System-Administrator, dies zu installieren.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Warnung::</b> Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wende Dich an Deinen Systemadministrator.",
"External Storage" => "Externer Speicher",
-"Mount point" => "Mount-Point",
-"Backend" => "Backend",
+"Folder name" => "Ordnername",
+"External storage" => "Externer Speicher",
"Configuration" => "Konfiguration",
"Options" => "Optionen",
"Applicable" => "Zutreffend",
-"Add mount point" => "Mount-Point hinzufügen",
+"Add storage" => "Speicher hinzufügen",
"None set" => "Nicht definiert",
"All Users" => "Alle Benutzer",
"Groups" => "Gruppen",
diff --git a/apps/files_external/l10n/de_DE.php b/apps/files_external/l10n/de_DE.php
new file mode 100644
index 00000000000..d55c0c6909d
--- /dev/null
+++ b/apps/files_external/l10n/de_DE.php
@@ -0,0 +1,25 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Zugriff gestattet",
+"Error configuring Dropbox storage" => "Fehler beim Einrichten von Dropbox",
+"Grant access" => "Zugriff gestatten",
+"Please provide a valid Dropbox app key and secret." => "Bitte tragen Sie einen gültigen Dropbox-App-Key mit Secret ein.",
+"Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Warnung:</b> \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitten Sie Ihren Systemadministrator, dies zu installieren.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Warnung::</b> Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wenden Sie sich an Ihren Systemadministrator.",
+"External Storage" => "Externer Speicher",
+"Folder name" => "Ordnername",
+"External storage" => "Externer Speicher",
+"Configuration" => "Konfiguration",
+"Options" => "Optionen",
+"Applicable" => "Zutreffend",
+"Add storage" => "Speicher hinzufügen",
+"None set" => "Nicht definiert",
+"All Users" => "Alle Benutzer",
+"Groups" => "Gruppen",
+"Users" => "Benutzer",
+"Delete" => "Löschen",
+"Enable User External Storage" => "Externen Speicher für Benutzer aktivieren",
+"Allow users to mount their own external storage" => "Erlaubt Benutzern ihre eigenen externen Speicher einzubinden",
+"SSL root certificates" => "SSL-Root-Zertifikate",
+"Import Root Certificate" => "Root-Zertifikate importieren"
+);
diff --git a/apps/files_external/l10n/el.php b/apps/files_external/l10n/el.php
index a1dae9d4888..38b5a098f62 100644
--- a/apps/files_external/l10n/el.php
+++ b/apps/files_external/l10n/el.php
@@ -2,16 +2,15 @@
"Access granted" => "Προσβαση παρασχέθηκε",
"Error configuring Dropbox storage" => "Σφάλμα ρυθμίζωντας αποθήκευση Dropbox ",
"Grant access" => "Παροχή πρόσβασης",
-"Fill out all required fields" => "Συμπληρώστε όλα τα απαιτούμενα πεδία",
"Please provide a valid Dropbox app key and secret." => "Παρακαλούμε δώστε έγκυρο κλειδί Dropbox και μυστικό.",
"Error configuring Google Drive storage" => "Σφάλμα ρυθμίζωντας αποθήκευση Google Drive ",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Προσοχή:</b> Ο \"smbclient\" δεν εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση CIFS/SMB. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Προσοχή:</b> Η υποστήριξη FTP στην PHP δεν ενεργοποιήθηκε ή εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση FTP. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.",
"External Storage" => "Εξωτερικό Αποθηκευτικό Μέσο",
-"Mount point" => "Σημείο προσάρτησης",
-"Backend" => "Σύστημα υποστήριξης",
+"Folder name" => "Όνομα φακέλου",
"Configuration" => "Ρυθμίσεις",
"Options" => "Επιλογές",
"Applicable" => "Εφαρμόσιμο",
-"Add mount point" => "Προσθήκη σημείου προσάρτησης",
"None set" => "Κανένα επιλεγμένο",
"All Users" => "Όλοι οι Χρήστες",
"Groups" => "Ομάδες",
diff --git a/apps/files_external/l10n/eo.php b/apps/files_external/l10n/eo.php
index 97453aafedb..b0afb77498f 100644
--- a/apps/files_external/l10n/eo.php
+++ b/apps/files_external/l10n/eo.php
@@ -2,16 +2,13 @@
"Access granted" => "Alirpermeso donita",
"Error configuring Dropbox storage" => "Eraro dum agordado de la memorservo Dropbox",
"Grant access" => "Doni alirpermeson",
-"Fill out all required fields" => "Plenigu ĉiujn neprajn kampojn",
"Please provide a valid Dropbox app key and secret." => "Bonvolu provizi ŝlosilon de la aplikaĵo Dropbox validan kaj sekretan.",
"Error configuring Google Drive storage" => "Eraro dum agordado de la memorservo Google Drive",
"External Storage" => "Malena memorilo",
-"Mount point" => "Surmetingo",
-"Backend" => "Motoro",
+"Folder name" => "Dosierujnomo",
"Configuration" => "Agordo",
"Options" => "Malneproj",
"Applicable" => "Aplikebla",
-"Add mount point" => "Aldoni surmetingon",
"None set" => "Nenio agordita",
"All Users" => "Ĉiuj uzantoj",
"Groups" => "Grupoj",
diff --git a/apps/files_external/l10n/es.php b/apps/files_external/l10n/es.php
index 32a0d896874..da22f410320 100644
--- a/apps/files_external/l10n/es.php
+++ b/apps/files_external/l10n/es.php
@@ -2,16 +2,17 @@
"Access granted" => "Acceso garantizado",
"Error configuring Dropbox storage" => "Error configurando el almacenamiento de Dropbox",
"Grant access" => "Garantizar acceso",
-"Fill out all required fields" => "Rellenar todos los campos requeridos",
"Please provide a valid Dropbox app key and secret." => "Por favor , proporcione un secreto y una contraseña válida de la app Dropbox.",
"Error configuring Google Drive storage" => "Error configurando el almacenamiento de Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Advertencia:</b> El cliente smb (smbclient) no se encuentra instalado. El montado de archivos o ficheros CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Advertencia:</b> El soporte de FTP en PHP no se encuentra instalado. El montado de archivos o ficheros FTP no es posible. Por favor pida al administrador de su sistema que lo instale.",
"External Storage" => "Almacenamiento externo",
-"Mount point" => "Punto de montaje",
-"Backend" => "Motor",
+"Folder name" => "Nombre de la carpeta",
+"External storage" => "Almacenamiento externo",
"Configuration" => "Configuración",
"Options" => "Opciones",
"Applicable" => "Aplicable",
-"Add mount point" => "Añadir punto de montaje",
+"Add storage" => "Añadir almacenamiento",
"None set" => "No se ha configurado",
"All Users" => "Todos los usuarios",
"Groups" => "Grupos",
diff --git a/apps/files_external/l10n/es_AR.php b/apps/files_external/l10n/es_AR.php
index 055fbe782e7..6706aa43a31 100644
--- a/apps/files_external/l10n/es_AR.php
+++ b/apps/files_external/l10n/es_AR.php
@@ -2,16 +2,17 @@
"Access granted" => "Acceso permitido",
"Error configuring Dropbox storage" => "Error al configurar el almacenamiento de Dropbox",
"Grant access" => "Permitir acceso",
-"Fill out all required fields" => "Rellenar todos los campos requeridos",
"Please provide a valid Dropbox app key and secret." => "Por favor, proporcioná un secreto y una contraseña válida para la aplicación Dropbox.",
"Error configuring Google Drive storage" => "Error al configurar el almacenamiento de Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Advertencia:</b> El cliente smb (smbclient) no se encuentra instalado. El montado de archivos o ficheros CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Advertencia:</b> El soporte de FTP en PHP no se encuentra instalado. El montado de archivos o ficheros FTP no es posible. Por favor pida al administrador de su sistema que lo instale.",
"External Storage" => "Almacenamiento externo",
-"Mount point" => "Punto de montaje",
-"Backend" => "Motor",
+"Folder name" => "Nombre de la carpeta",
+"External storage" => "Almacenamiento externo",
"Configuration" => "Configuración",
"Options" => "Opciones",
"Applicable" => "Aplicable",
-"Add mount point" => "Añadir punto de montaje",
+"Add storage" => "Añadir almacenamiento",
"None set" => "No fue configurado",
"All Users" => "Todos los usuarios",
"Groups" => "Grupos",
diff --git a/apps/files_external/l10n/et_EE.php b/apps/files_external/l10n/et_EE.php
index 280e2664336..fd0cdefd347 100644
--- a/apps/files_external/l10n/et_EE.php
+++ b/apps/files_external/l10n/et_EE.php
@@ -1,11 +1,14 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Ligipääs on antud",
+"Error configuring Dropbox storage" => "Viga Dropboxi salvestusruumi seadistamisel",
+"Grant access" => "Anna ligipääs",
+"Please provide a valid Dropbox app key and secret." => "Palun sisesta korrektne Dropboxi rakenduse võti ja salasõna.",
+"Error configuring Google Drive storage" => "Viga Google Drive'i salvestusruumi seadistamisel",
"External Storage" => "Väline salvestuskoht",
-"Mount point" => "Ühenduspunkt",
-"Backend" => "Admin",
+"Folder name" => "Kausta nimi",
"Configuration" => "Seadistamine",
"Options" => "Valikud",
"Applicable" => "Rakendatav",
-"Add mount point" => "Lisa ühenduspunkt",
"None set" => "Pole määratud",
"All Users" => "Kõik kasutajad",
"Groups" => "Grupid",
diff --git a/apps/files_external/l10n/eu.php b/apps/files_external/l10n/eu.php
index dccd377b119..83be50deb00 100644
--- a/apps/files_external/l10n/eu.php
+++ b/apps/files_external/l10n/eu.php
@@ -2,16 +2,17 @@
"Access granted" => "Sarrera baimendua",
"Error configuring Dropbox storage" => "Errore bat egon da Dropbox biltegiratzea konfiguratzean",
"Grant access" => "Baimendu sarrera",
-"Fill out all required fields" => "Bete eskatutako eremu guztiak",
"Please provide a valid Dropbox app key and secret." => "Mesedez eman baliozkoa den Dropbox app giltza eta sekretua",
"Error configuring Google Drive storage" => "Errore bat egon da Google Drive biltegiratzea konfiguratzean",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Abisua:</b> \"smbclient\" ez dago instalatuta. CIFS/SMB partekatutako karpetak montatzea ez da posible. Mesedez eskatu zure sistema kudeatzaileari instalatzea.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Abisua:</b> PHPren FTP modulua ez dago instalatuta edo gaitua. FTP partekatutako karpetak montatzea ez da posible. Mesedez eskatu zure sistema kudeatzaileari instalatzea.",
"External Storage" => "Kanpoko Biltegiratzea",
-"Mount point" => "Montatze puntua",
-"Backend" => "Motorra",
+"Folder name" => "Karpetaren izena",
+"External storage" => "Kanpoko biltegiratzea",
"Configuration" => "Konfigurazioa",
"Options" => "Aukerak",
"Applicable" => "Aplikagarria",
-"Add mount point" => "Gehitu muntatze puntua",
+"Add storage" => "Gehitu biltegiratzea",
"None set" => "Ezarri gabe",
"All Users" => "Erabiltzaile guztiak",
"Groups" => "Taldeak",
diff --git a/apps/files_external/l10n/fa.php b/apps/files_external/l10n/fa.php
new file mode 100644
index 00000000000..a7eac596b04
--- /dev/null
+++ b/apps/files_external/l10n/fa.php
@@ -0,0 +1,11 @@
+<?php $TRANSLATIONS = array(
+"External Storage" => "حافظه خارجی",
+"Configuration" => "پیکربندی",
+"Options" => "تنظیمات",
+"Applicable" => "قابل اجرا",
+"All Users" => "تمام کاربران",
+"Groups" => "گروه ها",
+"Users" => "کاربران",
+"Delete" => "حذف",
+"Enable User External Storage" => "فعال سازی حافظه خارجی کاربر"
+);
diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php
index d7b16e0d3ee..4cf97f2fc35 100644
--- a/apps/files_external/l10n/fi_FI.php
+++ b/apps/files_external/l10n/fi_FI.php
@@ -2,15 +2,15 @@
"Access granted" => "Pääsy sallittu",
"Error configuring Dropbox storage" => "Virhe Dropbox levyn asetuksia tehtäessä",
"Grant access" => "Salli pääsy",
-"Fill out all required fields" => "Täytä kaikki vaaditut kentät",
+"Please provide a valid Dropbox app key and secret." => "Anna kelvollinen Dropbox-sovellusavain ja salainen vastaus.",
"Error configuring Google Drive storage" => "Virhe Google Drive levyn asetuksia tehtäessä",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Varoitus:</b> \"smbclient\" ei ole asennettuna. CIFS-/SMB-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää asentamaan smbclient.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Varoitus:</b> PHP:n FTP-tuki ei ole käytössä tai sitä ei ole asennettu. FTP-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan FTP-tuki käyttöön.",
"External Storage" => "Erillinen tallennusväline",
-"Mount point" => "Liitospiste",
-"Backend" => "Taustaosa",
+"Folder name" => "Kansion nimi",
"Configuration" => "Asetukset",
"Options" => "Valinnat",
"Applicable" => "Sovellettavissa",
-"Add mount point" => "Lisää liitospiste",
"None set" => "Ei asetettu",
"All Users" => "Kaikki käyttäjät",
"Groups" => "Ryhmät",
diff --git a/apps/files_external/l10n/fr.php b/apps/files_external/l10n/fr.php
index 90007aafaaf..c42c89f8572 100644
--- a/apps/files_external/l10n/fr.php
+++ b/apps/files_external/l10n/fr.php
@@ -2,16 +2,17 @@
"Access granted" => "Accès autorisé",
"Error configuring Dropbox storage" => "Erreur lors de la configuration du support de stockage Dropbox",
"Grant access" => "Autoriser l'accès",
-"Fill out all required fields" => "Veuillez remplir tous les champs requis",
"Please provide a valid Dropbox app key and secret." => "Veuillez fournir une clé d'application (app key) ainsi qu'un mot de passe valides.",
"Error configuring Google Drive storage" => "Erreur lors de la configuration du support de stockage Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Attention : </b> \"smbclient\" n'est pas installé. Le montage des partages CIFS/SMB n'est pas disponible. Contactez votre administrateur système pour l'installer.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Attention : </b> Le support FTP de PHP n'est pas activé ou installé. Le montage des partages FTP n'est pas disponible. Contactez votre administrateur système pour l'installer.",
"External Storage" => "Stockage externe",
-"Mount point" => "Point de montage",
-"Backend" => "Infrastructure",
+"Folder name" => "Nom du dossier",
+"External storage" => "Stockage externe",
"Configuration" => "Configuration",
"Options" => "Options",
"Applicable" => "Disponible",
-"Add mount point" => "Ajouter un point de montage",
+"Add storage" => "Ajouter un support de stockage",
"None set" => "Aucun spécifié",
"All Users" => "Tous les utilisateurs",
"Groups" => "Groupes",
diff --git a/apps/files_external/l10n/gl.php b/apps/files_external/l10n/gl.php
index 3830efb70bf..715417e25a0 100644
--- a/apps/files_external/l10n/gl.php
+++ b/apps/files_external/l10n/gl.php
@@ -1,18 +1,25 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Concedeuse acceso",
+"Error configuring Dropbox storage" => "Produciuse un erro ao configurar o almacenamento en Dropbox",
+"Grant access" => "Permitir o acceso",
+"Please provide a valid Dropbox app key and secret." => "Forneza unha chave correcta e segreda do Dropbox.",
+"Error configuring Google Drive storage" => "Produciuse un erro ao configurar o almacenamento en Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Aviso:</b> «smbclient» non está instalado. Non é posibel a montaxe de comparticións CIFS/SMB. Consulte co administrador do sistema para instalalo.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Aviso:</b> A compatibilidade de FTP en PHP non está activada ou instalada. Non é posibel a montaxe de comparticións FTP. Consulte co administrador do sistema para instalalo.",
"External Storage" => "Almacenamento externo",
-"Mount point" => "Punto de montaxe",
-"Backend" => "Almacén",
+"Folder name" => "Nome do cartafol",
+"External storage" => "Almacenamento externo",
"Configuration" => "Configuración",
"Options" => "Opcións",
-"Applicable" => "Aplicable",
-"Add mount point" => "Engadir punto de montaxe",
-"None set" => "Non establecido",
-"All Users" => "Tódolos usuarios",
+"Applicable" => "Aplicábel",
+"Add storage" => "Engadir almacenamento",
+"None set" => "Ningún definido",
+"All Users" => "Todos os usuarios",
"Groups" => "Grupos",
"Users" => "Usuarios",
"Delete" => "Eliminar",
-"Enable User External Storage" => "Habilitar almacenamento externo do usuario",
+"Enable User External Storage" => "Activar o almacenamento externo do usuario",
"Allow users to mount their own external storage" => "Permitir aos usuarios montar os seus propios almacenamentos externos",
-"SSL root certificates" => "Certificados raíz SSL",
-"Import Root Certificate" => "Importar Certificado Raíz"
+"SSL root certificates" => "Certificados SSL root",
+"Import Root Certificate" => "Importar o certificado root"
);
diff --git a/apps/files_external/l10n/he.php b/apps/files_external/l10n/he.php
index 12dfa62e7c8..9cba045d1ea 100644
--- a/apps/files_external/l10n/he.php
+++ b/apps/files_external/l10n/he.php
@@ -1,7 +1,15 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "הוענקה גישה",
+"Error configuring Dropbox storage" => "אירעה שגיאה בעת הגדרת אחסון ב־Dropbox",
+"Grant access" => "הענקת גישה",
+"Please provide a valid Dropbox app key and secret." => "נא לספק קוד יישום וסוד תקניים של Dropbox.",
+"Error configuring Google Drive storage" => "אירעה שגיאה בעת הגדרת אחסון ב־Google Drive",
"External Storage" => "אחסון חיצוני",
+"Folder name" => "שם התיקייה",
"Configuration" => "הגדרות",
"Options" => "אפשרויות",
+"Applicable" => "ניתן ליישום",
+"None set" => "לא הוגדרה",
"All Users" => "כל המשתמשים",
"Groups" => "קבוצות",
"Users" => "משתמשים",
diff --git a/apps/files_external/l10n/hi.php b/apps/files_external/l10n/hi.php
new file mode 100644
index 00000000000..0482efc4b23
--- /dev/null
+++ b/apps/files_external/l10n/hi.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Users" => "उपयोगकर्ता"
+);
diff --git a/apps/files_external/l10n/hr.php b/apps/files_external/l10n/hr.php
new file mode 100644
index 00000000000..24f27ddf81b
--- /dev/null
+++ b/apps/files_external/l10n/hr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Grupe",
+"Users" => "Korisnici",
+"Delete" => "Obriši"
+);
diff --git a/apps/files_external/l10n/hu_HU.php b/apps/files_external/l10n/hu_HU.php
new file mode 100644
index 00000000000..9ecd2d1088b
--- /dev/null
+++ b/apps/files_external/l10n/hu_HU.php
@@ -0,0 +1,25 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Érvényes hozzáférés",
+"Error configuring Dropbox storage" => "A Dropbox tárolót nem sikerült beállítani",
+"Grant access" => "Megadom a hozzáférést",
+"Please provide a valid Dropbox app key and secret." => "Adjon meg egy érvényes Dropbox app key-t és secretet!",
+"Error configuring Google Drive storage" => "A Google Drive tárolót nem sikerült beállítani",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Figyelem:</b> az \"smbclient\" nincs telepítve a kiszolgálón. Emiatt nem lehet CIFS/SMB megosztásokat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Figyelem:</b> a PHP FTP támogatása vagy nincs telepítve, vagy nincs engedélyezve a kiszolgálón. Emiatt nem lehetséges FTP-tárolókat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.",
+"External Storage" => "Külső tárolási szolgáltatások becsatolása",
+"Folder name" => "Mappanév",
+"External storage" => "Külső tárolók",
+"Configuration" => "Beállítások",
+"Options" => "Opciók",
+"Applicable" => "Érvényességi kör",
+"Add storage" => "Tároló becsatolása",
+"None set" => "Nincs beállítva",
+"All Users" => "Az összes felhasználó",
+"Groups" => "Csoportok",
+"Users" => "Felhasználók",
+"Delete" => "Törlés",
+"Enable User External Storage" => "Külső tárolók engedélyezése a felhasználók részére",
+"Allow users to mount their own external storage" => "Lehetővé teszi, hogy a felhasználók külső tárolási szolgáltatásokat csatoljanak be a saját területükre",
+"SSL root certificates" => "SSL tanúsítványok",
+"Import Root Certificate" => "SSL tanúsítványok importálása"
+);
diff --git a/apps/files_external/l10n/hy.php b/apps/files_external/l10n/hy.php
new file mode 100644
index 00000000000..3b80487278a
--- /dev/null
+++ b/apps/files_external/l10n/hy.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Delete" => "Ջնջել"
+);
diff --git a/apps/files_external/l10n/ia.php b/apps/files_external/l10n/ia.php
new file mode 100644
index 00000000000..f57f96688bf
--- /dev/null
+++ b/apps/files_external/l10n/ia.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Gruppos",
+"Users" => "Usatores",
+"Delete" => "Deler"
+);
diff --git a/apps/files_external/l10n/id.php b/apps/files_external/l10n/id.php
new file mode 100644
index 00000000000..647220706ba
--- /dev/null
+++ b/apps/files_external/l10n/id.php
@@ -0,0 +1,22 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Akses diberikan",
+"Error configuring Dropbox storage" => "Kesalahan dalam mengkonfigurasi penyimpanan Dropbox",
+"Grant access" => "Berikan hak akses",
+"Please provide a valid Dropbox app key and secret." => "Masukkan kunci dan sandi aplikasi Dropbox yang benar.",
+"Error configuring Google Drive storage" => "Kesalahan dalam mengkonfigurasi penyimpanan Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Peringatan:</b> \"smbclient\" tidak terpasang. Mount direktori CIFS/SMB tidak dapat dilakukan. Silakan minta administrator sistem untuk memasangnya.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Peringatan:</b> Dukungan FTP di PHP tidak aktif atau tidak terpasang. Mount direktori FTP tidak dapat dilakukan. Silakan minta administrator sistem untuk memasangnya.",
+"External Storage" => "Penyimpanan Eksternal",
+"Configuration" => "Konfigurasi",
+"Options" => "Pilihan",
+"Applicable" => "Berlaku",
+"None set" => "Tidak satupun di set",
+"All Users" => "Semua Pengguna",
+"Groups" => "Grup",
+"Users" => "Pengguna",
+"Delete" => "Hapus",
+"Enable User External Storage" => "Aktifkan Penyimpanan Eksternal Pengguna",
+"Allow users to mount their own external storage" => "Ijinkan pengguna untuk me-mount penyimpanan eksternal mereka",
+"SSL root certificates" => "Sertifikat root SSL",
+"Import Root Certificate" => "Impor Sertifikat Root"
+);
diff --git a/apps/files_external/l10n/is.php b/apps/files_external/l10n/is.php
new file mode 100644
index 00000000000..6af53096fac
--- /dev/null
+++ b/apps/files_external/l10n/is.php
@@ -0,0 +1,23 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Aðgengi veitt",
+"Error configuring Dropbox storage" => "Villa við að setja upp Dropbox gagnasvæði",
+"Grant access" => "Veita aðgengi",
+"Please provide a valid Dropbox app key and secret." => "Gefðu upp virkan Dropbox lykil og leynikóða",
+"Error configuring Google Drive storage" => "Villa kom upp við að setja upp Google Drive gagnasvæði",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Aðvörun:</b> \"smbclient\" er ekki uppsettur. Uppsetning á CIFS/SMB gagnasvæðum er ekki möguleg. Hafðu samband við kerfisstjóra til að fá hann uppsettan.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Aðvörun:</b> FTP stuðningur í PHP er ekki virkur. Uppsetning á FTP gagnasvæðum er ekki möguleg. Hafðu samband við kerfisstjóra til að fá hann uppsettan.",
+"External Storage" => "Ytri gagnageymsla",
+"Folder name" => "Nafn möppu",
+"Configuration" => "Uppsetning",
+"Options" => "Stillingar",
+"Applicable" => "Gilt",
+"None set" => "Ekkert sett",
+"All Users" => "Allir notendur",
+"Groups" => "Hópar",
+"Users" => "Notendur",
+"Delete" => "Eyða",
+"Enable User External Storage" => "Virkja ytra gagnasvæði notenda",
+"Allow users to mount their own external storage" => "Leyfa notendum að bæta við sínum eigin ytri gagnasvæðum",
+"SSL root certificates" => "SSL rótar skilríki",
+"Import Root Certificate" => "Flytja inn rótar skilríki"
+);
diff --git a/apps/files_external/l10n/it.php b/apps/files_external/l10n/it.php
index 49effebdfc3..d7e0c81a0b0 100644
--- a/apps/files_external/l10n/it.php
+++ b/apps/files_external/l10n/it.php
@@ -2,16 +2,17 @@
"Access granted" => "Accesso consentito",
"Error configuring Dropbox storage" => "Errore durante la configurazione dell'archivio Dropbox",
"Grant access" => "Concedi l'accesso",
-"Fill out all required fields" => "Compila tutti i campi richiesti",
"Please provide a valid Dropbox app key and secret." => "Fornisci chiave di applicazione e segreto di Dropbox validi.",
"Error configuring Google Drive storage" => "Errore durante la configurazione dell'archivio Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Avviso:</b> \"smbclient\" non è installato. Impossibile montare condivisioni CIFS/SMB. Chiedi all'amministratore di sistema di installarlo.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Avviso:</b> il supporto FTP di PHP non è abilitato o non è installato. Impossibile montare condivisioni FTP. Chiedi all'amministratore di sistema di installarlo.",
"External Storage" => "Archiviazione esterna",
-"Mount point" => "Punto di mount",
-"Backend" => "Motore",
+"Folder name" => "Nome della cartella",
+"External storage" => "Archiviazione esterna",
"Configuration" => "Configurazione",
"Options" => "Opzioni",
"Applicable" => "Applicabile",
-"Add mount point" => "Aggiungi punto di mount",
+"Add storage" => "Aggiungi archiviazione",
"None set" => "Nessuna impostazione",
"All Users" => "Tutti gli utenti",
"Groups" => "Gruppi",
diff --git a/apps/files_external/l10n/ja_JP.php b/apps/files_external/l10n/ja_JP.php
index 92f74ce9f72..12a0b30938a 100644
--- a/apps/files_external/l10n/ja_JP.php
+++ b/apps/files_external/l10n/ja_JP.php
@@ -2,16 +2,17 @@
"Access granted" => "アクセスは許可されました",
"Error configuring Dropbox storage" => "Dropboxストレージの設定エラー",
"Grant access" => "アクセスを許可",
-"Fill out all required fields" => "すべての必須フィールドを埋めて下さい",
"Please provide a valid Dropbox app key and secret." => "有効なDropboxアプリのキーとパスワードを入力して下さい。",
"Error configuring Google Drive storage" => "Googleドライブストレージの設定エラー",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>警告:</b> \"smbclient\" はインストールされていません。CIFS/SMB 共有のマウントはできません。システム管理者にインストールをお願いして下さい。",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>警告:</b> PHPのFTPサポートは無効もしくはインストールされていません。FTP共有のマウントはできません。システム管理者にインストールをお願いして下さい。",
"External Storage" => "外部ストレージ",
-"Mount point" => "マウントポイント",
-"Backend" => "バックエンド",
+"Folder name" => "フォルダ名",
+"External storage" => "外部ストレージ",
"Configuration" => "設定",
"Options" => "オプション",
"Applicable" => "適用範囲",
-"Add mount point" => "マウントポイントを追加",
+"Add storage" => "ストレージを追加",
"None set" => "未設定",
"All Users" => "すべてのユーザ",
"Groups" => "グループ",
diff --git a/apps/files_external/l10n/ka.php b/apps/files_external/l10n/ka.php
new file mode 100644
index 00000000000..14c0625e1f2
--- /dev/null
+++ b/apps/files_external/l10n/ka.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Users" => "მომხმარებლები"
+);
diff --git a/apps/files_external/l10n/ka_GE.php b/apps/files_external/l10n/ka_GE.php
new file mode 100644
index 00000000000..efccca9fd78
--- /dev/null
+++ b/apps/files_external/l10n/ka_GE.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "ჯგუფები",
+"Users" => "მომხმარებელი",
+"Delete" => "წაშლა"
+);
diff --git a/apps/files_external/l10n/ko.php b/apps/files_external/l10n/ko.php
new file mode 100644
index 00000000000..47de9aad5e0
--- /dev/null
+++ b/apps/files_external/l10n/ko.php
@@ -0,0 +1,23 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "접근 허가됨",
+"Error configuring Dropbox storage" => "Dropbox 저장소 설정 오류",
+"Grant access" => "접근 권한 부여",
+"Please provide a valid Dropbox app key and secret." => "올바른 Dropbox 앱 키와 암호를 입력하십시오.",
+"Error configuring Google Drive storage" => "Google 드라이브 저장소 설정 오류",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>경고:</b> \"smbclient\"가 설치되지 않았습니다. CIFS/SMB 공유 자원에 연결할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>경고:</b> PHP FTP 지원이 비활성화되어 있거나 설치되지 않았습니다. FTP 공유를 마운트할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.",
+"External Storage" => "외부 저장소",
+"Folder name" => "폴더 이름",
+"Configuration" => "설정",
+"Options" => "옵션",
+"Applicable" => "적용 가능",
+"None set" => "설정되지 않음",
+"All Users" => "모든 사용자",
+"Groups" => "그룹",
+"Users" => "사용자",
+"Delete" => "삭제",
+"Enable User External Storage" => "사용자 외부 저장소 사용",
+"Allow users to mount their own external storage" => "사용자별 외부 저장소 마운트 허용",
+"SSL root certificates" => "SSL 루트 인증서",
+"Import Root Certificate" => "루트 인증서 가져오기"
+);
diff --git a/apps/files_external/l10n/ku_IQ.php b/apps/files_external/l10n/ku_IQ.php
new file mode 100644
index 00000000000..59eabb36c9f
--- /dev/null
+++ b/apps/files_external/l10n/ku_IQ.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Folder name" => "ناوی بوخچه",
+"Users" => "به‌كارهێنه‌ر"
+);
diff --git a/apps/files_external/l10n/lb.php b/apps/files_external/l10n/lb.php
new file mode 100644
index 00000000000..2a62cad3fe9
--- /dev/null
+++ b/apps/files_external/l10n/lb.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Folder name" => "Dossiers Numm:",
+"Groups" => "Gruppen",
+"Delete" => "Läschen"
+);
diff --git a/apps/files_external/l10n/lt_LT.php b/apps/files_external/l10n/lt_LT.php
index 00022aa3d38..f7dca99ef65 100644
--- a/apps/files_external/l10n/lt_LT.php
+++ b/apps/files_external/l10n/lt_LT.php
@@ -1,9 +1,21 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Priėjimas suteiktas",
+"Error configuring Dropbox storage" => "Klaida nustatinėjant Dropbox talpyklą",
+"Grant access" => "Suteikti priėjimą",
+"Please provide a valid Dropbox app key and secret." => "Prašome įvesti teisingus Dropbox \"app key\" ir \"secret\".",
+"Error configuring Google Drive storage" => "Klaida nustatinėjant Google Drive talpyklą",
+"External Storage" => "Išorinės saugyklos",
+"Folder name" => "Katalogo pavadinimas",
"Configuration" => "Konfigūracija",
"Options" => "Nustatymai",
+"Applicable" => "Pritaikyti",
"None set" => "Nieko nepasirinkta",
"All Users" => "Visi vartotojai",
"Groups" => "Grupės",
"Users" => "Vartotojai",
-"Delete" => "Ištrinti"
+"Delete" => "Ištrinti",
+"Enable User External Storage" => "Įjungti vartotojų išorines saugyklas",
+"Allow users to mount their own external storage" => "Leisti vartotojams pridėti savo išorines saugyklas",
+"SSL root certificates" => "SSL sertifikatas",
+"Import Root Certificate" => "Įkelti pagrindinį sertifikatą"
);
diff --git a/apps/files_external/l10n/lv.php b/apps/files_external/l10n/lv.php
new file mode 100644
index 00000000000..b37dbcbdc16
--- /dev/null
+++ b/apps/files_external/l10n/lv.php
@@ -0,0 +1,25 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Piešķirta pieeja",
+"Error configuring Dropbox storage" => "Kļūda, konfigurējot Dropbox krātuvi",
+"Grant access" => "Piešķirt pieeju",
+"Please provide a valid Dropbox app key and secret." => "Lūdzu, norādiet derīgu Dropbox lietotnes atslēgu un noslēpumu.",
+"Error configuring Google Drive storage" => "Kļūda, konfigurējot Google Drive krātuvi",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Brīdinājums:</b> nav uzinstalēts “smbclient”. Nevar montēt CIFS/SMB koplietojumus. Lūdzu, vaicājiet savam sistēmas administratoram, lai to uzinstalē.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Brīdinājums: </b> uz PHP nav aktivēts vai instalēts FTP atbalsts. Nevar montēt FTP koplietojumus. Lūdzu, vaicājiet savam sistēmas administratoram, lai to uzinstalē.",
+"External Storage" => "Ārējā krātuve",
+"Folder name" => "Mapes nosaukums",
+"External storage" => "Ārējā krātuve",
+"Configuration" => "Konfigurācija",
+"Options" => "Opcijas",
+"Applicable" => "Piemērojams",
+"Add storage" => "Pievienot krātuvi",
+"None set" => "Neviens nav iestatīts",
+"All Users" => "Visi lietotāji",
+"Groups" => "Grupas",
+"Users" => "Lietotāji",
+"Delete" => "Dzēst",
+"Enable User External Storage" => "Aktivēt lietotāja ārējo krātuvi",
+"Allow users to mount their own external storage" => "Ļaut lietotājiem montēt pašiem savu ārējo krātuvi",
+"SSL root certificates" => "SSL saknes sertifikāti",
+"Import Root Certificate" => "Importēt saknes sertifikātus"
+);
diff --git a/apps/files_external/l10n/mk.php b/apps/files_external/l10n/mk.php
new file mode 100644
index 00000000000..1f1a1c27831
--- /dev/null
+++ b/apps/files_external/l10n/mk.php
@@ -0,0 +1,23 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Пристапот е дозволен",
+"Error configuring Dropbox storage" => "Грешка при конфигурација на Dropbox",
+"Grant access" => "Дозволи пристап",
+"Please provide a valid Dropbox app key and secret." => "Ве молам доставите валиден Dropbox клуч и тајна лозинка.",
+"Error configuring Google Drive storage" => "Грешка при конфигурација на Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> \"smbclient\" не е инсталиран. Не е можно монтирање на CIFS/SMB дискови. Замолете го Вашиот систем администратор да го инсталира.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> Не е овозможена или инсталирани FTP подршка во PHP. Не е можно монтирање на FTP дискови. Замолете го Вашиот систем администратор да го инсталира.",
+"External Storage" => "Надворешно складиште",
+"Folder name" => "Име на папка",
+"Configuration" => "Конфигурација",
+"Options" => "Опции",
+"Applicable" => "Применливо",
+"None set" => "Ништо поставено",
+"All Users" => "Сите корисници",
+"Groups" => "Групи",
+"Users" => "Корисници",
+"Delete" => "Избриши",
+"Enable User External Storage" => "Овозможи надворешни за корисници",
+"Allow users to mount their own external storage" => "Дозволи им на корисниците да монтираат свои надворешни дискови",
+"SSL root certificates" => "SSL root сертификати",
+"Import Root Certificate" => "Увези"
+);
diff --git a/apps/files_external/l10n/ms_MY.php b/apps/files_external/l10n/ms_MY.php
new file mode 100644
index 00000000000..2fdb089ebc6
--- /dev/null
+++ b/apps/files_external/l10n/ms_MY.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Kumpulan",
+"Users" => "Pengguna",
+"Delete" => "Padam"
+);
diff --git a/apps/files_external/l10n/my_MM.php b/apps/files_external/l10n/my_MM.php
new file mode 100644
index 00000000000..5acfbb0321e
--- /dev/null
+++ b/apps/files_external/l10n/my_MM.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Users" => "သုံးစွဲသူ"
+);
diff --git a/apps/files_external/l10n/nb_NO.php b/apps/files_external/l10n/nb_NO.php
index 273b40cf8ee..961ef2b1046 100644
--- a/apps/files_external/l10n/nb_NO.php
+++ b/apps/files_external/l10n/nb_NO.php
@@ -1,8 +1,21 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Tilgang innvilget",
+"Error configuring Dropbox storage" => "Feil ved konfigurering av Dropbox-lagring",
+"Grant access" => "Gi tilgang",
+"External Storage" => "Ekstern lagring",
+"Folder name" => "Mappenavn",
+"External storage" => "Ekstern lagringsplass",
"Configuration" => "Konfigurasjon",
"Options" => "Innstillinger",
+"Applicable" => "Anvendelig",
+"Add storage" => "Legg til lagringsplass",
+"None set" => "Ingen valgt",
"All Users" => "Alle brukere",
"Groups" => "Grupper",
"Users" => "Brukere",
-"Delete" => "Slett"
+"Delete" => "Slett",
+"Enable User External Storage" => "Aktiver ekstern lagring for bruker",
+"Allow users to mount their own external storage" => "Tillat brukere å koble til egne eksterne lagringsmedium",
+"SSL root certificates" => "SSL root-sertifikater",
+"Import Root Certificate" => "Importer root-sertifikat"
);
diff --git a/apps/files_external/l10n/nl.php b/apps/files_external/l10n/nl.php
index 87ab87cfc9f..ad3eda9747f 100644
--- a/apps/files_external/l10n/nl.php
+++ b/apps/files_external/l10n/nl.php
@@ -2,22 +2,23 @@
"Access granted" => "Toegang toegestaan",
"Error configuring Dropbox storage" => "Fout tijdens het configureren van Dropbox opslag",
"Grant access" => "Sta toegang toe",
-"Fill out all required fields" => "Vul alle verplichte in",
"Please provide a valid Dropbox app key and secret." => "Geef een geldige Dropbox key en secret.",
"Error configuring Google Drive storage" => "Fout tijdens het configureren van Google Drive opslag",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Waarschuwing:</b> \"smbclient\" is niet geïnstalleerd. Mounten van CIFS/SMB shares is niet mogelijk. Vraag uw beheerder om smbclient te installeren.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Waarschuwing:</b> FTP ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van FTP shares is niet mogelijk. Vraag uw beheerder FTP ondersteuning te installeren.",
"External Storage" => "Externe opslag",
-"Mount point" => "Aankoppelpunt",
-"Backend" => "Backend",
+"Folder name" => "Mapnaam",
+"External storage" => "Externe opslag",
"Configuration" => "Configuratie",
"Options" => "Opties",
"Applicable" => "Van toepassing",
-"Add mount point" => "Voeg aankoppelpunt toe",
+"Add storage" => "Toevoegen opslag",
"None set" => "Niets ingesteld",
"All Users" => "Alle gebruikers",
"Groups" => "Groepen",
"Users" => "Gebruikers",
"Delete" => "Verwijder",
-"Enable User External Storage" => "Zet gebruiker's externe opslag aan",
+"Enable User External Storage" => "Externe opslag voor gebruikers activeren",
"Allow users to mount their own external storage" => "Sta gebruikers toe om hun eigen externe opslag aan te koppelen",
"SSL root certificates" => "SSL root certificaten",
"Import Root Certificate" => "Importeer root certificaat"
diff --git a/apps/files_external/l10n/nn_NO.php b/apps/files_external/l10n/nn_NO.php
new file mode 100644
index 00000000000..4b4b6167d88
--- /dev/null
+++ b/apps/files_external/l10n/nn_NO.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Grupper",
+"Users" => "Brukarar",
+"Delete" => "Slett"
+);
diff --git a/apps/files_external/l10n/oc.php b/apps/files_external/l10n/oc.php
new file mode 100644
index 00000000000..47db011473f
--- /dev/null
+++ b/apps/files_external/l10n/oc.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Grops",
+"Users" => "Usancièrs",
+"Delete" => "Escafa"
+);
diff --git a/apps/files_external/l10n/pl.php b/apps/files_external/l10n/pl.php
index 00514e59a5f..cd1b1fe84a1 100644
--- a/apps/files_external/l10n/pl.php
+++ b/apps/files_external/l10n/pl.php
@@ -2,16 +2,17 @@
"Access granted" => "Dostęp do",
"Error configuring Dropbox storage" => "Wystąpił błąd podczas konfigurowania zasobu Dropbox",
"Grant access" => "Udziel dostępu",
-"Fill out all required fields" => "Wypełnij wszystkie wymagane pola",
"Please provide a valid Dropbox app key and secret." => "Proszę podać prawidłowy klucz aplikacji Dropbox i klucz sekretny.",
"Error configuring Google Drive storage" => "Wystąpił błąd podczas konfigurowania zasobu Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Ostrzeżenie:</b> \"smbclient\" nie jest zainstalowany. Zamontowanie katalogów CIFS/SMB nie jest możliwe. Skontaktuj sie z administratorem w celu zainstalowania.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Ostrzeżenie:</b> Wsparcie dla FTP w PHP nie jest zainstalowane lub włączone. Skontaktuj sie z administratorem w celu zainstalowania lub włączenia go.",
"External Storage" => "Zewnętrzna zasoby dyskowe",
-"Mount point" => "Punkt montowania",
-"Backend" => "Zaplecze",
+"Folder name" => "Nazwa folderu",
+"External storage" => "Zewnętrzne zasoby dyskowe",
"Configuration" => "Konfiguracja",
"Options" => "Opcje",
"Applicable" => "Zastosowanie",
-"Add mount point" => "Dodaj punkt montowania",
+"Add storage" => "Dodaj zasoby dyskowe",
"None set" => "Nie ustawione",
"All Users" => "Wszyscy uzytkownicy",
"Groups" => "Grupy",
diff --git a/apps/files_external/l10n/pt_BR.php b/apps/files_external/l10n/pt_BR.php
index 26e927a423e..a358d569139 100644
--- a/apps/files_external/l10n/pt_BR.php
+++ b/apps/files_external/l10n/pt_BR.php
@@ -2,16 +2,17 @@
"Access granted" => "Acesso concedido",
"Error configuring Dropbox storage" => "Erro ao configurar armazenamento do Dropbox",
"Grant access" => "Permitir acesso",
-"Fill out all required fields" => "Preencha todos os campos obrigatórios",
"Please provide a valid Dropbox app key and secret." => "Por favor forneça um app key e secret válido do Dropbox",
"Error configuring Google Drive storage" => "Erro ao configurar armazenamento do Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Aviso:</b> \"smbclient\" não está instalado. Impossível montar compartilhamentos de CIFS/SMB. Por favor, peça ao seu administrador do sistema para instalá-lo.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Aviso:</b> O suporte para FTP do PHP não está ativado ou instalado. Impossível montar compartilhamentos FTP. Por favor, peça ao seu administrador do sistema para instalá-lo.",
"External Storage" => "Armazenamento Externo",
-"Mount point" => "Ponto de montagem",
-"Backend" => "Backend",
+"Folder name" => "Nome da pasta",
+"External storage" => "Armazenamento Externo",
"Configuration" => "Configuração",
"Options" => "Opções",
"Applicable" => "Aplicável",
-"Add mount point" => "Adicionar ponto de montagem",
+"Add storage" => "Adicionar Armazenamento",
"None set" => "Nenhum definido",
"All Users" => "Todos os Usuários",
"Groups" => "Grupos",
diff --git a/apps/files_external/l10n/pt_PT.php b/apps/files_external/l10n/pt_PT.php
index 4795a51a6b7..aac3c1c2ca0 100644
--- a/apps/files_external/l10n/pt_PT.php
+++ b/apps/files_external/l10n/pt_PT.php
@@ -2,21 +2,22 @@
"Access granted" => "Acesso autorizado",
"Error configuring Dropbox storage" => "Erro ao configurar o armazenamento do Dropbox",
"Grant access" => "Conceder acesso",
-"Fill out all required fields" => "Preencha todos os campos obrigatórios",
"Please provide a valid Dropbox app key and secret." => "Por favor forneça uma \"app key\" e \"secret\" do Dropbox válidas.",
"Error configuring Google Drive storage" => "Erro ao configurar o armazenamento do Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Atenção:</b> O cliente \"smbclient\" não está instalado. Não é possível montar as partilhas CIFS/SMB . Peça ao seu administrador para instalar.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Aviso:</b> O suporte FTP no PHP não está activate ou instalado. Não é possível montar as partilhas FTP. Peça ao seu administrador para instalar.",
"External Storage" => "Armazenamento Externo",
-"Mount point" => "Ponto de montagem",
-"Backend" => "Backend",
+"Folder name" => "Nome da pasta",
+"External storage" => "Armazenamento Externo",
"Configuration" => "Configuração",
"Options" => "Opções",
"Applicable" => "Aplicável",
-"Add mount point" => "Adicionar ponto de montagem",
-"None set" => "Nenhum configurado",
+"Add storage" => "Adicionar armazenamento",
+"None set" => "Não definido",
"All Users" => "Todos os utilizadores",
"Groups" => "Grupos",
"Users" => "Utilizadores",
-"Delete" => "Apagar",
+"Delete" => "Eliminar",
"Enable User External Storage" => "Activar Armazenamento Externo para o Utilizador",
"Allow users to mount their own external storage" => "Permitir que os utilizadores montem o seu próprio armazenamento externo",
"SSL root certificates" => "Certificados SSL de raiz",
diff --git a/apps/files_external/l10n/ro.php b/apps/files_external/l10n/ro.php
index 6a152786808..5747205dc05 100644
--- a/apps/files_external/l10n/ro.php
+++ b/apps/files_external/l10n/ro.php
@@ -1,11 +1,16 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Acces permis",
+"Error configuring Dropbox storage" => "Eroare la configurarea mediului de stocare Dropbox",
+"Grant access" => "Permite accesul",
+"Please provide a valid Dropbox app key and secret." => "Prezintă te rog o cheie de Dropbox validă și parola",
+"Error configuring Google Drive storage" => "Eroare la configurarea mediului de stocare Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Atenție:</b> \"smbclient\" nu este instalat. Montarea mediilor CIFS/SMB partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleaze.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Atenție:</b> suportul pentru FTP în PHP nu este activat sau instalat. Montarea mediilor FPT partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleze.",
"External Storage" => "Stocare externă",
-"Mount point" => "Punctul de montare",
-"Backend" => "Backend",
+"Folder name" => "Denumire director",
"Configuration" => "Configurație",
"Options" => "Opțiuni",
"Applicable" => "Aplicabil",
-"Add mount point" => "Adaugă punct de montare",
"None set" => "Niciunul",
"All Users" => "Toți utilizatorii",
"Groups" => "Grupuri",
diff --git a/apps/files_external/l10n/ru.php b/apps/files_external/l10n/ru.php
index eeef416a848..46b73a67f04 100644
--- a/apps/files_external/l10n/ru.php
+++ b/apps/files_external/l10n/ru.php
@@ -1,11 +1,18 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Доступ предоставлен",
+"Error configuring Dropbox storage" => "Ошибка при настройке хранилища Dropbox",
+"Grant access" => "Предоставление доступа",
+"Please provide a valid Dropbox app key and secret." => "Пожалуйста, предоставьте действующий ключ Dropbox и пароль.",
+"Error configuring Google Drive storage" => "Ошибка при настройке хранилища Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> \"smbclient\" не установлен. Подключение по CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> Поддержка FTP не включена в PHP. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы включить.",
"External Storage" => "Внешний носитель",
-"Mount point" => "Точка монтирования",
-"Backend" => "Подсистема",
+"Folder name" => "Имя папки",
+"External storage" => "Внешний носитель данных",
"Configuration" => "Конфигурация",
"Options" => "Опции",
"Applicable" => "Применимый",
-"Add mount point" => "Добавить точку монтирования",
+"Add storage" => "Добавить носитель данных",
"None set" => "Не установлено",
"All Users" => "Все пользователи",
"Groups" => "Группы",
diff --git a/apps/files_external/l10n/ru_RU.php b/apps/files_external/l10n/ru_RU.php
index 493e3f5bee4..406e284b278 100644
--- a/apps/files_external/l10n/ru_RU.php
+++ b/apps/files_external/l10n/ru_RU.php
@@ -2,16 +2,15 @@
"Access granted" => "Доступ разрешен",
"Error configuring Dropbox storage" => "Ошибка при конфигурировании хранилища Dropbox",
"Grant access" => "Предоставить доступ",
-"Fill out all required fields" => "Заполните все требуемые поля",
"Please provide a valid Dropbox app key and secret." => "Пожалуйста представьте допустимый ключ приложения Dropbox и пароль.",
"Error configuring Google Drive storage" => "Ошибка настройки хранилища Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Предупреждение:</b> \"smbclient\" не установлен. Подключение общих папок CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Предупреждение:</b> Поддержка FTP в PHP не включена или не установлена. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить ее.",
"External Storage" => "Внешние системы хранения данных",
-"Mount point" => "Точка монтирования",
-"Backend" => "Бэкэнд",
+"Folder name" => "Имя папки",
"Configuration" => "Конфигурация",
"Options" => "Опции",
"Applicable" => "Применимый",
-"Add mount point" => "Добавить точку монтирования",
"None set" => "Не задан",
"All Users" => "Все пользователи",
"Groups" => "Группы",
diff --git a/apps/files_external/l10n/si_LK.php b/apps/files_external/l10n/si_LK.php
new file mode 100644
index 00000000000..cc9cb9b9ce5
--- /dev/null
+++ b/apps/files_external/l10n/si_LK.php
@@ -0,0 +1,21 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "පිවිසීමට හැක",
+"Error configuring Dropbox storage" => "Dropbox ගබඩාව වින්‍යාස කිරීමේ දෝශයක් ඇත",
+"Grant access" => "පිවිසුම ලබාදෙන්න",
+"Please provide a valid Dropbox app key and secret." => "කරුණාකර වලංගු Dropbox යෙදුම් යතුරක් හා රහසක් ලබාදෙන්න.",
+"Error configuring Google Drive storage" => "Google Drive ගබඩාව වින්‍යාස කිරීමේ දෝශයක් ඇත",
+"External Storage" => "භාහිර ගබඩාව",
+"Folder name" => "ෆොල්ඩරයේ නම",
+"Configuration" => "වින්‍යාසය",
+"Options" => "විකල්පයන්",
+"Applicable" => "අදාළ",
+"None set" => "කිසිවක් නැත",
+"All Users" => "සියළු පරිශීලකයන්",
+"Groups" => "කණ්ඩායම්",
+"Users" => "පරිශීලකයන්",
+"Delete" => "මකා දමන්න",
+"Enable User External Storage" => "පරිශීලක භාහිර ගබඩාවන් සක්‍රිය කරන්න",
+"Allow users to mount their own external storage" => "පරිශීලකයන්ට තමාගේම භාහිර ගබඩාවන් මවුන්ට් කිරීමේ අයිතිය දෙන්න",
+"SSL root certificates" => "SSL මූල සහතිකයන්",
+"Import Root Certificate" => "මූල සහතිකය ආයාත කරන්න"
+);
diff --git a/apps/files_external/l10n/sk_SK.php b/apps/files_external/l10n/sk_SK.php
index 04257a80144..af6b7b4ae6e 100644
--- a/apps/files_external/l10n/sk_SK.php
+++ b/apps/files_external/l10n/sk_SK.php
@@ -1,23 +1,25 @@
<?php $TRANSLATIONS = array(
"Access granted" => "Prístup povolený",
+"Error configuring Dropbox storage" => "Chyba pri konfigurácii úložiska Dropbox",
"Grant access" => "Povoliť prístup",
-"Fill out all required fields" => "Vyplňte všetky vyžadované kolónky",
"Please provide a valid Dropbox app key and secret." => "Zadajte platný kľúč aplikácie a heslo Dropbox",
"Error configuring Google Drive storage" => "Chyba pri konfigurácii úložiska Google drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Upozornenie:</b> \"smbclient\" nie je nainštalovaný. Nie je možné pripojenie oddielov CIFS/SMB. Požiadajte administrátora systému, nech ho nainštaluje.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Upozornenie:</b> Podpora FTP v PHP nie je povolená alebo nainštalovaná. Nie je možné pripojenie oddielov FTP. Požiadajte administrátora systému, nech ho nainštaluje.",
"External Storage" => "Externé úložisko",
-"Mount point" => "Prípojný bod",
-"Backend" => "Backend",
+"Folder name" => "Meno priečinka",
+"External storage" => "Externé úložisko",
"Configuration" => "Nastavenia",
"Options" => "Možnosti",
"Applicable" => "Aplikovateľné",
-"Add mount point" => "Pridať prípojný bod",
+"Add storage" => "Pridať úložisko",
"None set" => "Žiadne nastavené",
-"All Users" => "Všetci užívatelia",
+"All Users" => "Všetci používatelia",
"Groups" => "Skupiny",
-"Users" => "Užívatelia",
+"Users" => "Používatelia",
"Delete" => "Odstrániť",
"Enable User External Storage" => "Povoliť externé úložisko",
-"Allow users to mount their own external storage" => "Povoliť užívateľom pripojiť ich vlastné externé úložisko",
+"Allow users to mount their own external storage" => "Povoliť používateľom pripojiť ich vlastné externé úložisko",
"SSL root certificates" => "Koreňové SSL certifikáty",
"Import Root Certificate" => "Importovať koreňový certifikát"
);
diff --git a/apps/files_external/l10n/sl.php b/apps/files_external/l10n/sl.php
index f455f8c56fa..4ff2eed3bf0 100644
--- a/apps/files_external/l10n/sl.php
+++ b/apps/files_external/l10n/sl.php
@@ -1,18 +1,25 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Dostop je odobren",
+"Error configuring Dropbox storage" => "Napaka nastavljanja shrambe Dropbox",
+"Grant access" => "Odobri dostop",
+"Please provide a valid Dropbox app key and secret." => "Vpisati je treba veljaven ključ programa in kodo za Dropbox",
+"Error configuring Google Drive storage" => "Napaka nastavljanja shrambe Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Opozorilo:</b> paket \"smbclient\" ni nameščen. Priklapljanje pogonov CIFS/SMB ne bo mogoče.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Opozorilo:</b> podpora FTP v PHP ni omogočena ali pa ni nameščena. Priklapljanje pogonov FTP zato ni mogoče.",
"External Storage" => "Zunanja podatkovna shramba",
-"Mount point" => "Priklopna točka",
-"Backend" => "Zaledje",
+"Folder name" => "Ime mape",
+"External storage" => "Zunanja shramba",
"Configuration" => "Nastavitve",
"Options" => "Možnosti",
"Applicable" => "Se uporablja",
-"Add mount point" => "Dodaj priklopno točko",
+"Add storage" => "Dodaj shrambo",
"None set" => "Ni nastavljeno",
"All Users" => "Vsi uporabniki",
"Groups" => "Skupine",
"Users" => "Uporabniki",
"Delete" => "Izbriši",
-"Enable User External Storage" => "Omogoči uporabo zunanje podatkovne shrambe za uporabnike",
+"Enable User External Storage" => "Omogoči uporabniško zunanjo podatkovno shrambo",
"Allow users to mount their own external storage" => "Dovoli uporabnikom priklop lastne zunanje podatkovne shrambe",
-"SSL root certificates" => "SSL korenski certifikati",
-"Import Root Certificate" => "Uvozi korenski certifikat"
+"SSL root certificates" => "Korenska potrdila SSL",
+"Import Root Certificate" => "Uvozi korensko potrdilo"
);
diff --git a/apps/files_external/l10n/sr.php b/apps/files_external/l10n/sr.php
new file mode 100644
index 00000000000..2554c498dda
--- /dev/null
+++ b/apps/files_external/l10n/sr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Групе",
+"Users" => "Корисници",
+"Delete" => "Обриши"
+);
diff --git a/apps/files_external/l10n/sr@latin.php b/apps/files_external/l10n/sr@latin.php
new file mode 100644
index 00000000000..24f27ddf81b
--- /dev/null
+++ b/apps/files_external/l10n/sr@latin.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "Grupe",
+"Users" => "Korisnici",
+"Delete" => "Obriši"
+);
diff --git a/apps/files_external/l10n/sv.php b/apps/files_external/l10n/sv.php
index 0a5e1c66d99..45d3589228f 100644
--- a/apps/files_external/l10n/sv.php
+++ b/apps/files_external/l10n/sv.php
@@ -2,16 +2,17 @@
"Access granted" => "Åtkomst beviljad",
"Error configuring Dropbox storage" => "Fel vid konfigurering av Dropbox",
"Grant access" => "Bevilja åtkomst",
-"Fill out all required fields" => "Fyll i alla obligatoriska fält",
"Please provide a valid Dropbox app key and secret." => "Ange en giltig Dropbox nyckel och hemlighet.",
"Error configuring Google Drive storage" => "Fel vid konfigurering av Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Varning:</b> \"smb-klienten\" är inte installerad. Montering av CIFS/SMB delningar är inte möjligt. Kontakta din systemadministratör för att få den installerad.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Varning:</b> Stöd för FTP i PHP är inte aktiverat eller installerat. Montering av FTP-delningar är inte möjligt. Kontakta din systemadministratör för att få det installerat.",
"External Storage" => "Extern lagring",
-"Mount point" => "Monteringspunkt",
-"Backend" => "Källa",
+"Folder name" => "Mappnamn",
+"External storage" => "Extern lagring",
"Configuration" => "Konfiguration",
"Options" => "Alternativ",
"Applicable" => "Tillämplig",
-"Add mount point" => "Lägg till monteringspunkt",
+"Add storage" => "Lägg till lagring",
"None set" => "Ingen angiven",
"All Users" => "Alla användare",
"Groups" => "Grupper",
diff --git a/apps/files_external/l10n/ta_LK.php b/apps/files_external/l10n/ta_LK.php
new file mode 100644
index 00000000000..cee3b6edb8a
--- /dev/null
+++ b/apps/files_external/l10n/ta_LK.php
@@ -0,0 +1,21 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "அனுமதி வழங்கப்பட்டது",
+"Error configuring Dropbox storage" => "Dropbox சேமிப்பை தகவமைப்பதில் வழு",
+"Grant access" => "அனுமதியை வழங்கல்",
+"Please provide a valid Dropbox app key and secret." => "தயவுசெய்து ஒரு செல்லுபடியான Dropbox செயலி சாவி மற்றும் இரகசியத்தை வழங்குக. ",
+"Error configuring Google Drive storage" => "Google இயக்க சேமிப்பகத்தை தகமைப்பதில் வழு",
+"External Storage" => "வெளி சேமிப்பு",
+"Folder name" => "கோப்புறை பெயர்",
+"Configuration" => "தகவமைப்பு",
+"Options" => "தெரிவுகள்",
+"Applicable" => "பயன்படத்தக்க",
+"None set" => "தொகுப்பில்லா",
+"All Users" => "பயனாளர்கள் எல்லாம்",
+"Groups" => "குழுக்கள்",
+"Users" => "பயனாளர்",
+"Delete" => "நீக்குக",
+"Enable User External Storage" => "பயனாளர் வெளி சேமிப்பை இயலுமைப்படுத்துக",
+"Allow users to mount their own external storage" => "பயனாளர் அவர்களுடைய சொந்த வெளியக சேமிப்பை ஏற்ற அனுமதிக்க",
+"SSL root certificates" => "SSL வேர் சான்றிதழ்கள்",
+"Import Root Certificate" => "வேர் சான்றிதழை இறக்குமதி செய்க"
+);
diff --git a/apps/files_external/l10n/th_TH.php b/apps/files_external/l10n/th_TH.php
index 70ab8d33485..2f733eab9e8 100644
--- a/apps/files_external/l10n/th_TH.php
+++ b/apps/files_external/l10n/th_TH.php
@@ -2,16 +2,15 @@
"Access granted" => "การเข้าถึงได้รับอนุญาตแล้ว",
"Error configuring Dropbox storage" => "เกิดข้อผิดพลาดในการกำหนดค่าพื้นที่จัดเก็บข้อมูล Dropbox",
"Grant access" => "อนุญาตให้เข้าถึงได้",
-"Fill out all required fields" => "กรอกข้อมูลในช่องข้อมูลที่จำเป็นต้องกรอกทั้งหมด",
"Please provide a valid Dropbox app key and secret." => "กรุณากรอกรหัส app key ของ Dropbox และรหัสลับ",
"Error configuring Google Drive storage" => "เกิดข้อผิดพลาดในการกำหนดค่าการจัดเก็บข้อมูลในพื้นที่ของ Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>คำเตือน:</b> \"smbclient\" ยังไม่ได้ถูกติดตั้ง. การชี้ CIFS/SMB เพื่อแชร์ข้อมูลไม่สามารถกระทำได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>คำเตือน:</b> การสนับสนุนการใช้งาน FTP ในภาษา PHP ยังไม่ได้ถูกเปิดใช้งานหรือถูกติดตั้ง. การชี้ FTP เพื่อแชร์ข้อมูลไม่สามารถดำเนินการได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง",
"External Storage" => "พื้นทีจัดเก็บข้อมูลจากภายนอก",
-"Mount point" => "จุดชี้ตำแหน่ง",
-"Backend" => "ด้านหลังระบบ",
+"Folder name" => "ชื่อโฟลเดอร์",
"Configuration" => "การกำหนดค่า",
"Options" => "ตัวเลือก",
"Applicable" => "สามารถใช้งานได้",
-"Add mount point" => "เพิ่มจุดชี้ตำแหน่ง",
"None set" => "ยังไม่มีการกำหนด",
"All Users" => "ผู้ใช้งานทั้งหมด",
"Groups" => "กลุ่ม",
diff --git a/apps/files_external/l10n/tr.php b/apps/files_external/l10n/tr.php
new file mode 100644
index 00000000000..cddb2b35e03
--- /dev/null
+++ b/apps/files_external/l10n/tr.php
@@ -0,0 +1,19 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "Giriş kabul edildi",
+"Grant access" => "Erişim sağlandı",
+"Please provide a valid Dropbox app key and secret." => "Lütfen Dropbox app key ve secret temin ediniz",
+"External Storage" => "Harici Depolama",
+"Folder name" => "Dizin ismi",
+"Configuration" => "Yapılandırma",
+"Options" => "Seçenekler",
+"Applicable" => "Uygulanabilir",
+"None set" => "Hiçbiri",
+"All Users" => "Tüm Kullanıcılar",
+"Groups" => "Gruplar",
+"Users" => "Kullanıcılar",
+"Delete" => "Sil",
+"Enable User External Storage" => "Kullanıcılar için Harici Depolamayı Etkinleştir",
+"Allow users to mount their own external storage" => "Kullanıcıların kendi harici depolamalarını bağlamalarına izin ver",
+"SSL root certificates" => "SSL kök sertifikaları",
+"Import Root Certificate" => "Kök Sertifikalarını İçe Aktar"
+);
diff --git a/apps/files_external/l10n/uk.php b/apps/files_external/l10n/uk.php
index 79920b9014a..34d19af0ee9 100644
--- a/apps/files_external/l10n/uk.php
+++ b/apps/files_external/l10n/uk.php
@@ -1,5 +1,25 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Доступ дозволено",
+"Error configuring Dropbox storage" => "Помилка при налаштуванні сховища Dropbox",
+"Grant access" => "Дозволити доступ",
+"Please provide a valid Dropbox app key and secret." => "Будь ласка, надайте дійсний ключ та пароль Dropbox.",
+"Error configuring Google Drive storage" => "Помилка при налаштуванні сховища Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Попередження:</b> Клієнт \"smbclient\" не встановлено. Під'єднанатися до CIFS/SMB тек неможливо. Попрохайте системного адміністратора встановити його.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Попередження:</b> Підтримка FTP в PHP не увімкнута чи не встановлена. Під'єднанатися до FTP тек неможливо. Попрохайте системного адміністратора встановити її.",
+"External Storage" => "Зовнішні сховища",
+"Folder name" => "Ім'я теки",
+"External storage" => "Зовнішнє сховище",
+"Configuration" => "Налаштування",
+"Options" => "Опції",
+"Applicable" => "Придатний",
+"Add storage" => "Додати сховище",
+"None set" => "Не встановлено",
+"All Users" => "Усі користувачі",
"Groups" => "Групи",
"Users" => "Користувачі",
-"Delete" => "Видалити"
+"Delete" => "Видалити",
+"Enable User External Storage" => "Активувати користувацькі зовнішні сховища",
+"Allow users to mount their own external storage" => "Дозволити користувачам монтувати власні зовнішні сховища",
+"SSL root certificates" => "SSL корневі сертифікати",
+"Import Root Certificate" => "Імпортувати корневі сертифікати"
);
diff --git a/apps/files_external/l10n/ur_PK.php b/apps/files_external/l10n/ur_PK.php
new file mode 100644
index 00000000000..278357b4d68
--- /dev/null
+++ b/apps/files_external/l10n/ur_PK.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Users" => "یوزرز"
+);
diff --git a/apps/files_external/l10n/vi.php b/apps/files_external/l10n/vi.php
index dd616e91153..84f31e88924 100644
--- a/apps/files_external/l10n/vi.php
+++ b/apps/files_external/l10n/vi.php
@@ -1,11 +1,16 @@
<?php $TRANSLATIONS = array(
+"Access granted" => "Đã cấp quyền truy cập",
+"Error configuring Dropbox storage" => "Lỗi cấu hình lưu trữ Dropbox ",
+"Grant access" => "Cấp quyền truy cập",
+"Please provide a valid Dropbox app key and secret." => "Xin vui lòng cung cấp một ứng dụng Dropbox hợp lệ và mã bí mật.",
+"Error configuring Google Drive storage" => "Lỗi cấu hình lưu trữ Google Drive",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Cảnh báo:</b> \"smbclient\" chưa được cài đặt. Mount CIFS/SMB shares là không thể thực hiện được. Hãy hỏi người quản trị hệ thống để cài đặt nó.",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Cảnh báo:</b> FTP trong PHP chưa được cài đặt hoặc chưa được mở. Mount FTP shares là không thể. Xin hãy yêu cầu quản trị hệ thống của bạn cài đặt nó.",
"External Storage" => "Lưu trữ ngoài",
-"Mount point" => "Điểm gắn",
-"Backend" => "phụ trợ",
+"Folder name" => "Tên thư mục",
"Configuration" => "Cấu hình",
"Options" => "Tùy chọn",
"Applicable" => "Áp dụng",
-"Add mount point" => "Thêm điểm lắp",
"None set" => "không",
"All Users" => "Tất cả người dùng",
"Groups" => "Nhóm",
diff --git a/apps/files_external/l10n/zh_CN.GB2312.php b/apps/files_external/l10n/zh_CN.GB2312.php
index 47983d3d7d4..74b9e3cad81 100644
--- a/apps/files_external/l10n/zh_CN.GB2312.php
+++ b/apps/files_external/l10n/zh_CN.GB2312.php
@@ -2,16 +2,13 @@
"Access granted" => "已授予权限",
"Error configuring Dropbox storage" => "配置 Dropbox 存储出错",
"Grant access" => "授予权限",
-"Fill out all required fields" => "填充全部必填字段",
"Please provide a valid Dropbox app key and secret." => "请提供一个有效的 Dropbox app key 和 secret。",
"Error configuring Google Drive storage" => "配置 Google Drive 存储失败",
"External Storage" => "外部存储",
-"Mount point" => "挂载点",
-"Backend" => "后端",
+"Folder name" => "文件夹名",
"Configuration" => "配置",
"Options" => "选项",
"Applicable" => "可应用",
-"Add mount point" => "添加挂载点",
"None set" => "未设置",
"All Users" => "所有用户",
"Groups" => "群组",
diff --git a/apps/files_external/l10n/zh_CN.php b/apps/files_external/l10n/zh_CN.php
new file mode 100644
index 00000000000..1860b6f70d7
--- /dev/null
+++ b/apps/files_external/l10n/zh_CN.php
@@ -0,0 +1,23 @@
+<?php $TRANSLATIONS = array(
+"Access granted" => "权限已授予。",
+"Error configuring Dropbox storage" => "配置Dropbox存储时出错",
+"Grant access" => "授权",
+"Please provide a valid Dropbox app key and secret." => "请提供有效的Dropbox应用key和secret",
+"Error configuring Google Drive storage" => "配置Google Drive存储时出错",
+"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>警告:</b>“smbclient” 尚未安装。CIFS/SMB 分享挂载无法实现。请咨询系统管理员进行安装。",
+"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>警告:</b>PHP中尚未启用或安装FTP。FTP 分享挂载无法实现。请咨询系统管理员进行安装。",
+"External Storage" => "外部存储",
+"Folder name" => "目录名称",
+"Configuration" => "配置",
+"Options" => "选项",
+"Applicable" => "适用的",
+"None set" => "未设置",
+"All Users" => "所有用户",
+"Groups" => "组",
+"Users" => "用户",
+"Delete" => "删除",
+"Enable User External Storage" => "启用用户外部存储",
+"Allow users to mount their own external storage" => "允许用户挂载自有外部存储",
+"SSL root certificates" => "SSL根证书",
+"Import Root Certificate" => "导入根证书"
+);
diff --git a/apps/files_external/l10n/zh_HK.php b/apps/files_external/l10n/zh_HK.php
new file mode 100644
index 00000000000..a85b5a03b8a
--- /dev/null
+++ b/apps/files_external/l10n/zh_HK.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Groups" => "群組",
+"Users" => "用戶",
+"Delete" => "刪除"
+);
diff --git a/apps/files_external/l10n/zh_TW.php b/apps/files_external/l10n/zh_TW.php
new file mode 100644
index 00000000000..512a151a3c8
--- /dev/null
+++ b/apps/files_external/l10n/zh_TW.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"External Storage" => "外部儲存裝置",
+"Folder name" => "資料夾名稱",
+"None set" => "尚未設定",
+"All Users" => "所有使用者",
+"Groups" => "群組",
+"Users" => "使用者",
+"Delete" => "刪除",
+"Import Root Certificate" => "匯入根憑證"
+);
diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php
index 41ec3c70b45..7bcefd4176c 100644
--- a/apps/files_external/lib/amazons3.php
+++ b/apps/files_external/lib/amazons3.php
@@ -1,40 +1,48 @@
<?php
/**
-* ownCloud
-*
-* @author Michael Gapczynski
-* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library. If not, see <http://www.gnu.org/licenses/>.
-*/
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Files\Storage;
require_once 'aws-sdk/sdk.class.php';
-class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
+class AmazonS3 extends \OC\Files\Storage\Common {
private $s3;
private $bucket;
private $objects = array();
+ private $id;
private static $tempFiles = array();
- // TODO options: storage class, encryption server side, encrypt before upload?
+ // TODO Update to new AWS SDK
public function __construct($params) {
- $this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret']));
- $this->bucket = $params['bucket'];
+ if (isset($params['key']) && isset($params['secret']) && isset($params['bucket'])) {
+ $this->id = 'amazon::' . $params['key'] . md5($params['secret']);
+ $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret']));
+ $this->bucket = $params['bucket'];
+ } else {
+ throw new \Exception();
+ }
}
private function getObject($path) {
@@ -45,9 +53,9 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
if ($response) {
$this->objects[$path] = $response;
return $response;
- // This object could be a folder, a '/' must be at the end of the path
+ // This object could be a folder, a '/' must be at the end of the path
} else if (substr($path, -1) != '/') {
- $response = $this->s3->get_object_metadata($this->bucket, $path.'/');
+ $response = $this->s3->get_object_metadata($this->bucket, $path . '/');
if ($response) {
$this->objects[$path] = $response;
return $response;
@@ -57,6 +65,10 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
return false;
}
+ public function getId() {
+ return $this->id;
+ }
+
public function mkdir($path) {
// Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name
if (substr($path, -1) != '/') {
@@ -96,8 +108,8 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
foreach ($response->body->CommonPrefixes as $object) {
$files[] = basename($object->Prefix);
}
- OC_FakeDirStream::$dirs['amazons3'.$path] = $files;
- return opendir('fakedir://amazons3'.$path);
+ \OC\Files\Stream\Dir::register('amazons3' . $path, $files);
+ return opendir('fakedir://amazons3' . $path);
}
return false;
}
@@ -107,12 +119,10 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
$stat['size'] = $this->s3->get_bucket_filesize($this->bucket);
$stat['atime'] = time();
$stat['mtime'] = $stat['atime'];
- $stat['ctime'] = $stat['atime'];
} else if ($object = $this->getObject($path)) {
$stat['size'] = $object['Size'];
$stat['atime'] = time();
$stat['mtime'] = strtotime($object['LastModified']);
- $stat['ctime'] = $stat['mtime'];
}
if (isset($stat)) {
return $stat;
@@ -123,12 +133,15 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
public function filetype($path) {
if ($path == '' || $path == '/') {
return 'dir';
- } else if ($object = $this->getObject($path)) {
- // Amazon S3 doesn't have typical folders, this is an alternative method to detect a folder
- if (substr($object['Key'], -1) == '/' && $object['Size'] == 0) {
- return 'dir';
- } else {
- return 'file';
+ } else {
+ $object = $this->getObject($path);
+ if ($object) {
+ // Amazon S3 doesn't have typical folders, this is an alternative method to detect a folder
+ if (substr($object['Key'], -1) == '/' && $object['Size'] == 0) {
+ return 'dir';
+ } else {
+ return 'file';
+ }
}
}
return false;
@@ -160,7 +173,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
switch ($mode) {
case 'r':
case 'rb':
- $tmpFile = OC_Helper::tmpFile();
+ $tmpFile = \OC_Helper::tmpFile();
$handle = fopen($tmpFile, 'w');
$response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle));
if ($response->isOK()) {
@@ -184,14 +197,14 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
} else {
$ext = '';
}
- $tmpFile = OC_Helper::tmpFile($ext);
- OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack');
+ $tmpFile = \OC_Helper::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if ($this->file_exists($path)) {
$source = $this->fopen($path, 'r');
file_put_contents($tmpFile, $source);
}
self::$tempFiles[$tmpFile] = $path;
- return fopen('close://'.$tmpFile, $mode);
+ return fopen('close://' . $tmpFile, $mode);
}
return false;
}
@@ -199,7 +212,9 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
public function writeBack($tmpFile) {
if (isset(self::$tempFiles[$tmpFile])) {
$handle = fopen($tmpFile, 'r');
- $response = $this->s3->create_object($this->bucket, self::$tempFiles[$tmpFile], array('fileUpload' => $handle));
+ $response = $this->s3->create_object($this->bucket,
+ self::$tempFiles[$tmpFile],
+ array('fileUpload' => $handle));
if ($response->isOK()) {
unlink($tmpFile);
}
@@ -209,17 +224,15 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
public function getMimeType($path) {
if ($this->filetype($path) == 'dir') {
return 'httpd/unix-directory';
- } else if ($object = $this->getObject($path)) {
- return $object['ContentType'];
+ } else {
+ $object = $this->getObject($path);
+ if ($object) {
+ return $object['ContentType'];
+ }
}
return false;
}
- public function free_space($path) {
- // Infinite?
- return false;
- }
-
public function touch($path, $mtime = null) {
if (is_null($mtime)) {
$mtime = time();
@@ -231,4 +244,12 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
return $response->isOK();
}
+ public function test() {
+ $test = $this->s3->get_canonical_user_id();
+ if (isset($test['id']) && $test['id'] != '') {
+ return true;
+ }
+ return false;
+ }
+
}
diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php
index 0a576f06f1f..11d24045fd9 100755
--- a/apps/files_external/lib/config.php
+++ b/apps/files_external/lib/config.php
@@ -38,16 +38,82 @@ class OC_Mount_Config {
* @return array
*/
public static function getBackends() {
- return array(
- 'OC_Filestorage_Local' => array('backend' => 'Local', 'configuration' => array('datadir' => 'Location')),
- 'OC_Filestorage_AmazonS3' => array('backend' => 'Amazon S3', 'configuration' => array('key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')),
- 'OC_Filestorage_Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('configured' => '#configured','app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret'), 'custom' => 'dropbox'),
- 'OC_Filestorage_FTP' => array('backend' => 'FTP', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure ftps://')),
- 'OC_Filestorage_Google' => array('backend' => 'Google Drive', 'configuration' => array('configured' => '#configured', 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'),
- 'OC_Filestorage_SWIFT' => array('backend' => 'OpenStack Swift', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')),
- 'OC_Filestorage_SMB' => array('backend' => 'SMB', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')),
- 'OC_Filestorage_DAV' => array('backend' => 'WebDAV', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://'))
- );
+
+ $backends['\OC\Files\Storage\Local']=array(
+ 'backend' => 'Local',
+ 'configuration' => array(
+ 'datadir' => 'Location'));
+
+ $backends['\OC\Files\Storage\AmazonS3']=array(
+ 'backend' => 'Amazon S3',
+ 'configuration' => array(
+ 'key' => 'Key',
+ 'secret' => '*Secret',
+ 'bucket' => 'Bucket'));
+
+ $backends['\OC\Files\Storage\Dropbox']=array(
+ 'backend' => 'Dropbox',
+ 'configuration' => array(
+ 'configured' => '#configured',
+ 'app_key' => 'App key',
+ 'app_secret' => 'App secret',
+ 'token' => '#token',
+ 'token_secret' => '#token_secret'),
+ 'custom' => 'dropbox');
+
+ if(OC_Mount_Config::checkphpftp()) $backends['\OC\Files\Storage\FTP']=array(
+ 'backend' => 'FTP',
+ 'configuration' => array(
+ 'host' => 'URL',
+ 'user' => 'Username',
+ 'password' => '*Password',
+ 'root' => '&Root',
+ 'secure' => '!Secure ftps://'));
+
+ $backends['\OC\Files\Storage\Google']=array(
+ 'backend' => 'Google Drive',
+ 'configuration' => array(
+ 'configured' => '#configured',
+ 'token' => '#token',
+ 'token_secret' => '#token secret'),
+ 'custom' => 'google');
+
+ $backends['\OC\Files\Storage\SWIFT']=array(
+ 'backend' => 'OpenStack Swift',
+ 'configuration' => array(
+ 'host' => 'URL',
+ 'user' => 'Username',
+ 'token' => '*Token',
+ 'root' => '&Root',
+ 'secure' => '!Secure ftps://'));
+
+ if(OC_Mount_Config::checksmbclient()) $backends['\OC\Files\Storage\SMB']=array(
+ 'backend' => 'SMB / CIFS',
+ 'configuration' => array(
+ 'host' => 'URL',
+ 'user' => 'Username',
+ 'password' => '*Password',
+ 'share' => 'Share',
+ 'root' => '&Root'));
+
+ $backends['\OC\Files\Storage\DAV']=array(
+ 'backend' => 'ownCloud / WebDAV',
+ 'configuration' => array(
+ 'host' => 'URL',
+ 'user' => 'Username',
+ 'password' => '*Password',
+ 'root' => '&Root',
+ 'secure' => '!Secure https://'));
+
+ $backends['\OC\Files\Storage\SFTP']=array(
+ 'backend' => 'SFTP',
+ 'configuration' => array(
+ 'host' => 'URL',
+ 'user' => 'Username',
+ 'password' => '*Password',
+ 'root' => '&Root'));
+
+ return($backends);
}
/**
@@ -62,13 +128,24 @@ class OC_Mount_Config {
if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) {
foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
foreach ($mounts as $mountPoint => $mount) {
+ // Update old classes to new namespace
+ if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
+ $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
+ }
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
// Merge the mount point into the current mount points
if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
- $system[$mountPoint]['applicable']['groups'] = array_merge($system[$mountPoint]['applicable']['groups'], array($group));
+ $system[$mountPoint]['applicable']['groups']
+ = array_merge($system[$mountPoint]['applicable']['groups'], array($group));
} else {
- $system[$mountPoint] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], 'applicable' => array('groups' => array($group), 'users' => array()));
+ $system[$mountPoint] = array(
+ 'class' => $mount['class'],
+ 'backend' => $backends[$mount['class']]['backend'],
+ 'configuration' => $mount['options'],
+ 'applicable' => array('groups' => array($group), 'users' => array()),
+ 'status' => self::getBackendStatus($mount['class'], $mount['options'])
+ );
}
}
}
@@ -76,13 +153,24 @@ class OC_Mount_Config {
if (isset($mountPoints[self::MOUNT_TYPE_USER])) {
foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) {
foreach ($mounts as $mountPoint => $mount) {
+ // Update old classes to new namespace
+ if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
+ $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
+ }
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
// Merge the mount point into the current mount points
if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
- $system[$mountPoint]['applicable']['users'] = array_merge($system[$mountPoint]['applicable']['users'], array($user));
+ $system[$mountPoint]['applicable']['users']
+ = array_merge($system[$mountPoint]['applicable']['users'], array($user));
} else {
- $system[$mountPoint] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], 'applicable' => array('groups' => array(), 'users' => array($user)));
+ $system[$mountPoint] = array(
+ 'class' => $mount['class'],
+ 'backend' => $backends[$mount['class']]['backend'],
+ 'configuration' => $mount['options'],
+ 'applicable' => array('groups' => array(), 'users' => array($user)),
+ 'status' => self::getBackendStatus($mount['class'], $mount['options'])
+ );
}
}
}
@@ -102,28 +190,36 @@ class OC_Mount_Config {
$personal = array();
if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) {
foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) {
+ // Update old classes to new namespace
+ if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
+ $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
+ }
// Remove '/uid/files/' from mount point
- $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options']);
+ $personal[substr($mountPoint, strlen($uid) + 8)] = array(
+ 'class' => $mount['class'],
+ 'backend' => $backends[$mount['class']]['backend'],
+ 'configuration' => $mount['options'],
+ 'status' => self::getBackendStatus($mount['class'], $mount['options'])
+ );
}
}
return $personal;
}
- /**
- * Add directory for mount point to the filesystem
- * @param OC_Fileview instance $view
- * @param string path to mount point
- */
- private static function addMountPointDirectory($view, $path) {
- $dir = '';
- foreach ( explode('/', $path) as $pathPart) {
- $dir = $dir.'/'.$pathPart;
- if ( !$view->file_exists($dir)) {
- $view->mkdir($dir);
- }
+ private static function getBackendStatus($class, $options) {
+ foreach ($options as &$option) {
+ $option = str_replace('$user', OCP\User::getUser(), $option);
+ }
+ if (class_exists($class)) {
+ try {
+ $storage = new $class($options);
+ return $storage->test();
+ } catch (Exception $exception) {
+ return false;
+ }
}
+ return false;
}
-
/**
* Add a mount point to the filesystem
@@ -135,40 +231,20 @@ class OC_Mount_Config {
* @param bool Personal or system mount point i.e. is this being called from the personal or admin page
* @return bool
*/
- public static function addMountPoint($mountPoint, $class, $classOptions, $mountType, $applicable, $isPersonal = false) {
+ public static function addMountPoint($mountPoint,
+ $class,
+ $classOptions,
+ $mountType,
+ $applicable,
+ $isPersonal = false) {
if ($isPersonal) {
// Verify that the mount point applies for the current user
// Prevent non-admin users from mounting local storage
- if ($applicable != OCP\User::getUser() || $class == 'OC_Filestorage_Local') {
+ if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') {
return false;
}
- $view = new OC_FilesystemView('/'.OCP\User::getUser().'/files');
- self::addMountPointDirectory($view, ltrim($mountPoint, '/'));
$mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/');
} else {
- $view = new OC_FilesystemView('/');
- switch ($mountType) {
- case 'user':
- if ($applicable == "all") {
- $users = OCP\User::getUsers();
- foreach ( $users as $user ) {
- $path = $user.'/files/'.ltrim($mountPoint, '/');
- self::addMountPointDirectory($view, $path);
- }
- } else {
- $path = $applicable.'/files/'.ltrim($mountPoint, '/');
- self::addMountPointDirectory($view, $path);
- }
- break;
- case 'group' :
- $groupMembers = OC_Group::usersInGroups(array($applicable));
- foreach ( $groupMembers as $user ) {
- $path = $user.'/files/'.ltrim($mountPoint, '/');
- self::addMountPointDirectory($view, $path);
- }
- break;
- }
-
$mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
}
$mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions)));
@@ -176,7 +252,8 @@ class OC_Mount_Config {
// Merge the new mount point into the current mount points
if (isset($mountPoints[$mountType])) {
if (isset($mountPoints[$mountType][$applicable])) {
- $mountPoints[$mountType][$applicable] = array_merge($mountPoints[$mountType][$applicable], $mount[$applicable]);
+ $mountPoints[$mountType][$applicable]
+ = array_merge($mountPoints[$mountType][$applicable], $mount[$applicable]);
} else {
$mountPoints[$mountType] = array_merge($mountPoints[$mountType], $mount);
}
@@ -184,7 +261,7 @@ class OC_Mount_Config {
$mountPoints[$mountType] = $mount;
}
self::writeData($isPersonal, $mountPoints);
- return true;
+ return self::getBackendStatus($class, $classOptions);
}
/**
@@ -225,13 +302,22 @@ class OC_Mount_Config {
* @return array
*/
private static function readData($isPersonal) {
+ $parser = new \OC\ArrayParser();
if ($isPersonal) {
- $file = OC_User::getHome(OCP\User::getUser()).'/mount.php';
+ $phpFile = OC_User::getHome(OCP\User::getUser()).'/mount.php';
+ $jsonFile = OC_User::getHome(OCP\User::getUser()).'/mount.json';
} else {
- $file = OC::$SERVERROOT.'/config/mount.php';
+ $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
+ $phpFile = OC::$SERVERROOT.'/config/mount.php';
+ $jsonFile = $datadir . '/mount.json';
}
- if (is_file($file)) {
- $mountPoints = include($file);
+ if (is_file($jsonFile)) {
+ $mountPoints = json_decode(file_get_contents($jsonFile), true);
+ if (is_array($mountPoints)) {
+ return $mountPoints;
+ }
+ } elseif (is_file($phpFile)) {
+ $mountPoints = $parser->parsePHP(file_get_contents($phpFile));
if (is_array($mountPoints)) {
return $mountPoints;
}
@@ -246,35 +332,12 @@ class OC_Mount_Config {
*/
private static function writeData($isPersonal, $data) {
if ($isPersonal) {
- $file = OC_User::getHome(OCP\User::getUser()).'/mount.php';
+ $file = OC_User::getHome(OCP\User::getUser()).'/mount.json';
} else {
- $file = OC::$SERVERROOT.'/config/mount.php';
+ $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
+ $file = $datadir . '/mount.json';
}
- $content = "<?php return array (\n";
- if (isset($data[self::MOUNT_TYPE_GROUP])) {
- $content .= "\t'group' => array (\n";
- foreach ($data[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
- $content .= "\t\t'".$group."' => array (\n";
- foreach ($mounts as $mountPoint => $mount) {
- $content .= "\t\t\t'".$mountPoint."' => ".str_replace("\n", '', var_export($mount, true)).",\n";
-
- }
- $content .= "\t\t),\n";
- }
- $content .= "\t),\n";
- }
- if (isset($data[self::MOUNT_TYPE_USER])) {
- $content .= "\t'user' => array (\n";
- foreach ($data[self::MOUNT_TYPE_USER] as $user => $mounts) {
- $content .= "\t\t'".$user."' => array (\n";
- foreach ($mounts as $mountPoint => $mount) {
- $content .= "\t\t\t'".$mountPoint."' => ".str_replace("\n", '', var_export($mount, true)).",\n";
- }
- $content .= "\t\t),\n";
- }
- $content .= "\t),\n";
- }
- $content .= ");\n?>";
+ $content = json_encode($data);
@file_put_contents($file, $content);
}
@@ -285,14 +348,19 @@ class OC_Mount_Config {
public static function getCertificates() {
$view = \OCP\Files::getStorage('files_external');
$path=\OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/';
- if (!is_dir($path)) mkdir($path);
+ \OCP\Util::writeLog('files_external', 'checking path '.$path, \OCP\Util::INFO);
+ if ( ! is_dir($path)) {
+ //path might not exist (e.g. non-standard OC_User::getHome() value)
+ //in this case create full path using 3rd (recursive=true) parameter.
+ mkdir($path, 0777, true);
+ }
$result = array();
$handle = opendir($path);
- if (!$handle) {
+ if ( ! $handle) {
return array();
}
while (false !== ($file = readdir($handle))) {
- if($file != '.' && $file != '..') $result[] = $file;
+ if ($file != '.' && $file != '..') $result[] = $file;
}
return $result;
}
@@ -322,4 +390,42 @@ class OC_Mount_Config {
return true;
}
+ /**
+ * check if smbclient is installed
+ */
+ public static function checksmbclient() {
+ if(function_exists('shell_exec')) {
+ $output=shell_exec('which smbclient');
+ return (empty($output)?false:true);
+ }else{
+ return(false);
+ }
+ }
+
+ /**
+ * check if php-ftp is installed
+ */
+ public static function checkphpftp() {
+ if(function_exists('ftp_login')) {
+ return(true);
+ }else{
+ return(false);
+ }
+ }
+
+ /**
+ * check dependencies
+ */
+ public static function checkDependencies() {
+ $l= new OC_L10N('files_external');
+ $txt='';
+ if(!OC_Mount_Config::checksmbclient()) {
+ $txt.=$l->t('<b>Warning:</b> "smbclient" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it.').'<br />';
+ }
+ if(!OC_Mount_Config::checkphpftp()) {
+ $txt.=$l->t('<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it.').'<br />';
+ }
+
+ return($txt);
+ }
}
diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php
index c8220832702..cb04e557f8a 100755
--- a/apps/files_external/lib/dropbox.php
+++ b/apps/files_external/lib/dropbox.php
@@ -20,38 +20,47 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+namespace OC\Files\Storage;
+
require_once 'Dropbox/autoload.php';
-class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
+class Dropbox extends \OC\Files\Storage\Common {
private $dropbox;
private $root;
+ private $id;
private $metaData = array();
private static $tempFiles = array();
public function __construct($params) {
- if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['app_key']) && isset($params['app_secret']) && isset($params['token']) && isset($params['token_secret'])) {
- $this->root=isset($params['root'])?$params['root']:'';
- $oauth = new Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']);
+ if (isset($params['configured']) && $params['configured'] == 'true'
+ && isset($params['app_key'])
+ && isset($params['app_secret'])
+ && isset($params['token'])
+ && isset($params['token_secret'])
+ ) {
+ $this->root = isset($params['root']) ? $params['root'] : '';
+ $this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $this->root;
+ $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']);
$oauth->setToken($params['token'], $params['token_secret']);
- $this->dropbox = new Dropbox_API($oauth, 'dropbox');
+ $this->dropbox = new \Dropbox_API($oauth, 'dropbox');
$this->mkdir('');
} else {
- throw new Exception('Creating OC_Filestorage_Dropbox storage failed');
+ throw new \Exception('Creating \OC\Files\Storage\Dropbox storage failed');
}
}
private function getMetaData($path, $list = false) {
$path = $this->root.$path;
- if (!$list && isset($this->metaData[$path])) {
+ if ( ! $list && isset($this->metaData[$path])) {
return $this->metaData[$path];
} else {
if ($list) {
try {
$response = $this->dropbox->getMetaData($path);
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
if ($response && isset($response['contents'])) {
@@ -71,21 +80,25 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
$response = $this->dropbox->getMetaData($path, 'false');
$this->metaData[$path] = $response;
return $response;
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
}
}
+ public function getId(){
+ return $this->id;
+ }
+
public function mkdir($path) {
$path = $this->root.$path;
try {
$this->dropbox->createFolder($path);
return true;
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
@@ -95,23 +108,24 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
}
public function opendir($path) {
- if ($contents = $this->getMetaData($path, true)) {
+ $contents = $this->getMetaData($path, true);
+ if ($contents) {
$files = array();
foreach ($contents as $file) {
$files[] = basename($file['path']);
}
- OC_FakeDirStream::$dirs['dropbox'.$path] = $files;
+ \OC\Files\Stream\Dir::register('dropbox'.$path, $files);
return opendir('fakedir://dropbox'.$path);
}
return false;
}
public function stat($path) {
- if ($metaData = $this->getMetaData($path)) {
+ $metaData = $this->getMetaData($path);
+ if ($metaData) {
$stat['size'] = $metaData['bytes'];
$stat['atime'] = time();
$stat['mtime'] = (isset($metaData['modified'])) ? strtotime($metaData['modified']) : time();
- $stat['ctime'] = $stat['mtime'];
return $stat;
}
return false;
@@ -120,11 +134,14 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
public function filetype($path) {
if ($path == '' || $path == '/') {
return 'dir';
- } else if ($metaData = $this->getMetaData($path)) {
- if ($metaData['is_dir'] == 'true') {
- return 'dir';
- } else {
- return 'file';
+ } else {
+ $metaData = $this->getMetaData($path);
+ if ($metaData) {
+ if ($metaData['is_dir'] == 'true') {
+ return 'dir';
+ } else {
+ return 'file';
+ }
}
}
return false;
@@ -153,8 +170,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
try {
$this->dropbox->delete($path);
return true;
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
@@ -165,8 +182,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
try {
$this->dropbox->move($path1, $path2);
return true;
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
@@ -177,8 +194,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
try {
$this->dropbox->copy($path1, $path2);
return true;
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
@@ -188,13 +205,13 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
switch ($mode) {
case 'r':
case 'rb':
- $tmpFile = OC_Helper::tmpFile();
+ $tmpFile = \OC_Helper::tmpFile();
try {
$data = $this->dropbox->getFile($path);
file_put_contents($tmpFile, $data);
return fopen($tmpFile, 'r');
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
case 'w':
@@ -214,8 +231,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
} else {
$ext = '';
}
- $tmpFile = OC_Helper::tmpFile($ext);
- OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack');
+ $tmpFile = \OC_Helper::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if ($this->file_exists($path)) {
$source = $this->fopen($path, 'r');
file_put_contents($tmpFile, $source);
@@ -232,8 +249,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
try {
$this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle);
unlink($tmpFile);
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
}
}
}
@@ -241,8 +258,11 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
public function getMimeType($path) {
if ($this->filetype($path) == 'dir') {
return 'httpd/unix-directory';
- } else if ($metaData = $this->getMetaData($path)) {
- return $metaData['mime_type'];
+ } else {
+ $metaData = $this->getMetaData($path);
+ if ($metaData) {
+ return $metaData['mime_type'];
+ }
}
return false;
}
@@ -251,8 +271,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common {
try {
$info = $this->dropbox->getAccountInfo();
return $info['quota_info']['quota'] - $info['quota_info']['normal'];
- } catch (Exception $exception) {
- OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR);
+ } catch (\Exception $exception) {
+ \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
return false;
}
}
diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php
index 261141455cc..8a7375ebe38 100644
--- a/apps/files_external/lib/ftp.php
+++ b/apps/files_external/lib/ftp.php
@@ -6,7 +6,9 @@
* See the COPYING-README file.
*/
-class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{
+namespace OC\Files\Storage;
+
+class FTP extends \OC\Files\Storage\StreamWrapper{
private $password;
private $user;
private $host;
@@ -16,34 +18,52 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{
private static $tempFiles=array();
public function __construct($params) {
- $this->host=$params['host'];
- $this->user=$params['user'];
- $this->password=$params['password'];
- $this->secure=isset($params['secure'])?(bool)$params['secure']:false;
- $this->root=isset($params['root'])?$params['root']:'/';
- if(!$this->root || $this->root[0]!='/') {
- $this->root='/'.$this->root;
- }
- //create the root folder if necesary
- if (!$this->is_dir('')) {
- $this->mkdir('');
+ if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ if (isset($params['secure'])) {
+ if (is_string($params['secure'])) {
+ $this->secure = ($params['secure'] === 'true');
+ } else {
+ $this->secure = (bool)$params['secure'];
+ }
+ } else {
+ $this->secure = false;
+ }
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if ( ! $this->root || $this->root[0]!='/') {
+ $this->root='/'.$this->root;
+ }
+ //create the root folder if necessary
+ if ( ! $this->is_dir('')) {
+ $this->mkdir('');
+ }
+ } else {
+ throw new \Exception();
}
+
+ }
+
+ public function getId(){
+ return 'ftp::' . $this->user . '@' . $this->host . '/' . $this->root;
}
/**
* construct the ftp url
- * @param string path
+ * @param string $path
* @return string
*/
public function constructUrl($path) {
$url='ftp';
- if($this->secure) {
+ if ($this->secure) {
$url.='s';
}
$url.='://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path;
return $url;
}
public function fopen($path,$mode) {
+ $this->init();
switch($mode) {
case 'r':
case 'rb':
@@ -53,7 +73,7 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{
case 'ab':
//these are supported by the wrapper
$context = stream_context_create(array('ftp' => array('overwrite' => true)));
- return fopen($this->constructUrl($path),$mode,false,$context);
+ return fopen($this->constructUrl($path), $mode, false, $context);
case 'r+':
case 'w+':
case 'wb+':
@@ -63,24 +83,26 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{
case 'c':
case 'c+':
//emulate these
- if(strrpos($path,'.')!==false) {
- $ext=substr($path,strrpos($path,'.'));
- }else{
+ if (strrpos($path, '.')!==false) {
+ $ext=substr($path, strrpos($path, '.'));
+ } else {
$ext='';
}
- $tmpFile=OCP\Files::tmpFile($ext);
- OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
- if($this->file_exists($path)) {
- $this->getFile($path,$tmpFile);
+ $tmpFile=\OCP\Files::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
+ if ($this->file_exists($path)) {
+ $this->getFile($path, $tmpFile);
}
self::$tempFiles[$tmpFile]=$path;
- return fopen('close://'.$tmpFile,$mode);
+ return fopen('close://'.$tmpFile, $mode);
}
+ return false;
}
public function writeBack($tmpFile) {
- if(isset(self::$tempFiles[$tmpFile])) {
- $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]);
+ $this->init();
+ if (isset(self::$tempFiles[$tmpFile])) {
+ $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
unlink($tmpFile);
}
}
diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php
index 9b83dcee537..ec7de3f3570 100644
--- a/apps/files_external/lib/google.php
+++ b/apps/files_external/lib/google.php
@@ -20,31 +20,45 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+namespace OC\Files\Storage;
+
require_once 'Google/common.inc.php';
-class OC_Filestorage_Google extends OC_Filestorage_Common {
+class Google extends \OC\Files\Storage\Common {
private $consumer;
private $oauth_token;
private $sig_method;
private $entries;
+ private $id;
private static $tempFiles = array();
public function __construct($params) {
- if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['token']) && isset($params['token_secret'])) {
+ if (isset($params['configured']) && $params['configured'] == 'true'
+ && isset($params['token'])
+ && isset($params['token_secret'])
+ ) {
$consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous';
$consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous';
- $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
- $this->oauth_token = new OAuthToken($params['token'], $params['token_secret']);
- $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1();
+ $this->id = 'google::' . $params['token'];
+ $this->consumer = new \OAuthConsumer($consumer_key, $consumer_secret);
+ $this->oauth_token = new \OAuthToken($params['token'], $params['token_secret']);
+ $this->sig_method = new \OAuthSignatureMethod_HMAC_SHA1();
$this->entries = array();
} else {
- throw new Exception('Creating OC_Filestorage_Google storage failed');
+ throw new \Exception('Creating \OC\Files\Storage\Google storage failed');
}
}
- private function sendRequest($uri, $httpMethod, $postData = null, $extraHeaders = null, $isDownload = false, $returnHeaders = false, $isContentXML = true, $returnHTTPCode = false) {
+ private function sendRequest($uri,
+ $httpMethod,
+ $postData = null,
+ $extraHeaders = null,
+ $isDownload = false,
+ $returnHeaders = false,
+ $isContentXML = true,
+ $returnHTTPCode = false) {
$uri = trim($uri);
// create an associative array from each key/value url query param pair.
$params = array();
@@ -58,7 +72,11 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$tempStr .= '&' . urlencode($key) . '=' . urlencode($value);
}
$uri = preg_replace('/&/', '?', $tempStr, 1);
- $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $httpMethod, $uri, $params);
+ $request = \OAuthRequest::from_consumer_and_token($this->consumer,
+ $this->oauth_token,
+ $httpMethod,
+ $uri,
+ $params);
$request->sign_request($this->sig_method, $this->consumer, $this->oauth_token);
$auth_header = $request->to_header();
$headers = array($auth_header, 'GData-Version: 3.0');
@@ -96,7 +114,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
if ($isDownload) {
- $tmpFile = OC_Helper::tmpFile();
+ $tmpFile = \OC_Helper::tmpFile();
$handle = fopen($tmpFile, 'w');
curl_setopt($curl, CURLOPT_FILE, $handle);
}
@@ -125,13 +143,18 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
private function getFeed($feedUri, $httpMethod, $postData = null) {
$result = $this->sendRequest($feedUri, $httpMethod, $postData);
if ($result) {
- $dom = new DOMDocument();
+ $dom = new \DOMDocument();
$dom->loadXML($result);
return $dom;
}
return false;
}
+ /**
+ * Base url for google docs feeds
+ */
+ const BASE_URI='https://docs.google.com/feeds';
+
private function getResource($path) {
$file = basename($path);
if (array_key_exists($file, $this->entries)) {
@@ -140,14 +163,14 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
// Strip the file extension; file could be a native Google Docs resource
if ($pos = strpos($file, '.')) {
$title = substr($file, 0, $pos);
- $dom = $this->getFeed('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET');
+ $dom = $this->getFeed(self::BASE_URI.'/default/private/full?showfolders=true&title='.$title, 'GET');
// Check if request was successful and entry exists
if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
$this->entries[$file] = $entry;
return $entry;
}
}
- $dom = $this->getFeed('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$file, 'GET');
+ $dom = $this->getFeed(self::BASE_URI.'/default/private/full?showfolders=true&title='.$file, 'GET');
// Check if request was successful and entry exists
if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
$this->entries[$file] = $entry;
@@ -175,25 +198,33 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
}
+ public function getId(){
+ return $this->id;
+ }
public function mkdir($path) {
$collection = dirname($path);
// Check if path parent is root directory
if ($collection == '/' || $collection == '\.' || $collection == '.') {
- $uri = 'https://docs.google.com/feeds/default/private/full';
- // Get parent content link
- } else if ($dom = $this->getResource(basename($collection))) {
- $uri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src');
+ $uri = self::BASE_URI.'/default/private/full';
+ } else {
+ // Get parent content link
+ $dom = $this->getResource(basename($collection));
+ if ($dom) {
+ $uri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src');
+ }
}
if (isset($uri)) {
$title = basename($path);
// Construct post data
$postData = '<?xml version="1.0" encoding="UTF-8"?>';
$postData .= '<entry xmlns="http://www.w3.org/2005/Atom">';
- $postData .= '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#folder"/>';
+ $postData .= '<category scheme="http://schemas.google.com/g/2005#kind"';
+ $postData .= ' term="http://schemas.google.com/docs/2007#folder"/>';
$postData .= '<title>'.$title.'</title>';
$postData .= '</entry>';
- if ($dom = $this->sendRequest($uri, 'POST', $postData)) {
+ $dom = $this->sendRequest($uri, 'POST', $postData);
+ if ($dom) {
return true;
}
}
@@ -206,9 +237,10 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
public function opendir($path) {
if ($path == '' || $path == '/') {
- $next = 'https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents';
+ $next = self::BASE_URI.'/default/private/full/folder%3Aroot/contents';
} else {
- if ($entry = $this->getResource($path)) {
+ $entry = $this->getResource($path);
+ if ($entry) {
$next = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
} else {
return false;
@@ -230,18 +262,18 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
foreach ($entries as $entry) {
$name = $entry->getElementsByTagName('title')->item(0)->nodeValue;
// Google Docs resources don't always include extensions in title
- if (!strpos($name, '.')) {
+ if ( ! strpos($name, '.')) {
$extension = $this->getExtension($entry);
if ($extension != '') {
$name .= '.'.$extension;
}
}
- $files[] = $name;
+ $files[] = basename($name);
// Cache entry for future use
$this->entries[$name] = $entry;
}
}
- OC_FakeDirStream::$dirs['google'.$path] = $files;
+ \OC\Files\Stream\Dir::register('google'.$path, $files);
return opendir('fakedir://google'.$path);
}
@@ -251,13 +283,18 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$stat['atime'] = time();
$stat['mtime'] = time();
$stat['ctime'] = time();
- } else if ($entry = $this->getResource($path)) {
- // NOTE: Native resources don't have a file size
- $stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
-// if (isset($atime = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue))
-// $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue);
- $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue);
- $stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue);
+ } else {
+ $entry = $this->getResource($path);
+ if ($entry) {
+ // NOTE: Native resources don't have a file size
+ $stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005',
+ 'quotaBytesUsed')->item(0)->nodeValue;
+ //if (isset($atime = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005',
+ // 'lastViewed')->item(0)->nodeValue))
+ //$stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005',
+ // 'lastViewed')->item(0)->nodeValue);
+ $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue);
+ }
}
if (isset($stat)) {
return $stat;
@@ -268,15 +305,18 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
public function filetype($path) {
if ($path == '' || $path == '/') {
return 'dir';
- } else if ($entry = $this->getResource($path)) {
- $categories = $entry->getElementsByTagName('category');
- foreach ($categories as $category) {
- if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
- $type = $category->getAttribute('label');
- if (strlen(strstr($type, 'folder')) > 0) {
- return 'dir';
- } else {
- return 'file';
+ } else {
+ $entry = $this->getResource($path);
+ if ($entry) {
+ $categories = $entry->getElementsByTagName('category');
+ foreach ($categories as $category) {
+ if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
+ $type = $category->getAttribute('label');
+ if (strlen(strstr($type, 'folder')) > 0) {
+ return 'dir';
+ } else {
+ return 'file';
+ }
}
}
}
@@ -291,14 +331,17 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
public function isUpdatable($path) {
if ($path == '' || $path == '/') {
return true;
- } else if ($entry = $this->getResource($path)) {
- // Check if edit or edit-media links exist
- $links = $entry->getElementsByTagName('link');
- foreach ($links as $link) {
- if ($link->getAttribute('rel') == 'edit') {
- return true;
- } else if ($link->getAttribute('rel') == 'edit-media') {
- return true;
+ } else {
+ $entry = $this->getResource($path);
+ if ($entry) {
+ // Check if edit or edit-media links exist
+ $links = $entry->getElementsByTagName('link');
+ foreach ($links as $link) {
+ if ($link->getAttribute('rel') == 'edit') {
+ return true;
+ } else if ($link->getAttribute('rel') == 'edit-media') {
+ return true;
+ }
}
}
}
@@ -316,7 +359,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
public function unlink($path) {
// Get resource self link to trash resource
- if ($entry = $this->getResource($path)) {
+ $entry = $this->getResource($path);
+ if ($entry) {
$links = $entry->getElementsByTagName('link');
foreach ($links as $link) {
if ($link->getAttribute('rel') == 'self') {
@@ -333,7 +377,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
public function rename($path1, $path2) {
- if ($entry = $this->getResource($path1)) {
+ $entry = $this->getResource($path1);
+ if ($entry) {
$collection = dirname($path2);
if (dirname($path1) == $collection) {
// Get resource edit link to rename resource
@@ -348,14 +393,18 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$title = basename($path2);
// Construct post data
$postData = '<?xml version="1.0" encoding="UTF-8"?>';
- $postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>';
+ $postData .= '<entry xmlns="http://www.w3.org/2005/Atom"';
+ $postData .= ' xmlns:docs="http://schemas.google.com/docs/2007"';
+ $postData .= ' xmlns:gd="http://schemas.google.com/g/2005"';
+ $postData .= ' gd:etag='.$etag.'>';
$postData .= '<title>'.$title.'</title>';
$postData .= '</entry>';
$this->sendRequest($uri, 'PUT', $postData);
return true;
} else {
// Move to different collection
- if ($collectionEntry = $this->getResource($collection)) {
+ $collectionEntry = $this->getResource($collection);
+ if ($collectionEntry) {
$feedUri = $collectionEntry->getElementsByTagName('content')->item(0)->getAttribute('src');
// Construct post data
$postData = '<?xml version="1.0" encoding="UTF-8"?>';
@@ -374,7 +423,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
switch ($mode) {
case 'r':
case 'rb':
- if ($entry = $this->getResource($path)) {
+ $entry = $this->getResource($path);
+ if ($entry) {
$extension = $this->getExtension($entry);
$downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
// TODO Non-native documents don't need these additional parameters
@@ -394,13 +444,13 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
case 'x+':
case 'c':
case 'c+':
- if (strrpos($path,'.') !== false) {
- $ext = substr($path,strrpos($path,'.'));
+ if (strrpos($path, '.') !== false) {
+ $ext = substr($path, strrpos($path, '.'));
} else {
$ext = '';
}
- $tmpFile = OC_Helper::tmpFile($ext);
- OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack');
+ $tmpFile = \OC_Helper::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if ($this->file_exists($path)) {
$source = $this->fopen($path, 'r');
file_put_contents($tmpFile, $source);
@@ -420,14 +470,14 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
private function uploadFile($path, $target) {
$entry = $this->getResource($target);
- if (!$entry) {
+ if ( ! $entry) {
if (dirname($target) == '.' || dirname($target) == '/') {
- $uploadUri = 'https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents';
+ $uploadUri = self::BASE_URI.'/upload/create-session/default/private/full/folder%3Aroot/contents';
} else {
$entry = $this->getResource(dirname($target));
}
}
- if (!isset($uploadUri) && $entry) {
+ if ( ! isset($uploadUri) && $entry) {
$links = $entry->getElementsByTagName('link');
foreach ($links as $link) {
if ($link->getAttribute('rel') == 'http://schemas.google.com/g/2005#resumable-create-media') {
@@ -438,7 +488,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
if (isset($uploadUri) && $handle = fopen($path, 'r')) {
$uploadUri .= '?convert=false';
- $mimetype = OC_Helper::getMimeType($path);
+ $mimetype = \OC_Helper::getMimeType($path);
$size = filesize($path);
$headers = array('X-Upload-Content-Type: ' => $mimetype, 'X-Upload-Content-Length: ' => $size);
$postData = '<?xml version="1.0" encoding="UTF-8"?>';
@@ -466,7 +516,9 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
}
$end = $i + $chunkSize - 1;
- $headers = array('Content-Length: '.$chunkSize, 'Content-Type: '.$mimetype, 'Content-Range: bytes '.$i.'-'.$end.'/'.$size);
+ $headers = array('Content-Length: '.$chunkSize,
+ 'Content-Type: '.$mimetype,
+ 'Content-Range: bytes '.$i.'-'.$end.'/'.$size);
$postData = fread($handle, $chunkSize);
$result = $this->sendRequest($uploadUri, 'PUT', $postData, $headers, false, true, false, true);
if ($result['code'] == '308') {
@@ -484,7 +536,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
public function getMimeType($path, $entry = null) {
- // Entry can be passed, because extension is required for opendir and the entry can't be cached without the extension
+ // Entry can be passed, because extension is required for opendir
+ // and the entry can't be cached without the extension
if ($entry == null) {
if ($path == '' || $path == '/') {
return 'httpd/unix-directory';
@@ -494,8 +547,10 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
if ($entry) {
$mimetype = $entry->getElementsByTagName('content')->item(0)->getAttribute('type');
- // Native Google Docs resources often default to text/html, but it may be more useful to default to a corresponding ODF mimetype
- // Collections get reported as application/atom+xml, make sure it actually is a folder and fix the mimetype
+ // Native Google Docs resources often default to text/html,
+ // but it may be more useful to default to a corresponding ODF mimetype
+ // Collections get reported as application/atom+xml,
+ // make sure it actually is a folder and fix the mimetype
if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml;type=feed') {
$categories = $entry->getElementsByTagName('category');
foreach ($categories as $category) {
@@ -512,7 +567,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
} else if (strlen(strstr($type, 'drawing')) > 0) {
return 'application/vnd.oasis.opendocument.graphics';
} else {
- // If nothing matches return text/html, all native Google Docs resources can be exported as text/html
+ // If nothing matches return text/html,
+ // all native Google Docs resources can be exported as text/html
return 'text/html';
}
}
@@ -524,10 +580,13 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
public function free_space($path) {
- if ($dom = $this->getFeed('https://docs.google.com/feeds/metadata/default', 'GET')) {
+ $dom = $this->getFeed(self::BASE_URI.'/metadata/default', 'GET');
+ if ($dom) {
// NOTE: Native Google Docs resources don't count towards quota
- $total = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesTotal')->item(0)->nodeValue;
- $used = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
+ $total = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005',
+ 'quotaBytesTotal')->item(0)->nodeValue;
+ $used = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005',
+ 'quotaBytesUsed')->item(0)->nodeValue;
return $total - $used;
}
return false;
@@ -537,4 +596,11 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
}
-} \ No newline at end of file
+ public function test() {
+ if ($this->free_space('')) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php
new file mode 100644
index 00000000000..ede6c251fd9
--- /dev/null
+++ b/apps/files_external/lib/sftp.php
@@ -0,0 +1,288 @@
+<?php
+/**
+ * Copyright (c) 2012 Henrik Kjölhede <hkjolhede@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+namespace OC\Files\Storage;
+
+set_include_path(get_include_path() . PATH_SEPARATOR .
+ \OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib');
+require 'Net/SFTP.php';
+
+class SFTP extends \OC\Files\Storage\Common {
+ private $host;
+ private $user;
+ private $password;
+ private $root;
+
+ private $client;
+
+ private static $tempFiles = array();
+
+ public function __construct($params) {
+ $this->host = $params['host'];
+ $proto = strpos($this->host, '://');
+ if ($proto != false) {
+ $this->host = substr($this->host, $proto+3);
+ }
+ $this->user = $params['user'];
+ $this->password = $params['password'];
+ $this->root = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
+ if ($this->root[0] != '/') $this->root = '/' . $this->root;
+ if (substr($this->root, -1, 1) != '/') $this->root .= '/';
+
+ $host_keys = $this->read_host_keys();
+
+ $this->client = new \Net_SFTP($this->host);
+ if (!$this->client->login($this->user, $this->password)) {
+ throw new \Exception('Login failed');
+ }
+
+ $current_host_key = $this->client->getServerPublicHostKey();
+
+ if (array_key_exists($this->host, $host_keys)) {
+ if ($host_keys[$this->host] != $current_host_key) {
+ throw new \Exception('Host public key does not match known key');
+ }
+ } else {
+ $host_keys[$this->host] = $current_host_key;
+ $this->write_host_keys($host_keys);
+ }
+
+ if(!$this->file_exists('')){
+ $this->mkdir('');
+ }
+ }
+
+ public function test() {
+ if (!isset($params['host']) || !isset($params['user']) || !isset($params['password'])) {
+ throw new \Exception("Required parameters not set");
+ }
+ }
+
+ public function getId(){
+ return 'sftp::' . $this->user . '@' . $this->host . '/' . $this->root;
+ }
+
+ private function abs_path($path) {
+ return $this->root . $this->cleanPath($path);
+ }
+
+ private function host_keys_path() {
+ try {
+ $storage_view = \OCP\Files::getStorage('files_external');
+ if ($storage_view) {
+ return \OCP\Config::getSystemValue('datadirectory') .
+ $storage_view->getAbsolutePath('') .
+ 'ssh_host_keys';
+ }
+ } catch (\Exception $e) {
+ }
+ return false;
+ }
+
+ private function write_host_keys($keys) {
+ try {
+ $key_path = $this->host_keys_path();
+ $fp = fopen($key_path, 'w');
+ foreach ($keys as $host => $key) {
+ fwrite($fp, $host . '::' . $key . "\n");
+ }
+ fclose($fp);
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ private function read_host_keys() {
+ try {
+ $key_path = $this->host_keys_path();
+ if (file_exists($key_path)) {
+ $hosts = array();
+ $keys = array();
+ $lines = file($key_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ if ($lines) {
+ foreach ($lines as $line) {
+ $host_key_arr = explode("::", $line, 2);
+ if (count($host_key_arr) == 2) {
+ $hosts[] = $host_key_arr[0];
+ $keys[] = $host_key_arr[1];
+ }
+ }
+ return array_combine($hosts, $keys);
+ }
+ }
+ } catch (\Exception $e) {
+ }
+ return array();
+ }
+
+ public function mkdir($path) {
+ try {
+ return $this->client->mkdir($this->abs_path($path));
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function rmdir($path) {
+ try {
+ return $this->client->delete($this->abs_path($path), true);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function opendir($path) {
+ try {
+ $list = $this->client->nlist($this->abs_path($path));
+
+ $id = md5('sftp:' . $path);
+ $dir_stream = array();
+ foreach($list as $file) {
+ if ($file != '.' && $file != '..') {
+ $dir_stream[] = $file;
+ }
+ }
+ \OC\Files\Stream\Dir::register($id, $dir_stream);
+ return opendir('fakedir://' . $id);
+ } catch(\Exception $e) {
+ return false;
+ }
+ }
+
+ public function filetype($path) {
+ try {
+ $stat = $this->client->stat($this->abs_path($path));
+ if ($stat['type'] == NET_SFTP_TYPE_REGULAR) return 'file';
+ if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) return 'dir';
+ } catch (\Exeption $e) {
+ }
+ return false;
+ }
+
+ public function isReadable($path) {
+ return true;
+ }
+
+ public function isUpdatable($path) {
+ return true;
+ }
+
+ public function file_exists($path) {
+ try {
+ return $this->client->stat($this->abs_path($path)) === false ? false : true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function unlink($path) {
+ try {
+ return $this->client->delete($this->abs_path($path), true);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function fopen($path, $mode) {
+ try {
+ $abs_path = $this->abs_path($path);
+ switch($mode) {
+ case 'r':
+ case 'rb':
+ if ( !$this->file_exists($path)) return false;
+ if (strrpos($path, '.')!==false) {
+ $ext=substr($path, strrpos($path, '.'));
+ } else {
+ $ext='';
+ }
+ $tmp = \OC_Helper::tmpFile($ext);
+ $this->getFile($abs_path, $tmp);
+ return fopen($tmp, $mode);
+
+ case 'w':
+ case 'wb':
+ case 'a':
+ case 'ab':
+ case 'r+':
+ case 'w+':
+ case 'wb+':
+ case 'a+':
+ case 'x':
+ case 'x+':
+ case 'c':
+ case 'c+':
+ if (strrpos($path, '.')!==false) {
+ $ext=substr($path, strrpos($path, '.'));
+ } else {
+ $ext='';
+ }
+ $tmpFile=\OC_Helper::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
+ if ($this->file_exists($path)) {
+ $this->getFile($abs_path, $tmpFile);
+ }
+ self::$tempFiles[$tmpFile]=$abs_path;
+ return fopen('close://'.$tmpFile, $mode);
+ }
+ } catch (\Exception $e) {
+ }
+ return false;
+ }
+
+ public function writeBack($tmpFile) {
+ if (array_key_exists($tmpFile, self::$tempFiles)) {
+ $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
+ unlink($tmpFile);
+ unset(self::$tempFiles[$tmpFile]);
+ }
+ }
+
+ public function touch($path, $mtime=null) {
+ try {
+ if (!is_null($mtime)) return false;
+ if (!$this->file_exists($path)) {
+ $this->client->put($this->abs_path($path), '');
+ } else {
+ return false;
+ }
+ } catch (\Exception $e) {
+ return false;
+ }
+ return true;
+ }
+
+ public function getFile($path, $target) {
+ $this->client->get($path, $target);
+ }
+
+ public function uploadFile($path, $target) {
+ $this->client->put($target, $path, NET_SFTP_LOCAL_FILE);
+ }
+
+ public function rename($source, $target) {
+ try {
+ return $this->client->rename($this->abs_path($source), $this->abs_path($target));
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function stat($path) {
+ try {
+ $stat = $this->client->stat($this->abs_path($path));
+
+ $mtime = $stat ? $stat['mtime'] : -1;
+ $size = $stat ? $stat['size'] : 0;
+
+ return array('mtime' => $mtime, 'size' => $size, 'ctime' => -1);
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ }
+}
diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php
index eed2582dc99..961efb1a50a 100644
--- a/apps/files_external/lib/smb.php
+++ b/apps/files_external/lib/smb.php
@@ -6,9 +6,11 @@
* See the COPYING-README file.
*/
+namespace OC\Files\Storage;
+
require_once 'smb4php/smb.php';
-class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{
+class SMB extends \OC\Files\Storage\StreamWrapper{
private $password;
private $user;
private $host;
@@ -16,62 +18,67 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{
private $share;
public function __construct($params) {
- $this->host=$params['host'];
- $this->user=$params['user'];
- $this->password=$params['password'];
- $this->share=$params['share'];
- $this->root=isset($params['root'])?$params['root']:'/';
- if(!$this->root || $this->root[0]!='/') {
- $this->root='/'.$this->root;
- }
- if(substr($this->root,-1,1)!='/') {
- $this->root.='/';
- }
- if(!$this->share || $this->share[0]!='/') {
- $this->share='/'.$this->share;
- }
- if(substr($this->share,-1,1)=='/') {
- $this->share=substr($this->share,0,-1);
+ if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ $this->share=$params['share'];
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if ( ! $this->root || $this->root[0]!='/') {
+ $this->root='/'.$this->root;
+ }
+ if (substr($this->root, -1, 1)!='/') {
+ $this->root.='/';
+ }
+ if ( ! $this->share || $this->share[0]!='/') {
+ $this->share='/'.$this->share;
+ }
+ if (substr($this->share, -1, 1)=='/') {
+ $this->share = substr($this->share, 0, -1);
+ }
+ } else {
+ throw new \Exception();
}
+ }
- //create the root folder if necesary
- if(!$this->is_dir('')) {
- $this->mkdir('');
- }
+ public function getId(){
+ return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root;
}
public function constructUrl($path) {
- if(substr($path,-1)=='/') {
- $path=substr($path,0,-1);
+ if (substr($path, -1)=='/') {
+ $path=substr($path, 0, -1);
}
- return 'smb://'.$this->user.':'.$this->password.'@'.$this->host.$this->share.$this->root.$path;
+ $path = urlencode($path);
+ $user = urlencode($this->user);
+ $pass = urlencode($this->password);
+ return 'smb://'.$user.':'.$pass.'@'.$this->host.$this->share.$this->root.$path;
}
public function stat($path) {
- if(!$path and $this->root=='/') {//mtime doesn't work for shares
+ if ( ! $path and $this->root=='/') {//mtime doesn't work for shares
$mtime=$this->shareMTime();
$stat=stat($this->constructUrl($path));
$stat['mtime']=$mtime;
return $stat;
- }else{
+ } else {
return stat($this->constructUrl($path));
}
}
- public function filetype($path) {
- return (bool)@$this->opendir($path) ? 'dir' : 'file';//using opendir causes the same amount of requests and caches the content of the folder in one go
- }
-
/**
* check if a file or folder has been updated since $time
+ * @param string $path
* @param int $time
* @return bool
*/
public function hasUpdated($path,$time) {
+ $this->init();
if(!$path and $this->root=='/') {
- //mtime doesn't work for shares, but giving the nature of the backend, doing a full update is still just fast enough
+ // mtime doesn't work for shares, but giving the nature of the backend,
+ // doing a full update is still just fast enough
return true;
- }else{
+ } else {
$actualTime=$this->filemtime($path);
return $actualTime>$time;
}
@@ -84,9 +91,9 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{
$dh=$this->opendir('');
$lastCtime=0;
while($file=readdir($dh)) {
- if($file!='.' and $file!='..') {
+ if ($file!='.' and $file!='..') {
$ctime=$this->filemtime($file);
- if($ctime>$lastCtime) {
+ if ($ctime>$lastCtime) {
$lastCtime=$ctime;
}
}
diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php
index 7263ef23253..4685877f26b 100644
--- a/apps/files_external/lib/streamwrapper.php
+++ b/apps/files_external/lib/streamwrapper.php
@@ -6,29 +6,48 @@
* See the COPYING-README file.
*/
+namespace OC\Files\Storage;
+
+abstract class StreamWrapper extends \OC\Files\Storage\Common{
+ private $ready = false;
+
+ protected function init(){
+ if($this->ready) {
+ return;
+ }
+ $this->ready = true;
+
+ //create the root folder if necesary
+ if(!$this->is_dir('')) {
+ $this->mkdir('');
+ }
+ }
-abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{
abstract public function constructUrl($path);
public function mkdir($path) {
+ $this->init();
return mkdir($this->constructUrl($path));
}
public function rmdir($path) {
+ $this->init();
if($this->file_exists($path)) {
- $succes=rmdir($this->constructUrl($path));
+ $succes = rmdir($this->constructUrl($path));
clearstatcache();
return $succes;
- }else{
+ } else {
return false;
}
}
public function opendir($path) {
+ $this->init();
return opendir($this->constructUrl($path));
}
public function filetype($path) {
+ $this->init();
return filetype($this->constructUrl($path));
}
@@ -41,49 +60,51 @@ abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{
}
public function file_exists($path) {
+ $this->init();
return file_exists($this->constructUrl($path));
}
public function unlink($path) {
- $succes=unlink($this->constructUrl($path));
+ $this->init();
+ $succes = unlink($this->constructUrl($path));
clearstatcache();
return $succes;
}
- public function fopen($path,$mode) {
- return fopen($this->constructUrl($path),$mode);
- }
-
- public function free_space($path) {
- return 0;
+ public function fopen($path, $mode) {
+ $this->init();
+ return fopen($this->constructUrl($path), $mode);
}
- public function touch($path,$mtime=null) {
+ public function touch($path, $mtime=null) {
+ $this->init();
if(is_null($mtime)) {
- $fh=$this->fopen($path,'a');
- fwrite($fh,'');
+ $fh = $this->fopen($path, 'a');
+ fwrite($fh, '');
fclose($fh);
- }else{
+ } else {
return false;//not supported
}
}
- public function getFile($path,$target) {
- return copy($this->constructUrl($path),$target);
+ public function getFile($path, $target) {
+ $this->init();
+ return copy($this->constructUrl($path), $target);
}
- public function uploadFile($path,$target) {
- return copy($path,$this->constructUrl($target));
+ public function uploadFile($path, $target) {
+ $this->init();
+ return copy($path, $this->constructUrl($target));
}
- public function rename($path1,$path2) {
- return rename($this->constructUrl($path1),$this->constructUrl($path2));
+ public function rename($path1, $path2) {
+ $this->init();
+ return rename($this->constructUrl($path1), $this->constructUrl($path2));
}
public function stat($path) {
+ $this->init();
return stat($this->constructUrl($path));
}
-
-
}
diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php
index 4b0b8c25fda..68c4b48f17c 100644
--- a/apps/files_external/lib/swift.php
+++ b/apps/files_external/lib/swift.php
@@ -6,24 +6,28 @@
* See the COPYING-README file.
*/
+namespace OC\Files\Storage;
+
require_once 'php-cloudfiles/cloudfiles.php';
-class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
+class SWIFT extends \OC\Files\Storage\Common{
+ private $id;
private $host;
private $root;
private $user;
private $token;
private $secure;
+ private $ready = false;
/**
- * @var CF_Authentication auth
+ * @var \CF_Authentication auth
*/
private $auth;
/**
- * @var CF_Connection conn
+ * @var \CF_Connection conn
*/
private $conn;
/**
- * @var CF_Container rootContainer
+ * @var \CF_Container rootContainer
*/
private $rootContainer;
@@ -35,79 +39,79 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* translate directory path to container name
- * @param string path
+ * @param string $path
* @return string
*/
private function getContainerName($path) {
- $path=trim(trim($this->root,'/')."/".$path,'/.');
- return str_replace('/','\\',$path);
+ $path=trim(trim($this->root, '/') . "/".$path, '/.');
+ return str_replace('/', '\\', $path);
}
/**
* get container by path
- * @param string path
- * @return CF_Container
+ * @param string $path
+ * @return \CF_Container
*/
private function getContainer($path) {
- if($path=='' or $path=='/') {
+ if ($path=='' or $path=='/') {
return $this->rootContainer;
}
- if(isset($this->containers[$path])) {
+ if (isset($this->containers[$path])) {
return $this->containers[$path];
}
- try{
+ try {
$container=$this->conn->get_container($this->getContainerName($path));
$this->containers[$path]=$container;
return $container;
- }catch(NoSuchContainerException $e) {
+ } catch(\NoSuchContainerException $e) {
return null;
}
}
/**
* create container
- * @param string path
- * @return CF_Container
+ * @param string $path
+ * @return \CF_Container
*/
private function createContainer($path) {
- if($path=='' or $path=='/' or $path=='.') {
+ if ($path=='' or $path=='/' or $path=='.') {
return $this->conn->create_container($this->getContainerName($path));
}
$parent=dirname($path);
- if($parent=='' or $parent=='/' or $parent=='.') {
+ if ($parent=='' or $parent=='/' or $parent=='.') {
$parentContainer=$this->rootContainer;
- }else{
- if(!$this->containerExists($parent)) {
+ } else {
+ if ( ! $this->containerExists($parent)) {
$parentContainer=$this->createContainer($parent);
- }else{
+ } else {
$parentContainer=$this->getContainer($parent);
}
}
- $this->addSubContainer($parentContainer,basename($path));
+ $this->addSubContainer($parentContainer, basename($path));
return $this->conn->create_container($this->getContainerName($path));
}
/**
* get object by path
- * @param string path
- * @return CF_Object
+ * @param string $path
+ * @return \CF_Object
*/
private function getObject($path) {
- if(isset($this->objects[$path])) {
+ if (isset($this->objects[$path])) {
return $this->objects[$path];
}
$container=$this->getContainer(dirname($path));
- if(is_null($container)) {
+ if (is_null($container)) {
return null;
- }else{
+ } else {
if ($path=="/" or $path=='') {
return null;
}
- try{
+ try {
$obj=$container->get_object(basename($path));
$this->objects[$path]=$obj;
return $obj;
- }catch(NoSuchObjectException $e) {
+ } catch(\NoSuchObjectException $e) {
return null;
}
}
@@ -119,11 +123,11 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
* @return array
*/
private function getObjects($container) {
- if(is_null($container)) {
+ if (is_null($container)) {
return array();
- }else{
+ } else {
$files=$container->get_objects();
- foreach($files as &$file) {
+ foreach ($files as &$file) {
$file=$file->name;
}
return $files;
@@ -132,12 +136,12 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* create object
- * @param string path
- * @return CF_Object
+ * @param string $path
+ * @return \CF_Object
*/
private function createObject($path) {
$container=$this->getContainer(dirname($path));
- if(!is_null($container)) {
+ if ( ! is_null($container)) {
$container=$this->createContainer(dirname($path));
}
return $container->create_object(basename($path));
@@ -154,7 +158,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* check if container for path exists
- * @param string path
+ * @param string $path
* @return bool
*/
private function containerExists($path) {
@@ -163,21 +167,21 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* get the list of emulated sub containers
- * @param CF_Container container
+ * @param \CF_Container $container
* @return array
*/
private function getSubContainers($container) {
- $tmpFile=OCP\Files::tmpFile();
+ $tmpFile=\OCP\Files::tmpFile();
$obj=$this->getSubContainerFile($container);
- try{
+ try {
$obj->save_to_filename($tmpFile);
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return array();
}
$obj->save_to_filename($tmpFile);
$containers=file($tmpFile);
unlink($tmpFile);
- foreach($containers as &$sub) {
+ foreach ($containers as &$sub) {
$sub=trim($sub);
}
return $containers;
@@ -185,32 +189,31 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* add an emulated sub container
- * @param CF_Container container
- * @param string name
+ * @param \CF_Container $container
+ * @param string $name
* @return bool
*/
- private function addSubContainer($container,$name) {
- if(!$name) {
+ private function addSubContainer($container, $name) {
+ if ( ! $name) {
return false;
}
- $tmpFile=OCP\Files::tmpFile();
+ $tmpFile=\OCP\Files::tmpFile();
$obj=$this->getSubContainerFile($container);
- try{
+ try {
$obj->save_to_filename($tmpFile);
$containers=file($tmpFile);
- foreach($containers as &$sub) {
+ foreach ($containers as &$sub) {
$sub=trim($sub);
}
- if(array_search($name,$containers)!==false) {
+ if(array_search($name, $containers) !== false) {
unlink($tmpFile);
return false;
- }else{
- $fh=fopen($tmpFile,'a');
- fwrite($fh,$name."\n");
+ } else {
+ $fh=fopen($tmpFile, 'a');
+ fwrite($fh, $name . "\n");
}
- }catch(Exception $e) {
- $containers=array();
- file_put_contents($tmpFile,$name."\n");
+ } catch(\Exception $e) {
+ file_put_contents($tmpFile, $name . "\n");
}
$obj->load_from_filename($tmpFile);
@@ -220,32 +223,32 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* remove an emulated sub container
- * @param CF_Container container
- * @param string name
+ * @param \CF_Container $container
+ * @param string $name
* @return bool
*/
- private function removeSubContainer($container,$name) {
- if(!$name) {
+ private function removeSubContainer($container, $name) {
+ if ( ! $name) {
return false;
}
- $tmpFile=OCP\Files::tmpFile();
+ $tmpFile=\OCP\Files::tmpFile();
$obj=$this->getSubContainerFile($container);
- try{
+ try {
$obj->save_to_filename($tmpFile);
$containers=file($tmpFile);
- }catch(Exception $e) {
+ } catch (\Exception $e) {
return false;
}
- foreach($containers as &$sub) {
+ foreach ($containers as &$sub) {
$sub=trim($sub);
}
- $i=array_search($name,$containers);
- if($i===false) {
+ $i=array_search($name, $containers);
+ if ($i===false) {
unlink($tmpFile);
return false;
- }else{
+ } else {
unset($containers[$i]);
- file_put_contents($tmpFile,implode("\n",$containers)."\n");
+ file_put_contents($tmpFile, implode("\n", $containers)."\n");
}
$obj->load_from_filename($tmpFile);
@@ -255,56 +258,83 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* ensure a subcontainer file exists and return it's object
- * @param CF_Container container
- * @return CF_Object
+ * @param \CF_Container $container
+ * @return \CF_Object
*/
private function getSubContainerFile($container) {
- try{
+ try {
return $container->get_object(self::SUBCONTAINER_FILE);
- }catch(NoSuchObjectException $e) {
+ } catch(\NoSuchObjectException $e) {
return $container->create_object(self::SUBCONTAINER_FILE);
}
}
public function __construct($params) {
- $this->token=$params['token'];
- $this->host=$params['host'];
- $this->user=$params['user'];
- $this->root=isset($params['root'])?$params['root']:'/';
- $this->secure=isset($params['secure'])?(bool)$params['secure']:true;
- if(!$this->root || $this->root[0]!='/') {
- $this->root='/'.$this->root;
- }
- $this->auth = new CF_Authentication($this->user, $this->token, null, $this->host);
+ if (isset($params['token']) && isset($params['host']) && isset($params['user'])) {
+ $this->token=$params['token'];
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if (isset($params['secure'])) {
+ if (is_string($params['secure'])) {
+ $this->secure = ($params['secure'] === 'true');
+ } else {
+ $this->secure = (bool)$params['secure'];
+ }
+ } else {
+ $this->secure = false;
+ }
+ if ( ! $this->root || $this->root[0]!='/') {
+ $this->root='/'.$this->root;
+ }
+ } else {
+ throw new \Exception();
+ }
+
+ }
+
+ private function init(){
+ if($this->ready) {
+ return;
+ }
+ $this->ready = true;
+
+ $this->auth = new \CF_Authentication($this->user, $this->token, null, $this->host);
$this->auth->authenticate();
- $this->conn = new CF_Connection($this->auth);
+ $this->conn = new \CF_Connection($this->auth);
- if(!$this->containerExists('/')) {
+ if ( ! $this->containerExists('/')) {
$this->rootContainer=$this->createContainer('/');
- }else{
+ } else {
$this->rootContainer=$this->getContainer('/');
}
}
+ public function getId(){
+ return $this->id;
+ }
+
public function mkdir($path) {
- if($this->containerExists($path)) {
+ $this->init();
+ if ($this->containerExists($path)) {
return false;
- }else{
+ } else {
$this->createContainer($path);
return true;
}
}
public function rmdir($path) {
- if(!$this->containerExists($path)) {
+ $this->init();
+ if (!$this->containerExists($path)) {
return false;
- }else{
+ } else {
$this->emptyContainer($path);
- if($path!='' and $path!='/') {
+ if ($path!='' and $path!='/') {
$parentContainer=$this->getContainer(dirname($path));
- $this->removeSubContainer($parentContainer,basename($path));
+ $this->removeSubContainer($parentContainer, basename($path));
}
$this->conn->delete_container($this->getContainerName($path));
@@ -315,12 +345,12 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
private function emptyContainer($path) {
$container=$this->getContainer($path);
- if(is_null($container)) {
+ if (is_null($container)) {
return;
}
$subContainers=$this->getSubContainers($container);
- foreach($subContainers as $sub) {
- if($sub) {
+ foreach ($subContainers as $sub) {
+ if ($sub) {
$this->emptyContainer($path.'/'.$sub);
$this->conn->delete_container($this->getContainerName($path.'/'.$sub));
unset($this->containers[$path.'/'.$sub]);
@@ -328,30 +358,32 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
}
$objects=$this->getObjects($container);
- foreach($objects as $object) {
+ foreach ($objects as $object) {
$container->delete_object($object);
unset($this->objects[$path.'/'.$object]);
}
}
public function opendir($path) {
+ $this->init();
$container=$this->getContainer($path);
$files=$this->getObjects($container);
- $i=array_search(self::SUBCONTAINER_FILE,$files);
- if($i!==false) {
+ $i=array_search(self::SUBCONTAINER_FILE, $files);
+ if ($i!==false) {
unset($files[$i]);
}
$subContainers=$this->getSubContainers($container);
- $files=array_merge($files,$subContainers);
+ $files=array_merge($files, $subContainers);
$id=$this->getContainerName($path);
- OC_FakeDirStream::$dirs[$id]=$files;
+ \OC\Files\Stream\Dir::register($id, $files);
return opendir('fakedir://'.$id);
}
public function filetype($path) {
- if($this->containerExists($path)) {
+ $this->init();
+ if ($this->containerExists($path)) {
return 'dir';
- }else{
+ } else {
return 'file';
}
}
@@ -365,26 +397,29 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
}
public function file_exists($path) {
- if($this->is_dir($path)) {
+ $this->init();
+ if ($this->is_dir($path)) {
return true;
- }else{
+ } else {
return $this->objectExists($path);
}
}
public function file_get_contents($path) {
+ $this->init();
$obj=$this->getObject($path);
- if(is_null($obj)) {
+ if (is_null($obj)) {
return false;
}
return $obj->read();
}
- public function file_put_contents($path,$content) {
+ public function file_put_contents($path, $content) {
+ $this->init();
$obj=$this->getObject($path);
- if(is_null($obj)) {
+ if (is_null($obj)) {
$container=$this->getContainer(dirname($path));
- if(is_null($container)) {
+ if (is_null($container)) {
return false;
}
$obj=$container->create_object(basename($path));
@@ -394,19 +429,21 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
}
public function unlink($path) {
- if($this->containerExists($path)) {
+ $this->init();
+ if ($this->containerExists($path)) {
return $this->rmdir($path);
}
- if($this->objectExists($path)) {
+ if ($this->objectExists($path)) {
$container=$this->getContainer(dirname($path));
$container->delete_object(basename($path));
unset($this->objects[$path]);
- }else{
+ } else {
return false;
}
}
- public function fopen($path,$mode) {
+ public function fopen($path, $mode) {
+ $this->init();
switch($mode) {
case 'r':
case 'rb':
@@ -432,29 +469,26 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
case 'c':
case 'c+':
$tmpFile=$this->getTmpFile($path);
- OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
self::$tempFiles[$tmpFile]=$path;
- return fopen('close://'.$tmpFile,$mode);
+ return fopen('close://'.$tmpFile, $mode);
}
}
public function writeBack($tmpFile) {
- if(isset(self::$tempFiles[$tmpFile])) {
- $this->fromTmpFile($tmpFile,self::$tempFiles[$tmpFile]);
+ if (isset(self::$tempFiles[$tmpFile])) {
+ $this->fromTmpFile($tmpFile, self::$tempFiles[$tmpFile]);
unlink($tmpFile);
}
}
- public function free_space($path) {
- return 1024*1024*1024*8;
- }
-
- public function touch($path,$mtime=null) {
+ public function touch($path, $mtime=null) {
+ $this->init();
$obj=$this->getObject($path);
- if(is_null($obj)) {
+ if (is_null($obj)) {
return false;
}
- if(is_null($mtime)) {
+ if (is_null($mtime)) {
$mtime=time();
}
@@ -463,23 +497,25 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
$obj->sync_metadata();
}
- public function rename($path1,$path2) {
+ public function rename($path1, $path2) {
+ $this->init();
$sourceContainer=$this->getContainer(dirname($path1));
$targetContainer=$this->getContainer(dirname($path2));
- $result=$sourceContainer->move_object_to(basename($path1),$targetContainer,basename($path2));
+ $result=$sourceContainer->move_object_to(basename($path1), $targetContainer, basename($path2));
unset($this->objects[$path1]);
- if($result) {
+ if ($result) {
$targetObj=$this->getObject($path2);
$this->resetMTime($targetObj);
}
return $result;
}
- public function copy($path1,$path2) {
+ public function copy($path1, $path2) {
+ $this->init();
$sourceContainer=$this->getContainer(dirname($path1));
$targetContainer=$this->getContainer(dirname($path2));
- $result=$sourceContainer->copy_object_to(basename($path1),$targetContainer,basename($path2));
- if($result) {
+ $result=$sourceContainer->copy_object_to(basename($path1), $targetContainer, basename($path2));
+ if ($result) {
$targetObj=$this->getObject($path2);
$this->resetMTime($targetObj);
}
@@ -487,8 +523,9 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
}
public function stat($path) {
+ $this->init();
$container=$this->getContainer($path);
- if (!is_null($container)) {
+ if ( ! is_null($container)) {
return array(
'mtime'=>-1,
'size'=>$container->bytes_used,
@@ -498,13 +535,13 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
$obj=$this->getObject($path);
- if(is_null($obj)) {
+ if (is_null($obj)) {
return false;
}
- if(isset($obj->metadata['Mtime']) and $obj->metadata['Mtime']>-1) {
+ if (isset($obj->metadata['Mtime']) and $obj->metadata['Mtime']>-1) {
$mtime=$obj->metadata['Mtime'];
- }else{
+ } else {
$mtime=strtotime($obj->last_modified);
}
return array(
@@ -515,19 +552,21 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
}
private function getTmpFile($path) {
+ $this->init();
$obj=$this->getObject($path);
- if(!is_null($obj)) {
- $tmpFile=OCP\Files::tmpFile();
+ if ( ! is_null($obj)) {
+ $tmpFile=\OCP\Files::tmpFile();
$obj->save_to_filename($tmpFile);
return $tmpFile;
- }else{
- return OCP\Files::tmpFile();
+ } else {
+ return \OCP\Files::tmpFile();
}
}
- private function fromTmpFile($tmpFile,$path) {
+ private function fromTmpFile($tmpFile, $path) {
+ $this->init();
$obj=$this->getObject($path);
- if(is_null($obj)) {
+ if (is_null($obj)) {
$obj=$this->createObject($path);
}
$obj->load_from_filename($tmpFile);
@@ -536,10 +575,10 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{
/**
* remove custom mtime metadata
- * @param CF_Object obj
+ * @param \CF_Object $obj
*/
private function resetMTime($obj) {
- if(isset($obj->metadata['Mtime'])) {
+ if (isset($obj->metadata['Mtime'])) {
$obj->metadata['Mtime']=-1;
$obj->sync_metadata();
}
diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php
index 5e185839158..3ba7c48cd57 100644
--- a/apps/files_external/lib/webdav.php
+++ b/apps/files_external/lib/webdav.php
@@ -6,57 +6,84 @@
* See the COPYING-README file.
*/
-class OC_FileStorage_DAV extends OC_Filestorage_Common{
+namespace OC\Files\Storage;
+
+class DAV extends \OC\Files\Storage\Common{
private $password;
private $user;
private $host;
private $secure;
private $root;
+ private $ready;
/**
- * @var Sabre_DAV_Client
+ * @var \Sabre_DAV_Client
*/
private $client;
private static $tempFiles=array();
public function __construct($params) {
- $host = $params['host'];
- //remove leading http[s], will be generated in createBaseUri()
- if (substr($host,0,8) == "https://") $host = substr($host, 8);
- else if (substr($host,0,7) == "http://") $host = substr($host, 7);
- $this->host=$host;
- $this->user=$params['user'];
- $this->password=$params['password'];
- $this->secure=(isset($params['secure']) && $params['secure'] == 'true')?true:false;
- $this->root=isset($params['root'])?$params['root']:'/';
- if(!$this->root || $this->root[0]!='/') {
- $this->root='/'.$this->root;
+ if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
+ $host = $params['host'];
+ //remove leading http[s], will be generated in createBaseUri()
+ if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
+ else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
+ $this->host=$host;
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ if (isset($params['secure'])) {
+ if (is_string($params['secure'])) {
+ $this->secure = ($params['secure'] === 'true');
+ } else {
+ $this->secure = (bool)$params['secure'];
+ }
+ } else {
+ $this->secure = false;
+ }
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if ( ! $this->root || $this->root[0]!='/') {
+ $this->root='/'.$this->root;
+ }
+ if (substr($this->root, -1, 1)!='/') {
+ $this->root.='/';
+ }
+ } else {
+ throw new \Exception();
}
- if(substr($this->root,-1,1)!='/') {
- $this->root.='/';
+ }
+
+ private function init(){
+ if($this->ready) {
+ return;
}
+ $this->ready = true;
- $settings = array(
- 'baseUri' => $this->createBaseUri(),
- 'userName' => $this->user,
- 'password' => $this->password,
- );
+ $settings = array(
+ 'baseUri' => $this->createBaseUri(),
+ 'userName' => $this->user,
+ 'password' => $this->password,
+ );
- $this->client = new OC_Connector_Sabre_Client($settings);
+ $this->client = new \Sabre_DAV_Client($settings);
- if($caview = \OCP\Files::getStorage('files_external')) {
+ $caview = \OCP\Files::getStorage('files_external');
+ if ($caview) {
$certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt';
- if (file_exists($certPath)) {
+ if (file_exists($certPath)) {
$this->client->addTrustedCertificates($certPath);
}
}
- //create the root folder if necesary
+ //create the root folder if necessary
$this->mkdir('');
}
+ public function getId(){
+ return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
+ }
+
private function createBaseUri() {
$baseUri='http';
- if($this->secure) {
+ if ($this->secure) {
$baseUri.='s';
}
$baseUri.='://'.$this->host.$this->root;
@@ -64,40 +91,45 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
}
public function mkdir($path) {
+ $this->init();
$path=$this->cleanPath($path);
- return $this->simpleResponse('MKCOL',$path,null,201);
+ return $this->simpleResponse('MKCOL', $path, null, 201);
}
public function rmdir($path) {
+ $this->init();
$path=$this->cleanPath($path);
- return $this->simpleResponse('DELETE',$path,null,204);
+ return $this->simpleResponse('DELETE', $path, null, 204);
}
public function opendir($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
- $response=$this->client->propfind($path, array(),1);
+ try {
+ $response=$this->client->propfind($path, array(), 1);
$id=md5('webdav'.$this->root.$path);
- OC_FakeDirStream::$dirs[$id]=array();
+ $content = array();
$files=array_keys($response);
array_shift($files);//the first entry is the current directory
- foreach($files as $file) {
+ foreach ($files as $file) {
$file = urldecode(basename($file));
- OC_FakeDirStream::$dirs[$id][]=$file;
+ $content[]=$file;
}
+ \OC\Files\Stream\Dir::register($id, $content);
return opendir('fakedir://'.$id);
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return false;
}
}
public function filetype($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
+ try {
$response=$this->client->propfind($path, array('{DAV:}resourcetype'));
$responseType=$response["{DAV:}resourcetype"]->resourceType;
return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
- }catch(Exception $e) {
+ } catch(\Exception $e) {
error_log($e->getMessage());
\OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR);
return false;
@@ -113,31 +145,34 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
}
public function file_exists($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
+ try {
$this->client->propfind($path, array('{DAV:}resourcetype'));
return true;//no 404 exception
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return false;
}
}
public function unlink($path) {
- return $this->simpleResponse('DELETE',$path,null,204);
+ $this->init();
+ return $this->simpleResponse('DELETE', $path, null, 204);
}
- public function fopen($path,$mode) {
+ public function fopen($path, $mode) {
+ $this->init();
$path=$this->cleanPath($path);
switch($mode) {
case 'r':
case 'rb':
- if(!$this->file_exists($path)) {
+ if ( ! $this->file_exists($path)) {
return false;
}
//straight up curl instead of sabredav here, sabredav put's the entire get result in memory
$curl = curl_init();
$fp = fopen('php://temp', 'r+');
- curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password);
curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$path);
curl_setopt($curl, CURLOPT_FILE, $fp);
@@ -158,60 +193,64 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
case 'c':
case 'c+':
//emulate these
- if(strrpos($path,'.')!==false) {
- $ext=substr($path,strrpos($path,'.'));
- }else{
+ if (strrpos($path, '.')!==false) {
+ $ext=substr($path, strrpos($path, '.'));
+ } else {
$ext='';
}
- $tmpFile=OCP\Files::tmpFile($ext);
- OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
+ $tmpFile = \OCP\Files::tmpFile($ext);
+ \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if($this->file_exists($path)) {
- $this->getFile($path,$tmpFile);
+ $this->getFile($path, $tmpFile);
}
self::$tempFiles[$tmpFile]=$path;
- return fopen('close://'.$tmpFile,$mode);
+ return fopen('close://'.$tmpFile, $mode);
}
}
public function writeBack($tmpFile) {
- if(isset(self::$tempFiles[$tmpFile])) {
- $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]);
+ if (isset(self::$tempFiles[$tmpFile])) {
+ $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
unlink($tmpFile);
}
}
public function free_space($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
+ try {
$response=$this->client->propfind($path, array('{DAV:}quota-available-bytes'));
- if(isset($response['{DAV:}quota-available-bytes'])) {
+ if (isset($response['{DAV:}quota-available-bytes'])) {
return (int)$response['{DAV:}quota-available-bytes'];
- }else{
+ } else {
return 0;
}
- }catch(Exception $e) {
- return 0;
+ } catch(\Exception $e) {
+ return \OC\Files\FREE_SPACE_UNKNOWN;
}
}
- public function touch($path,$mtime=null) {
- if(is_null($mtime)) {
+ public function touch($path, $mtime=null) {
+ $this->init();
+ if (is_null($mtime)) {
$mtime=time();
}
$path=$this->cleanPath($path);
$this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime));
}
- public function getFile($path,$target) {
- $source=$this->fopen($path,'r');
- file_put_contents($target,$source);
+ public function getFile($path, $target) {
+ $this->init();
+ $source=$this->fopen($path, 'r');
+ file_put_contents($target, $source);
}
- public function uploadFile($path,$target) {
- $source=fopen($path,'r');
+ public function uploadFile($path, $target) {
+ $this->init();
+ $source=fopen($path, 'r');
$curl = curl_init();
- curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password);
curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$target);
curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer
@@ -221,80 +260,77 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{
curl_close ($curl);
}
- public function rename($path1,$path2) {
+ public function rename($path1, $path2) {
+ $this->init();
$path1=$this->cleanPath($path1);
$path2=$this->root.$this->cleanPath($path2);
- try{
- $response=$this->client->request('MOVE',$path1,null,array('Destination'=>$path2));
+ try {
+ $this->client->request('MOVE', $path1, null, array('Destination'=>$path2));
return true;
- }catch(Exception $e) {
- echo $e;
- echo 'fail';
- var_dump($response);
+ } catch(\Exception $e) {
return false;
}
}
- public function copy($path1,$path2) {
+ public function copy($path1, $path2) {
+ $this->init();
$path1=$this->cleanPath($path1);
$path2=$this->root.$this->cleanPath($path2);
- try{
- $response=$this->client->request('COPY',$path1,null,array('Destination'=>$path2));
+ try {
+ $this->client->request('COPY', $path1, null, array('Destination'=>$path2));
return true;
- }catch(Exception $e) {
- echo $e;
- echo 'fail';
- var_dump($response);
+ } catch(\Exception $e) {
return false;
}
}
public function stat($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
- $response=$this->client->propfind($path, array('{DAV:}getlastmodified','{DAV:}getcontentlength'));
+ try {
+ $response=$this->client->propfind($path, array('{DAV:}getlastmodified', '{DAV:}getcontentlength'));
return array(
'mtime'=>strtotime($response['{DAV:}getlastmodified']),
'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
- 'ctime'=>-1,
);
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return array();
}
}
public function getMimeType($path) {
+ $this->init();
$path=$this->cleanPath($path);
- try{
- $response=$this->client->propfind($path, array('{DAV:}getcontenttype','{DAV:}resourcetype'));
+ try {
+ $response=$this->client->propfind($path, array('{DAV:}getcontenttype', '{DAV:}resourcetype'));
$responseType=$response["{DAV:}resourcetype"]->resourceType;
$type=(count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
- if($type=='dir') {
+ if ($type=='dir') {
return 'httpd/unix-directory';
- }elseif(isset($response['{DAV:}getcontenttype'])) {
+ } elseif (isset($response['{DAV:}getcontenttype'])) {
return $response['{DAV:}getcontenttype'];
- }else{
+ } else {
return false;
}
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return false;
}
}
- private function cleanPath($path) {
- if(!$path || $path[0]=='/') {
- return substr($path,1);
- }else{
+ public function cleanPath($path) {
+ if ( ! $path || $path[0]=='/') {
+ return substr($path, 1);
+ } else {
return $path;
}
}
- private function simpleResponse($method,$path,$body,$expected) {
+ private function simpleResponse($method, $path, $body, $expected) {
$path=$this->cleanPath($path);
- try{
- $response=$this->client->request($method,$path,$body);
+ try {
+ $response=$this->client->request($method, $path, $body);
return $response['statusCode']==$expected;
- }catch(Exception $e) {
+ } catch(\Exception $e) {
return false;
}
}
diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php
index f0d76460f54..90f5e159535 100755
--- a/apps/files_external/personal.php
+++ b/apps/files_external/personal.php
@@ -24,10 +24,11 @@ OCP\Util::addScript('files_external', 'settings');
OCP\Util::addStyle('files_external', 'settings');
$backends = OC_Mount_Config::getBackends();
// Remove local storage
-unset($backends['OC_Filestorage_Local']);
+unset($backends['\OC\Files\Storage\Local']);
$tmpl = new OCP\Template('files_external', 'settings');
-$tmpl->assign('isAdminPage', false, false);
+$tmpl->assign('isAdminPage', false);
$tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints());
$tmpl->assign('certs', OC_Mount_Config::getCertificates());
+$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
$tmpl->assign('backends', $backends);
return $tmpl->fetchPage();
diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php
index d2be21b7116..1a39affe2e6 100644
--- a/apps/files_external/settings.php
+++ b/apps/files_external/settings.php
@@ -20,15 +20,18 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+OC_Util::checkAdminUser();
+
OCP\Util::addScript('files_external', 'settings');
OCP\Util::addscript('3rdparty', 'chosen/chosen.jquery.min');
OCP\Util::addStyle('files_external', 'settings');
OCP\Util::addStyle('3rdparty', 'chosen/chosen');
$tmpl = new OCP\Template('files_external', 'settings');
-$tmpl->assign('isAdminPage', true, false);
+$tmpl->assign('isAdminPage', true);
$tmpl->assign('mounts', OC_Mount_Config::getSystemMountPoints());
$tmpl->assign('backends', OC_Mount_Config::getBackends());
$tmpl->assign('groups', OC_Group::getGroups());
$tmpl->assign('users', OCP\User::getUsers());
+$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes'));
return $tmpl->fetchPage();
diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php
index 5a7b25d59a9..b3b94a1dafd 100644
--- a/apps/files_external/templates/settings.php
+++ b/apps/files_external/templates/settings.php
@@ -1,33 +1,44 @@
<form id="files_external">
<fieldset class="personalblock">
- <legend><strong><?php echo $l->t('External Storage'); ?></strong></legend>
- <table id="externalStorage" data-admin='<?php echo json_encode($_['isAdminPage']); ?>'>
+ <legend><strong><?php p($l->t('External Storage')); ?></strong></legend>
+ <?php if (isset($_['dependencies']) and ($_['dependencies']<>'')) print_unescaped(''.$_['dependencies'].''); ?>
+ <table id="externalStorage" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'>
<thead>
<tr>
- <th><?php echo $l->t('Mount point'); ?></th>
- <th><?php echo $l->t('Backend'); ?></th>
- <th><?php echo $l->t('Configuration'); ?></th>
- <!--<th><?php echo $l->t('Options'); ?></th> -->
- <?php if ($_['isAdminPage']) echo '<th>'.$l->t('Applicable').'</th>'; ?>
+ <th></th>
+ <th><?php p($l->t('Folder name')); ?></th>
+ <th><?php p($l->t('External storage')); ?></th>
+ <th><?php p($l->t('Configuration')); ?></th>
+ <!--<th><?php p($l->t('Options')); ?></th> -->
+ <?php if ($_['isAdminPage']) print_unescaped('<th>'.$l->t('Applicable').'</th>'); ?>
<th>&nbsp;</th>
</tr>
</thead>
<tbody width="100%">
<?php $_['mounts'] = array_merge($_['mounts'], array('' => array())); ?>
<?php foreach ($_['mounts'] as $mountPoint => $mount): ?>
- <tr <?php echo ($mountPoint != '') ? 'class="'.$mount['class'].'"' : 'id="addMountPoint"'; ?>>
- <td class="mountPoint"><input type="text" name="mountPoint" value="<?php echo $mountPoint; ?>" placeholder="<?php echo $l->t('Mount point'); ?>" /></td>
+ <tr <?php print_unescaped(($mountPoint != '') ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>>
+ <td class="status">
+ <?php if (isset($mount['status'])): ?>
+ <span class="<?php p(($mount['status']) ? 'success' : 'error'); ?>"></span>
+ <?php endif; ?>
+ </td>
+ <td class="mountPoint"><input type="text" name="mountPoint"
+ value="<?php p($mountPoint); ?>"
+ placeholder="<?php p($l->t('Folder name')); ?>" /></td>
<?php if ($mountPoint == ''): ?>
<td class="backend">
- <select id="selectBackend" data-configurations='<?php echo json_encode($_['backends']); ?>'>
- <option value="" disabled selected style="display:none;"><?php echo $l->t('Add mount point'); ?></option>
+ <select id="selectBackend" data-configurations='<?php print_unescaped(json_encode($_['backends'])); ?>'>
+ <option value="" disabled selected
+ style="display:none;"><?php p($l->t('Add storage')); ?></option>
<?php foreach ($_['backends'] as $class => $backend): ?>
- <option value="<?php echo $class; ?>"><?php echo $backend['backend']; ?></option>
+ <option value="<?php p($class); ?>"><?php p($backend['backend']); ?></option>
<?php endforeach; ?>
</select>
</td>
<?php else: ?>
- <td class="backend" data-class="<?php echo $mount['class']; ?>"><?php echo $mount['backend']; ?></td>
+ <td class="backend"
+ data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?></td>
<?php endif; ?>
<td class ="configuration" width="100%">
<?php if (isset($mount['configuration'])): ?>
@@ -35,46 +46,74 @@
<?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?>
<?php $placeholder = $_['backends'][$mount['class']]['configuration'][$parameter]; ?>
<?php if (strpos($placeholder, '*') !== false): ?>
- <input type="password" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo substr($placeholder, 1); ?>" />
- <?php elseif(strpos($placeholder, '!') !== false): ?>
- <label><input type="checkbox" data-parameter="<?php echo $parameter; ?>" <?php if ($value == 'true') echo ' checked="checked"'; ?> /><?php echo substr($placeholder, 1); ?></label>
+ <input type="password"
+ data-parameter="<?php p($parameter); ?>"
+ value="<?php p($value); ?>"
+ placeholder="<?php p(substr($placeholder, 1)); ?>" />
+ <?php elseif (strpos($placeholder, '!') !== false): ?>
+ <label><input type="checkbox"
+ data-parameter="<?php p($parameter); ?>"
+ <?php if ($value == 'true'): ?> checked="checked"<?php endif; ?>
+ /><?php p(substr($placeholder, 1)); ?></label>
<?php elseif (strpos($placeholder, '&') !== false): ?>
- <input type="text" class="optional" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo substr($placeholder, 5); ?>" />
+ <input type="text"
+ class="optional"
+ data-parameter="<?php p($parameter); ?>"
+ value="<?php p($value); ?>"
+ placeholder="<?php p(substr($placeholder, 5)); ?>" />
<?php elseif (strpos($placeholder, '#') !== false): ?>
- <input type="hidden" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" />
+ <input type="hidden"
+ data-parameter="<?php p($parameter); ?>"
+ value="<?php p($value); ?>" />
<?php else: ?>
- <input type="text" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo $placeholder; ?>" />
+ <input type="text"
+ data-parameter="<?php p($parameter); ?>"
+ value="<?php p($value); ?>"
+ placeholder="<?php p($placeholder); ?>" />
<?php endif; ?>
<?php endif; ?>
<?php endforeach; ?>
- <?php if (isset($_['backends'][$mount['class']]['custom'])) OCP\Util::addScript('files_external', $_['backends'][$mount['class']]['custom']); ?>
+ <?php if (isset($_['backends'][$mount['class']]['custom']) && !in_array('files_external/js/'.$_['backends'][$mount['class']]['custom'], \OC_Util::$scripts)): ?>
+ <?php OCP\Util::addScript('files_external', $_['backends'][$mount['class']]['custom']); ?>
+ <?php endif; ?>
<?php endif; ?>
</td>
- <!--<td class="options">
- <select class="selectOptions" title="<?php echo $l->t('None set')?>" multiple="multiple" disabled>
- <?php if (OCP\App::isEnabled('files_encryption')) echo '<option value="Encrypt">Encrypt</option>'; ?>
- <?php if (OCP\App::isEnabled('files_versions')) echo '<option value="Version control">Version control</option>'; ?>
- <?php if (OCP\App::isEnabled('files_sharing')) echo '<option value="Allow sharing">Allow sharing</option>'; ?>
- </select>
- </td>-->
<?php if ($_['isAdminPage']): ?>
- <td class="applicable" align="right" data-applicable-groups='<?php if (isset($mount['applicable']['groups'])) echo json_encode($mount['applicable']['groups']); ?>' data-applicable-users='<?php if (isset($mount['applicable']['users'])) echo json_encode($mount['applicable']['users']); ?>'>
- <select class="chzn-select" multiple style="width:20em;" data-placeholder="<?php echo $l->t('None set'); ?>">
- <option value="all"><?php echo $l->t('All Users'); ?></option>
- <optgroup label="<?php echo $l->t('Groups'); ?>">
- <?php foreach ($_['groups'] as $group): ?>
- <option value="<?php echo $group; ?>(group)" <?php if (isset($mount['applicable']['groups']) && in_array($group, $mount['applicable']['groups'])) echo 'selected="selected"'; ?>><?php echo $group; ?></option>
- <?php endforeach; ?>
+ <td class="applicable"
+ align="right"
+ data-applicable-groups='<?php if (isset($mount['applicable']['groups']))
+ print_unescaped(json_encode($mount['applicable']['groups'])); ?>'
+ data-applicable-users='<?php if (isset($mount['applicable']['users']))
+ print_unescaped(json_encode($mount['applicable']['users'])); ?>'>
+ <select class="chzn-select"
+ multiple style="width:20em;"
+ data-placeholder="<?php p($l->t('None set')); ?>">
+ <option value="all" <?php if (isset($mount['applicable']['users']) && in_array('all', $mount['applicable']['users'])) print_unescaped('selected="selected"');?> ><?php p($l->t('All Users')); ?></option>
+ <optgroup label="<?php p($l->t('Groups')); ?>">
+ <?php foreach ($_['groups'] as $group): ?>
+ <option value="<?php p($group); ?>(group)"
+ <?php if (isset($mount['applicable']['groups']) && in_array($group, $mount['applicable']['groups'])): ?>
+ selected="selected"
+ <?php endif; ?>><?php p($group); ?></option>
+ <?php endforeach; ?>
</optgroup>
- <optgroup label="<?php echo $l->t('Users'); ?>">
- <?php foreach ($_['users'] as $user): ?>
- <option value="<?php echo $user; ?>" <?php if (isset($mount['applicable']['users']) && in_array($user, $mount['applicable']['users'])) echo 'selected="selected"'; ?>"><?php echo $user; ?></option>
- <?php endforeach; ?>
+ <optgroup label="<?php p($l->t('Users')); ?>">
+ <?php foreach ($_['users'] as $user): ?>
+ <option value="<?php p($user); ?>"
+ <?php if (isset($mount['applicable']['users']) && in_array($user, $mount['applicable']['users'])): ?>
+ selected="selected"
+ <?php endif; ?>><?php p($user); ?></option>
+ <?php endforeach; ?>
</optgroup>
</select>
</td>
<?php endif; ?>
- <td <?php echo ($mountPoint != '') ? 'class="remove"' : 'style="visibility:hidden;"'; ?>><img alt="<?php echo $l->t('Delete'); ?>" title="<?php echo $l->t('Delete'); ?>" class="svg action" src="<?php echo image_path('core', 'actions/delete.svg'); ?>" /></td>
+ <td <?php if ($mountPoint != ''): ?>class="remove"
+ <?php else: ?>style="visibility:hidden;"
+ <?php endif ?>><img alt="<?php p($l->t('Delete')); ?>"
+ title="<?php p($l->t('Delete')); ?>"
+ class="svg action"
+ src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" /></td>
</tr>
<?php endforeach; ?>
</tbody>
@@ -83,34 +122,41 @@
<?php if ($_['isAdminPage']): ?>
<br />
- <input type="checkbox" name="allowUserMounting" id="allowUserMounting" value="1" <?php if ($_['allowUserMounting'] == 'yes') echo ' checked="checked"'; ?> />
- <label for="allowUserMounting"><?php echo $l->t('Enable User External Storage'); ?></label><br/>
- <em><?php echo $l->t('Allow users to mount their own external storage'); ?></em>
+ <input type="checkbox"
+ name="allowUserMounting"
+ id="allowUserMounting"
+ value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> />
+ <label for="allowUserMounting"><?php p($l->t('Enable User External Storage')); ?></label><br/>
+ <em><?php p($l->t('Allow users to mount their own external storage')); ?></em>
<?php endif; ?>
</fieldset>
</form>
-<form id="files_external" method="post" enctype="multipart/form-data" action="<?php echo OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php'); ?>">
+<?php if ( ! $_['isAdminPage']): ?>
+<form id="files_external"
+ method="post"
+ enctype="multipart/form-data"
+ action="<?php p(OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php')); ?>">
<fieldset class="personalblock">
-<?php if (!$_['isAdminPage']): ?>
- <table id="sslCertificate" data-admin='<?php echo json_encode($_['isAdminPage']); ?>'>
- <thead>
- <tr>
- <th><?php echo $l->t('SSL root certificates'); ?></th>
- <th>&nbsp;</th>
- </tr>
- </thead>
+ <legend><strong><?php p($l->t('SSL root certificates'));?></strong></legend>
+ <table id="sslCertificate" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'>
<tbody width="100%">
<?php foreach ($_['certs'] as $rootCert): ?>
- <tr id="<?php echo $rootCert ?>">
- <td class="rootCert"><?php echo $rootCert ?></td>
- <td <?php echo ($rootCert != '') ? 'class="remove"' : 'style="visibility:hidden;"'; ?>><img alt="<?php echo $l->t('Delete'); ?>" title="<?php echo $l->t('Delete'); ?>" class="svg action" src="<?php echo image_path('core', 'actions/delete.svg'); ?>" /></td>
+ <tr id="<?php p($rootCert) ?>">
+ <td class="rootCert"><?php p($rootCert) ?></td>
+ <td <?php if ($rootCert != ''): ?>class="remove"
+ <?php else: ?>style="visibility:hidden;"
+ <?php endif; ?>><img alt="<?php p($l->t('Delete')); ?>"
+ title="<?php p($l->t('Delete')); ?>"
+ class="svg action"
+ src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" /></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
- <input type="file" id="rootcert_import" name="rootcert_import">
- <input type="submit" name="cert_import" value="<?php echo $l->t('Import Root Certificate'); ?>" />
- <?php endif; ?>
+ <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>">
+ <input type="file" id="rootcert_import" name="rootcert_import">
+ <input type="submit" name="cert_import" value="<?php p($l->t('Import Root Certificate')); ?>" />
</fieldset>
</form>
+<?php endif; ?>
diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php
index 725f4ba05da..6b3a942b5ba 100644
--- a/apps/files_external/tests/amazons3.php
+++ b/apps/files_external/tests/amazons3.php
@@ -20,7 +20,9 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
-class Test_Filestorage_AmazonS3 extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class AmazonS3 extends Storage {
private $config;
private $id;
@@ -28,16 +30,17 @@ class Test_Filestorage_AmazonS3 extends Test_FileStorage {
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['amazons3']) or !$this->config['amazons3']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['amazons3']) or ! $this->config['amazons3']['run']) {
$this->markTestSkipped('AmazonS3 backend not configured');
}
$this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in
- $this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']);
+ $this->instance = new \OC\Files\Storage\AmazonS3($this->config['amazons3']);
}
public function tearDown() {
if ($this->instance) {
- $s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret']));
+ $s3 = new \AmazonS3(array('key' => $this->config['amazons3']['key'],
+ 'secret' => $this->config['amazons3']['secret']));
if ($s3->delete_all_objects($this->id)) {
$s3->delete_bucket($this->id);
}
diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php
index ff16b1c1d8a..1d4f30c713d 100644
--- a/apps/files_external/tests/config.php
+++ b/apps/files_external/tests/config.php
@@ -51,5 +51,12 @@ return array(
'app_secret' => '',
'token' => '',
'token_secret' => ''
+ ),
+ 'sftp' => array (
+ 'run'=>false,
+ 'host'=>'localhost',
+ 'user'=>'test',
+ 'password'=>'test',
+ 'root'=>'/test'
)
);
diff --git a/apps/files_external/tests/dropbox.php b/apps/files_external/tests/dropbox.php
index 56319b9f5d7..e4e598b06b0 100644
--- a/apps/files_external/tests/dropbox.php
+++ b/apps/files_external/tests/dropbox.php
@@ -6,17 +6,19 @@
* See the COPYING-README file.
*/
-class Test_Filestorage_Dropbox extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class Dropbox extends Storage {
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['dropbox']) or !$this->config['dropbox']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['dropbox']) or ! $this->config['dropbox']['run']) {
$this->markTestSkipped('Dropbox backend not configured');
}
$this->config['dropbox']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_Dropbox($this->config['dropbox']);
+ $this->instance = new \OC\Files\Storage\Dropbox($this->config['dropbox']);
}
public function tearDown() {
diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php
index 4549c420410..923b5e39681 100644
--- a/apps/files_external/tests/ftp.php
+++ b/apps/files_external/tests/ftp.php
@@ -6,22 +6,46 @@
* See the COPYING-README file.
*/
-class Test_Filestorage_FTP extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class FTP extends Storage {
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['ftp']) or !$this->config['ftp']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['ftp']) or ! $this->config['ftp']['run']) {
$this->markTestSkipped('FTP backend not configured');
}
$this->config['ftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_FTP($this->config['ftp']);
+ $this->instance = new \OC\Files\Storage\FTP($this->config['ftp']);
}
public function tearDown() {
if ($this->instance) {
- OCP\Files::rmdirr($this->instance->constructUrl(''));
+ \OCP\Files::rmdirr($this->instance->constructUrl(''));
}
}
+
+ public function testConstructUrl(){
+ $config = array ( 'host' => 'localhost',
+ 'user' => 'ftp',
+ 'password' => 'ftp',
+ 'root' => '/',
+ 'secure' => false );
+ $instance = new OC_Filestorage_FTP($config);
+ $this->assertEquals('ftp://ftp:ftp@localhost/', $instance->constructUrl(''));
+
+ $config['secure'] = true;
+ $instance = new OC_Filestorage_FTP($config);
+ $this->assertEquals('ftps://ftp:ftp@localhost/', $instance->constructUrl(''));
+
+ $config['secure'] = 'false';
+ $instance = new OC_Filestorage_FTP($config);
+ $this->assertEquals('ftp://ftp:ftp@localhost/', $instance->constructUrl(''));
+
+ $config['secure'] = 'true';
+ $instance = new OC_Filestorage_FTP($config);
+ $this->assertEquals('ftps://ftp:ftp@localhost/', $instance->constructUrl(''));
+ }
}
diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php
index 46e622cc180..f344163a8b9 100644
--- a/apps/files_external/tests/google.php
+++ b/apps/files_external/tests/google.php
@@ -20,18 +20,19 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
-class Test_Filestorage_Google extends Test_FileStorage {
+namespace Test\Files\Storage;
+class Google extends Storage {
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['google']) or !$this->config['google']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['google']) or ! $this->config['google']['run']) {
$this->markTestSkipped('Google backend not configured');
}
$this->config['google']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_Google($this->config['google']);
+ $this->instance = new \OC\Files\Storage\Google($this->config['google']);
}
public function tearDown() {
diff --git a/apps/files_external/tests/sftp.php b/apps/files_external/tests/sftp.php
new file mode 100644
index 00000000000..16964e20878
--- /dev/null
+++ b/apps/files_external/tests/sftp.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Henrik Kjölhede
+ * @copyright 2013 Henrik Kjölhede hkjolhede@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Test\Files\Storage;
+
+class SFTP extends Storage {
+ private $config;
+
+ public function setUp() {
+ $id = uniqid();
+ $this->config = include('files_external/tests/config.php');
+ if ( ! is_array($this->config) or ! isset($this->config['sftp']) or ! $this->config['sftp']['run']) {
+ $this->markTestSkipped('SFTP backend not configured');
+ }
+ $this->config['sftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
+ $this->instance = new \OC\Files\Storage\SFTP($this->config['sftp']);
+ }
+
+ public function tearDown() {
+ if ($this->instance) {
+ $this->instance->rmdir('/');
+ }
+ }
+} \ No newline at end of file
diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php
index 2c03ef5dbd0..be3ea5a8308 100644
--- a/apps/files_external/tests/smb.php
+++ b/apps/files_external/tests/smb.php
@@ -6,22 +6,25 @@
* See the COPYING-README file.
*/
-class Test_Filestorage_SMB extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class SMB extends Storage {
+
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['smb']) or !$this->config['smb']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['smb']) or ! $this->config['smb']['run']) {
$this->markTestSkipped('Samba backend not configured');
}
$this->config['smb']['root'] .= $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_SMB($this->config['smb']);
+ $this->instance = new \OC\Files\Storage\SMB($this->config['smb']);
}
public function tearDown() {
if ($this->instance) {
- OCP\Files::rmdirr($this->instance->constructUrl(''));
+ \OCP\Files::rmdirr($this->instance->constructUrl(''));
}
}
}
diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php
index 8cf2a3abc76..5c782840246 100644
--- a/apps/files_external/tests/swift.php
+++ b/apps/files_external/tests/swift.php
@@ -6,17 +6,19 @@
* See the COPYING-README file.
*/
-class Test_Filestorage_SWIFT extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class SWIFT extends Storage {
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['swift']) or !$this->config['swift']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['swift']) or ! $this->config['swift']['run']) {
$this->markTestSkipped('OpenStack SWIFT backend not configured');
}
$this->config['swift']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_SWIFT($this->config['swift']);
+ $this->instance = new \OC\Files\Storage\SWIFT($this->config['swift']);
}
diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php
index 2da88f63edd..1702898045e 100644
--- a/apps/files_external/tests/webdav.php
+++ b/apps/files_external/tests/webdav.php
@@ -6,17 +6,20 @@
* See the COPYING-README file.
*/
-class Test_Filestorage_DAV extends Test_FileStorage {
+namespace Test\Files\Storage;
+
+class DAV extends Storage {
+
private $config;
public function setUp() {
$id = uniqid();
$this->config = include('files_external/tests/config.php');
- if (!is_array($this->config) or !isset($this->config['webdav']) or !$this->config['webdav']['run']) {
+ if ( ! is_array($this->config) or ! isset($this->config['webdav']) or ! $this->config['webdav']['run']) {
$this->markTestSkipped('WebDAV backend not configured');
}
$this->config['webdav']['root'] .= '/' . $id; //make sure we have an new empty folder to work in
- $this->instance = new OC_Filestorage_DAV($this->config['webdav']);
+ $this->instance = new \OC\Files\Storage\DAV($this->config['webdav']);
}
public function tearDown() {
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 109f86b2e87..9363a5431fa 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -1,9 +1,18 @@
<?php
-OC::$CLASSPATH['OC_Share_Backend_File'] = "apps/files_sharing/lib/share/file.php";
-OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder.php';
-OC::$CLASSPATH['OC_Filestorage_Shared'] = "apps/files_sharing/lib/sharedstorage.php";
-OCP\Util::connectHook('OC_Filesystem', 'setup', 'OC_Filestorage_Shared', 'setup');
+OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php';
+OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php';
+OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.php';
+OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php';
+OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php';
+OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php';
+OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php';
+OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
OCP\Util::addScript('files_sharing', 'share');
+\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Shared_Updater', 'writeHook');
+\OC_Hook::connect('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook');
+\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
+\OC_Hook::connect('OCP\Share', 'post_shared', '\OC\Files\Cache\Shared_Updater', 'shareHook');
+\OC_Hook::connect('OCP\Share', 'pre_unshare', '\OC\Files\Cache\Shared_Updater', 'shareHook'); \ No newline at end of file
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml
index a44d0338bb6..9a199281a76 100644
--- a/apps/files_sharing/appinfo/info.xml
+++ b/apps/files_sharing/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>File sharing between users</description>
<licence>AGPL</licence>
<author>Michael Gapczynski</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
<default_enable/>
<types>
diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php
index 23f2afea7e1..48e41e93048 100644
--- a/apps/files_sharing/appinfo/update.php
+++ b/apps/files_sharing/appinfo/update.php
@@ -9,21 +9,23 @@ if (version_compare($installedVersion, '0.3', '<')) {
OC_User::useBackend(new OC_User_Database());
OC_Group::useBackend(new OC_Group_Database());
OC_App::loadApps(array('authentication'));
+ $rootView = new \OC\Files\View('');
while ($row = $result->fetchRow()) {
- $itemSource = OC_FileCache::getId($row['source'], '');
+ $meta = $rootView->getFileInfo($$row['source']);
+ $itemSource = $meta['fileid'];
if ($itemSource != -1) {
- $file = OC_FileCache::get($row['source'], '');
+ $file = $meta;
if ($file['mimetype'] == 'httpd/unix-directory') {
$itemType = 'folder';
} else {
$itemType = 'file';
}
if ($row['permissions'] == 0) {
- $permissions = OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE;
+ $permissions = OCP\PERMISSION_READ | OCP\PERMISSION_SHARE;
} else {
- $permissions = OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_SHARE;
+ $permissions = OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_SHARE;
if ($itemType == 'folder') {
- $permissions |= OCP\Share::PERMISSION_CREATE;
+ $permissions |= OCP\PERMISSION_CREATE;
}
}
$pos = strrpos($row['uid_shared_with'], '@');
@@ -50,11 +52,15 @@ if (version_compare($installedVersion, '0.3', '<')) {
}
catch (Exception $e) {
$update_error = true;
- OCP\Util::writeLog('files_sharing', 'Upgrade Routine: Skipping sharing "'.$row['source'].'" to "'.$shareWith.'" (error is "'.$e->getMessage().'")', OCP\Util::WARN);
+ OCP\Util::writeLog('files_sharing',
+ 'Upgrade Routine: Skipping sharing "'.$row['source'].'" to "'.$shareWith
+ .'" (error is "'.$e->getMessage().'")',
+ OCP\Util::WARN);
}
OC_Util::tearDownFS();
}
}
+ OC_User::setUserId(null);
if ($update_error) {
OCP\Util::writeLog('files_sharing', 'There were some problems upgrading the sharing of files', OCP\Util::ERROR);
}
@@ -67,6 +73,6 @@ if (version_compare($installedVersion, '0.3.3', '<')) {
OC_App::loadApps(array('authentication'));
$users = OC_User::getUsers();
foreach ($users as $user) {
- OC_FileCache::delete('Shared', '/'.$user.'/files/');
+// OC_FileCache::delete('Shared', '/'.$user.'/files/');
}
-} \ No newline at end of file
+}
diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css
index fb4794e0474..13298f113f8 100644
--- a/apps/files_sharing/css/public.css
+++ b/apps/files_sharing/css/public.css
@@ -1,11 +1,75 @@
-body { background:#ddd; }
-#header { position:fixed; top:0; left:0; right:0; z-index:100; height:2.5em; line-height:2.5em; padding:.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; }
-#details { color:#fff; }
-#header #download { margin-left:2em; font-weight:bold; }
-#header #download img { padding-left: 0.1em; padding-right: 0.3em; vertical-align: text-bottom; }
-#preview { min-height:30em; margin:50px auto; padding-top:2em; border-bottom:1px solid #f8f8f8; background:#eee; text-align:center; }
-#noPreview { display:none; padding-top:5em; }
-p.info { width:22em; text-align: center; margin:2em auto; color:#777; text-shadow:#fff 0 1px 0; }
-p.info a { font-weight:bold; color:#777; }
-#imgframe { width:80%; height: 75%; margin: 0 auto; padding-bottom:2em; }
-#imgframe img { max-height:100%; max-width: 100%; } \ No newline at end of file
+body {
+ background:#ddd;
+}
+
+#header {
+ background:#1d2d44;
+ box-shadow:0 0 10px rgba(0,0,0,.5), inset 0 -2px 10px #222;
+ height:2.5em;
+ left:0;
+ line-height:2.5em;
+ position:fixed;
+ right:0;
+ top:0;
+ z-index:100;
+ padding:.5em;
+}
+
+#details {
+ color:#fff;
+}
+
+#header #download {
+ font-weight:700;
+ margin-left:2em;
+}
+
+#header #download img {
+ padding-left:.1em;
+ padding-right:.3em;
+ vertical-align:text-bottom;
+}
+
+#preview {
+ background:#eee;
+ border-bottom:1px solid #f8f8f8;
+ min-height:30em;
+ text-align:center;
+ margin:45px auto;
+}
+
+#noPreview {
+ display:none;
+ padding-top:5em;
+}
+
+p.info {
+ color:#777;
+ text-align:center;
+ text-shadow:#fff 0 1px 0;
+ width:22em;
+ margin:2em auto;
+}
+
+p.info a {
+ color:#777;
+ font-weight:700;
+}
+
+#imgframe {
+ height:75%;
+ padding-bottom:2em;
+ padding-top:2em;
+ width:80%;
+ margin:0 auto;
+}
+
+#imgframe img {
+ max-height:100%;
+ max-width:100%;
+}
+
+thead{
+ background-color: white;
+ padding-left:0 !important; /* fixes multiselect bar offset on shared page */
+}
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 72663c068c9..eb5a6e8cb7f 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -1,36 +1,10 @@
$(document).ready(function() {
- if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !publicListView) {
- OC.Share.loadIcons('file');
- FileActions.register('all', 'Share', OC.PERMISSION_READ, function(filename) {
- // Return the correct sharing icon
- if (scanFiles.scanning) { return; } // workaround to prevent additional http request block scanning feedback
- if ($('#dir').val() == '/') {
- var item = $('#dir').val() + filename;
- } else {
- var item = $('#dir').val() + '/' + filename;
- }
- // Check if status is in cache
- if (OC.Share.statuses[item] === true) {
- return OC.imagePath('core', 'actions/public');
- } else if (OC.Share.statuses[item] === false) {
- return OC.imagePath('core', 'actions/shared');
- } else {
- var last = '';
- var path = OC.Share.dirname(item);
- // Search for possible parent folders that are shared
- while (path != last) {
- if (OC.Share.statuses[path] === true) {
- return OC.imagePath('core', 'actions/public');
- } else if (OC.Share.statuses[path] === false) {
- return OC.imagePath('core', 'actions/shared');
- }
- last = path;
- path = OC.Share.dirname(path);
- }
- return OC.imagePath('core', 'actions/share');
- }
- }, function(filename) {
+ var disableSharing = $('#disableSharing').data('status');
+
+ if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) {
+
+ FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) {
if ($('#dir').val() == '/') {
var item = $('#dir').val() + filename;
} else {
@@ -59,6 +33,6 @@ $(document).ready(function() {
OC.Share.showDropDown(itemType, $(tr).data('id'), appendTo, true, possiblePermissions);
}
});
+ OC.Share.loadIcons('file');
}
-
-}); \ No newline at end of file
+});
diff --git a/apps/files_sharing/l10n/af_ZA.php b/apps/files_sharing/l10n/af_ZA.php
new file mode 100644
index 00000000000..344585a62fc
--- /dev/null
+++ b/apps/files_sharing/l10n/af_ZA.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Wagwoord",
+"web services under your control" => "webdienste onder jou beheer"
+);
diff --git a/apps/files_sharing/l10n/ar.php b/apps/files_sharing/l10n/ar.php
new file mode 100644
index 00000000000..4cf3f8c0920
--- /dev/null
+++ b/apps/files_sharing/l10n/ar.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "كلمة المرور",
+"Submit" => "تطبيق",
+"%s shared the folder %s with you" => "%s شارك المجلد %s معك",
+"%s shared the file %s with you" => "%s شارك الملف %s معك",
+"Download" => "تحميل",
+"No preview available for" => "لا يوجد عرض مسبق لـ",
+"web services under your control" => "خدمات الشبكة تحت سيطرتك"
+);
diff --git a/apps/files_sharing/l10n/bg_BG.php b/apps/files_sharing/l10n/bg_BG.php
new file mode 100644
index 00000000000..ac94358c4f9
--- /dev/null
+++ b/apps/files_sharing/l10n/bg_BG.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Парола",
+"Submit" => "Потвърждение",
+"%s shared the folder %s with you" => "%s сподели папката %s с Вас",
+"%s shared the file %s with you" => "%s сподели файла %s с Вас",
+"Download" => "Изтегляне",
+"No preview available for" => "Няма наличен преглед за",
+"web services under your control" => "уеб услуги под Ваш контрол"
+);
diff --git a/apps/files_sharing/l10n/bn_BD.php b/apps/files_sharing/l10n/bn_BD.php
new file mode 100644
index 00000000000..c3af434ee29
--- /dev/null
+++ b/apps/files_sharing/l10n/bn_BD.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "কূটশব্দ",
+"Submit" => "জমা দাও",
+"%s shared the folder %s with you" => "%s আপনার সাথে %s ফোল্ডারটি ভাগাভাগি করেছেন",
+"%s shared the file %s with you" => "%s আপনার সাথে %s ফাইলটি ভাগাভাগি করেছেন",
+"Download" => "ডাউনলোড",
+"No preview available for" => "এর জন্য কোন প্রাকবীক্ষণ সুলভ নয়",
+"web services under your control" => "ওয়েব সার্ভিস আপনার হাতের মুঠোয়"
+);
diff --git a/apps/files_sharing/l10n/de.php b/apps/files_sharing/l10n/de.php
index c2fec215451..7f4cbb1adad 100644
--- a/apps/files_sharing/l10n/de.php
+++ b/apps/files_sharing/l10n/de.php
@@ -1,8 +1,8 @@
<?php $TRANSLATIONS = array(
"Password" => "Passwort",
"Submit" => "Absenden",
-"%s shared the folder %s with you" => "%s hat den Ordner %s für dich freigegeben",
-"%s shared the file %s with you" => "%s hat die Datei %s für dich freigegeben",
+"%s shared the folder %s with you" => "%s hat den Ordner %s mit Dir geteilt",
+"%s shared the file %s with you" => "%s hat die Datei %s mit Dir geteilt",
"Download" => "Download",
"No preview available for" => "Es ist keine Vorschau verfügbar für",
"web services under your control" => "Web-Services unter Deiner Kontrolle"
diff --git a/apps/files_sharing/l10n/de_DE.php b/apps/files_sharing/l10n/de_DE.php
new file mode 100644
index 00000000000..b92d6d478c9
--- /dev/null
+++ b/apps/files_sharing/l10n/de_DE.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Passwort",
+"Submit" => "Absenden",
+"%s shared the folder %s with you" => "%s hat den Ordner %s mit Ihnen geteilt",
+"%s shared the file %s with you" => "%s hat die Datei %s mit Ihnen geteilt",
+"Download" => "Download",
+"No preview available for" => "Es ist keine Vorschau verfügbar für",
+"web services under your control" => "Web-Services unter Ihrer Kontrolle"
+);
diff --git a/apps/files_sharing/l10n/et_EE.php b/apps/files_sharing/l10n/et_EE.php
index 94b9b1d7aef..36290ad2787 100644
--- a/apps/files_sharing/l10n/et_EE.php
+++ b/apps/files_sharing/l10n/et_EE.php
@@ -1,6 +1,8 @@
<?php $TRANSLATIONS = array(
"Password" => "Parool",
"Submit" => "Saada",
+"%s shared the folder %s with you" => "%s jagas sinuga kausta %s",
+"%s shared the file %s with you" => "%s jagas sinuga faili %s",
"Download" => "Lae alla",
"No preview available for" => "Eelvaadet pole saadaval",
"web services under your control" => "veebitenused sinu kontrolli all"
diff --git a/apps/files_sharing/l10n/fa.php b/apps/files_sharing/l10n/fa.php
index 06e1862e8b3..4313acae1ad 100644
--- a/apps/files_sharing/l10n/fa.php
+++ b/apps/files_sharing/l10n/fa.php
@@ -1,6 +1,9 @@
<?php $TRANSLATIONS = array(
-"Size" => "اندازه",
-"Modified" => "تاریخ",
-"Delete all" => "حذف همه",
-"Delete" => "حذف"
+"Password" => "گذرواژه",
+"Submit" => "ثبت",
+"%s shared the folder %s with you" => "%sپوشه %s را با شما به اشتراک گذاشت",
+"%s shared the file %s with you" => "%sفایل %s را با شما به اشتراک گذاشت",
+"Download" => "دانلود",
+"No preview available for" => "هیچگونه پیش نمایشی موجود نیست",
+"web services under your control" => "سرویس های تحت وب در کنترل شما"
);
diff --git a/apps/files_sharing/l10n/gl.php b/apps/files_sharing/l10n/gl.php
index c9644d720e3..d03f1a5005f 100644
--- a/apps/files_sharing/l10n/gl.php
+++ b/apps/files_sharing/l10n/gl.php
@@ -1,7 +1,9 @@
<?php $TRANSLATIONS = array(
"Password" => "Contrasinal",
"Submit" => "Enviar",
-"Download" => "Baixar",
-"No preview available for" => "Sen vista previa dispoñible para ",
+"%s shared the folder %s with you" => "%s compartiu o cartafol %s con vostede",
+"%s shared the file %s with you" => "%s compartiu o ficheiro %s con vostede",
+"Download" => "Descargar",
+"No preview available for" => "Sen vista previa dispoñíbel para",
"web services under your control" => "servizos web baixo o seu control"
);
diff --git a/apps/files_sharing/l10n/hu_HU.php b/apps/files_sharing/l10n/hu_HU.php
index 881b5afd817..f8ca541260d 100644
--- a/apps/files_sharing/l10n/hu_HU.php
+++ b/apps/files_sharing/l10n/hu_HU.php
@@ -1,6 +1,9 @@
<?php $TRANSLATIONS = array(
-"Size" => "Méret",
-"Modified" => "Módosítva",
-"Delete all" => "Összes törlése",
-"Delete" => "Törlés"
+"Password" => "Jelszó",
+"Submit" => "Elküld",
+"%s shared the folder %s with you" => "%s megosztotta Önnel ezt a mappát: %s",
+"%s shared the file %s with you" => "%s megosztotta Önnel ezt az állományt: %s",
+"Download" => "Letöltés",
+"No preview available for" => "Nem áll rendelkezésre előnézet ehhez: ",
+"web services under your control" => "webszolgáltatások saját kézben"
);
diff --git a/apps/files_sharing/l10n/id.php b/apps/files_sharing/l10n/id.php
new file mode 100644
index 00000000000..8897269d989
--- /dev/null
+++ b/apps/files_sharing/l10n/id.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "kata kunci",
+"Submit" => "kirim",
+"%s shared the folder %s with you" => "%s membagikan folder %s dengan anda",
+"%s shared the file %s with you" => "%s membagikan file %s dengan anda",
+"Download" => "unduh",
+"No preview available for" => "tidak ada pratinjau tersedia untuk",
+"web services under your control" => "servis web dibawah kendali anda"
+);
diff --git a/apps/files_sharing/l10n/is.php b/apps/files_sharing/l10n/is.php
new file mode 100644
index 00000000000..bf1975c54ae
--- /dev/null
+++ b/apps/files_sharing/l10n/is.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Lykilorð",
+"Submit" => "Senda",
+"%s shared the folder %s with you" => "%s deildi möppunni %s með þér",
+"%s shared the file %s with you" => "%s deildi skránni %s með þér",
+"Download" => "Niðurhal",
+"No preview available for" => "Yfirlit ekki í boði fyrir",
+"web services under your control" => "vefþjónusta undir þinni stjórn"
+);
diff --git a/apps/files_sharing/l10n/ja_JP.php b/apps/files_sharing/l10n/ja_JP.php
index 058e4f2cd7e..02142e2879a 100644
--- a/apps/files_sharing/l10n/ja_JP.php
+++ b/apps/files_sharing/l10n/ja_JP.php
@@ -1,8 +1,8 @@
<?php $TRANSLATIONS = array(
"Password" => "パスワード",
"Submit" => "送信",
-"%s shared the folder %s with you" => "%s はフォルダー %s をあなたと共有",
-"%s shared the file %s with you" => "%s はファイル %s をあなたと共有",
+"%s shared the folder %s with you" => "%s はフォルダー %s をあなたと共有中です",
+"%s shared the file %s with you" => "%s はファイル %s をあなたと共有中です",
"Download" => "ダウンロード",
"No preview available for" => "プレビューはありません",
"web services under your control" => "管理下のウェブサービス"
diff --git a/apps/files_sharing/l10n/ka.php b/apps/files_sharing/l10n/ka.php
new file mode 100644
index 00000000000..0270d5d6f8c
--- /dev/null
+++ b/apps/files_sharing/l10n/ka.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "პაროლი",
+"Download" => "გადმოწერა"
+);
diff --git a/apps/files_sharing/l10n/ka_GE.php b/apps/files_sharing/l10n/ka_GE.php
new file mode 100644
index 00000000000..ef42196d2cb
--- /dev/null
+++ b/apps/files_sharing/l10n/ka_GE.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"Password" => "პაროლი",
+"Submit" => "გაგზავნა",
+"Download" => "ჩამოტვირთვა",
+"web services under your control" => "web services under your control"
+);
diff --git a/apps/files_sharing/l10n/ko.php b/apps/files_sharing/l10n/ko.php
new file mode 100644
index 00000000000..600168d9bfa
--- /dev/null
+++ b/apps/files_sharing/l10n/ko.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "암호",
+"Submit" => "제출",
+"%s shared the folder %s with you" => "%s 님이 폴더 %s을(를) 공유하였습니다",
+"%s shared the file %s with you" => "%s 님이 파일 %s을(를) 공유하였습니다",
+"Download" => "다운로드",
+"No preview available for" => "다음 항목을 미리 볼 수 없음:",
+"web services under your control" => "내가 관리하는 웹 서비스"
+);
diff --git a/apps/files_sharing/l10n/lb.php b/apps/files_sharing/l10n/lb.php
new file mode 100644
index 00000000000..8aba5806aa0
--- /dev/null
+++ b/apps/files_sharing/l10n/lb.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Passwuert"
+);
diff --git a/apps/files_sharing/l10n/lv.php b/apps/files_sharing/l10n/lv.php
new file mode 100644
index 00000000000..0b224867089
--- /dev/null
+++ b/apps/files_sharing/l10n/lv.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Parole",
+"Submit" => "Iesniegt",
+"%s shared the folder %s with you" => "%s ar jums dalījās ar mapi %s",
+"%s shared the file %s with you" => "%s ar jums dalījās ar datni %s",
+"Download" => "Lejupielādēt",
+"No preview available for" => "Nav pieejams priekšskatījums priekš",
+"web services under your control" => "jūsu vadībā esošie tīmekļa servisi"
+);
diff --git a/apps/files_sharing/l10n/mk.php b/apps/files_sharing/l10n/mk.php
new file mode 100644
index 00000000000..16c7ee0eb04
--- /dev/null
+++ b/apps/files_sharing/l10n/mk.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Лозинка",
+"Submit" => "Прати",
+"%s shared the folder %s with you" => "%s ја сподели папката %s со Вас",
+"%s shared the file %s with you" => "%s ја сподели датотеката %s со Вас",
+"Download" => "Преземи",
+"No preview available for" => "Нема достапно преглед за",
+"web services under your control" => "веб сервиси под Ваша контрола"
+);
diff --git a/apps/files_sharing/l10n/my_MM.php b/apps/files_sharing/l10n/my_MM.php
new file mode 100644
index 00000000000..dc7ec17e9c5
--- /dev/null
+++ b/apps/files_sharing/l10n/my_MM.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"Password" => "စကားဝှက်",
+"Submit" => "ထည့်သွင်းမည်",
+"Download" => "ဒေါင်းလုတ်",
+"web services under your control" => "သင်၏ထိန်းချုပ်မှု့အောက်တွင်ရှိသော Web services"
+);
diff --git a/apps/files_sharing/l10n/nb_NO.php b/apps/files_sharing/l10n/nb_NO.php
index 6102b03db74..4934c341067 100644
--- a/apps/files_sharing/l10n/nb_NO.php
+++ b/apps/files_sharing/l10n/nb_NO.php
@@ -1,6 +1,9 @@
<?php $TRANSLATIONS = array(
-"Size" => "Størrelse",
-"Modified" => "Endret",
-"Delete all" => "Slett alle",
-"Delete" => "Slett"
+"Password" => "Passord",
+"Submit" => "Send inn",
+"%s shared the folder %s with you" => "%s delte mappen %s med deg",
+"%s shared the file %s with you" => "%s delte filen %s med deg",
+"Download" => "Last ned",
+"No preview available for" => "Forhåndsvisning ikke tilgjengelig for",
+"web services under your control" => "web tjenester du kontrollerer"
);
diff --git a/apps/files_sharing/l10n/ru.php b/apps/files_sharing/l10n/ru.php
index 398d9a8bbc3..7fd116e0aae 100644
--- a/apps/files_sharing/l10n/ru.php
+++ b/apps/files_sharing/l10n/ru.php
@@ -1,6 +1,8 @@
<?php $TRANSLATIONS = array(
"Password" => "Пароль",
"Submit" => "Отправить",
+"%s shared the folder %s with you" => "%s открыл доступ к папке %s для Вас",
+"%s shared the file %s with you" => "%s открыл доступ к файлу %s для Вас",
"Download" => "Скачать",
"No preview available for" => "Предпросмотр недоступен для",
"web services under your control" => "веб-сервисы под вашим управлением"
diff --git a/apps/files_sharing/l10n/si_LK.php b/apps/files_sharing/l10n/si_LK.php
new file mode 100644
index 00000000000..1c69c608178
--- /dev/null
+++ b/apps/files_sharing/l10n/si_LK.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "මුරපදය",
+"Submit" => "යොමු කරන්න",
+"%s shared the folder %s with you" => "%s ඔබව %s ෆෝල්ඩරයට හවුල් කරගත්තේය",
+"%s shared the file %s with you" => "%s ඔබ සමඟ %s ගොනුව බෙදාහදාගත්තේය",
+"Download" => "භාගත කරන්න",
+"No preview available for" => "පූර්වදර්ශනයක් නොමැත",
+"web services under your control" => "ඔබට පාලනය කළ හැකි වෙබ් සේවාවන්"
+);
diff --git a/apps/files_sharing/l10n/sl.php b/apps/files_sharing/l10n/sl.php
index 6d5556ea4b0..6bcbb0070b3 100644
--- a/apps/files_sharing/l10n/sl.php
+++ b/apps/files_sharing/l10n/sl.php
@@ -1,9 +1,9 @@
<?php $TRANSLATIONS = array(
"Password" => "Geslo",
"Submit" => "Pošlji",
-"%s shared the folder %s with you" => "%s je dal v souporabo z vami mapo %s",
-"%s shared the file %s with you" => "%s je dal v souporabo z vami datoteko %s",
-"Download" => "Prenesi",
+"%s shared the folder %s with you" => "Oseba %s je določila mapo %s za souporabo",
+"%s shared the file %s with you" => "Oseba %s je določila datoteko %s za souporabo",
+"Download" => "Prejmi",
"No preview available for" => "Predogled ni na voljo za",
"web services under your control" => "spletne storitve pod vašim nadzorom"
);
diff --git a/apps/files_sharing/l10n/sr.php b/apps/files_sharing/l10n/sr.php
new file mode 100644
index 00000000000..6e277f67711
--- /dev/null
+++ b/apps/files_sharing/l10n/sr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Лозинка",
+"Submit" => "Пошаљи",
+"Download" => "Преузми"
+);
diff --git a/apps/files_sharing/l10n/ta_LK.php b/apps/files_sharing/l10n/ta_LK.php
new file mode 100644
index 00000000000..6cf6f6236b7
--- /dev/null
+++ b/apps/files_sharing/l10n/ta_LK.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "கடவுச்சொல்",
+"Submit" => "சமர்ப்பிக்குக",
+"%s shared the folder %s with you" => "%s கோப்புறையானது %s உடன் பகிரப்பட்டது",
+"%s shared the file %s with you" => "%s கோப்பானது %s உடன் பகிரப்பட்டது",
+"Download" => "பதிவிறக்குக",
+"No preview available for" => "அதற்கு முன்னோக்கு ஒன்றும் இல்லை",
+"web services under your control" => "வலைய சேவைகள் உங்களுடைய கட்டுப்பாட்டின் கீழ் உள்ளது"
+);
diff --git a/apps/files_sharing/l10n/tr.php b/apps/files_sharing/l10n/tr.php
new file mode 100644
index 00000000000..f2e6e5697d6
--- /dev/null
+++ b/apps/files_sharing/l10n/tr.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Şifre",
+"Submit" => "Gönder",
+"%s shared the folder %s with you" => "%s sizinle paylaşılan %s klasör",
+"%s shared the file %s with you" => "%s sizinle paylaşılan %s klasör",
+"Download" => "İndir",
+"No preview available for" => "Kullanılabilir önizleme yok",
+"web services under your control" => "Bilgileriniz güvenli ve şifreli"
+);
diff --git a/apps/files_sharing/l10n/uk.php b/apps/files_sharing/l10n/uk.php
index 43b86a28c17..cdc103ad465 100644
--- a/apps/files_sharing/l10n/uk.php
+++ b/apps/files_sharing/l10n/uk.php
@@ -1,4 +1,9 @@
<?php $TRANSLATIONS = array(
"Password" => "Пароль",
-"Download" => "Завантажити"
+"Submit" => "Submit",
+"%s shared the folder %s with you" => "%s опублікував каталог %s для Вас",
+"%s shared the file %s with you" => "%s опублікував файл %s для Вас",
+"Download" => "Завантажити",
+"No preview available for" => "Попередній перегляд недоступний для",
+"web services under your control" => "підконтрольні Вам веб-сервіси"
);
diff --git a/apps/files_sharing/l10n/ur_PK.php b/apps/files_sharing/l10n/ur_PK.php
new file mode 100644
index 00000000000..f68b714350f
--- /dev/null
+++ b/apps/files_sharing/l10n/ur_PK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "پاسورڈ",
+"web services under your control" => "آپ کے اختیار میں ویب سروسیز"
+);
diff --git a/apps/files_sharing/l10n/vi.php b/apps/files_sharing/l10n/vi.php
index d4faee06bf2..afeec5c6481 100644
--- a/apps/files_sharing/l10n/vi.php
+++ b/apps/files_sharing/l10n/vi.php
@@ -1,6 +1,8 @@
<?php $TRANSLATIONS = array(
"Password" => "Mật khẩu",
"Submit" => "Xác nhận",
+"%s shared the folder %s with you" => "%s đã chia sẻ thư mục %s với bạn",
+"%s shared the file %s with you" => "%s đã chia sẻ tập tin %s với bạn",
"Download" => "Tải về",
"No preview available for" => "Không có xem trước cho",
"web services under your control" => "dịch vụ web dưới sự kiểm soát của bạn"
diff --git a/apps/files_sharing/l10n/zh_HK.php b/apps/files_sharing/l10n/zh_HK.php
new file mode 100644
index 00000000000..7ef0f19ca43
--- /dev/null
+++ b/apps/files_sharing/l10n/zh_HK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "密碼",
+"Download" => "下載"
+);
diff --git a/apps/files_sharing/l10n/zh_TW.php b/apps/files_sharing/l10n/zh_TW.php
index 27fa634c03d..f1d28731a7f 100644
--- a/apps/files_sharing/l10n/zh_TW.php
+++ b/apps/files_sharing/l10n/zh_TW.php
@@ -1,6 +1,9 @@
<?php $TRANSLATIONS = array(
"Password" => "密碼",
"Submit" => "送出",
+"%s shared the folder %s with you" => "%s 分享了資料夾 %s 給您",
+"%s shared the file %s with you" => "%s 分享了檔案 %s 給您",
"Download" => "下載",
-"No preview available for" => "無法預覽"
+"No preview available for" => "無法預覽",
+"web services under your control" => "在您掌控之下的網路服務"
);
diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php
new file mode 100644
index 00000000000..9fccd0b46f3
--- /dev/null
+++ b/apps/files_sharing/lib/cache.php
@@ -0,0 +1,272 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Files\Cache;
+
+/**
+ * Metadata cache for shared files
+ *
+ * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead
+ */
+class Shared_Cache extends Cache {
+
+ private $storage;
+ private $files = array();
+
+ public function __construct($storage) {
+ $this->storage = $storage;
+ }
+
+ /**
+ * @brief Get the source cache of a shared file or folder
+ * @param string $target Shared target file path
+ * @return \OC\Files\Cache\Cache
+ */
+ private function getSourceCache($target) {
+ $source = \OC_Share_Backend_File::getSource($target);
+ if (isset($source['path']) && isset($source['fileOwner'])) {
+ \OC\Files\Filesystem::initMountPoints($source['fileOwner']);
+ $mount = \OC\Files\Mount::findByNumericId($source['storage']);
+ if ($mount) {
+ $fullPath = $mount->getMountPoint().$source['path'];
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath);
+ if ($storage) {
+ $this->files[$target] = $internalPath;
+ $cache = $storage->getCache();
+ $this->storageId = $storage->getId();
+ $this->numericId = $cache->getNumericStorageId();
+ return $cache;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * get the stored metadata of a file or folder
+ *
+ * @param string/int $file
+ * @return array
+ */
+ public function get($file) {
+ if ($file == '') {
+ $data = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT);
+ $etag = \OCP\Config::getUserValue(\OCP\User::getUser(), 'files_sharing', 'etag');
+ if (!isset($etag)) {
+ $etag = $this->storage->getETag('');
+ \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $etag);
+ }
+ $data['etag'] = $etag;
+ return $data;
+ } else if (is_string($file)) {
+ if ($cache = $this->getSourceCache($file)) {
+ return $cache->get($this->files[$file]);
+ }
+ } else {
+ $query = \OC_DB::prepare(
+ 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,'
+ .' `size`, `mtime`, `encrypted`'
+ .' FROM `*PREFIX*filecache` WHERE `fileid` = ?');
+ $result = $query->execute(array($file));
+ $data = $result->fetchRow();
+ $data['fileid'] = (int)$data['fileid'];
+ $data['size'] = (int)$data['size'];
+ $data['mtime'] = (int)$data['mtime'];
+ $data['encrypted'] = (bool)$data['encrypted'];
+ $data['mimetype'] = $this->getMimetype($data['mimetype']);
+ $data['mimepart'] = $this->getMimetype($data['mimepart']);
+ return $data;
+ }
+ return false;
+ }
+
+ /**
+ * get the metadata of all files stored in $folder
+ *
+ * @param string $folder
+ * @return array
+ */
+ public function getFolderContents($folder) {
+ if ($folder == '') {
+ $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS);
+ foreach ($files as &$file) {
+ $file['mimetype'] = $this->getMimetype($file['mimetype']);
+ $file['mimepart'] = $this->getMimetype($file['mimepart']);
+ }
+ return $files;
+ } else {
+ if ($cache = $this->getSourceCache($folder)) {
+ return $cache->getFolderContents($this->files[$folder]);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * store meta data for a file or folder
+ *
+ * @param string $file
+ * @param array $data
+ *
+ * @return int file id
+ */
+ public function put($file, array $data) {
+ if ($file === '' && isset($data['etag'])) {
+ return \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $data['etag']);
+ } else if ($cache = $this->getSourceCache($file)) {
+ return $cache->put($this->files[$file], $data);
+ }
+ return false;
+ }
+
+ /**
+ * get the file id for a file
+ *
+ * @param string $file
+ * @return int
+ */
+ public function getId($file) {
+ if ($cache = $this->getSourceCache($file)) {
+ return $cache->getId($this->files[$file]);
+ }
+ return -1;
+ }
+
+ /**
+ * check if a file is available in the cache
+ *
+ * @param string $file
+ * @return bool
+ */
+ public function inCache($file) {
+ if ($file == '') {
+ return true;
+ }
+ return parent::inCache($file);
+ }
+
+ /**
+ * remove a file or folder from the cache
+ *
+ * @param string $file
+ */
+ public function remove($file) {
+ if ($cache = $this->getSourceCache($file)) {
+ $cache->remove($this->files[$file]);
+ }
+ }
+
+ /**
+ * Move a file or folder in the cache
+ *
+ * @param string $source
+ * @param string $target
+ */
+ public function move($source, $target) {
+ if ($cache = $this->getSourceCache($source)) {
+ $targetPath = \OC_Share_Backend_File::getSourcePath(dirname($target));
+ if ($targetPath) {
+ $targetPath .= '/' . basename($target);
+ $cache->move($this->files[$source], $targetPath);
+ }
+
+ }
+ }
+
+ /**
+ * remove all entries for files that are stored on the storage from the cache
+ */
+ public function clear() {
+ // Not a valid action for Shared Cache
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
+ */
+ public function getStatus($file) {
+ if ($file == '') {
+ return self::COMPLETE;
+ }
+ if ($cache = $this->getSourceCache($file)) {
+ return $cache->getStatus($this->files[$file]);
+ }
+ return self::NOT_FOUND;
+ }
+
+ /**
+ * search for files matching $pattern
+ *
+ * @param string $pattern
+ * @return array of file data
+ */
+ public function search($pattern) {
+ // TODO
+ }
+
+ /**
+ * search for files by mimetype
+ *
+ * @param string $part1
+ * @param string $part2
+ * @return array
+ */
+ public function searchByMime($mimetype) {
+ if (strpos($mimetype, '/')) {
+ $where = '`mimetype` = ?';
+ } else {
+ $where = '`mimepart` = ?';
+ }
+ $mimetype = $this->getMimetypeId($mimetype);
+ $ids = $this->getAll();
+ $placeholders = join(',', array_fill(0, count($ids), '?'));
+ $query = \OC_DB::prepare('
+ SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`
+ FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')'
+ );
+ $result = $query->execute(array_merge(array($mimetype), $ids));
+ return $result->fetchAll();
+ }
+
+ /**
+ * get the size of a folder and set it in the cache
+ *
+ * @param string $path
+ * @return int
+ */
+ public function calculateFolderSize($path) {
+ if ($cache = $this->getSourceCache($path)) {
+ return $cache->calculateFolderSize($this->files[$path]);
+ }
+ return 0;
+ }
+
+ /**
+ * get all file ids on the files on the storage
+ *
+ * @return int[]
+ */
+ public function getAll() {
+ return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL);
+ }
+
+}
diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php
new file mode 100644
index 00000000000..6747faa4d43
--- /dev/null
+++ b/apps/files_sharing/lib/permissions.php
@@ -0,0 +1,86 @@
+<?php
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+namespace OC\Files\Cache;
+
+class Shared_Permissions extends Permissions {
+
+ /**
+ * get the permissions for a single file
+ *
+ * @param int $fileId
+ * @param string $user
+ * @return int (-1 if file no permissions set)
+ */
+ public function get($fileId, $user) {
+ if ($fileId == -1) {
+ return \OCP\PERMISSION_READ;
+ }
+ $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE,
+ null, true);
+ if ($source) {
+ return $source['permissions'];
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * set the permissions of a file
+ *
+ * @param int $fileId
+ * @param string $user
+ * @param int $permissions
+ */
+ public function set($fileId, $user, $permissions) {
+ // Not a valid action for Shared Permissions
+ }
+
+ /**
+ * get the permissions of multiply files
+ *
+ * @param int[] $fileIds
+ * @param string $user
+ * @return int[]
+ */
+ public function getMultiple($fileIds, $user) {
+ if (count($fileIds) === 0) {
+ return array();
+ }
+ foreach ($fileIds as $fileId) {
+ $filePermissions[$fileId] = self::get($fileId, $user);
+ }
+ return $filePermissions;
+ }
+
+ /**
+ * remove the permissions for a file
+ *
+ * @param int $fileId
+ * @param string $user
+ */
+ public function remove($fileId, $user = null) {
+ // Not a valid action for Shared Permissions
+ }
+
+ public function removeMultiple($fileIds, $user) {
+ // Not a valid action for Shared Permissions
+ }
+}
diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php
index 9a880505926..62948651806 100644
--- a/apps/files_sharing/lib/share/file.php
+++ b/apps/files_sharing/lib/share/file.php
@@ -22,16 +22,18 @@
class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
const FORMAT_SHARED_STORAGE = 0;
- const FORMAT_FILE_APP = 1;
+ const FORMAT_GET_FOLDER_CONTENTS = 1;
const FORMAT_FILE_APP_ROOT = 2;
const FORMAT_OPENDIR = 3;
+ const FORMAT_GET_ALL = 4;
private $path;
public function isValidSource($itemSource, $uidOwner) {
- $path = OC_FileCache::getPath($itemSource, $uidOwner);
- if ($path) {
- $this->path = $path;
+ $query = \OC_DB::prepare('SELECT `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?');
+ $result = $query->execute(array($itemSource));
+ if ($row = $result->fetchRow()) {
+ $this->path = $row['name'];
return true;
}
return false;
@@ -70,37 +72,28 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
public function formatItems($items, $format, $parameters = null) {
if ($format == self::FORMAT_SHARED_STORAGE) {
// Only 1 item should come through for this format call
- return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']);
- } else if ($format == self::FORMAT_FILE_APP) {
- if (isset($parameters['mimetype_filter']) && $parameters['mimetype_filter']) {
- $mimetype_filter = $parameters['mimetype_filter'];
- }
+ return array(
+ 'parent' => $items[key($items)]['parent'],
+ 'path' => $items[key($items)]['path'],
+ 'storage' => $items[key($items)]['storage'],
+ 'permissions' => $items[key($items)]['permissions'],
+ 'uid_owner' => $items[key($items)]['uid_owner']
+ );
+ } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) {
$files = array();
foreach ($items as $item) {
- if (isset($mimetype_filter)
- && strpos($item['mimetype'], $mimetype_filter) !== 0
- && $item['mimetype'] != 'httpd/unix-directory') {
- continue;
- }
$file = array();
- $file['id'] = $item['file_source'];
+ $file['fileid'] = $item['file_source'];
+ $file['storage'] = $item['storage'];
$file['path'] = $item['file_target'];
+ $file['parent'] = $item['file_parent'];
$file['name'] = basename($item['file_target']);
- $file['ctime'] = $item['ctime'];
- $file['mtime'] = $item['mtime'];
$file['mimetype'] = $item['mimetype'];
+ $file['mimepart'] = $item['mimepart'];
$file['size'] = $item['size'];
+ $file['mtime'] = $item['mtime'];
$file['encrypted'] = $item['encrypted'];
- $file['versioned'] = $item['versioned'];
- $file['directory'] = $parameters['folder'];
- $file['type'] = ($item['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file';
- $file['permissions'] = $item['permissions'];
- if ($file['type'] == 'file') {
- // Remove Create permission if type is file
- $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE;
- }
- // NOTE: Temporary fix to allow unsharing of files in root of Shared directory
- $file['permissions'] |= OCP\Share::PERMISSION_DELETE;
+ $file['etag'] = $item['etag'];
$files[] = $file;
}
return $files;
@@ -111,17 +104,69 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
if ($item['mtime'] > $mtime) {
$mtime = $item['mtime'];
}
- $size += $item['size'];
+ $size += (int)$item['size'];
}
- return array(0 => array('id' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\Share::PERMISSION_READ));
+ return array(
+ 'fileid' => -1,
+ 'name' => 'Shared',
+ 'mtime' => $mtime,
+ 'mimetype' => 'httpd/unix-directory',
+ 'size' => $size
+ );
} else if ($format == self::FORMAT_OPENDIR) {
$files = array();
foreach ($items as $item) {
$files[] = basename($item['file_target']);
}
return $files;
+ } else if ($format == self::FORMAT_GET_ALL) {
+ $ids = array();
+ foreach ($items as $item) {
+ $ids[] = $item['file_source'];
+ }
+ return $ids;
}
return array();
}
+ public static function getSource($target) {
+ if ($target == '') {
+ return false;
+ }
+ $target = '/'.$target;
+ $target = rtrim($target, '/');
+ $pos = strpos($target, '/', 1);
+ // Get shared folder name
+ if ($pos !== false) {
+ $folder = substr($target, 0, $pos);
+ $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
+ if ($source) {
+ $source['path'] = $source['path'].substr($target, strlen($folder));
+ }
+ } else {
+ $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
+ }
+ if ($source) {
+ if (isset($source['parent'])) {
+ $parent = $source['parent'];
+ while (isset($parent)) {
+ $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1);
+ $item = $query->execute(array($parent))->fetchRow();
+ if (isset($item['parent'])) {
+ $parent = $item['parent'];
+ } else {
+ $fileOwner = $item['uid_owner'];
+ break;
+ }
+ }
+ } else {
+ $fileOwner = $source['uid_owner'];
+ }
+ $source['fileOwner'] = $fileOwner;
+ return $source;
+ }
+ \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR);
+ return false;
+ }
+
}
diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php
index e29e9b7e002..4426beec636 100644
--- a/apps/files_sharing/lib/share/folder.php
+++ b/apps/files_sharing/lib/share/folder.php
@@ -21,51 +21,31 @@
class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share_Backend_Collection {
- public function formatItems($items, $format, $parameters = null) {
- if ($format == self::FORMAT_SHARED_STORAGE) {
- // Only 1 item should come through for this format call
- return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']);
- } else if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) {
- // Only 1 item should come through for this format call
- $folder = $items[key($items)];
- if (isset($parameters['mimetype_filter'])) {
- $mimetype_filter = $parameters['mimetype_filter'];
- } else {
- $mimetype_filter = '';
- }
- $path = $folder['path'].substr($parameters['folder'], 7 + strlen($folder['file_target']));
- $files = OC_FileCache::getFolderContent($path, '', $mimetype_filter);
- foreach ($files as &$file) {
- $file['directory'] = $parameters['folder'];
- $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file';
- $file['permissions'] = $folder['permissions'];
- if ($file['type'] == 'file') {
- // Remove Create permission if type is file
- $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE;
- }
- }
- return $files;
- }
- return array();
- }
-
public function getChildren($itemSource) {
$children = array();
$parents = array($itemSource);
+ $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?');
+ $result = $query->execute(array('httpd/unix-directory'));
+ if ($row = $result->fetchRow()) {
+ $mimetype = $row['id'];
+ } else {
+ $mimetype = -1;
+ }
while (!empty($parents)) {
$parents = "'".implode("','", $parents)."'";
- $query = OC_DB::prepare('SELECT `id`, `name`, `mimetype` FROM `*PREFIX*fscache` WHERE `parent` IN ('.$parents.')');
+ $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache`'
+ .' WHERE `parent` IN ('.$parents.')');
$result = $query->execute();
$parents = array();
while ($file = $result->fetchRow()) {
- $children[] = array('source' => $file['id'], 'file_path' => $file['name']);
- // If a child folder is found look inside it
- if ($file['mimetype'] == 'httpd/unix-directory') {
- $parents[] = $file['id'];
+ $children[] = array('source' => $file['fileid'], 'file_path' => $file['name']);
+ // If a child folder is found look inside it
+ if ($file['mimetype'] == $mimetype) {
+ $parents[] = $file['fileid'];
}
}
}
return $children;
}
-} \ No newline at end of file
+}
diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php
index 6dba76955a0..ffd4e5ced22 100644
--- a/apps/files_sharing/lib/sharedstorage.php
+++ b/apps/files_sharing/lib/sharedstorage.php
@@ -20,10 +20,12 @@
*
*/
+namespace OC\Files\Storage;
+
/**
* Convert target path to source path and pass the function call to the correct storage provider
*/
-class OC_Filestorage_Shared extends OC_Filestorage_Common {
+class Shared extends \OC\Files\Storage\Common {
private $sharedFolder;
private $files = array();
@@ -32,54 +34,51 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
$this->sharedFolder = $arguments['sharedFolder'];
}
+ public function getId(){
+ return 'shared::' . $this->sharedFolder;
+ }
+
/**
- * @brief Get the source file path and the permissions granted for a shared file
+ * @brief Get the source file path, permissions, and owner for a shared file
* @param string Shared target file path
- * @return Returns array with the keys path and permissions or false if not found
+ * @return Returns array with the keys path, permissions, and owner or false if not found
*/
private function getFile($target) {
- $target = '/'.$target;
- $target = rtrim($target, '/');
- if (isset($this->files[$target])) {
- return $this->files[$target];
- } else {
- $pos = strpos($target, '/', 1);
- // Get shared folder name
- if ($pos !== false) {
- $folder = substr($target, 0, $pos);
- if (isset($this->files[$folder])) {
- $file = $this->files[$folder];
- } else {
- $file = OCP\Share::getItemSharedWith('folder', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
- }
- if ($file) {
- $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder));
- $this->files[$target]['permissions'] = $file['permissions'];
- return $this->files[$target];
+ if (!isset($this->files[$target])) {
+ // Check for partial files
+ if (pathinfo($target, PATHINFO_EXTENSION) === 'part') {
+ $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5));
+ if ($source) {
+ $source['path'] .= '.part';
+ // All partial files have delete permission
+ $source['permissions'] |= \OCP\PERMISSION_DELETE;
}
} else {
- $file = OCP\Share::getItemSharedWith('file', $target, OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
- if ($file) {
- $this->files[$target] = $file;
- return $this->files[$target];
- }
+ $source = \OC_Share_Backend_File::getSource($target);
}
- OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, OCP\Util::ERROR);
- return false;
+ $this->files[$target] = $source;
}
+ return $this->files[$target];
}
/**
* @brief Get the source file path for a shared file
* @param string Shared target file path
- * @return Returns source file path or false if not found
+ * @return string source file path or false if not found
*/
private function getSourcePath($target) {
- $file = $this->getFile($target);
- if (isset($file['path'])) {
- $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1);
- OC_Filesystem::mount('OC_Filestorage_Local', array('datadir' => OC_User::getHome($uid)), $uid);
- return $file['path'];
+ $source = $this->getFile($target);
+ if ($source) {
+ if (!isset($source['fullPath'])) {
+ \OC\Files\Filesystem::initMountPoints($source['fileOwner']);
+ $mount = \OC\Files\Mount::findByNumericId($source['storage']);
+ if ($mount) {
+ $this->files[$target]['fullPath'] = $mount->getMountPoint().$source['path'];
+ } else {
+ $this->files[$target]['fullPath'] = false;
+ }
+ }
+ return $this->files[$target]['fullPath'];
}
return false;
}
@@ -87,53 +86,42 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
/**
* @brief Get the permissions granted for a shared file
* @param string Shared target file path
- * @return Returns CRUDS permissions granted or false if not found
+ * @return int CRUDS permissions granted or false if not found
*/
- private function getPermissions($target) {
- $file = $this->getFile($target);
- if (isset($file['permissions'])) {
- return $file['permissions'];
+ public function getPermissions($target) {
+ $source = $this->getFile($target);
+ if ($source) {
+ return $source['permissions'];
}
return false;
}
- /**
- * @brief Get the internal path to pass to the storage filesystem call
- * @param string Source file path
- * @return Source file path with mount point stripped out
- */
- private function getInternalPath($path) {
- $mountPoint = OC_Filesystem::getMountPoint($path);
- $internalPath = substr($path, strlen($mountPoint));
- return $internalPath;
- }
-
public function mkdir($path) {
if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) {
return false;
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->mkdir($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->mkdir($internalPath);
}
return false;
}
public function rmdir($path) {
if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->rmdir($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->rmdir($internalPath);
}
return false;
}
public function opendir($path) {
if ($path == '' || $path == '/') {
- $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_Folder::FORMAT_OPENDIR);
- OC_FakeDirStream::$dirs['shared'] = $files;
+ $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR);
+ \OC\Files\Stream\Dir::register('shared', $files);
return opendir('fakedir://shared');
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->opendir($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->opendir($internalPath);
}
return false;
}
@@ -142,16 +130,16 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '' || $path == '/') {
return true;
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->is_dir($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->is_dir($internalPath);
}
return false;
}
public function is_file($path) {
if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->is_file($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->is_file($internalPath);
}
return false;
}
@@ -160,11 +148,10 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '' || $path == '/') {
$stat['size'] = $this->filesize($path);
$stat['mtime'] = $this->filemtime($path);
- $stat['ctime'] = $this->filectime($path);
return $stat;
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->stat($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->stat($internalPath);
}
return false;
}
@@ -173,8 +160,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '' || $path == '/') {
return 'dir';
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->filetype($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->filetype($internalPath);
}
return false;
}
@@ -183,8 +170,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '' || $path == '/' || $this->is_dir($path)) {
return 0;
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->filesize($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->filesize($internalPath);
}
return false;
}
@@ -193,7 +180,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '') {
return false;
}
- return ($this->getPermissions($path) & OCP\Share::PERMISSION_CREATE);
+ return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE);
}
public function isReadable($path) {
@@ -204,54 +191,33 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($path == '') {
return false;
}
- return ($this->getPermissions($path) & OCP\Share::PERMISSION_UPDATE);
+ return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE);
}
public function isDeletable($path) {
if ($path == '') {
return true;
}
- return ($this->getPermissions($path) & OCP\Share::PERMISSION_DELETE);
+ return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE);
}
public function isSharable($path) {
if ($path == '') {
return false;
}
- return ($this->getPermissions($path) & OCP\Share::PERMISSION_SHARE);
+ return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE);
}
public function file_exists($path) {
if ($path == '' || $path == '/') {
return true;
} else if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->file_exists($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->file_exists($internalPath);
}
return false;
}
- public function filectime($path) {
- if ($path == '' || $path == '/') {
- $ctime = 0;
- if ($dh = $this->opendir($path)) {
- while (($filename = readdir($dh)) !== false) {
- $tempctime = $this->filectime($filename);
- if ($tempctime < $ctime) {
- $ctime = $tempctime;
- }
- }
- }
- return $ctime;
- } else {
- $source = $this->getSourcePath($path);
- if ($source) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->filectime($this->getInternalPath($source));
- }
- }
- }
-
public function filemtime($path) {
if ($path == '' || $path == '/') {
$mtime = 0;
@@ -267,8 +233,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
} else {
$source = $this->getSourcePath($path);
if ($source) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->filemtime($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->filemtime($internalPath);
}
}
}
@@ -280,25 +246,26 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
'target' => $this->sharedFolder.$path,
'source' => $source,
);
- OCP\Util::emitHook('OC_Filestorage_Shared', 'file_get_contents', $info);
- $storage = OC_Filesystem::getStorage($source);
- return $storage->file_get_contents($this->getInternalPath($source));
+ \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->file_get_contents($internalPath);
}
}
public function file_put_contents($path, $data) {
if ($source = $this->getSourcePath($path)) {
// Check if permission is granted
- if (($this->file_exists($path) && !$this->isUpdatable($path)) || ($this->is_dir($path) && !$this->isCreatable($path))) {
+ if (($this->file_exists($path) && !$this->isUpdatable($path))
+ || ($this->is_dir($path) && !$this->isCreatable($path))) {
return false;
}
$info = array(
'target' => $this->sharedFolder.$path,
'source' => $source,
);
- OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info);
- $storage = OC_Filesystem::getStorage($source);
- $result = $storage->file_put_contents($this->getInternalPath($source), $data);
+ \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ $result = $storage->file_put_contents($internalPath, $data);
return $result;
}
return false;
@@ -308,8 +275,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
// Delete the file if DELETE permission is granted
if ($source = $this->getSourcePath($path)) {
if ($this->isDeletable($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->unlink($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->unlink($internalPath);
} else if (dirname($path) == '/' || dirname($path) == '.') {
// Unshare the file from the user if in the root of the Shared folder
if ($this->is_dir($path)) {
@@ -317,38 +284,49 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
} else {
$itemType = 'file';
}
- return OCP\Share::unshareFromSelf($itemType, $path);
+ return \OCP\Share::unshareFromSelf($itemType, $path);
}
}
return false;
}
public function rename($path1, $path2) {
- // Renaming/moving is only allowed within shared folders
- $pos1 = strpos($path1, '/', 1);
- $pos2 = strpos($path2, '/', 1);
- if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) {
- $newSource = $this->getSourcePath(dirname($path2)).'/'.basename($path2);
- if (dirname($path1) == dirname($path2)) {
- // Rename the file if UPDATE permission is granted
- if ($this->isUpdatable($path1)) {
- $storage = OC_Filesystem::getStorage($oldSource);
- return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource));
- }
- } else {
- // Move the file if DELETE and CREATE permissions are granted
- if ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) {
- // Get the root shared folder
- $folder1 = substr($path1, 0, $pos1);
- $folder2 = substr($path2, 0, $pos2);
- // Copy and unlink the file if it exists in a different shared folder
- if ($folder1 != $folder2) {
- if ($this->copy($path1, $path2)) {
- return $this->unlink($path1);
+ // Check for partial files
+ if (pathinfo($path1, PATHINFO_EXTENSION) === 'part') {
+ if ($oldSource = $this->getSourcePath($path1)) {
+ list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource);
+ $newInternalPath = substr($oldInternalPath, 0, -5);
+ return $storage->rename($oldInternalPath, $newInternalPath);
+ }
+ } else {
+ // Renaming/moving is only allowed within shared folders
+ $pos1 = strpos($path1, '/', 1);
+ $pos2 = strpos($path2, '/', 1);
+ if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) {
+ $newSource = $this->getSourcePath(dirname($path2)).'/'.basename($path2);
+ if (dirname($path1) == dirname($path2)) {
+ // Rename the file if UPDATE permission is granted
+ if ($this->isUpdatable($path1)) {
+ list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource);
+ list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource);
+ return $storage->rename($oldInternalPath, $newInternalPath);
+ }
+ } else {
+ // Move the file if DELETE and CREATE permissions are granted
+ if ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) {
+ // Get the root shared folder
+ $folder1 = substr($path1, 0, $pos1);
+ $folder2 = substr($path2, 0, $pos2);
+ // Copy and unlink the file if it exists in a different shared folder
+ if ($folder1 != $folder2) {
+ if ($this->copy($path1, $path2)) {
+ return $this->unlink($path1);
+ }
+ } else {
+ list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource);
+ list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource);
+ return $storage->rename($oldInternalPath, $newInternalPath);
}
- } else {
- $storage = OC_Filesystem::getStorage($oldSource);
- return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource));
}
}
}
@@ -361,7 +339,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
if ($this->isCreatable(dirname($path2))) {
$source = $this->fopen($path1, 'r');
$target = $this->fopen($path2, 'w');
- return OC_Helper::streamCopy($source, $target);
+ list ($count, $result) = \OC_Helper::streamCopy($source, $target);
+ return $result;
}
return false;
}
@@ -392,9 +371,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
'source' => $source,
'mode' => $mode,
);
- OCP\Util::emitHook('OC_Filestorage_Shared', 'fopen', $info);
- $storage = OC_Filesystem::getStorage($source);
- return $storage->fopen($this->getInternalPath($source), $mode);
+ \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->fopen($internalPath, $mode);
}
return false;
}
@@ -404,47 +383,91 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common {
return 'httpd/unix-directory';
}
if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->getMimeType($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->getMimeType($internalPath);
}
return false;
}
public function free_space($path) {
+ if ($path == '') {
+ return \OC\Files\FREE_SPACE_UNKNOWN;
+ }
$source = $this->getSourcePath($path);
if ($source) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->free_space($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->free_space($internalPath);
}
}
public function getLocalFile($path) {
if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->getLocalFile($this->getInternalPath($source));
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->getLocalFile($internalPath);
}
return false;
}
public function touch($path, $mtime = null) {
if ($source = $this->getSourcePath($path)) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->touch($this->getInternalPath($source), $mtime);
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->touch($internalPath, $mtime);
}
return false;
}
public static function setup($options) {
- $user_dir = $options['user_dir'];
- OC_Filesystem::mount('OC_Filestorage_Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/');
+ if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user']
+ || \OCP\Share::getItemsSharedWith('file')) {
+ $user_dir = $options['user_dir'];
+ \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared',
+ array('sharedFolder' => '/Shared'),
+ $user_dir.'/Shared/');
+ }
}
- /**
- * check if a file or folder has been updated since $time
- * @param int $time
- * @return bool
- */
- public function hasUpdated($path,$time) {
- //TODO
+ public function hasUpdated($path, $time) {
+ if ($path == '') {
+ return false;
+ }
+ return $this->filemtime($path) > $time;
+ }
+
+ public function getCache($path = '') {
+ return new \OC\Files\Cache\Shared_Cache($this);
+ }
+
+ public function getScanner($path = '') {
+ return new \OC\Files\Cache\Scanner($this);
+ }
+
+ public function getPermissionsCache($path = '') {
+ return new \OC\Files\Cache\Shared_Permissions($this);
+ }
+
+ public function getWatcher($path = '') {
+ return new \OC\Files\Cache\Shared_Watcher($this);
+ }
+
+ public function getOwner($path) {
+ if ($path == '') {
+ return false;
+ }
+ $source = $this->getFile($path);
+ if ($source) {
+ return $source['fileOwner'];
+ }
return false;
}
+
+ public function getETag($path) {
+ if ($path == '') {
+ return parent::getETag($path);
+ }
+ if ($source = $this->getSourcePath($path)) {
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->getETag($internalPath);
+ }
+ return null;
+ }
+
}
diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php
new file mode 100644
index 00000000000..73e7808f24a
--- /dev/null
+++ b/apps/files_sharing/lib/updater.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2013 Michael Gapczynski mtgap@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Files\Cache;
+
+class Shared_Updater {
+
+ /**
+ * Correct the parent folders' ETags for all users shared the file at $target
+ *
+ * @param string $target
+ */
+ static public function correctFolders($target) {
+ $uid = \OCP\User::getUser();
+ $uidOwner = \OC\Files\Filesystem::getOwner($target);
+ $info = \OC\Files\Filesystem::getFileInfo($target);
+ // Correct Shared folders of other users shared with
+ $users = \OCP\Share::getUsersItemShared('file', $info['fileid'], $uidOwner, true);
+ if (!empty($users)) {
+ while (!empty($users)) {
+ $reshareUsers = array();
+ foreach ($users as $user) {
+ $etag = \OC\Files\Filesystem::getETag('');
+ \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag);
+ // Look for reshares
+ $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $info['fileid'], $user, true));
+ }
+ $users = $reshareUsers;
+ }
+ // Correct folders of shared file owner
+ $target = substr($target, 8);
+ if ($uidOwner !== $uid && $source = \OC_Share_Backend_File::getSource($target)) {
+ \OC\Files\Filesystem::initMountPoints($uidOwner);
+ $source = '/'.$uidOwner.'/'.$source['path'];
+ \OC\Files\Cache\Updater::correctFolder($source, $info['mtime']);
+ }
+ }
+ }
+
+ /**
+ * @param array $params
+ */
+ static public function writeHook($params) {
+ self::correctFolders($params['path']);
+ }
+
+ /**
+ * @param array $params
+ */
+ static public function renameHook($params) {
+ self::correctFolders($params['oldpath']);
+ self::correctFolders($params['newpath']);
+ }
+
+ /**
+ * @param array $params
+ */
+ static public function deleteHook($params) {
+ self::correctFolders($params['path']);
+ }
+
+ /**
+ * @param array $params
+ */
+ static public function shareHook($params) {
+ if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
+ $uidOwner = \OCP\User::getUser();
+ $users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true);
+ if (!empty($users)) {
+ while (!empty($users)) {
+ $reshareUsers = array();
+ foreach ($users as $user) {
+ $etag = \OC\Files\Filesystem::getETag('');
+ \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag);
+ // Look for reshares
+ $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $params['fileSource'], $user, true));
+ }
+ $users = $reshareUsers;
+ }
+ }
+ }
+ }
+
+}
diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php
new file mode 100644
index 00000000000..e67d1ee9086
--- /dev/null
+++ b/apps/files_sharing/lib/watcher.php
@@ -0,0 +1,51 @@
+<?php
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace OC\Files\Cache;
+
+/**
+ * check the storage backends for updates and change the cache accordingly
+ */
+class Shared_Watcher extends Watcher {
+
+ /**
+ * check $path for updates
+ *
+ * @param string $path
+ */
+ public function checkUpdate($path) {
+ if ($path != '') {
+ parent::checkUpdate($path);
+ }
+ }
+
+ /**
+ * remove deleted files in $path from the cache
+ *
+ * @param string $path
+ */
+ public function cleanFolder($path) {
+ if ($path != '') {
+ parent::cleanFolder($path);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php
index e9f318efd9d..1da972ad7e3 100644
--- a/apps/files_sharing/public.php
+++ b/apps/files_sharing/public.php
@@ -1,198 +1,216 @@
<?php
+$RUNTIME_NOSETUPFS = true;
// Load other apps for file previews
OC_App::loadApps();
-// Compatibility with shared-by-link items from ownCloud 4.0
-// requires old Sharing table !
-// support will be removed in OC 5.0,a
-if (isset($_GET['token'])) {
- unset($_GET['file']);
- $qry = \OC_DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? LIMIT 1');
- $filepath = $qry->execute(array($_GET['token']))->fetchOne();
- if(isset($filepath)) {
- $info = OC_FileCache_Cached::get($filepath, '');
- if(strtolower($info['mimetype']) == 'httpd/unix-directory') {
- $_GET['dir'] = $filepath;
+function fileCmp($a, $b) {
+ if ($a['type'] == 'dir' and $b['type'] != 'dir') {
+ return -1;
+ } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') {
+ return 1;
+ } else {
+ return strnatcasecmp($a['name'], $b['name']);
+ }
+}
+
+if (isset($_GET['t'])) {
+ $token = $_GET['t'];
+ $linkItem = OCP\Share::getShareByToken($token);
+ if (is_array($linkItem) && isset($linkItem['uid_owner'])) {
+ // seems to be a valid share
+ $type = $linkItem['item_type'];
+ $fileSource = $linkItem['file_source'];
+ $shareOwner = $linkItem['uid_owner'];
+ $fileOwner = null;
+ $path = null;
+ if (isset($linkItem['parent'])) {
+ $parent = $linkItem['parent'];
+ while (isset($parent)) {
+ $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1);
+ $item = $query->execute(array($parent))->fetchRow();
+ if (isset($item['parent'])) {
+ $parent = $item['parent'];
+ } else {
+ $fileOwner = $item['uid_owner'];
+ break;
+ }
+ }
} else {
- $_GET['file'] = $filepath;
+ $fileOwner = $shareOwner;
+ }
+ if (isset($fileOwner)) {
+ OC_Util::setupFS($fileOwner);
+ $path = \OC\Files\Filesystem::getPath($linkItem['file_source']);
}
- \OCP\Util::writeLog('files_sharing', 'You have files that are shared by link originating from ownCloud 4.0. Redistribute the new links, because backwards compatibility will be removed in ownCloud 5.', \OCP\Util::WARN);
}
}
-// Enf of backward compatibility
-
-if (isset($_GET['file']) || isset($_GET['dir'])) {
- if (isset($_GET['dir'])) {
- $type = 'folder';
- $path = $_GET['dir'];
- $baseDir = $path;
- $dir = $baseDir;
- } else {
- $type = 'file';
- $path = $_GET['file'];
+if (isset($path)) {
+ if (!isset($linkItem['item_type'])) {
+ OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
+ header('HTTP/1.0 404 Not Found');
+ $tmpl = new OCP\Template('', '404', 'guest');
+ $tmpl->printPage();
+ exit();
}
- $uidOwner = substr($path, 1, strpos($path, '/', 1) - 1);
- if (OCP\User::userExists($uidOwner)) {
- OC_Util::setupFS($uidOwner);
- $fileSource = OC_Filecache::getId($path, '');
- if ($fileSource != -1 && ($linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $uidOwner))) {
- // TODO Fix in the getItems
- if (!isset($linkItem['item_type']) || $linkItem['item_type'] != $type) {
+ if (isset($linkItem['share_with'])) {
+ // Authenticate share_with
+ $url = OCP\Util::linkToPublic('files') . '&t=' . $token;
+ if (isset($_GET['file'])) {
+ $url .= '&file=' . urlencode($_GET['file']);
+ } else {
+ if (isset($_GET['dir'])) {
+ $url .= '&dir=' . urlencode($_GET['dir']);
+ }
+ }
+ if (isset($_POST['password'])) {
+ $password = $_POST['password'];
+ if ($linkItem['share_type'] == OCP\Share::SHARE_TYPE_LINK) {
+ // Check Password
+ $forcePortable = (CRYPT_BLOWFISH != 1);
+ $hasher = new PasswordHash(8, $forcePortable);
+ if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''),
+ $linkItem['share_with']))) {
+ $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest');
+ $tmpl->assign('URL', $url);
+ $tmpl->assign('error', true);
+ $tmpl->printPage();
+ exit();
+ } else {
+ // Save item id in session for future requests
+ $_SESSION['public_link_authenticated'] = $linkItem['id'];
+ }
+ } else {
+ OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type']
+ .' for share id '.$linkItem['id'], \OCP\Util::ERROR);
header('HTTP/1.0 404 Not Found');
$tmpl = new OCP\Template('', '404', 'guest');
$tmpl->printPage();
exit();
}
- if (isset($linkItem['share_with'])) {
- // Check password
- if (isset($_GET['file'])) {
- $url = OCP\Util::linkToPublic('files').'&file='.$_GET['file'];
- } else {
- $url = OCP\Util::linkToPublic('files').'&dir='.$_GET['dir'];
- }
- if (isset($_POST['password'])) {
- $password = $_POST['password'];
- $storedHash = $linkItem['share_with'];
- $forcePortable = (CRYPT_BLOWFISH != 1);
- $hasher = new PasswordHash(8, $forcePortable);
- if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''), $storedHash))) {
- $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest');
- $tmpl->assign('URL', $url);
- $tmpl->assign('error', true);
- $tmpl->printPage();
- exit();
+
+ } else {
+ // Check if item id is set in session
+ if (!isset($_SESSION['public_link_authenticated'])
+ || $_SESSION['public_link_authenticated'] !== $linkItem['id']
+ ) {
+ // Prompt for password
+ $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest');
+ $tmpl->assign('URL', $url);
+ $tmpl->printPage();
+ exit();
+ }
+ }
+ }
+ $basePath = $path;
+ if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($basePath . $_GET['path'])) {
+ $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']);
+ $path .= $getPath;
+ } else {
+ $getPath = '';
+ }
+ $dir = dirname($path);
+ $file = basename($path);
+ // Download the file
+ if (isset($_GET['download'])) {
+ if (isset($_GET['files'])) { // download selected files
+ OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
+ } else {
+ OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
+ }
+ exit();
+ } else {
+ OCP\Util::addStyle('files_sharing', 'public');
+ OCP\Util::addScript('files_sharing', 'public');
+ OCP\Util::addScript('files', 'fileactions');
+ $tmpl = new OCP\Template('files_sharing', 'public', 'base');
+ $tmpl->assign('uidOwner', $shareOwner);
+ $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner));
+ $tmpl->assign('filename', $file);
+ $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path));
+ $tmpl->assign('fileTarget', basename($linkItem['file_target']));
+ $urlLinkIdentifiers= (isset($token)?'&t='.$token:'')
+ .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'')
+ .(isset($_GET['file'])?'&file='.$_GET['file']:'');
+ // Show file list
+ if (\OC\Files\Filesystem::is_dir($path)) {
+ $tmpl->assign('dir', $getPath);
+
+ OCP\Util::addStyle('files', 'files');
+ OCP\Util::addScript('files', 'files');
+ OCP\Util::addScript('files', 'filelist');
+ OCP\Util::addscript('files', 'keyboardshortcuts');
+ $files = array();
+ $rootLength = strlen($basePath) + 1;
+ $totalSize = 0;
+ foreach (\OC\Files\Filesystem::getDirectoryContent($path) as $i) {
+ $totalSize += $i['size'];
+ $i['date'] = OCP\Util::formatDate($i['mtime']);
+ if ($i['type'] == 'file') {
+ $fileinfo = pathinfo($i['name']);
+ $i['basename'] = $fileinfo['filename'];
+ if (!empty($fileinfo['extension'])) {
+ $i['extension'] = '.' . $fileinfo['extension'];
} else {
- // Save item id in session for future requests
- $_SESSION['public_link_authenticated'] = $linkItem['id'];
+ $i['extension'] = '';
}
- // Check if item id is set in session
- } else if (!isset($_SESSION['public_link_authenticated']) || $_SESSION['public_link_authenticated'] !== $linkItem['id']) {
- // Prompt for password
- $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest');
- $tmpl->assign('URL', $url);
- $tmpl->printPage();
- exit();
}
+ $i['directory'] = $getPath;
+ $i['permissions'] = OCP\PERMISSION_READ;
+ $files[] = $i;
}
- $path = $linkItem['path'];
- if (isset($_GET['path'])) {
- $path .= $_GET['path'];
- $dir .= $_GET['path'];
- if (!OC_Filesystem::file_exists($path)) {
- header('HTTP/1.0 404 Not Found');
- $tmpl = new OCP\Template('', '404', 'guest');
- $tmpl->printPage();
- exit();
+ usort($files, "fileCmp");
+
+ // Make breadcrumb
+ $breadcrumb = array();
+ $pathtohere = '';
+ foreach (explode('/', $getPath) as $i) {
+ if ($i != '') {
+ $pathtohere .= '/' . $i;
+ $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i);
}
}
- // Download the file
- if (isset($_GET['download'])) {
- if (isset($_GET['dir'])) {
- if ( isset($_GET['files']) ) { // download selected files
- OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
- } else if (isset($_GET['path']) && $_GET['path'] != '' ) { // download a file from a shared directory
- OC_Files::get('', $path, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
- } else { // download the whole shared directory
- OC_Files::get($path, '', $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
- }
- } else { // download a single shared file
- OC_Files::get("", $path, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false);
- }
-
+ $list = new OCP\Template('files', 'part.list', '');
+ $list->assign('files', $files);
+ $list->assign('disableSharing', true);
+ $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=');
+ $list->assign('downloadURL',
+ OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=');
+ $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '');
+ $breadcrumbNav->assign('breadcrumb', $breadcrumb);
+ $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=');
+ $folder = new OCP\Template('files', 'index', '');
+ $folder->assign('fileList', $list->fetchPage());
+ $folder->assign('breadcrumb', $breadcrumbNav->fetchPage());
+ $folder->assign('dir', $getPath);
+ $folder->assign('isCreatable', false);
+ $folder->assign('permissions', 0);
+ $folder->assign('files', $files);
+ $folder->assign('uploadMaxFilesize', 0);
+ $folder->assign('uploadMaxHumanFilesize', 0);
+ $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
+ $folder->assign('usedSpacePercent', 0);
+ $tmpl->assign('folder', $folder->fetchPage());
+ $allowZip = OCP\Config::getSystemValue('allowZipDownload', true)
+ && $totalSize <= OCP\Config::getSystemValue('maxZipInputSize', OCP\Util::computerFileSize('800 MB'));
+ $tmpl->assign('allowZipDownload', intval($allowZip));
+ $tmpl->assign('downloadURL',
+ OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath));
+ } else {
+ $tmpl->assign('dir', $dir);
+
+ // Show file preview if viewer is available
+ if ($type == 'file') {
+ $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download');
} else {
- OCP\Util::addStyle('files_sharing', 'public');
- OCP\Util::addScript('files_sharing', 'public');
- OCP\Util::addScript('files', 'fileactions');
- $tmpl = new OCP\Template('files_sharing', 'public', 'base');
- $tmpl->assign('owner', $uidOwner);
- // Show file list
- if (OC_Filesystem::is_dir($path)) {
- OCP\Util::addStyle('files', 'files');
- OCP\Util::addScript('files', 'files');
- OCP\Util::addScript('files', 'filelist');
- $files = array();
- $rootLength = strlen($baseDir) + 1;
- foreach (OC_Files::getDirectoryContent($path) as $i) {
- $i['date'] = OCP\Util::formatDate($i['mtime']);
- if ($i['type'] == 'file') {
- $fileinfo = pathinfo($i['name']);
- $i['basename'] = $fileinfo['filename'];
- $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : '';
- }
- $i['directory'] = '/'.substr('/'.$uidOwner.'/files'.$i['directory'], $rootLength);
- if ($i['directory'] == '/') {
- $i['directory'] = '';
- }
- $i['permissions'] = OCP\Share::PERMISSION_READ;
- $files[] = $i;
- }
- // Make breadcrumb
- $breadcrumb = array();
- $pathtohere = '';
- $count = 1;
- foreach (explode('/', $dir) as $i) {
- if ($i != '') {
- if ($i != $baseDir) {
- $pathtohere .= '/'.$i;
- }
- if ( strlen($pathtohere) < strlen($_GET['dir'])) {
- continue;
- }
- $breadcrumb[] = array('dir' => str_replace($_GET['dir'], "", $pathtohere, $count), 'name' => $i);
- }
- }
- $list = new OCP\Template('files', 'part.list', '');
- $list->assign('files', $files, false);
- $list->assign('publicListView', true);
- $list->assign('baseURL', OCP\Util::linkToPublic('files').'&dir='.$_GET['dir'].'&path=', false);
- $list->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.$_GET['dir'].'&path=', false);
- $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' );
- $breadcrumbNav->assign('breadcrumb', $breadcrumb, false);
- $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files').'&dir='.$_GET['dir'].'&path=', false);
- $folder = new OCP\Template('files', 'index', '');
- $folder->assign('fileList', $list->fetchPage(), false);
- $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false);
- $folder->assign('dir', basename($dir));
- $folder->assign('isCreatable', false);
- $folder->assign('permissions', 0);
- $folder->assign('files', $files);
- $folder->assign('uploadMaxFilesize', 0);
- $folder->assign('uploadMaxHumanFilesize', 0);
- $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
- $tmpl->assign('folder', $folder->fetchPage(), false);
- $tmpl->assign('uidOwner', $uidOwner);
- $tmpl->assign('dir', basename($dir));
- $tmpl->assign('filename', basename($path));
- $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path));
- $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
- if (isset($_GET['path'])) {
- $getPath = $_GET['path'];
- } else {
- $getPath = '';
- }
- $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.$_GET['dir'].'&path='.$getPath);
- } else {
- // Show file preview if viewer is available
- $tmpl->assign('uidOwner', $uidOwner);
- $tmpl->assign('dir', dirname($path));
- $tmpl->assign('filename', basename($path));
- $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path));
- if ($type == 'file') {
- $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&file='.$_GET['file'].'&download');
- } else {
- if (isset($_GET['path'])) {
- $getPath = $_GET['path'];
- } else {
- $getPath = '';
- }
- $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.$_GET['dir'].'&path='.$getPath);
- }
- }
- $tmpl->printPage();
+ $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files')
+ .$urlLinkIdentifiers.'&download&path='.urlencode($getPath));
}
- exit();
}
+ $tmpl->printPage();
}
+ exit();
+} else {
+ OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
}
header('HTTP/1.0 404 Not Found');
$tmpl = new OCP\Template('', '404', 'guest');
diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php
index 9695caebf18..7a67b6e5503 100644
--- a/apps/files_sharing/templates/authenticate.php
+++ b/apps/files_sharing/templates/authenticate.php
@@ -1,9 +1,9 @@
-<form action="<?php echo $_['URL']; ?>" method="post">
+<form action="<?php p($_['URL']); ?>" method="post">
<fieldset>
- <p>
- <label for="password" class="infield"><?php echo $l->t('Password'); ?></label>
- <input type="password" name="password" id="password" value="" />
- <input type="submit" value="<?php echo $l->t('Submit'); ?>" />
+ <p class="infield">
+ <label for="password" class="infield"><?php p($l->t('Password')); ?></label>
+ <input type="password" name="password" id="password" placeholder="" value="" autofocus />
+ <input type="submit" value="<?php p($l->t('Submit')); ?>" />
</p>
</fieldset>
-</form> \ No newline at end of file
+</form>
diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php
index ef81e296d82..88692445ec3 100644
--- a/apps/files_sharing/templates/public.php
+++ b/apps/files_sharing/templates/public.php
@@ -1,35 +1,43 @@
-<input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir">
-<input type="hidden" name="downloadURL" value="<?php echo $_['downloadURL'] ?>" id="downloadURL">
-<input type="hidden" name="filename" value="<?php echo $_['filename'] ?>" id="filename">
-<input type="hidden" name="mimetype" value="<?php echo $_['mimetype'] ?>" id="mimetype">
+<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
+<input type="hidden" name="downloadURL" value="<?php p($_['downloadURL']) ?>" id="downloadURL">
+<input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename">
+<input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype">
<header><div id="header">
- <a href="<?php echo link_to('', 'index.php'); ?>" title="" id="owncloud"><img class="svg" src="<?php echo image_path('', 'logo-wide.svg'); ?>" alt="ownCloud" /></a>
+ <a href="<?php print_unescaped(link_to('', 'index.php')); ?>" title="" id="owncloud"><img class="svg"
+ src="<?php print_unescaped(image_path('', 'logo-wide.svg')); ?>" alt="ownCloud" /></a>
<div class="header-right">
<?php if (isset($_['folder'])): ?>
- <span id="details"><?php echo $l->t('%s shared the folder %s with you', array($_['uidOwner'], $_['filename'])) ?></span>
+ <span id="details"><?php p($l->t('%s shared the folder %s with you',
+ array($_['displayName'], $_['fileTarget']))) ?></span>
<?php else: ?>
- <span id="details"><?php echo $l->t('%s shared the file %s with you', array($_['uidOwner'], $_['filename'])) ?></span>
+ <span id="details"><?php p($l->t('%s shared the file %s with you',
+ array($_['displayName'], $_['fileTarget']))) ?></span>
<?php endif; ?>
<?php if (!isset($_['folder']) || $_['allowZipDownload']): ?>
- <a href="<?php echo $_['downloadURL']; ?>" class="button" id="download"><img class="svg" alt="Download" src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" /><?php echo $l->t('Download')?></a>
+ <a href="<?php p($_['downloadURL']); ?>" class="button" id="download"><img
+ class="svg" alt="Download" src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"
+ /><?php p($l->t('Download'))?></a>
<?php endif; ?>
</div>
</div></header>
<div id="preview">
<?php if (isset($_['folder'])): ?>
- <?php echo $_['folder']; ?>
+ <?php print_unescaped($_['folder']); ?>
<?php else: ?>
- <?php if (substr($_['mimetype'], 0 , strpos($_['mimetype'], '/')) == 'image'): ?>
+ <?php if (substr($_['mimetype'], 0, strpos($_['mimetype'], '/')) == 'image'): ?>
<div id="imgframe">
- <img src="<?php echo $_['downloadURL']; ?>" />
+ <img src="<?php p($_['downloadURL']); ?>" />
</div>
<?php endif; ?>
<ul id="noPreview">
<li class="error">
- <?php echo $l->t('No preview available for').' '.$_['filename']; ?><br />
- <a href="<?php echo $_['downloadURL']; ?>" id="download"><img class="svg" alt="Download" src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" /><?php echo $l->t('Download')?></a>
+ <?php p($l->t('No preview available for').' '.$_['fileTarget']); ?><br />
+ <a href="<?php p($_['downloadURL']); ?>" id="download"><img class="svg" alt="Download"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"
+ /><?php p($l->t('Download'))?></a>
</li>
</ul>
<?php endif; ?>
</div>
-<footer><p class="info"><a href="http://owncloud.org/">ownCloud</a> &ndash; <?php echo $l->t('web services under your control'); ?></p></footer>
+<footer><p class="info"><a href="http://owncloud.org/">ownCloud</a> &ndash;
+<?php p($l->t('web services under your control')); ?></p></footer>
diff --git a/apps/files_trashbin/ajax/delete.php b/apps/files_trashbin/ajax/delete.php
new file mode 100644
index 00000000000..1834fb54003
--- /dev/null
+++ b/apps/files_trashbin/ajax/delete.php
@@ -0,0 +1,45 @@
+<?php
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
+$files = $_POST['files'];
+$dirlisting = $_POST['dirlisting'];
+$list = json_decode($files);
+
+$error = array();
+$success = array();
+
+$i = 0;
+foreach ($list as $file) {
+ if ( $dirlisting=='0') {
+ $delimiter = strrpos($file, '.d');
+ $filename = substr($file, 0, $delimiter);
+ $timestamp = substr($file, $delimiter+2);
+ } else {
+ $filename = $file;
+ $timestamp = null;
+ }
+
+ OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp);
+ if (!OCA\Files_Trashbin\Trashbin::file_exists($filename, $timestamp)) {
+ $success[$i]['filename'] = $file;
+ $success[$i]['timestamp'] = $timestamp;
+ $i++;
+ } else {
+ $error[] = $filename;
+ }
+}
+
+if ( $error ) {
+ $filelist = '';
+ foreach ( $error as $e ) {
+ $filelist .= $e.', ';
+ }
+ $l = OC_L10N::get('files_trashbin');
+ $message = $l->t("Couldn't delete %s permanently", array(rtrim($filelist, ', ')));
+ OCP\JSON::error(array("data" => array("message" => $message,
+ "success" => $success, "error" => $error)));
+} else {
+ OCP\JSON::success(array("data" => array("success" => $success)));
+}
diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php
new file mode 100644
index 00000000000..80de3c31f91
--- /dev/null
+++ b/apps/files_trashbin/ajax/undelete.php
@@ -0,0 +1,46 @@
+<?php
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
+$files = $_POST['files'];
+$dirlisting = $_POST['dirlisting'];
+$list = json_decode($files);
+
+$error = array();
+$success = array();
+
+$i = 0;
+foreach ($list as $file) {
+ if ( $dirlisting=='0') {
+ $delimiter = strrpos($file, '.d');
+ $filename = substr($file, 0, $delimiter);
+ $timestamp = substr($file, $delimiter+2);
+ } else {
+ $path_parts = pathinfo($file);
+ $filename = $path_parts['basename'];
+ $timestamp = null;
+ }
+
+ if ( !OCA\Files_Trashbin\Trashbin::restore($file, $filename, $timestamp) ) {
+ $error[] = $filename;
+ } else {
+ $success[$i]['filename'] = $file;
+ $success[$i]['timestamp'] = $timestamp;
+ $i++;
+ }
+
+}
+
+if ( $error ) {
+ $filelist = '';
+ foreach ( $error as $e ) {
+ $filelist .= $e.', ';
+ }
+ $l = OC_L10N::get('files_trashbin');
+ $message = $l->t("Couldn't restore %s", array(rtrim($filelist, ', ')));
+ OCP\JSON::error(array("data" => array("message" => $message,
+ "success" => $success, "error" => $error)));
+} else {
+ OCP\JSON::success(array("data" => array("success" => $success)));
+}
diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php
new file mode 100644
index 00000000000..a6a99db034c
--- /dev/null
+++ b/apps/files_trashbin/appinfo/app.php
@@ -0,0 +1,7 @@
+<?php
+
+OC::$CLASSPATH['OCA\Files_Trashbin\Hooks'] = 'files_trashbin/lib/hooks.php';
+OC::$CLASSPATH['OCA\Files_Trashbin\Trashbin'] = 'files_trashbin/lib/trash.php';
+
+
+OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Trashbin\Hooks", "remove_hook");
diff --git a/apps/files_trashbin/appinfo/database.xml b/apps/files_trashbin/appinfo/database.xml
new file mode 100644
index 00000000000..aae334b1489
--- /dev/null
+++ b/apps/files_trashbin/appinfo/database.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+ <name>*dbprefix*files_trash</name>
+
+ <declaration>
+
+ <field>
+ <name>id</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>250</length>
+ </field>
+
+ <field>
+ <name>user</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>timestamp</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>12</length>
+ </field>
+
+ <field>
+ <name>location</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>512</length>
+ </field>
+
+ <field>
+ <name>type</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>mime</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>30</length>
+ </field>
+
+ <index>
+ <name>id_index</name>
+ <field>
+ <name>id</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ <index>
+ <name>timestamp_index</name>
+ <field>
+ <name>timestamp</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ <index>
+ <name>user_index</name>
+ <field>
+ <name>user</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ </declaration>
+
+ </table>
+
+ <table>
+
+ <name>*dbprefix*files_trashsize</name>
+
+ <declaration>
+
+ <field>
+ <name>user</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>size</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>50</length>
+ </field>
+
+ </declaration>
+
+ </table>
+
+</database>
diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml
new file mode 100644
index 00000000000..7f807da579e
--- /dev/null
+++ b/apps/files_trashbin/appinfo/info.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<info>
+ <id>files_trashbin</id>
+ <name>Deleted files</name>
+ <description>Keep a copy of deleted files so that they can be restored if needed</description>
+ <licence>AGPL</licence>
+ <author>Bjoern Schiessle</author>
+ <shipped>true</shipped>
+ <require>4.9</require>
+ <default_enable/>
+ <types>
+ <filesystem/>
+ </types>
+</info>
diff --git a/apps/files_trashbin/appinfo/update.php b/apps/files_trashbin/appinfo/update.php
new file mode 100644
index 00000000000..b0bf79cc510
--- /dev/null
+++ b/apps/files_trashbin/appinfo/update.php
@@ -0,0 +1,40 @@
+<?php
+
+$installedVersion=OCP\Config::getAppValue('files_trashbin', 'installed_version');
+// move versions to new directory
+
+if (version_compare($installedVersion, '0.2', '<')) {
+ $datadir = \OCP\Config::getSystemValue('datadirectory').'/';
+
+ $users = \OCP\User::getUsers();
+ foreach ($users as $user) {
+
+ //create new folders
+ @mkdir($datadir.$user.'/files_trashbin/files');
+ @mkdir($datadir.$user.'/files_trashbin/versions');
+ @mkdir($datadir.$user.'/files_trashbin/keyfiles');
+
+ // move files to the new folders
+ if ($handle = opendir($datadir.$user.'/files_trashbin')) {
+ while (false !== ($file = readdir($handle))) {
+ if ($file != "." && $file != ".." && $file != 'files' && $file != 'versions' && $file != 'keyfiles') {
+ rename($datadir.$user.'/files_trashbin/'.$file,
+ $datadir.$user.'/files_trashbin/files/'.$file);
+ }
+ }
+ closedir($handle);
+ }
+
+ // move versions to the new folder
+ if ($handle = opendir($datadir.$user.'/versions_trashbin')) {
+ while (false !== ($file = readdir($handle))) {
+ rename($datadir.$user.'/versions_trashbin/'.$file,
+ $datadir.$user.'/files_trashbin/versions/'.$file);
+ }
+ closedir($handle);
+ }
+
+ @rmdir($datadir.$user.'/versions_trashbin');
+
+ }
+} \ No newline at end of file
diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version
new file mode 100644
index 00000000000..be586341736
--- /dev/null
+++ b/apps/files_trashbin/appinfo/version
@@ -0,0 +1 @@
+0.3
diff --git a/apps/files_trashbin/download.php b/apps/files_trashbin/download.php
new file mode 100644
index 00000000000..60328e1dddb
--- /dev/null
+++ b/apps/files_trashbin/download.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+* ownCloud - trash bin
+*
+* @author Bjoern Schiessle
+* @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+// Check if we are a user
+OCP\User::checkLoggedIn();
+
+$filename = $_GET["file"];
+
+$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin/files');
+
+if(!$view->file_exists($filename)) {
+ header("HTTP/1.0 404 Not Found");
+ $tmpl = new OCP\Template( '', '404', 'guest' );
+ $tmpl->assign('file', $filename);
+ $tmpl->printPage();
+ exit;
+}
+
+$ftype=$view->getMimeType( $filename );
+
+header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
+ header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
+} else {
+ header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
+ . '; filename="' . rawurlencode( basename($filename) ) . '"' );
+}
+OCP\Response::disableCaching();
+header('Content-Length: '. $view->filesize($filename));
+
+OC_Util::obEnd();
+$view->readfile( $filename );
diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php
new file mode 100644
index 00000000000..8a5875b9ce6
--- /dev/null
+++ b/apps/files_trashbin/index.php
@@ -0,0 +1,116 @@
+<?php
+
+// Check if we are a user
+OCP\User::checkLoggedIn();
+
+OCP\App::setActiveNavigationEntry('files_index');
+
+OCP\Util::addScript('files_trashbin', 'trash');
+OCP\Util::addScript('files_trashbin', 'disableDefaultActions');
+OCP\Util::addScript('files', 'fileactions');
+$tmpl = new OCP\Template('files_trashbin', 'index', 'user');
+
+$user = \OCP\User::getUser();
+$view = new OC_Filesystemview('/'.$user.'/files_trashbin/files');
+
+OCP\Util::addStyle('files', 'files');
+OCP\Util::addScript('files', 'filelist');
+
+$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : '';
+
+$result = array();
+if ($dir) {
+ $dirlisting = true;
+ $fullpath = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($dir);
+ $dirContent = opendir($fullpath);
+ $i = 0;
+ while($entryName = readdir($dirContent)) {
+ if ( $entryName != '.' && $entryName != '..' ) {
+ $pos = strpos($dir.'/', '/', 1);
+ $tmp = substr($dir, 0, $pos);
+ $pos = strrpos($tmp, '.d');
+ $timestamp = substr($tmp, $pos+2);
+ $result[] = array(
+ 'id' => $entryName,
+ 'timestamp' => $timestamp,
+ 'mime' => $view->getMimeType($dir.'/'.$entryName),
+ 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file',
+ 'location' => $dir,
+ );
+ }
+ }
+ closedir($dirContent);
+
+} else {
+ $dirlisting = false;
+ $query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE `user` = ?');
+ $result = $query->execute(array($user))->fetchAll();
+}
+
+$files = array();
+foreach ($result as $r) {
+ $i = array();
+ $i['name'] = $r['id'];
+ $i['date'] = OCP\Util::formatDate($r['timestamp']);
+ $i['timestamp'] = $r['timestamp'];
+ $i['mimetype'] = $r['mime'];
+ $i['type'] = $r['type'];
+ if ($i['type'] == 'file') {
+ $fileinfo = pathinfo($r['id']);
+ $i['basename'] = $fileinfo['filename'];
+ $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : '';
+ }
+ $i['directory'] = $r['location'];
+ if ($i['directory'] == '/') {
+ $i['directory'] = '';
+ }
+ $i['permissions'] = OCP\PERMISSION_READ;
+ $files[] = $i;
+}
+
+function fileCmp($a, $b) {
+ if ($a['type'] == 'dir' and $b['type'] != 'dir') {
+ return -1;
+ } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') {
+ return 1;
+ } else {
+ return strnatcasecmp($a['name'], $b['name']);
+ }
+}
+
+usort($files, "fileCmp");
+
+// Make breadcrumb
+$pathtohere = '';
+$breadcrumb = array();
+foreach (explode('/', $dir) as $i) {
+ if ($i != '') {
+ if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) {
+ $name = $match[1];
+ } else {
+ $name = $i;
+ }
+ $pathtohere .= '/' . $i;
+ $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name);
+ }
+}
+
+$breadcrumbNav = new OCP\Template('files_trashbin', 'part.breadcrumb', '');
+$breadcrumbNav->assign('breadcrumb', $breadcrumb);
+$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=');
+$breadcrumbNav->assign('home', OCP\Util::linkTo('files', 'index.php'));
+
+$list = new OCP\Template('files_trashbin', 'part.list', '');
+$list->assign('files', $files);
+$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir);
+$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir);
+$list->assign('disableSharing', true);
+$list->assign('dirlisting', $dirlisting);
+$tmpl->assign('dirlisting', $dirlisting);
+$list->assign('disableDownloadActions', true);
+$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage());
+$tmpl->assign('fileList', $list->fetchPage());
+$tmpl->assign('files', $files);
+$tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($view->getAbsolutePath()));
+
+$tmpl->printPage();
diff --git a/apps/files_trashbin/js/disableDefaultActions.js b/apps/files_trashbin/js/disableDefaultActions.js
new file mode 100644
index 00000000000..df08bfb1a50
--- /dev/null
+++ b/apps/files_trashbin/js/disableDefaultActions.js
@@ -0,0 +1,4 @@
+/* disable download and sharing actions */
+var disableDownloadActions = true;
+var disableSharing = true;
+var trashBinApp = true; \ No newline at end of file
diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js
new file mode 100644
index 00000000000..39e76e10c9c
--- /dev/null
+++ b/apps/files_trashbin/js/trash.js
@@ -0,0 +1,236 @@
+
+$(document).ready(function() {
+
+ if (typeof FileActions !== 'undefined') {
+ FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/undelete.png'), function(filename) {
+ var tr=$('tr').filterAttr('data-file', filename);
+ var spinner = '<img class="move2trash" title="'+t('files_trashbin', 'perform restore operation')+'" src="'+ OC.imagePath('core', 'loader.gif') +'"></a>';
+ var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date");
+ var files = tr.attr('data-file');
+ undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner;
+ $.post(OC.filePath('files_trashbin','ajax','undelete.php'),
+ {files:JSON.stringify([files]), dirlisting:tr.attr('data-dirlisting') },
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+
+ });
+ };
+
+ FileActions.register('all', 'Delete', OC.PERMISSION_READ, function () {
+ return OC.imagePath('core', 'actions/delete');
+ }, function (filename) {
+ $('.tipsy').remove();
+
+ var tr=$('tr').filterAttr('data-file', filename);
+ var deleteAction = $('tr').filterAttr('data-file',filename).children("td.date").children(".action.delete");
+ var oldHTML = deleteAction[0].outerHTML;
+ var newHTML = '<img class="move2trash" data-action="Delete" title="'+t('files', 'delete file permanently')+'" src="'+ OC.imagePath('core', 'loading.gif') +'"></a>';
+ var files = tr.attr('data-file');
+ deleteAction[0].outerHTML = newHTML;
+
+ $.post(OC.filePath('files_trashbin','ajax','delete.php'),
+ {files:JSON.stringify([files]), dirlisting:tr.attr('data-dirlisting') },
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+
+ });
+
+ // Sets the select_all checkbox behaviour :
+ $('#select_all').click(function() {
+ if($(this).attr('checked')){
+ // Check all
+ $('td.filename input:checkbox').attr('checked', true);
+ $('td.filename input:checkbox').parent().parent().addClass('selected');
+ }else{
+ // Uncheck all
+ $('td.filename input:checkbox').attr('checked', false);
+ $('td.filename input:checkbox').parent().parent().removeClass('selected');
+ }
+ processSelection();
+ });
+
+ $('td.filename input:checkbox').live('change',function(event) {
+ if (event.shiftKey) {
+ var last = $(lastChecked).parent().parent().prevAll().length;
+ var first = $(this).parent().parent().prevAll().length;
+ var start = Math.min(first, last);
+ var end = Math.max(first, last);
+ var rows = $(this).parent().parent().parent().children('tr');
+ for (var i = start; i < end; i++) {
+ $(rows).each(function(index) {
+ if (index == i) {
+ var checkbox = $(this).children().children('input:checkbox');
+ $(checkbox).attr('checked', 'checked');
+ $(checkbox).parent().parent().addClass('selected');
+ }
+ });
+ }
+ }
+ var selectedCount=$('td.filename input:checkbox:checked').length;
+ $(this).parent().parent().toggleClass('selected');
+ if(!$(this).attr('checked')){
+ $('#select_all').attr('checked',false);
+ }else{
+ if(selectedCount==$('td.filename input:checkbox').length){
+ $('#select_all').attr('checked',true);
+ }
+ }
+ processSelection();
+ });
+
+ $('.undelete').click('click',function(event) {
+ var spinner = '<img class="move2trash" title="'+t('files_trashbin', 'perform restore operation')+'" src="'+ OC.imagePath('core', 'loader.gif') +'"></a>';
+ var files=getSelectedFiles('file');
+ var fileslist = JSON.stringify(files);
+ var dirlisting=getSelectedFiles('dirlisting')[0];
+
+ for (var i=0; i<files.length; i++) {
+ var undeleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date");
+ undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner;
+ }
+
+ $.post(OC.filePath('files_trashbin','ajax','undelete.php'),
+ {files:fileslist, dirlisting:dirlisting},
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+ });
+
+ $('.delete').click('click',function(event) {
+ console.log("delete selected");
+ var spinner = '<img class="move2trash" title="'+t('files_trashbin', 'Delete permanently')+'" src="'+ OC.imagePath('core', 'loading.gif') +'"></a>';
+ var files=getSelectedFiles('file');
+ var fileslist = JSON.stringify(files);
+ var dirlisting=getSelectedFiles('dirlisting')[0];
+
+ for (var i=0; i<files.length; i++) {
+ var deleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date");
+ deleteAction[0].innerHTML = deleteAction[0].innerHTML+spinner;
+ }
+
+ $.post(OC.filePath('files_trashbin','ajax','delete.php'),
+ {files:fileslist, dirlisting:dirlisting},
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+ });
+
+ $('#fileList').on('click', 'td.filename a', function(event) {
+ var mime = $(this).parent().parent().data('mime');
+ if (mime !== 'httpd/unix-directory') {
+ event.preventDefault();
+ }
+ var filename = $(this).parent().parent().attr('data-file');
+ var tr = $('tr').filterAttr('data-file',filename);
+ var renaming = tr.data('renaming');
+ if(!renaming && !FileList.isLoading(filename)){
+ if(mime.substr(0, 5) === 'text/'){ //no texteditor for now
+ return;
+ }
+ var type = $(this).parent().parent().data('type');
+ var permissions = $(this).parent().parent().data('permissions');
+ var action = FileActions.getDefault(mime, type, permissions);
+ if(action){
+ event.preventDefault();
+ action(filename);
+ }
+ }
+ });
+
+ FileActions.actions.dir = {};
+});
+
+function processSelection(){
+ var selected=getSelectedFiles();
+ var selectedFiles=selected.filter(function(el){return el.type=='file'});
+ var selectedFolders=selected.filter(function(el){return el.type=='dir'});
+ if(selectedFiles.length==0 && selectedFolders.length==0) {
+ $('#headerName>span.name').text(t('files','Name'));
+ $('#modified').text(t('files','Deleted'));
+ $('table').removeClass('multiselect');
+ $('.selectedActions').hide();
+ }
+ else {
+ $('.selectedActions').show();
+ var selection='';
+ if(selectedFolders.length>0){
+ if(selectedFolders.length==1){
+ selection+=t('files','1 folder');
+ }else{
+ selection+=t('files','{count} folders',{count: selectedFolders.length});
+ }
+ if(selectedFiles.length>0){
+ selection+=' & ';
+ }
+ }
+ if(selectedFiles.length>0){
+ if(selectedFiles.length==1){
+ selection+=t('files','1 file');
+ }else{
+ selection+=t('files','{count} files',{count: selectedFiles.length});
+ }
+ }
+ $('#headerName>span.name').text(selection);
+ $('#modified').text('');
+ $('table').addClass('multiselect');
+ }
+}
+
+/**
+ * @brief get a list of selected files
+ * @param string property (option) the property of the file requested
+ * @return array
+ *
+ * possible values for property: name, mime, size and type
+ * if property is set, an array with that property for each file is returnd
+ * if it's ommited an array of objects with all properties is returned
+ */
+function getSelectedFiles(property){
+ var elements=$('td.filename input:checkbox:checked').parent().parent();
+ var files=[];
+ elements.each(function(i,element){
+ var file={
+ name:$(element).attr('data-filename'),
+ file:$(element).attr('data-file'),
+ timestamp:$(element).attr('data-timestamp'),
+ type:$(element).attr('data-type'),
+ dirlisting:$(element).attr('data-dirlisting')
+ };
+ if(property){
+ files.push(file[property]);
+ }else{
+ files.push(file);
+ }
+ });
+ return files;
+}
+
+function fileDownloadPath(dir, file) {
+ return OC.filePath('files_trashbin', '', 'download.php') + '?file='+encodeURIComponent(file);
+}
diff --git a/apps/files_trashbin/l10n/.gitkeep b/apps/files_trashbin/l10n/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/files_trashbin/l10n/.gitkeep
diff --git a/apps/files_trashbin/l10n/ar.php b/apps/files_trashbin/l10n/ar.php
new file mode 100644
index 00000000000..7b2e2863367
--- /dev/null
+++ b/apps/files_trashbin/l10n/ar.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "اسم",
+"Delete" => "إلغاء"
+);
diff --git a/apps/files_trashbin/l10n/bg_BG.php b/apps/files_trashbin/l10n/bg_BG.php
new file mode 100644
index 00000000000..29e69a02fbb
--- /dev/null
+++ b/apps/files_trashbin/l10n/bg_BG.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Невъзможно изтриване на %s завинаги",
+"Couldn't restore %s" => "Невъзможно възтановяване на %s",
+"perform restore operation" => "извършване на действие по възстановяване",
+"delete file permanently" => "изтриване на файла завинаги",
+"Delete permanently" => "Изтриване завинаги",
+"Name" => "Име",
+"Deleted" => "Изтрито",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папки",
+"1 file" => "1 файл",
+"{count} files" => "{count} файла",
+"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
+"Restore" => "Възтановяване",
+"Delete" => "Изтриване"
+);
diff --git a/apps/files_trashbin/l10n/bn_BD.php b/apps/files_trashbin/l10n/bn_BD.php
new file mode 100644
index 00000000000..d61355c52c2
--- /dev/null
+++ b/apps/files_trashbin/l10n/bn_BD.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "রাম",
+"1 folder" => "১টি ফোল্ডার",
+"{count} folders" => "{count} টি ফোল্ডার",
+"1 file" => "১টি ফাইল",
+"{count} files" => "{count} টি ফাইল",
+"Delete" => "মুছে"
+);
diff --git a/apps/files_trashbin/l10n/ca.php b/apps/files_trashbin/l10n/ca.php
new file mode 100644
index 00000000000..79ed5f871a2
--- /dev/null
+++ b/apps/files_trashbin/l10n/ca.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "No s'ha pogut esborrar permanentment %s",
+"Couldn't restore %s" => "No s'ha pogut restaurar %s",
+"perform restore operation" => "executa l'operació de restauració",
+"delete file permanently" => "esborra el fitxer permanentment",
+"Delete permanently" => "Esborra permanentment",
+"Name" => "Nom",
+"Deleted" => "Eliminat",
+"1 folder" => "1 carpeta",
+"{count} folders" => "{count} carpetes",
+"1 file" => "1 fitxer",
+"{count} files" => "{count} fitxers",
+"Nothing in here. Your trash bin is empty!" => "La paperera està buida!",
+"Restore" => "Recupera",
+"Delete" => "Esborra",
+"Deleted Files" => "Fitxers eliminats"
+);
diff --git a/apps/files_trashbin/l10n/cs_CZ.php b/apps/files_trashbin/l10n/cs_CZ.php
new file mode 100644
index 00000000000..7fab334b7df
--- /dev/null
+++ b/apps/files_trashbin/l10n/cs_CZ.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Nelze trvale odstranit %s",
+"Couldn't restore %s" => "Nelze obnovit %s",
+"perform restore operation" => "provést obnovu",
+"delete file permanently" => "trvale odstranit soubor",
+"Delete permanently" => "Trvale odstranit",
+"Name" => "Název",
+"Deleted" => "Smazáno",
+"1 folder" => "1 složka",
+"{count} folders" => "{count} složky",
+"1 file" => "1 soubor",
+"{count} files" => "{count} soubory",
+"Nothing in here. Your trash bin is empty!" => "Žádný obsah. Váš koš je prázdný.",
+"Restore" => "Obnovit",
+"Delete" => "Smazat",
+"Deleted Files" => "Smazané soubory"
+);
diff --git a/apps/files_trashbin/l10n/da.php b/apps/files_trashbin/l10n/da.php
new file mode 100644
index 00000000000..ca4a2e8215d
--- /dev/null
+++ b/apps/files_trashbin/l10n/da.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Kunne ikke slette %s permanent",
+"Couldn't restore %s" => "Kunne ikke gendanne %s",
+"perform restore operation" => "udfør gendannelsesoperation",
+"delete file permanently" => "slet fil permanent",
+"Delete permanently" => "Slet permanent",
+"Name" => "Navn",
+"Deleted" => "Slettet",
+"1 folder" => "1 mappe",
+"{count} folders" => "{count} mapper",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Nothing in here. Your trash bin is empty!" => "Intet at se her. Din papirkurv er tom!",
+"Restore" => "Gendan",
+"Delete" => "Slet"
+);
diff --git a/apps/files_trashbin/l10n/de.php b/apps/files_trashbin/l10n/de.php
new file mode 100644
index 00000000000..60a0e40d455
--- /dev/null
+++ b/apps/files_trashbin/l10n/de.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Konnte %s nicht dauerhaft löschen",
+"Couldn't restore %s" => "Konnte %s nicht wiederherstellen",
+"perform restore operation" => "Wiederherstellung ausführen",
+"delete file permanently" => "Datei dauerhaft löschen",
+"Delete permanently" => "Endgültig löschen",
+"Name" => "Name",
+"Deleted" => "gelöscht",
+"1 folder" => "1 Ordner",
+"{count} folders" => "{count} Ordner",
+"1 file" => "1 Datei",
+"{count} files" => "{count} Dateien",
+"Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, der Papierkorb ist leer!",
+"Restore" => "Wiederherstellen",
+"Delete" => "Löschen",
+"Deleted Files" => "Gelöschte Dateien"
+);
diff --git a/apps/files_trashbin/l10n/de_DE.php b/apps/files_trashbin/l10n/de_DE.php
new file mode 100644
index 00000000000..802a110fd18
--- /dev/null
+++ b/apps/files_trashbin/l10n/de_DE.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Konnte %s nicht dauerhaft löschen",
+"Couldn't restore %s" => "Konnte %s nicht wiederherstellen",
+"perform restore operation" => "Wiederherstellung ausführen",
+"delete file permanently" => "Datei dauerhaft löschen",
+"Delete permanently" => "Endgültig löschen",
+"Name" => "Name",
+"Deleted" => "Gelöscht",
+"1 folder" => "1 Ordner",
+"{count} folders" => "{count} Ordner",
+"1 file" => "1 Datei",
+"{count} files" => "{count} Dateien",
+"Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, Ihr Papierkorb ist leer!",
+"Restore" => "Wiederherstellen",
+"Delete" => "Löschen",
+"Deleted Files" => "Gelöschte Dateien"
+);
diff --git a/apps/files_trashbin/l10n/el.php b/apps/files_trashbin/l10n/el.php
new file mode 100644
index 00000000000..32e29456861
--- /dev/null
+++ b/apps/files_trashbin/l10n/el.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Αδύνατη η μόνιμη διαγραφή του %s",
+"Couldn't restore %s" => "Αδυναμία επαναφοράς %s",
+"perform restore operation" => "εκτέλεση λειτουργία επαναφοράς",
+"delete file permanently" => "μόνιμη διαγραφή αρχείου",
+"Delete permanently" => "Μόνιμη διαγραφή",
+"Name" => "Όνομα",
+"Deleted" => "Διαγράφηκε",
+"1 folder" => "1 φάκελος",
+"{count} folders" => "{count} φάκελοι",
+"1 file" => "1 αρχείο",
+"{count} files" => "{count} αρχεία",
+"Nothing in here. Your trash bin is empty!" => "Δεν υπάρχει τίποτα εδώ. Ο κάδος σας είναι άδειος!",
+"Restore" => "Επαναφορά",
+"Delete" => "Διαγραφή",
+"Deleted Files" => "Διαγραμμένα Αρχεία"
+);
diff --git a/apps/files_trashbin/l10n/eo.php b/apps/files_trashbin/l10n/eo.php
new file mode 100644
index 00000000000..8e0cc160393
--- /dev/null
+++ b/apps/files_trashbin/l10n/eo.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nomo",
+"1 folder" => "1 dosierujo",
+"{count} folders" => "{count} dosierujoj",
+"1 file" => "1 dosiero",
+"{count} files" => "{count} dosierujoj",
+"Restore" => "Restaŭri",
+"Delete" => "Forigi",
+"Deleted Files" => "Forigitaj dosieroj"
+);
diff --git a/apps/files_trashbin/l10n/es.php b/apps/files_trashbin/l10n/es.php
new file mode 100644
index 00000000000..3ae4be92a47
--- /dev/null
+++ b/apps/files_trashbin/l10n/es.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "No se puede eliminar %s permanentemente",
+"Couldn't restore %s" => "No se puede restaurar %s",
+"perform restore operation" => "Restaurar",
+"delete file permanently" => "Eliminar archivo permanentemente",
+"Delete permanently" => "Eliminar permanentemente",
+"Name" => "Nombre",
+"Deleted" => "Eliminado",
+"1 folder" => "1 carpeta",
+"{count} folders" => "{count} carpetas",
+"1 file" => "1 archivo",
+"{count} files" => "{count} archivos",
+"Nothing in here. Your trash bin is empty!" => "Nada aqui. La papelera esta vacia!",
+"Restore" => "Recuperar",
+"Delete" => "Eliminar",
+"Deleted Files" => "Archivos Eliminados"
+);
diff --git a/apps/files_trashbin/l10n/es_AR.php b/apps/files_trashbin/l10n/es_AR.php
new file mode 100644
index 00000000000..c18444e73e3
--- /dev/null
+++ b/apps/files_trashbin/l10n/es_AR.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "No fue posible borrar %s de manera permanente",
+"Couldn't restore %s" => "No se pudo restaurar %s",
+"perform restore operation" => "Restaurar",
+"delete file permanently" => "Borrar archivo de manera permanente",
+"Delete permanently" => "Borrar de manera permanente",
+"Name" => "Nombre",
+"Deleted" => "Borrado",
+"1 folder" => "1 directorio",
+"{count} folders" => "{count} directorios",
+"1 file" => "1 archivo",
+"{count} files" => "{count} archivos",
+"Nothing in here. Your trash bin is empty!" => "No hay nada acá. ¡La papelera está vacía!",
+"Restore" => "Recuperar",
+"Delete" => "Borrar",
+"Deleted Files" => "Archivos eliminados"
+);
diff --git a/apps/files_trashbin/l10n/et_EE.php b/apps/files_trashbin/l10n/et_EE.php
new file mode 100644
index 00000000000..73ae9dbee18
--- /dev/null
+++ b/apps/files_trashbin/l10n/et_EE.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "%s jäädavalt kustutamine ebaõnnestus",
+"Couldn't restore %s" => "%s ei saa taastada",
+"perform restore operation" => "soorita taastamine",
+"delete file permanently" => "kustuta fail jäädavalt",
+"Delete permanently" => "Kustuta jäädavalt",
+"Name" => "Nimi",
+"Deleted" => "Kustutatud",
+"1 folder" => "1 kaust",
+"{count} folders" => "{count} kausta",
+"1 file" => "1 fail",
+"{count} files" => "{count} faili",
+"Nothing in here. Your trash bin is empty!" => "Siin pole midagi. Sinu prügikast on tühi!",
+"Restore" => "Taasta",
+"Delete" => "Kustuta"
+);
diff --git a/apps/files_trashbin/l10n/eu.php b/apps/files_trashbin/l10n/eu.php
new file mode 100644
index 00000000000..3622de2694c
--- /dev/null
+++ b/apps/files_trashbin/l10n/eu.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Ezin izan da %s betirako ezabatu",
+"Couldn't restore %s" => "Ezin izan da %s berreskuratu",
+"perform restore operation" => "berreskuratu",
+"delete file permanently" => "ezabatu fitxategia betirako",
+"Delete permanently" => "Ezabatu betirako",
+"Name" => "Izena",
+"Deleted" => "Ezabatuta",
+"1 folder" => "karpeta bat",
+"{count} folders" => "{count} karpeta",
+"1 file" => "fitxategi bat",
+"{count} files" => "{count} fitxategi",
+"Nothing in here. Your trash bin is empty!" => "Ez dago ezer ez. Zure zakarrontzia hutsik dago!",
+"Restore" => "Berrezarri",
+"Delete" => "Ezabatu",
+"Deleted Files" => "Ezabatutako Fitxategiak"
+);
diff --git a/apps/files_trashbin/l10n/fa.php b/apps/files_trashbin/l10n/fa.php
new file mode 100644
index 00000000000..879ee722a63
--- /dev/null
+++ b/apps/files_trashbin/l10n/fa.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"Delete permanently" => "حذف قطعی",
+"Name" => "نام",
+"1 folder" => "1 پوشه",
+"{count} folders" => "{ شمار} پوشه ها",
+"1 file" => "1 پرونده",
+"{count} files" => "{ شمار } فایل ها",
+"Restore" => "بازیابی",
+"Delete" => "حذف"
+);
diff --git a/apps/files_trashbin/l10n/fi_FI.php b/apps/files_trashbin/l10n/fi_FI.php
new file mode 100644
index 00000000000..30aef805563
--- /dev/null
+++ b/apps/files_trashbin/l10n/fi_FI.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Kohdetta %s ei voitu poistaa pysyvästi",
+"Couldn't restore %s" => "Kohteen %s palautus epäonnistui",
+"perform restore operation" => "suorita palautustoiminto",
+"delete file permanently" => "poista tiedosto pysyvästi",
+"Delete permanently" => "Poista pysyvästi",
+"Name" => "Nimi",
+"Deleted" => "Poistettu",
+"1 folder" => "1 kansio",
+"{count} folders" => "{count} kansiota",
+"1 file" => "1 tiedosto",
+"{count} files" => "{count} tiedostoa",
+"Nothing in here. Your trash bin is empty!" => "Tyhjää täynnä! Roskakorissa ei ole mitään.",
+"Restore" => "Palauta",
+"Delete" => "Poista",
+"Deleted Files" => "Poistetut tiedostot"
+);
diff --git a/apps/files_trashbin/l10n/fr.php b/apps/files_trashbin/l10n/fr.php
new file mode 100644
index 00000000000..092e1e65d1b
--- /dev/null
+++ b/apps/files_trashbin/l10n/fr.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Impossible d'effacer %s de façon permanente",
+"Couldn't restore %s" => "Impossible de restaurer %s",
+"perform restore operation" => "effectuer l'opération de restauration",
+"delete file permanently" => "effacer définitivement le fichier",
+"Delete permanently" => "Supprimer de façon définitive",
+"Name" => "Nom",
+"Deleted" => "Effacé",
+"1 folder" => "1 dossier",
+"{count} folders" => "{count} dossiers",
+"1 file" => "1 fichier",
+"{count} files" => "{count} fichiers",
+"Nothing in here. Your trash bin is empty!" => "Il n'y a rien ici. Votre corbeille est vide !",
+"Restore" => "Restaurer",
+"Delete" => "Supprimer",
+"Deleted Files" => "Fichiers effacés"
+);
diff --git a/apps/files_trashbin/l10n/gl.php b/apps/files_trashbin/l10n/gl.php
new file mode 100644
index 00000000000..da44b1bee38
--- /dev/null
+++ b/apps/files_trashbin/l10n/gl.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Non foi posíbel eliminar %s permanente",
+"Couldn't restore %s" => "Non foi posíbel restaurar %s",
+"perform restore operation" => "realizar a operación de restauración",
+"delete file permanently" => "eliminar o ficheiro permanentemente",
+"Delete permanently" => "Eliminar permanentemente",
+"Name" => "Nome",
+"Deleted" => "Eliminado",
+"1 folder" => "1 cartafol",
+"{count} folders" => "{count} cartafoles",
+"1 file" => "1 ficheiro",
+"{count} files" => "{count} ficheiros",
+"Nothing in here. Your trash bin is empty!" => "Aquí non hai nada. O cesto do lixo está baleiro!",
+"Restore" => "Restablecer",
+"Delete" => "Eliminar",
+"Deleted Files" => "Ficheiros eliminados"
+);
diff --git a/apps/files_trashbin/l10n/he.php b/apps/files_trashbin/l10n/he.php
new file mode 100644
index 00000000000..9c767d2222c
--- /dev/null
+++ b/apps/files_trashbin/l10n/he.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "בלתי אפשרי למחוק את %s לצמיתות",
+"Couldn't restore %s" => "בלתי אפשרי לשחזר את %s",
+"perform restore operation" => "בצע פעולת שחזור",
+"delete file permanently" => "מחק קובץ לצמיתות",
+"Delete permanently" => "מחק לצמיתות",
+"Name" => "שם",
+"Deleted" => "נמחק",
+"1 folder" => "תיקייה אחת",
+"{count} folders" => "{count} תיקיות",
+"1 file" => "קובץ אחד",
+"{count} files" => "{count} קבצים",
+"Nothing in here. Your trash bin is empty!" => "שום דבר כאן. סל המחזור שלך ריק!",
+"Restore" => "שחזר",
+"Delete" => "מחיקה"
+);
diff --git a/apps/files_trashbin/l10n/hr.php b/apps/files_trashbin/l10n/hr.php
new file mode 100644
index 00000000000..2cb86adfd40
--- /dev/null
+++ b/apps/files_trashbin/l10n/hr.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Ime",
+"Delete" => "Obriši"
+);
diff --git a/apps/files_trashbin/l10n/hu_HU.php b/apps/files_trashbin/l10n/hu_HU.php
new file mode 100644
index 00000000000..1d86190daa3
--- /dev/null
+++ b/apps/files_trashbin/l10n/hu_HU.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Nem sikerült %s végleges törlése",
+"Couldn't restore %s" => "Nem sikerült %s visszaállítása",
+"perform restore operation" => "a visszaállítás végrehajtása",
+"delete file permanently" => "az állomány végleges törlése",
+"Delete permanently" => "Végleges törlés",
+"Name" => "Név",
+"Deleted" => "Törölve",
+"1 folder" => "1 mappa",
+"{count} folders" => "{count} mappa",
+"1 file" => "1 fájl",
+"{count} files" => "{count} fájl",
+"Nothing in here. Your trash bin is empty!" => "Itt nincs semmi. Az Ön szemetes mappája üres!",
+"Restore" => "Visszaállítás",
+"Delete" => "Törlés",
+"Deleted Files" => "Törölt fájlok"
+);
diff --git a/apps/files_trashbin/l10n/hy.php b/apps/files_trashbin/l10n/hy.php
new file mode 100644
index 00000000000..3b80487278a
--- /dev/null
+++ b/apps/files_trashbin/l10n/hy.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Delete" => "Ջնջել"
+);
diff --git a/apps/files_trashbin/l10n/ia.php b/apps/files_trashbin/l10n/ia.php
new file mode 100644
index 00000000000..0a51752312f
--- /dev/null
+++ b/apps/files_trashbin/l10n/ia.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nomine",
+"Delete" => "Deler"
+);
diff --git a/apps/files_trashbin/l10n/id.php b/apps/files_trashbin/l10n/id.php
new file mode 100644
index 00000000000..ab5fe25ac6b
--- /dev/null
+++ b/apps/files_trashbin/l10n/id.php
@@ -0,0 +1,15 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s",
+"Couldn't restore %s" => "Tidak dapat memulihkan %s",
+"perform restore operation" => "jalankan operasi pemulihan",
+"delete file permanently" => "hapus berkas secara permanen",
+"Name" => "Nama",
+"Deleted" => "Dihapus",
+"1 folder" => "1 map",
+"{count} folders" => "{count} map",
+"1 file" => "1 berkas",
+"{count} files" => "{count} berkas",
+"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!",
+"Restore" => "Pulihkan",
+"Delete" => "Hapus"
+);
diff --git a/apps/files_trashbin/l10n/is.php b/apps/files_trashbin/l10n/is.php
new file mode 100644
index 00000000000..fba36c91cb5
--- /dev/null
+++ b/apps/files_trashbin/l10n/is.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nafn",
+"1 folder" => "1 mappa",
+"{count} folders" => "{count} möppur",
+"1 file" => "1 skrá",
+"{count} files" => "{count} skrár",
+"Delete" => "Eyða"
+);
diff --git a/apps/files_trashbin/l10n/it.php b/apps/files_trashbin/l10n/it.php
new file mode 100644
index 00000000000..14400624ce5
--- /dev/null
+++ b/apps/files_trashbin/l10n/it.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Impossibile eliminare %s definitivamente",
+"Couldn't restore %s" => "Impossibile ripristinare %s",
+"perform restore operation" => "esegui operazione di ripristino",
+"delete file permanently" => "elimina il file definitivamente",
+"Delete permanently" => "Elimina definitivamente",
+"Name" => "Nome",
+"Deleted" => "Eliminati",
+"1 folder" => "1 cartella",
+"{count} folders" => "{count} cartelle",
+"1 file" => "1 file",
+"{count} files" => "{count} file",
+"Nothing in here. Your trash bin is empty!" => "Qui non c'è niente. Il tuo cestino è vuoto.",
+"Restore" => "Ripristina",
+"Delete" => "Elimina",
+"Deleted Files" => "File eliminati"
+);
diff --git a/apps/files_trashbin/l10n/ja_JP.php b/apps/files_trashbin/l10n/ja_JP.php
new file mode 100644
index 00000000000..a6e4261bc60
--- /dev/null
+++ b/apps/files_trashbin/l10n/ja_JP.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "%s を完全に削除出来ませんでした",
+"Couldn't restore %s" => "%s を復元出来ませんでした",
+"perform restore operation" => "復元操作を実行する",
+"delete file permanently" => "ファイルを完全に削除する",
+"Delete permanently" => "完全に削除する",
+"Name" => "名前",
+"Deleted" => "削除済み",
+"1 folder" => "1 フォルダ",
+"{count} folders" => "{count} フォルダ",
+"1 file" => "1 ファイル",
+"{count} files" => "{count} ファイル",
+"Nothing in here. Your trash bin is empty!" => "ここには何もありません。ゴミ箱は空です!",
+"Restore" => "復元",
+"Delete" => "削除",
+"Deleted Files" => "削除されたファイル"
+);
diff --git a/apps/files_trashbin/l10n/ka_GE.php b/apps/files_trashbin/l10n/ka_GE.php
new file mode 100644
index 00000000000..05068767863
--- /dev/null
+++ b/apps/files_trashbin/l10n/ka_GE.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "სახელი",
+"1 folder" => "1 საქაღალდე",
+"{count} folders" => "{count} საქაღალდე",
+"1 file" => "1 ფაილი",
+"{count} files" => "{count} ფაილი",
+"Delete" => "წაშლა"
+);
diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php
new file mode 100644
index 00000000000..b40546e34b8
--- /dev/null
+++ b/apps/files_trashbin/l10n/ko.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Name" => "이름",
+"1 folder" => "폴더 1개",
+"{count} folders" => "폴더 {count}개",
+"1 file" => "파일 1개",
+"{count} files" => "파일 {count}개",
+"Restore" => "복원",
+"Delete" => "삭제"
+);
diff --git a/apps/files_trashbin/l10n/ku_IQ.php b/apps/files_trashbin/l10n/ku_IQ.php
new file mode 100644
index 00000000000..cbdbe4644d1
--- /dev/null
+++ b/apps/files_trashbin/l10n/ku_IQ.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Name" => "ناو"
+);
diff --git a/apps/files_trashbin/l10n/lb.php b/apps/files_trashbin/l10n/lb.php
new file mode 100644
index 00000000000..01deea23500
--- /dev/null
+++ b/apps/files_trashbin/l10n/lb.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Numm",
+"Delete" => "Läschen"
+);
diff --git a/apps/files_trashbin/l10n/lt_LT.php b/apps/files_trashbin/l10n/lt_LT.php
new file mode 100644
index 00000000000..513c7626c4e
--- /dev/null
+++ b/apps/files_trashbin/l10n/lt_LT.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Pavadinimas",
+"1 folder" => "1 aplankalas",
+"{count} folders" => "{count} aplankalai",
+"1 file" => "1 failas",
+"{count} files" => "{count} failai",
+"Delete" => "Ištrinti"
+);
diff --git a/apps/files_trashbin/l10n/lv.php b/apps/files_trashbin/l10n/lv.php
new file mode 100644
index 00000000000..98a734224ff
--- /dev/null
+++ b/apps/files_trashbin/l10n/lv.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Nevarēja pilnībā izdzēst %s",
+"Couldn't restore %s" => "Nevarēja atjaunot %s",
+"perform restore operation" => "veikt atjaunošanu",
+"delete file permanently" => "dzēst datni pavisam",
+"Delete permanently" => "Dzēst pavisam",
+"Name" => "Nosaukums",
+"Deleted" => "Dzēsts",
+"1 folder" => "1 mape",
+"{count} folders" => "{count} mapes",
+"1 file" => "1 datne",
+"{count} files" => "{count} datnes",
+"Nothing in here. Your trash bin is empty!" => "Šeit nekā nav. Jūsu miskaste ir tukša!",
+"Restore" => "Atjaunot",
+"Delete" => "Dzēst",
+"Deleted Files" => "Dzēstās datnes"
+);
diff --git a/apps/files_trashbin/l10n/mk.php b/apps/files_trashbin/l10n/mk.php
new file mode 100644
index 00000000000..22b288b002f
--- /dev/null
+++ b/apps/files_trashbin/l10n/mk.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Име",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папки",
+"1 file" => "1 датотека",
+"{count} files" => "{count} датотеки",
+"Delete" => "Избриши"
+);
diff --git a/apps/files_trashbin/l10n/ms_MY.php b/apps/files_trashbin/l10n/ms_MY.php
new file mode 100644
index 00000000000..381d599865e
--- /dev/null
+++ b/apps/files_trashbin/l10n/ms_MY.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nama",
+"Delete" => "Padam"
+);
diff --git a/apps/files_trashbin/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php
new file mode 100644
index 00000000000..fa9543a5eb0
--- /dev/null
+++ b/apps/files_trashbin/l10n/nb_NO.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Kunne ikke slette %s fullstendig",
+"Couldn't restore %s" => "Kunne ikke gjenopprette %s",
+"perform restore operation" => "utfør gjenopprettings operasjon",
+"delete file permanently" => "slett filer permanent",
+"Delete permanently" => "Slett permanent",
+"Name" => "Navn",
+"Deleted" => "Slettet",
+"1 folder" => "1 mappe",
+"{count} folders" => "{count} mapper",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Nothing in here. Your trash bin is empty!" => "Ingenting her. Søppelkassen din er tom!",
+"Restore" => "Gjenopprett",
+"Delete" => "Slett"
+);
diff --git a/apps/files_trashbin/l10n/nl.php b/apps/files_trashbin/l10n/nl.php
new file mode 100644
index 00000000000..b33ee8bc4dc
--- /dev/null
+++ b/apps/files_trashbin/l10n/nl.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Kon %s niet permanent verwijderen",
+"Couldn't restore %s" => "Kon %s niet herstellen",
+"perform restore operation" => "uitvoeren restore operatie",
+"delete file permanently" => "verwijder bestanden definitief",
+"Delete permanently" => "Verwijder definitief",
+"Name" => "Naam",
+"Deleted" => "Verwijderd",
+"1 folder" => "1 map",
+"{count} folders" => "{count} mappen",
+"1 file" => "1 bestand",
+"{count} files" => "{count} bestanden",
+"Nothing in here. Your trash bin is empty!" => "Niets te vinden. Uw prullenbak is leeg!",
+"Restore" => "Herstellen",
+"Delete" => "Verwijder",
+"Deleted Files" => "Verwijderde bestanden"
+);
diff --git a/apps/files_trashbin/l10n/nn_NO.php b/apps/files_trashbin/l10n/nn_NO.php
new file mode 100644
index 00000000000..f8ab465ee42
--- /dev/null
+++ b/apps/files_trashbin/l10n/nn_NO.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Namn",
+"Delete" => "Slett"
+);
diff --git a/apps/files_trashbin/l10n/oc.php b/apps/files_trashbin/l10n/oc.php
new file mode 100644
index 00000000000..e6b939dac0c
--- /dev/null
+++ b/apps/files_trashbin/l10n/oc.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nom",
+"Delete" => "Escafa"
+);
diff --git a/apps/files_trashbin/l10n/pl.php b/apps/files_trashbin/l10n/pl.php
new file mode 100644
index 00000000000..c3e7fd8e73d
--- /dev/null
+++ b/apps/files_trashbin/l10n/pl.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Nie można trwale usunąć %s",
+"Couldn't restore %s" => "Nie można przywrócić %s",
+"perform restore operation" => "wykonywanie operacji przywracania",
+"delete file permanently" => "trwale usuń plik",
+"Delete permanently" => "Trwale usuń",
+"Name" => "Nazwa",
+"Deleted" => "Usunięte",
+"1 folder" => "1 folder",
+"{count} folders" => "{count} foldery",
+"1 file" => "1 plik",
+"{count} files" => "{count} pliki",
+"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!",
+"Restore" => "Przywróć",
+"Delete" => "Usuń",
+"Deleted Files" => "Usunięte pliki"
+);
diff --git a/apps/files_trashbin/l10n/pt_BR.php b/apps/files_trashbin/l10n/pt_BR.php
new file mode 100644
index 00000000000..dcf58e083ca
--- /dev/null
+++ b/apps/files_trashbin/l10n/pt_BR.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Não foi possível excluir %s permanentemente",
+"Couldn't restore %s" => "Não foi possível restaurar %s",
+"perform restore operation" => "realizar operação de restauração",
+"delete file permanently" => "excluir arquivo permanentemente",
+"Delete permanently" => "Excluir permanentemente",
+"Name" => "Nome",
+"Deleted" => "Excluído",
+"1 folder" => "1 pasta",
+"{count} folders" => "{count} pastas",
+"1 file" => "1 arquivo",
+"{count} files" => "{count} arquivos",
+"Nothing in here. Your trash bin is empty!" => "Nada aqui. Sua lixeira está vazia!",
+"Restore" => "Restaurar",
+"Delete" => "Excluir",
+"Deleted Files" => "Arquivos Apagados"
+);
diff --git a/apps/files_trashbin/l10n/pt_PT.php b/apps/files_trashbin/l10n/pt_PT.php
new file mode 100644
index 00000000000..f1dc71b44e9
--- /dev/null
+++ b/apps/files_trashbin/l10n/pt_PT.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Não foi possível eliminar %s de forma permanente",
+"Couldn't restore %s" => "Não foi possível restaurar %s",
+"perform restore operation" => "Restaurar",
+"delete file permanently" => "Eliminar permanentemente o(s) ficheiro(s)",
+"Delete permanently" => "Eliminar permanentemente",
+"Name" => "Nome",
+"Deleted" => "Apagado",
+"1 folder" => "1 pasta",
+"{count} folders" => "{count} pastas",
+"1 file" => "1 ficheiro",
+"{count} files" => "{count} ficheiros",
+"Nothing in here. Your trash bin is empty!" => "Não ha ficheiros. O lixo está vazio",
+"Restore" => "Restaurar",
+"Delete" => "Apagar",
+"Deleted Files" => "Ficheiros Apagados"
+);
diff --git a/apps/files_trashbin/l10n/ro.php b/apps/files_trashbin/l10n/ro.php
new file mode 100644
index 00000000000..6a919b62dfb
--- /dev/null
+++ b/apps/files_trashbin/l10n/ro.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Nume",
+"1 folder" => "1 folder",
+"{count} folders" => "{count} foldare",
+"1 file" => "1 fisier",
+"{count} files" => "{count} fisiere",
+"Delete" => "Șterge"
+);
diff --git a/apps/files_trashbin/l10n/ru.php b/apps/files_trashbin/l10n/ru.php
new file mode 100644
index 00000000000..ef493ddcbe8
--- /dev/null
+++ b/apps/files_trashbin/l10n/ru.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "%s не может быть удалён навсегда",
+"Couldn't restore %s" => "%s не может быть восстановлен",
+"perform restore operation" => "выполнить операцию восстановления",
+"delete file permanently" => "удалить файл навсегда",
+"Delete permanently" => "Удалено навсегда",
+"Name" => "Имя",
+"Deleted" => "Удалён",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папок",
+"1 file" => "1 файл",
+"{count} files" => "{count} файлов",
+"Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!",
+"Restore" => "Восстановить",
+"Delete" => "Удалить",
+"Deleted Files" => "Удаленные файлы"
+);
diff --git a/apps/files_trashbin/l10n/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php
new file mode 100644
index 00000000000..9c79c7fba60
--- /dev/null
+++ b/apps/files_trashbin/l10n/ru_RU.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "%s не может быть удалён навсегда",
+"Couldn't restore %s" => "%s не может быть восстановлен",
+"perform restore operation" => "выполнить операцию восстановления",
+"delete file permanently" => "удалить файл навсегда",
+"Delete permanently" => "Удалить навсегда",
+"Name" => "Имя",
+"Deleted" => "Удалён",
+"1 folder" => "1 папка",
+"{count} folders" => "{количество} папок",
+"1 file" => "1 файл",
+"{count} files" => "{количество} файлов",
+"Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!",
+"Restore" => "Восстановить",
+"Delete" => "Удалить",
+"Deleted Files" => "Удаленные файлы"
+);
diff --git a/apps/files_trashbin/l10n/si_LK.php b/apps/files_trashbin/l10n/si_LK.php
new file mode 100644
index 00000000000..71c56329776
--- /dev/null
+++ b/apps/files_trashbin/l10n/si_LK.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"Name" => "නම",
+"1 folder" => "1 ෆොල්ඩරයක්",
+"1 file" => "1 ගොනුවක්",
+"Delete" => "මකා දමන්න"
+);
diff --git a/apps/files_trashbin/l10n/sk_SK.php b/apps/files_trashbin/l10n/sk_SK.php
new file mode 100644
index 00000000000..b7ca91b1c55
--- /dev/null
+++ b/apps/files_trashbin/l10n/sk_SK.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Nemožno zmazať %s navždy",
+"Couldn't restore %s" => "Nemožno obnoviť %s",
+"perform restore operation" => "vykonať obnovu",
+"delete file permanently" => "trvalo zmazať súbor",
+"Delete permanently" => "Zmazať trvalo",
+"Name" => "Meno",
+"Deleted" => "Zmazané",
+"1 folder" => "1 priečinok",
+"{count} folders" => "{count} priečinkov",
+"1 file" => "1 súbor",
+"{count} files" => "{count} súborov",
+"Nothing in here. Your trash bin is empty!" => "Žiadny obsah. Kôš je prázdny!",
+"Restore" => "Obnoviť",
+"Delete" => "Zmazať",
+"Deleted Files" => "Zmazané súbory"
+);
diff --git a/apps/files_trashbin/l10n/sl.php b/apps/files_trashbin/l10n/sl.php
new file mode 100644
index 00000000000..edef7294a4a
--- /dev/null
+++ b/apps/files_trashbin/l10n/sl.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Datoteke %s ni mogoče dokončno izbrisati.",
+"Couldn't restore %s" => "Ni mogoče obnoviti %s",
+"perform restore operation" => "izvedi opravilo obnavljanja",
+"delete file permanently" => "dokončno izbriši datoteko",
+"Delete permanently" => "Izbriši dokončno",
+"Name" => "Ime",
+"Deleted" => "Izbrisano",
+"1 folder" => "1 mapa",
+"{count} folders" => "{count} map",
+"1 file" => "1 datoteka",
+"{count} files" => "{count} datotek",
+"Nothing in here. Your trash bin is empty!" => "Mapa smeti je prazna.",
+"Restore" => "Obnovi",
+"Delete" => "Izbriši",
+"Deleted Files" => "Izbrisane datoteke"
+);
diff --git a/apps/files_trashbin/l10n/sr.php b/apps/files_trashbin/l10n/sr.php
new file mode 100644
index 00000000000..2e7c139e389
--- /dev/null
+++ b/apps/files_trashbin/l10n/sr.php
@@ -0,0 +1,12 @@
+<?php $TRANSLATIONS = array(
+"perform restore operation" => "врати у претходно стање",
+"Name" => "Име",
+"Deleted" => "Обрисано",
+"1 folder" => "1 фасцикла",
+"{count} folders" => "{count} фасцикле/и",
+"1 file" => "1 датотека",
+"{count} files" => "{count} датотеке/а",
+"Nothing in here. Your trash bin is empty!" => "Овде нема ништа. Корпа за отпатке је празна.",
+"Restore" => "Врати",
+"Delete" => "Обриши"
+);
diff --git a/apps/files_trashbin/l10n/sr@latin.php b/apps/files_trashbin/l10n/sr@latin.php
new file mode 100644
index 00000000000..2cb86adfd40
--- /dev/null
+++ b/apps/files_trashbin/l10n/sr@latin.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "Ime",
+"Delete" => "Obriši"
+);
diff --git a/apps/files_trashbin/l10n/sv.php b/apps/files_trashbin/l10n/sv.php
new file mode 100644
index 00000000000..15128e5f776
--- /dev/null
+++ b/apps/files_trashbin/l10n/sv.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Kunde inte radera %s permanent",
+"Couldn't restore %s" => "Kunde inte återställa %s",
+"perform restore operation" => "utför återställning",
+"delete file permanently" => "radera filen permanent",
+"Delete permanently" => "Radera permanent",
+"Name" => "Namn",
+"Deleted" => "Raderad",
+"1 folder" => "1 mapp",
+"{count} folders" => "{count} mappar",
+"1 file" => "1 fil",
+"{count} files" => "{count} filer",
+"Nothing in here. Your trash bin is empty!" => "Ingenting här. Din papperskorg är tom!",
+"Restore" => "Återskapa",
+"Delete" => "Radera",
+"Deleted Files" => "Raderade filer"
+);
diff --git a/apps/files_trashbin/l10n/ta_LK.php b/apps/files_trashbin/l10n/ta_LK.php
new file mode 100644
index 00000000000..f21e5fc750d
--- /dev/null
+++ b/apps/files_trashbin/l10n/ta_LK.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "பெயர்",
+"1 folder" => "1 கோப்புறை",
+"{count} folders" => "{எண்ணிக்கை} கோப்புறைகள்",
+"1 file" => "1 கோப்பு",
+"{count} files" => "{எண்ணிக்கை} கோப்புகள்",
+"Delete" => "நீக்குக"
+);
diff --git a/apps/files_trashbin/l10n/th_TH.php b/apps/files_trashbin/l10n/th_TH.php
new file mode 100644
index 00000000000..e2875feaa39
--- /dev/null
+++ b/apps/files_trashbin/l10n/th_TH.php
@@ -0,0 +1,13 @@
+<?php $TRANSLATIONS = array(
+"perform restore operation" => "ดำเนินการคืนค่า",
+"Name" => "ชื่อ",
+"Deleted" => "ลบแล้ว",
+"1 folder" => "1 โฟลเดอร์",
+"{count} folders" => "{count} โฟลเดอร์",
+"1 file" => "1 ไฟล์",
+"{count} files" => "{count} ไฟล์",
+"Nothing in here. Your trash bin is empty!" => "ไม่มีอะไรอยู่ในนี้ ถังขยะของคุณยังว่างอยู่",
+"Restore" => "คืนค่า",
+"Delete" => "ลบ",
+"Deleted Files" => "ไฟล์ที่ลบทิ้ง"
+);
diff --git a/apps/files_trashbin/l10n/tr.php b/apps/files_trashbin/l10n/tr.php
new file mode 100644
index 00000000000..c874d73316a
--- /dev/null
+++ b/apps/files_trashbin/l10n/tr.php
@@ -0,0 +1,16 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "%s Kalıcı olarak silinemedi",
+"Couldn't restore %s" => "%s Geri yüklenemedi",
+"perform restore operation" => "Geri yükleme işlemini gerçekleştir",
+"delete file permanently" => "Dosyayı kalıcı olarak sil",
+"Delete permanently" => "Kalıcı olarak sil",
+"Name" => "İsim",
+"Deleted" => "Silindi",
+"1 folder" => "1 dizin",
+"{count} folders" => "{count} dizin",
+"1 file" => "1 dosya",
+"{count} files" => "{count} dosya",
+"Nothing in here. Your trash bin is empty!" => "Burası boş. Çöp kutun tamamen boş.",
+"Restore" => "Geri yükle",
+"Delete" => "Sil"
+);
diff --git a/apps/files_trashbin/l10n/uk.php b/apps/files_trashbin/l10n/uk.php
new file mode 100644
index 00000000000..e0f22d4387b
--- /dev/null
+++ b/apps/files_trashbin/l10n/uk.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Неможливо видалити %s назавжди",
+"Couldn't restore %s" => "Неможливо відновити %s",
+"perform restore operation" => "виконати операцію відновлення",
+"delete file permanently" => "видалити файл назавжди",
+"Delete permanently" => "Видалити назавжди",
+"Name" => "Ім'я",
+"Deleted" => "Видалено",
+"1 folder" => "1 папка",
+"{count} folders" => "{count} папок",
+"1 file" => "1 файл",
+"{count} files" => "{count} файлів",
+"Nothing in here. Your trash bin is empty!" => "Нічого немає. Ваший кошик для сміття пустий!",
+"Restore" => "Відновити",
+"Delete" => "Видалити",
+"Deleted Files" => "Видалено Файлів"
+);
diff --git a/apps/files_trashbin/l10n/vi.php b/apps/files_trashbin/l10n/vi.php
new file mode 100644
index 00000000000..7bc1fd237d9
--- /dev/null
+++ b/apps/files_trashbin/l10n/vi.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "Không thể óa %s vĩnh viễn",
+"Couldn't restore %s" => "Không thể khôi phục %s",
+"perform restore operation" => "thực hiện phục hồi",
+"delete file permanently" => "xóa file vĩnh viễn",
+"Delete permanently" => "Xóa vĩnh vễn",
+"Name" => "Tên",
+"Deleted" => "Đã xóa",
+"1 folder" => "1 thư mục",
+"{count} folders" => "{count} thư mục",
+"1 file" => "1 tập tin",
+"{count} files" => "{count} tập tin",
+"Nothing in here. Your trash bin is empty!" => "Không có gì ở đây. Thùng rác của bạn rỗng!",
+"Restore" => "Khôi phục",
+"Delete" => "Xóa",
+"Deleted Files" => "File đã xóa"
+);
diff --git a/apps/files_trashbin/l10n/zh_CN.GB2312.php b/apps/files_trashbin/l10n/zh_CN.GB2312.php
new file mode 100644
index 00000000000..606d80d441b
--- /dev/null
+++ b/apps/files_trashbin/l10n/zh_CN.GB2312.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Name" => "名称",
+"1 folder" => "1 个文件夹",
+"{count} folders" => "{count} 个文件夹",
+"1 file" => "1 个文件",
+"{count} files" => "{count} 个文件",
+"Delete" => "删除"
+);
diff --git a/apps/files_trashbin/l10n/zh_CN.php b/apps/files_trashbin/l10n/zh_CN.php
new file mode 100644
index 00000000000..c2cc1f123e2
--- /dev/null
+++ b/apps/files_trashbin/l10n/zh_CN.php
@@ -0,0 +1,17 @@
+<?php $TRANSLATIONS = array(
+"Couldn't delete %s permanently" => "无法彻底删除文件%s",
+"Couldn't restore %s" => "无法恢复%s",
+"perform restore operation" => "执行恢复操作",
+"delete file permanently" => "彻底删除文件",
+"Delete permanently" => "永久删除",
+"Name" => "名称",
+"Deleted" => "已删除",
+"1 folder" => "1个文件夹",
+"{count} folders" => "{count} 个文件夹",
+"1 file" => "1 个文件",
+"{count} files" => "{count} 个文件",
+"Nothing in here. Your trash bin is empty!" => "这里没有东西. 你的回收站是空的!",
+"Restore" => "恢复",
+"Delete" => "删除",
+"Deleted Files" => "已删除文件"
+);
diff --git a/apps/files_trashbin/l10n/zh_HK.php b/apps/files_trashbin/l10n/zh_HK.php
new file mode 100644
index 00000000000..6967e190355
--- /dev/null
+++ b/apps/files_trashbin/l10n/zh_HK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Name" => "名稱",
+"Delete" => "刪除"
+);
diff --git a/apps/files_trashbin/l10n/zh_TW.php b/apps/files_trashbin/l10n/zh_TW.php
new file mode 100644
index 00000000000..baa3c0c1ada
--- /dev/null
+++ b/apps/files_trashbin/l10n/zh_TW.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"Delete permanently" => "永久刪除",
+"Name" => "名稱",
+"1 folder" => "1 個資料夾",
+"{count} folders" => "{count} 個資料夾",
+"1 file" => "1 個檔案",
+"{count} files" => "{count} 個檔案",
+"Delete" => "刪除",
+"Deleted Files" => "已刪除的檔案"
+);
diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php
new file mode 100644
index 00000000000..9081706a2c5
--- /dev/null
+++ b/apps/files_trashbin/lib/hooks.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * ownCloud - trash bin
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * This class contains all hooks.
+ */
+
+namespace OCA\Files_Trashbin;
+
+class Hooks {
+
+ /**
+ * @brief Copy files to trash bin
+ * @param array
+ *
+ * This function is connected to the delete signal of OC_Filesystem
+ * to copy the file to the trash bin
+ */
+ public static function remove_hook($params) {
+
+ if ( \OCP\App::isEnabled('files_trashbin') ) {
+ $path = $params['path'];
+ Trashbin::move2trash($path);
+ }
+ }
+}
diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php
new file mode 100644
index 00000000000..33abe608d8f
--- /dev/null
+++ b/apps/files_trashbin/lib/trash.php
@@ -0,0 +1,517 @@
+<?php
+/**
+ * ownCloud - trash bin
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin;
+
+class Trashbin {
+ // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days)
+ const DEFAULT_RETENTION_OBLIGATION=180;
+
+ // unit: percentage; 50% of available disk space/quota
+ const DEFAULTMAXSIZE=50;
+
+ /**
+ * move file to the trash bin
+ *
+ * @param $file_path path to the deleted file/directory relative to the files root directory
+ */
+ public static function move2trash($file_path) {
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/'. $user);
+ if (!$view->is_dir('files_trashbin')) {
+ $view->mkdir('files_trashbin');
+ $view->mkdir("files_trashbin/files");
+ $view->mkdir("files_trashbin/versions");
+ $view->mkdir("files_trashbin/keyfiles");
+ }
+
+ $path_parts = pathinfo($file_path);
+
+ $deleted = $path_parts['basename'];
+ $location = $path_parts['dirname'];
+ $timestamp = time();
+ $mime = $view->getMimeType('files'.$file_path);
+
+ if ( $view->is_dir('files'.$file_path) ) {
+ $type = 'dir';
+ } else {
+ $type = 'file';
+ }
+
+ $trashbinSize = self::getTrashbinSize($user);
+ if ( $trashbinSize === false || $trashbinSize < 0 ) {
+ $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
+ }
+ $trashbinSize += self::copy_recursive($file_path, 'files_trashbin/files/'.$deleted.'.d'.$timestamp, $view);
+
+ if ( $view->file_exists('files_trashbin/files/'.$deleted.'.d'.$timestamp) ) {
+ $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (`id`,`timestamp`,`location`,`type`,`mime`,`user`) VALUES (?,?,?,?,?,?)");
+ $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user));
+ if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin.
+ $view->deleteAll('files_trashbin/files/'.$deleted.'.d'.$timestamp);
+ \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR);
+ return;
+ }
+ \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash',
+ array('filePath' => \OC\Files\Filesystem::normalizePath($file_path),
+ 'trashPath' => \OC\Files\Filesystem::normalizePath($deleted.'.d'.$timestamp)));
+
+ // Take care of file versions
+ if ( \OCP\App::isEnabled('files_versions') ) {
+ if ( $view->is_dir('files_versions/'.$file_path) ) {
+ $trashbinSize += self::calculateSize(new \OC\Files\View('/'. $user.'/files_versions/'.$file_path));
+ $view->rename('files_versions/'.$file_path, 'files_trashbin/versions'. $deleted.'.d'.$timestamp);
+ } else if ( $versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path) ) {
+ foreach ($versions as $v) {
+ $trashbinSize += $view->filesize('files_versions'.$v['path'].'.v'.$v['version']);
+ $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'files_trashbin/versions/'. $deleted.'.v'.$v['version'].'.d'.$timestamp);
+ }
+ }
+ }
+
+ // Take care of encryption keys
+ $keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/'.$file_path);
+ if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile.'.key') ) {
+ if ( $view->is_dir('files'.$file_path) ) {
+ $trashbinSize += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile));
+ $view->rename($keyfile, 'files_trashbin/keyfiles/'. $deleted.'.d'.$timestamp);
+ } else {
+ $trashbinSize += $view->filesize($keyfile.'.key');
+ $view->rename($keyfile.'.key', 'files_trashbin/keyfiles/'. $deleted.'.key.d'.$timestamp);
+ }
+ }
+ } else {
+ \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin', \OC_log::ERROR);
+ }
+
+ // get available disk space for user
+ $quota = \OC_Preferences::getValue($user, 'files', 'quota');
+ if ( $quota === null || $quota === 'default') {
+ $quota = \OC_Appconfig::getValue('files', 'default_quota');
+ }
+ if ( $quota === null || $quota === 'none' ) {
+ $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers());
+ } else {
+ $quota = \OCP\Util::computerFileSize($quota);
+ }
+
+ // calculate available space for trash bin
+ $rootInfo = $view->getFileInfo('/files');
+ $free = $quota-$rootInfo['size']; // remaining free space for user
+ if ( $free > 0 ) {
+ $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions
+ } else {
+ $availableSpace = $free-$trashbinSize;
+ }
+ $trashbinSize -= self::expire($availableSpace);
+
+ self::setTrashbinSize($user, $trashbinSize);
+
+ }
+
+
+ /**
+ * restore files from trash bin
+ * @param $file path to the deleted file
+ * @param $filename name of the file
+ * @param $timestamp time when the file was deleted
+ */
+ public static function restore($file, $filename, $timestamp) {
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/'.$user);
+
+ $trashbinSize = self::getTrashbinSize($user);
+ if ( $trashbinSize === false || $trashbinSize < 0 ) {
+ $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
+ }
+ if ( $timestamp ) {
+ $query = \OC_DB::prepare('SELECT `location`,`type` FROM *PREFIX*files_trash'
+ .' WHERE `user`=? AND `id`=? AND `timestamp`=?');
+ $result = $query->execute(array($user,$filename,$timestamp))->fetchAll();
+ if ( count($result) != 1 ) {
+ \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR);
+ return false;
+ }
+
+ // if location no longer exists, restore file in the root directory
+ $location = $result[0]['location'];
+ if ( $result[0]['location'] != '/' &&
+ (!$view->is_dir('files'.$result[0]['location']) ||
+ !$view->isUpdatable('files'.$result[0]['location'])) ) {
+ $location = '';
+ }
+ } else {
+ $path_parts = pathinfo($file);
+ $result[] = array(
+ 'location' => $path_parts['dirname'],
+ 'type' => $view->is_dir('/files_trashbin/files/'.$file) ? 'dir' : 'files',
+ );
+ $location = '';
+ }
+
+ $source = \OC\Files\Filesystem::normalizePath('files_trashbin/files/'.$file);
+ $target = \OC\Files\Filesystem::normalizePath('files/'.$location.'/'.$filename);
+
+ // we need a extension in case a file/dir with the same name already exists
+ $ext = self::getUniqueExtension($location, $filename, $view);
+ $mtime = $view->filemtime($source);
+ if( $view->rename($source, $target.$ext) ) {
+ $view->touch($target.$ext, $mtime);
+ \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore',
+ array('filePath' => \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext),
+ 'trashPath' => \OC\Files\Filesystem::normalizePath($file)));
+ if ($view->is_dir($target.$ext)) {
+ $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.$target.$ext));
+ } else {
+ $trashbinSize -= $view->filesize($target.$ext);
+ }
+ // if versioning app is enabled, copy versions from the trash bin back to the original location
+ if ( \OCP\App::isEnabled('files_versions') ) {
+ if ($timestamp ) {
+ $versionedFile = $filename;
+ } else {
+ $versionedFile = $file;
+ }
+ if ( $result[0]['type'] === 'dir' ) {
+ $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.'files_trashbin/versions/'. $file));
+ $view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/'. $file), \OC\Files\Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext));
+ } else if ( $versions = self::getVersionsFromTrash($versionedFile, $timestamp) ) {
+ foreach ($versions as $v) {
+ if ($timestamp ) {
+ $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp);
+ $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v);
+ } else {
+ $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v);
+ $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v);
+ }
+ }
+ }
+ }
+
+ // Take care of encryption keys TODO! Get '.key' in file between file name and delete date (also for permanent delete!)
+ $parts = pathinfo($file);
+ if ( $result[0]['type'] === 'dir' ) {
+ $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename);
+ } else {
+ $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename.'.key');
+ }
+ if ($timestamp) {
+ $keyfile .= '.d'.$timestamp;
+ }
+ if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) {
+ if ( $result[0]['type'] === 'dir' ) {
+ $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile));
+ $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename);
+ } else {
+ $trashbinSize -= $view->filesize($keyfile);
+ $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename.'.key');
+ }
+ }
+
+ if ( $timestamp ) {
+ $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE `user`=? AND `id`=? AND `timestamp`=?');
+ $query->execute(array($user,$filename,$timestamp));
+ }
+
+ self::setTrashbinSize($user, $trashbinSize);
+
+ return true;
+ } else {
+ \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename, \OC_log::ERROR);
+ }
+
+ return false;
+ }
+
+ /**
+ * delete file from trash bin permanently
+ * @param $filename path to the file
+ * @param $timestamp of deletion time
+ * @return size of deleted files
+ */
+ public static function delete($filename, $timestamp=null) {
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/'.$user);
+ $size = 0;
+
+ $trashbinSize = self::getTrashbinSize($user);
+ if ( $trashbinSize === false || $trashbinSize < 0 ) {
+ $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
+ }
+
+ if ( $timestamp ) {
+ $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE `user`=? AND `id`=? AND `timestamp`=?');
+ $query->execute(array($user,$filename,$timestamp));
+ $file = $filename.'.d'.$timestamp;
+ } else {
+ $file = $filename;
+ }
+
+ if ( \OCP\App::isEnabled('files_versions') ) {
+ if ($view->is_dir('files_trashbin/versions/'.$file)) {
+ $size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
+ $view->unlink('files_trashbin/versions/'.$file);
+ } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) {
+ foreach ($versions as $v) {
+ if ($timestamp ) {
+ $size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp);
+ $view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp);
+ } else {
+ $size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v);
+ $view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v);
+ }
+ }
+ }
+ }
+
+ // Take care of encryption keys
+ $parts = pathinfo($file);
+ if ( $view->is_dir('/files_trashbin/files/'.$file) ) {
+ $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename);
+ } else {
+ $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename.'.key');
+ }
+ if ($timestamp) {
+ $keyfile .= '.d'.$timestamp;
+ }
+ if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) {
+ if ( $view->is_dir($keyfile) ) {
+ $size += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile));
+ } else {
+ $size += $view->filesize($keyfile);
+ }
+ $view->unlink($keyfile);
+ }
+
+ if ($view->is_dir('/files_trashbin/files/'.$file)) {
+ $size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
+ } else {
+ $size += $view->filesize('/files_trashbin/files/'.$file);
+ }
+ $view->unlink('/files_trashbin/files/'.$file);
+ $trashbinSize -= $size;
+ self::setTrashbinSize($user, $trashbinSize);
+
+ return $size;
+ }
+
+ /**
+ * check to see whether a file exists in trashbin
+ * @param $filename path to the file
+ * @param $timestamp of deletion time
+ * @return true if file exists, otherwise false
+ */
+ public static function file_exists($filename, $timestamp=null) {
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/'.$user);
+
+ if ($timestamp) {
+ $filename = $filename.'.d'.$timestamp;
+ } else {
+ $filename = $filename;
+ }
+
+ $target = \OC\Files\Filesystem::normalizePath('files_trashbin/files/'.$filename);
+ return $view->file_exists($target);
+ }
+
+ /**
+ * clean up the trash bin
+ * @param max. available disk space for trashbin
+ */
+ private static function expire($availableSpace) {
+
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/'.$user);
+ $size = 0;
+
+ $query = \OC_DB::prepare('SELECT `location`,`type`,`id`,`timestamp` FROM *PREFIX*files_trash WHERE `user`=?');
+ $result = $query->execute(array($user))->fetchAll();
+
+ $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation',
+ self::DEFAULT_RETENTION_OBLIGATION);
+
+ $limit = time() - ($retention_obligation * 86400);
+
+ foreach ( $result as $r ) {
+ $timestamp = $r['timestamp'];
+ $filename = $r['id'];
+ if ( $r['timestamp'] < $limit ) {
+ $size += self::delete($filename, $timestamp);
+ }
+ }
+ $availableSpace = $availableSpace + $size;
+ // if size limit for trash bin reached, delete oldest files in trash bin
+ if ($availableSpace < 0) {
+ $query = \OC_DB::prepare('SELECT `location`,`type`,`id`,`timestamp` FROM *PREFIX*files_trash'
+ .' WHERE `user`=? ORDER BY `timestamp` ASC');
+ $result = $query->execute(array($user))->fetchAll();
+ $length = count($result);
+ $i = 0;
+ while ( $i < $length && $availableSpace < 0 ) {
+ $tmp = self::delete($result[$i]['id'], $result[$i]['timestamp']);
+ $availableSpace += $tmp;
+ $size += $tmp;
+ $i++;
+ }
+
+
+ }
+
+ return $size;
+ }
+
+ /**
+ * recursive copy to copy a whole directory
+ *
+ * @param $source source path, relative to the users files directory
+ * @param $destination destination path relative to the users root directoy
+ * @param $view file view for the users root directory
+ */
+ private static function copy_recursive( $source, $destination, $view ) {
+ $size = 0;
+ if ( $view->is_dir( 'files'.$source ) ) {
+ $view->mkdir( $destination );
+ $view->touch($destination, $view->filemtime('files'.$source));
+ foreach ( \OC_Files::getDirectoryContent($source) as $i ) {
+ $pathDir = $source.'/'.$i['name'];
+ if ( $view->is_dir('files'.$pathDir) ) {
+ $size += self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view);
+ } else {
+ $size += $view->filesize('files'.$pathDir);
+ $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] );
+ $view->touch($destination . '/' . $i['name'], $view->filemtime('files'.$pathDir));
+ }
+ }
+ } else {
+ $size += $view->filesize('files'.$source);
+ $view->copy( 'files'.$source, $destination );
+ $view->touch($destination, $view->filemtime('files'.$source));
+ }
+ return $size;
+ }
+
+ /**
+ * find all versions which belong to the file we want to restore
+ * @param $filename name of the file which should be restored
+ * @param $timestamp timestamp when the file was deleted
+ */
+ private static function getVersionsFromTrash($filename, $timestamp) {
+ $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_trashbin/versions');
+ $versionsName = $view->getLocalFile($filename);
+ $versions = array();
+ if ($timestamp ) {
+ // fetch for old versions
+ $matches = glob( $versionsName.'.v*.d'.$timestamp );
+ $offset = -strlen($timestamp)-2;
+ } else {
+ $matches = glob( $versionsName.'.v*' );
+ }
+
+ foreach( $matches as $ma ) {
+ if ( $timestamp ) {
+ $parts = explode( '.v', substr($ma, 0, $offset) );
+ $versions[] = ( end( $parts ) );
+ } else {
+ $parts = explode( '.v', $ma );
+ $versions[] = ( end( $parts ) );
+ }
+ }
+ return $versions;
+ }
+
+ /**
+ * find unique extension for restored file if a file with the same name already exists
+ * @param $location where the file should be restored
+ * @param $filename name of the file
+ * @param $view filesystem view relative to users root directory
+ * @return string with unique extension
+ */
+ private static function getUniqueExtension($location, $filename, $view) {
+ $ext = '';
+ if ( $view->file_exists('files'.$location.'/'.$filename) ) {
+ $tmpext = '.restored';
+ $ext = $tmpext;
+ $i = 1;
+ while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) {
+ $ext = $tmpext.$i;
+ $i++;
+ }
+ }
+ return $ext;
+ }
+
+ /**
+ * @brief get the size from a given root folder
+ * @param $view file view on the root folder
+ * @return size of the folder
+ */
+ private static function calculateSize($view) {
+ $root = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath('');
+ if (!file_exists($root)) {
+ return 0;
+ }
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root),
+ \RecursiveIteratorIterator::CHILD_FIRST);
+ $size = 0;
+
+ foreach ($iterator as $path) {
+ $relpath = substr($path, strlen($root)-1);
+ if ( !$view->is_dir($relpath) ) {
+ $size += $view->filesize($relpath);
+ }
+ }
+ return $size;
+ }
+
+ /**
+ * get current size of trash bin from a given user
+ *
+ * @param $user user who owns the trash bin
+ * @return mixed trash bin size or false if no trash bin size is stored
+ */
+ private static function getTrashbinSize($user) {
+ $query = \OC_DB::prepare('SELECT `size` FROM *PREFIX*files_trashsize WHERE `user`=?');
+ $result = $query->execute(array($user))->fetchAll();
+
+ if ($result) {
+ return $result[0]['size'];
+ }
+ return false;
+ }
+
+ /**
+ * write to the database how much space is in use for the trash bin
+ *
+ * @param $user owner of the trash bin
+ * @param $size size of the trash bin
+ */
+ private static function setTrashbinSize($user, $size) {
+ if ( self::getTrashbinSize($user) === false) {
+ $query = \OC_DB::prepare('INSERT INTO *PREFIX*files_trashsize (`size`, `user`) VALUES (?, ?)');
+ }else {
+ $query = \OC_DB::prepare('UPDATE *PREFIX*files_trashsize SET `size`=? WHERE `user`=?');
+ }
+ $query->execute(array($size, $user));
+ }
+
+}
diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php
new file mode 100644
index 00000000000..cb5edaa2c91
--- /dev/null
+++ b/apps/files_trashbin/templates/index.php
@@ -0,0 +1,41 @@
+<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]-->
+<div id="controls">
+ <?php print_unescaped($_['breadcrumb']); ?>
+ <div id="file_action_panel"></div>
+</div>
+<div id='notification'></div>
+
+<?php if (isset($_['files']) && count($_['files'])==0 && $_['dirlisting'] == false):?>
+ <div id="emptyfolder"><?php p($l->t('Nothing in here. Your trash bin is empty!'))?></div>
+<?php endif; ?>
+
+<table id="filestable">
+ <thead>
+ <tr>
+ <th id='headerName'>
+ <input type="checkbox" id="select_all" />
+ <span class='name'><?php p($l->t( 'Name' )); ?></span>
+ <span class='selectedActions'>
+ <a href="" class="undelete">
+ <img class="svg" alt="<?php p($l->t( 'Restore' )); ?>"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/undelete.png")); ?>" />
+ <?php p($l->t('Restore'))?>
+ </a>
+ </span>
+ </th>
+ <th id="headerDate">
+ <span id="modified"><?php p($l->t( 'Deleted' )); ?></span>
+ <span class="selectedActions">
+ <a href="" class="delete">
+ <?php p($l->t('Delete'))?>
+ <img class="svg" alt="<?php p($l->t('Delete'))?>"
+ src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
+ </a>
+ </span>
+ </th>
+ </tr>
+ </thead>
+ <tbody id="fileList">
+ <?php print_unescaped($_['fileList']); ?>
+ </tbody>
+</table>
diff --git a/apps/files_trashbin/templates/part.breadcrumb.php b/apps/files_trashbin/templates/part.breadcrumb.php
new file mode 100644
index 00000000000..2801e04e9ad
--- /dev/null
+++ b/apps/files_trashbin/templates/part.breadcrumb.php
@@ -0,0 +1,20 @@
+<div class="crumb">
+ <a href="<?php print_unescaped($_['home']); ?>">
+ <img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
+ </a>
+</div>
+<?php if(count($_["breadcrumb"])):?>
+ <div class="crumb svg"
+ data-dir='<?php print_unescaped($_['baseURL']); ?>'>
+ <a href="<?php p($_['baseURL']); ?>"><?php p($l->t("Deleted Files")); ?></a>
+ </div>
+<?php endif;?>
+<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
+ $crumb = $_["breadcrumb"][$i];
+ $dir = str_replace('+', '%20', urlencode($crumb["dir"]));
+ $dir = str_replace('%2F', '/', $dir); ?>
+ <div class="crumb <?php if($i == count($_["breadcrumb"])-1) p('last');?> svg"
+ data-dir='<?php p($dir);?>'>
+ <a href="<?php p($_['baseURL'].$dir); ?>"><?php p($crumb["name"]); ?></a>
+ </div>
+<?php endfor;
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php
new file mode 100644
index 00000000000..92a38bd2635
--- /dev/null
+++ b/apps/files_trashbin/templates/part.list.php
@@ -0,0 +1,71 @@
+<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>">
+<?php foreach($_['files'] as $file):
+ $relative_deleted_date = OCP\relative_modified_date($file['timestamp']);
+ // the older the file, the brighter the shade of grey; days*14
+ $relative_date_color = round((time()-$file['date'])/60/60/24*14);
+ if($relative_date_color>200) $relative_date_color = 200;
+ $name = str_replace('+', '%20', urlencode($file['name']));
+ $name = str_replace('%2F', '/', $name);
+ $directory = str_replace('+', '%20', urlencode($file['directory']));
+ $directory = str_replace('%2F', '/', $directory); ?>
+ <tr data-filename="<?php p($file['name']);?>"
+ data-type="<?php ($file['type'] == 'dir')?p('dir'):p('file')?>"
+ data-mime="<?php p($file['mimetype'])?>"
+ data-permissions='<?php p($file['permissions']); ?>'
+ <?php if ( $_['dirlisting'] ): ?>
+ id="<?php p($file['directory'].'/'.$file['name']);?>"
+ data-file="<?php p($file['directory'].'/'.$file['name']);?>"
+ data-timestamp=''
+ data-dirlisting=1
+ <?php else: ?>
+ id="<?php p($file['name'].'.d'.$file['timestamp']);?>"
+ data-file="<?php p($file['name'].'.d'.$file['timestamp']);?>"
+ data-timestamp='<?php p($file['timestamp']);?>'
+ data-dirlisting=0
+ <?php endif; ?>>
+ <td class="filename svg"
+ <?php if($file['type'] == 'dir'): ?>
+ style="background-image:url(<?php print_unescaped(OCP\mimetype_icon('dir')); ?>)"
+ <?php else: ?>
+ style="background-image:url(<?php print_unescaped(OCP\mimetype_icon($file['mimetype'])); ?>)"
+ <?php endif; ?>
+ >
+ <?php if(!isset($_['readonly']) || !$_['readonly']): ?><input type="checkbox" /><?php endif; ?>
+ <?php if($file['type'] == 'dir'): ?>
+ <?php if( $_['dirlisting'] ): ?>
+ <a class="name" href="<?php p($_['baseURL'].'/'.$name); ?>" title="">
+ <?php else: ?>
+ <a class="name" href="<?php p($_['baseURL'].'/'.$name.'.d'.$file['timestamp']); ?>" title="">
+ <?php endif; ?>
+ <?php else: ?>
+ <?php if( $_['dirlisting'] ): ?>
+ <a class="name" href="<?php p($_['downloadURL'].'/'.$name); ?>" title="">
+ <?php else: ?>
+ <a class="name" href="<?php p($_['downloadURL'].'/'.$name.'.d'.$file['timestamp']);?>" title="">
+ <?php endif; ?>
+ <?php endif; ?>
+ <span class="nametext">
+ <?php if($file['type'] == 'dir'):?>
+ <?php print_unescaped(htmlspecialchars($file['name']));?>
+ <?php else:?>
+ <?php print_unescaped(htmlspecialchars($file['basename']));?><span
+ class='extension'><?php p($file['extension']);?></span>
+ <?php endif;?>
+ </span>
+ <?php if($file['type'] == 'dir'):?>
+ <span class="uploadtext" currentUploads="0">
+ </span>
+ <?php endif;?>
+ </a>
+ </td>
+ <td class="date">
+ <span class="modified"
+ title="<?php p($file['date']); ?>"
+ style="color:rgb(<?php p($relative_date_color.','
+ .$relative_date_color.','
+ .$relative_date_color) ?>)">
+ <?php p($relative_deleted_date); ?>
+ </span>
+ </td>
+ </tr>
+<?php endforeach;
diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php
index 8476e5e8a51..f9174c45a65 100644
--- a/apps/files_versions/ajax/getVersions.php
+++ b/apps/files_versions/ajax/getVersions.php
@@ -1,13 +1,11 @@
<?php
OCP\JSON::checkAppEnabled('files_versions');
-$userDirectory = "/".OCP\USER::getUser()."/files";
$source = $_GET['source'];
+list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($source);
+$count = 5; //show the newest revisions
+if( ($versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $count)) ) {
-if( OCA_Versions\Storage::isversioned( $source ) ) {
-
- $count=5; //show the newest revisions
- $versions = OCA_Versions\Storage::getVersions( $source, $count);
$versionsFormatted = array();
foreach ( $versions AS $version ) {
diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php
index f1b02eb4b92..284b46ee093 100644
--- a/apps/files_versions/ajax/rollbackVersion.php
+++ b/apps/files_versions/ajax/rollbackVersion.php
@@ -8,10 +8,9 @@ $userDirectory = "/".OCP\USER::getUser()."/files";
$file = $_GET['file'];
$revision=(int)$_GET['revision'];
-if( OCA_Versions\Storage::isversioned( $file ) ) {
- if(OCA_Versions\Storage::rollback( $file, $revision )) {
- OCP\JSON::success(array("data" => array( "revision" => $revision, "file" => $file )));
- }else{
- OCP\JSON::error(array("data" => array( "message" => "Could not revert:" . $file )));
- }
+if(OCA\Files_Versions\Storage::rollback( $file, $revision )) {
+ OCP\JSON::success(array("data" => array( "revision" => $revision, "file" => $file )));
+}else{
+ $l = OC_L10N::get('files_versions');
+ OCP\JSON::error(array("data" => array( "message" => $l->t("Could not revert: %s", array($file) ))));
}
diff --git a/apps/files_versions/ajax/togglesettings.php b/apps/files_versions/ajax/togglesettings.php
deleted file mode 100644
index 546b37ae1aa..00000000000
--- a/apps/files_versions/ajax/togglesettings.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-OCP\JSON::checkAppEnabled('files_versions');
-OCP\JSON::checkAdminUser();
-OCP\JSON::callCheck();
-if (OCP\Config::getSystemValue('versions', 'true')=='true') {
- OCP\Config::setSystemValue('versions', 'false');
-} else {
- OCP\Config::setSystemValue('versions', 'true');
-}
diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php
index 746f89a8139..9be1c889682 100644
--- a/apps/files_versions/appinfo/app.php
+++ b/apps/files_versions/appinfo/app.php
@@ -1,16 +1,14 @@
<?php
//require_once 'files_versions/versions.php';
-OC::$CLASSPATH['OCA_Versions\Storage'] = 'apps/files_versions/lib/versions.php';
-OC::$CLASSPATH['OCA_Versions\Hooks'] = 'apps/files_versions/lib/hooks.php';
-
-OCP\App::registerAdmin('files_versions', 'settings');
-OCP\App::registerPersonal('files_versions','settings-personal');
+OC::$CLASSPATH['OCA\Files_Versions\Storage'] = 'files_versions/lib/versions.php';
+OC::$CLASSPATH['OCA\Files_Versions\Hooks'] = 'files_versions/lib/hooks.php';
+OC::$CLASSPATH['OCA\Files_Versions\Capabilities'] = 'files_versions/lib/capabilities.php';
OCP\Util::addscript('files_versions', 'versions');
// Listen to write signals
-OCP\Util::connectHook('OC_Filesystem', 'post_write', "OCA_Versions\Hooks", "write_hook");
+OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook");
// Listen to delete and rename signals
-OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Versions\Hooks", "remove_hook");
-OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook"); \ No newline at end of file
+OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook");
+OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook");
diff --git a/apps/files_versions/appinfo/database.xml b/apps/files_versions/appinfo/database.xml
new file mode 100644
index 00000000000..d3854776980
--- /dev/null
+++ b/apps/files_versions/appinfo/database.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+ <name>*dbprefix*files_versions</name>
+
+ <declaration>
+
+ <field>
+ <name>user</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>size</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>50</length>
+ </field>
+
+ </declaration>
+
+ </table>
+
+</database>
diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml
index e4e5a365d51..44878da5e4d 100644
--- a/apps/files_versions/appinfo/info.xml
+++ b/apps/files_versions/appinfo/info.xml
@@ -4,7 +4,7 @@
<name>Versions</name>
<licence>AGPL</licence>
<author>Frank Karlitschek</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
<description>Versioning of files</description>
<types>
diff --git a/apps/files_versions/appinfo/routes.php b/apps/files_versions/appinfo/routes.php
new file mode 100644
index 00000000000..8cef57c9e4d
--- /dev/null
+++ b/apps/files_versions/appinfo/routes.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Copyright (c) 2013, Tom Needham <tom@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+// Register with the capabilities API
+OC_API::register('get', '/cloud/capabilities', array('OCA\Files_Versions\Capabilities', 'getCapabilities'), 'files_versions', OC_API::USER_AUTH); \ No newline at end of file
diff --git a/apps/files_versions/appinfo/version b/apps/files_versions/appinfo/version
index e6d5cb833c6..e4c0d46e55f 100644
--- a/apps/files_versions/appinfo/version
+++ b/apps/files_versions/appinfo/version
@@ -1 +1 @@
-1.0.2 \ No newline at end of file
+1.0.3 \ No newline at end of file
diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php
index 0ebb34f45e4..719a7208fed 100644
--- a/apps/files_versions/history.php
+++ b/apps/files_versions/history.php
@@ -22,51 +22,56 @@
*/
OCP\User::checkLoggedIn( );
-OCP\Util::addStyle('files_versions','versions');
+OCP\Util::addStyle('files_versions', 'versions');
$tmpl = new OCP\Template( 'files_versions', 'history', 'user' );
+$l = OC_L10N::get('files_versions');
if ( isset( $_GET['path'] ) ) {
$path = $_GET['path'];
- $path = $path;
$tmpl->assign( 'path', $path );
- $versions = new OCA_Versions\Storage();
+ $versions = new OCA\Files_Versions\Storage();
// roll back to old version if button clicked
- if( isset( $_GET['revert'] ) ) {
+ if( isset( $_GET['revert'] ) ) {
if( $versions->rollback( $path, $_GET['revert'] ) ) {
- $tmpl->assign( 'outcome_stat', 'success' );
+ $tmpl->assign( 'outcome_stat', $l->t('success') );
- $tmpl->assign( 'outcome_msg', "File {$_GET['path']} was reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) );
+ $message = $l->t('File %s was reverted to version %s',
+ array($_GET['path'], OCP\Util::formatDate( doubleval($_GET['revert']) ) ) );
+
+ $tmpl->assign( 'outcome_msg', $message);
} else {
- $tmpl->assign( 'outcome_stat', 'failure' );
+ $tmpl->assign( 'outcome_stat', $l->t('failure') );
+
+ $message = $l->t('File %s could not be reverted to version %s',
+ array($_GET['path'], OCP\Util::formatDate( doubleval($_GET['revert']) ) ) );
- $tmpl->assign( 'outcome_msg', "File {$_GET['path']} could not be reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) );
+ $tmpl->assign( 'outcome_msg', $message);
}
}
// show the history only if there is something to show
- if( OCA_Versions\Storage::isversioned( $path ) ) {
-
- $count = 999; //show the newest revisions
- $versions = OCA_Versions\Storage::getVersions( $path, $count);
+ $count = 999; //show the newest revisions
+ list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($path);
+ if( ($versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $count)) ) {
$tmpl->assign( 'versions', array_reverse( $versions ) );
}else{
- $tmpl->assign( 'message', 'No old versions available' );
+ $tmpl->assign( 'message', $l->t('No old versions available') );
}
}else{
- $tmpl->assign( 'message', 'No path specified' );
+ $tmpl->assign( 'message', $l->t('No path specified') );
}
diff --git a/apps/files_versions/js/settings-personal.js b/apps/files_versions/js/settings-personal.js
deleted file mode 100644
index 1e6b036fdab..00000000000
--- a/apps/files_versions/js/settings-personal.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// TODO: allow the button to be clicked only once
-
-$( document ).ready(function(){
- //
- $( '#expireAllBtn' ).click(
-
- function( event ) {
-
- // Prevent page from reloading
- event.preventDefault();
-
- // Show loading gif
- $('.expireAllLoading').show();
-
- $.getJSON(
- OC.filePath('files_versions','ajax','expireAll.php'),
- function(result){
- if (result.status == 'success') {
- $('.expireAllLoading').hide();
- $('#expireAllBtn').html('Expiration successful');
- } else {
-
- // Cancel loading
- $('#expireAllBtn').html('Expiration failed');
-
- // Show Dialog
- OC.dialogs.alert(
- 'Something went wrong, your files may not have been expired',
- 'An error has occurred',
- function(){
- $('#expireAllBtn').html(t('files_versions', 'Expire all versions')+'<img style="display: none;" class="loading" src="'+OC.filePath('core','img','loading.gif')+'" />');
- }
- );
- }
- }
- );
- }
- );
-}); \ No newline at end of file
diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js
index 07c5655560e..a5b24417483 100644
--- a/apps/files_versions/js/versions.js
+++ b/apps/files_versions/js/versions.js
@@ -1,19 +1,9 @@
-$(document).ready(function() {
- $('#versions').bind('change', function() {
- var checked = 1;
- if (!this.checked) {
- checked = 0;
- }
- $.post(OC.filePath('files_versions','ajax','togglesettings.php'), 'versions='+checked);
- });
-});
-
$(document).ready(function(){
if (typeof FileActions !== 'undefined') {
- // Add history button to 'files/index.php'
+ // Add versions button to 'files/index.php'
FileActions.register(
'file'
- , t('files_versions', 'History')
+ , t('files_versions', 'Versions')
, OC.PERMISSION_UPDATE
, function() {
// Specify icon for hitory button
@@ -41,6 +31,10 @@ $(document).ready(function(){
}
});
+function goToVersionPage(url){
+ window.location.assign(url);
+}
+
function createVersionsDropdown(filename, files) {
var historyUrl = OC.linkTo('files_versions', 'history.php') + '?path='+encodeURIComponent( $( '#dir' ).val() ).replace( /%2F/g, '/' )+'/'+encodeURIComponent( filename );
@@ -51,7 +45,7 @@ function createVersionsDropdown(filename, files) {
html += '<option value=""></option>';
html += '</select>';
html += '</div>';
- html += '<input type="button" value="All versions..." onclick="window.location=\''+historyUrl+'\'" name="makelink" id="makelink" />';
+ html += '<input type="button" value="All versions..." name="makelink" id="makelink" />';
html += '<input id="link" style="display:none; width:90%;" />';
if (filename) {
@@ -61,6 +55,10 @@ function createVersionsDropdown(filename, files) {
$(html).appendTo($('thead .share'));
}
+ $("#makelink").click(function() {
+ goToVersionPage(historyUrl);
+ });
+
$.ajax({
type: 'GET',
url: OC.filePath('files_versions', 'ajax', 'getVersions.php'),
@@ -68,12 +66,11 @@ function createVersionsDropdown(filename, files) {
data: { source: files },
async: false,
success: function( versions ) {
-
+
if (versions) {
$.each( versions, function(index, row ) {
addVersion( row );
});
- $('#found_versions').chosen();
} else {
$('#found_versions').hide();
$('#makelink').hide();
@@ -128,7 +125,7 @@ function createVersionsDropdown(filename, files) {
version.appendTo('#found_versions');
}
-
+
$('tr').filterAttr('data-file',filename).addClass('mouseOver');
$('#dropdown').show('blind');
@@ -144,6 +141,6 @@ $(this).click(
});
}
-
+
}
);
diff --git a/apps/files_versions/l10n/ar.php b/apps/files_versions/l10n/ar.php
new file mode 100644
index 00000000000..b84445972d3
--- /dev/null
+++ b/apps/files_versions/l10n/ar.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Versions" => "الإصدارات"
+);
diff --git a/apps/files_versions/l10n/bg_BG.php b/apps/files_versions/l10n/bg_BG.php
new file mode 100644
index 00000000000..6a1882c2bfd
--- /dev/null
+++ b/apps/files_versions/l10n/bg_BG.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Versions" => "Версии"
+);
diff --git a/apps/files_versions/l10n/bn_BD.php b/apps/files_versions/l10n/bn_BD.php
new file mode 100644
index 00000000000..f3b0071a356
--- /dev/null
+++ b/apps/files_versions/l10n/bn_BD.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Versions" => "ভার্সন"
+);
diff --git a/apps/files_versions/l10n/ca.php b/apps/files_versions/l10n/ca.php
index 0076d02992f..433d974c8cb 100644
--- a/apps/files_versions/l10n/ca.php
+++ b/apps/files_versions/l10n/ca.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expira totes les versions",
-"History" => "Historial",
+"Could not revert: %s" => "No s'ha pogut revertir: %s",
+"success" => "èxit",
+"File %s was reverted to version %s" => "El fitxer %s s'ha revertit a la versió %s",
+"failure" => "fallada",
+"File %s could not be reverted to version %s" => "El fitxer %s no s'ha pogut revertir a la versió %s",
+"No old versions available" => "No hi ha versións antigues disponibles",
+"No path specified" => "No heu especificat el camí",
"Versions" => "Versions",
-"This will delete all existing backup versions of your files" => "Això eliminarà totes les versions de còpia de seguretat dels vostres fitxers",
-"Files Versioning" => "Fitxers de Versions",
-"Enable" => "Habilita"
+"Revert a file to a previous version by clicking on its revert button" => "Reverteix un fitxer a una versió anterior fent clic en el seu botó de reverteix"
);
diff --git a/apps/files_versions/l10n/cs_CZ.php b/apps/files_versions/l10n/cs_CZ.php
index 3995334d9ee..087d800137e 100644
--- a/apps/files_versions/l10n/cs_CZ.php
+++ b/apps/files_versions/l10n/cs_CZ.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Vypršet všechny verze",
-"History" => "Historie",
+"Could not revert: %s" => "Nelze navrátit: %s",
+"success" => "úspěch",
+"File %s was reverted to version %s" => "Soubor %s byl navrácen na verzi %s",
+"failure" => "sehlhání",
+"File %s could not be reverted to version %s" => "Soubor %s nemohl být navrácen na verzi %s",
+"No old versions available" => "Nejsou dostupné žádné starší verze",
+"No path specified" => "Nezadána cesta",
"Versions" => "Verze",
-"This will delete all existing backup versions of your files" => "Odstraní všechny existující zálohované verze Vašich souborů",
-"Files Versioning" => "Verzování souborů",
-"Enable" => "Povolit"
+"Revert a file to a previous version by clicking on its revert button" => "Navraťte soubor do předchozí verze kliknutím na tlačítko navrátit"
);
diff --git a/apps/files_versions/l10n/da.php b/apps/files_versions/l10n/da.php
index bc02b47f2ad..447a3d2b85d 100644
--- a/apps/files_versions/l10n/da.php
+++ b/apps/files_versions/l10n/da.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Lad alle versioner udløbe",
-"History" => "Historik",
+"Could not revert: %s" => "Kunne ikke genskabe: %s",
+"success" => "success",
+"File %s was reverted to version %s" => "Filen %s blev genskabt til version: %s",
+"failure" => "fejl",
+"File %s could not be reverted to version %s" => "Filen %s blev genskabt til version: %s",
+"No old versions available" => "Ingen gamle version tilgængelige",
+"No path specified" => "Ingen sti specificeret",
"Versions" => "Versioner",
-"This will delete all existing backup versions of your files" => "Dette vil slette alle eksisterende backupversioner af dine filer",
-"Files Versioning" => "Versionering af filer",
-"Enable" => "Aktiver"
+"Revert a file to a previous version by clicking on its revert button" => "Genskab en fil til en tidligere version ved at klikke på denne genskab knap."
);
diff --git a/apps/files_versions/l10n/de.php b/apps/files_versions/l10n/de.php
index 092bbfbff70..c34a8c1fd3e 100644
--- a/apps/files_versions/l10n/de.php
+++ b/apps/files_versions/l10n/de.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Alle Versionen löschen",
-"History" => "Historie",
+"Could not revert: %s" => "Konnte %s nicht zurücksetzen",
+"success" => "Erfolgreich",
+"File %s was reverted to version %s" => "Datei %s wurde auf Version %s zurückgesetzt",
+"failure" => "Fehlgeschlagen",
+"File %s could not be reverted to version %s" => "Datei %s konnte nicht auf Version %s zurückgesetzt werden",
+"No old versions available" => "Keine älteren Versionen verfügbar",
+"No path specified" => "Kein Pfad angegeben",
"Versions" => "Versionen",
-"This will delete all existing backup versions of your files" => "Dies löscht alle vorhandenen Sicherungsversionen Deiner Dateien.",
-"Files Versioning" => "Dateiversionierung",
-"Enable" => "Aktivieren"
+"Revert a file to a previous version by clicking on its revert button" => "Setze eine Datei durch klicken auf den Zurücksetzen Button zurück"
);
diff --git a/apps/files_versions/l10n/de_DE.php b/apps/files_versions/l10n/de_DE.php
new file mode 100644
index 00000000000..c0b2f2a83f7
--- /dev/null
+++ b/apps/files_versions/l10n/de_DE.php
@@ -0,0 +1,11 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Konnte %s nicht zurücksetzen",
+"success" => "Erfolgreich",
+"File %s was reverted to version %s" => "Die Datei %s wurde zur Version %s zurückgesetzt",
+"failure" => "Fehlgeschlagen",
+"File %s could not be reverted to version %s" => "Die Datei %s konnte nicht zur Version %s zurückgesetzt werden",
+"No old versions available" => "Keine älteren Versionen verfügbar",
+"No path specified" => "Kein Pfad angegeben",
+"Versions" => "Versionen",
+"Revert a file to a previous version by clicking on its revert button" => "Setze eine Datei durch Klicken auf den Zurücksetzen-Button auf eine frühere Version zurück"
+);
diff --git a/apps/files_versions/l10n/el.php b/apps/files_versions/l10n/el.php
index f6b9a5b2998..6e1900b233b 100644
--- a/apps/files_versions/l10n/el.php
+++ b/apps/files_versions/l10n/el.php
@@ -1,8 +1,10 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Λήξη όλων των εκδόσεων",
-"History" => "Ιστορικό",
-"Versions" => "Εκδόσεις",
-"This will delete all existing backup versions of your files" => "Αυτό θα διαγράψει όλες τις υπάρχουσες εκδόσεις των αντιγράφων ασφαλείας των αρχείων σας",
-"Files Versioning" => "Εκδόσεις Αρχείων",
-"Enable" => "Ενεργοποίηση"
+"Could not revert: %s" => "Αδυναμία επαναφοράς του: %s",
+"success" => "επιτυχία",
+"File %s was reverted to version %s" => "Το αρχείο %s επαναφέρθηκε στην έκδοση %s",
+"failure" => "αποτυχία",
+"File %s could not be reverted to version %s" => "Το αρχείο %s δεν είναι δυνατό να επαναφερθεί στην έκδοση %s",
+"No old versions available" => "Μη διαθέσιμες παλιές εκδόσεις",
+"No path specified" => "Δεν καθορίστηκε διαδρομή",
+"Revert a file to a previous version by clicking on its revert button" => "Επαναφορά ενός αρχείου σε προηγούμενη έκδοση πατώντας στο κουμπί επαναφοράς"
);
diff --git a/apps/files_versions/l10n/eo.php b/apps/files_versions/l10n/eo.php
index 0c3835373ef..87b314655c0 100644
--- a/apps/files_versions/l10n/eo.php
+++ b/apps/files_versions/l10n/eo.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Eksvalidigi ĉiujn eldonojn",
"History" => "Historio",
-"Versions" => "Eldonoj",
-"This will delete all existing backup versions of your files" => "Ĉi tio forigos ĉiujn estantajn sekurkopiajn eldonojn de viaj dosieroj",
"Files Versioning" => "Dosiereldonigo",
"Enable" => "Kapabligi"
);
diff --git a/apps/files_versions/l10n/es.php b/apps/files_versions/l10n/es.php
index f6b63df7c2b..8c1a30f822a 100644
--- a/apps/files_versions/l10n/es.php
+++ b/apps/files_versions/l10n/es.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expirar todas las versiones",
-"History" => "Historial",
-"Versions" => "Versiones",
-"This will delete all existing backup versions of your files" => "Esto eliminará todas las versiones guardadas como copia de seguridad de tus archivos",
-"Files Versioning" => "Versionado de archivos",
-"Enable" => "Habilitar"
+"Could not revert: %s" => "No se puede revertir: %s",
+"success" => "exitoso",
+"File %s was reverted to version %s" => "El archivo %s fue revertido a la version %s",
+"failure" => "fallo",
+"File %s could not be reverted to version %s" => "El archivo %s no puede ser revertido a la version %s",
+"No old versions available" => "No hay versiones antiguas disponibles",
+"No path specified" => "Ruta no especificada",
+"Versions" => "Revisiones",
+"Revert a file to a previous version by clicking on its revert button" => "Revertir un archivo a una versión anterior haciendo clic en el boton de revertir"
);
diff --git a/apps/files_versions/l10n/es_AR.php b/apps/files_versions/l10n/es_AR.php
index a78264de03f..363693c913b 100644
--- a/apps/files_versions/l10n/es_AR.php
+++ b/apps/files_versions/l10n/es_AR.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expirar todas las versiones",
-"History" => "Historia",
+"Could not revert: %s" => "No se pudo revertir: %s ",
+"success" => "Éxito",
+"File %s was reverted to version %s" => "El archivo %s fue revertido a la versión %s",
+"failure" => "error",
+"File %s could not be reverted to version %s" => "El archivo %s no pudo ser revertido a la versión %s",
+"No old versions available" => "No hay versiones antiguas disponibles",
+"No path specified" => "Ruta de acceso no especificada",
"Versions" => "Versiones",
-"This will delete all existing backup versions of your files" => "Hacer estom borrará todas las versiones guardadas como copia de seguridad de tus archivos",
-"Files Versioning" => "Versionado de archivos",
-"Enable" => "Activar"
+"Revert a file to a previous version by clicking on its revert button" => "Revertí un archivo a una versión anterior haciendo click en su botón de \"revertir\""
);
diff --git a/apps/files_versions/l10n/et_EE.php b/apps/files_versions/l10n/et_EE.php
index f1ebd082ad5..fa2a33f9dda 100644
--- a/apps/files_versions/l10n/et_EE.php
+++ b/apps/files_versions/l10n/et_EE.php
@@ -1,5 +1,6 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Kõikide versioonide aegumine",
-"Versions" => "Versioonid",
-"This will delete all existing backup versions of your files" => "See kustutab kõik sinu failidest tehtud varuversiooni"
+"success" => "korras",
+"failure" => "ebaõnnestus",
+"No old versions available" => "Vanu versioone pole saadaval",
+"No path specified" => "Asukohta pole määratud"
);
diff --git a/apps/files_versions/l10n/eu.php b/apps/files_versions/l10n/eu.php
index d84d9011707..2a7f279af16 100644
--- a/apps/files_versions/l10n/eu.php
+++ b/apps/files_versions/l10n/eu.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Iraungi bertsio guztiak",
-"History" => "Historia",
+"Could not revert: %s" => "Ezin izan da leheneratu: %s",
+"success" => "arrakasta",
+"File %s was reverted to version %s" => "%s fitxategia %s bertsiora leheneratu da",
+"failure" => "errorea",
+"File %s could not be reverted to version %s" => "%s fitxategia ezin da %s bertsiora leheneratu",
+"No old versions available" => "Ez dago bertsio zaharrik eskuragarri",
+"No path specified" => "Ez da bidea zehaztu",
"Versions" => "Bertsioak",
-"This will delete all existing backup versions of your files" => "Honek zure fitxategien bertsio guztiak ezabatuko ditu",
-"Files Versioning" => "Fitxategien Bertsioak",
-"Enable" => "Gaitu"
+"Revert a file to a previous version by clicking on its revert button" => "Itzuli fitxategi bat aurreko bertsio batera leheneratu bere leheneratu botoia sakatuz"
);
diff --git a/apps/files_versions/l10n/fa.php b/apps/files_versions/l10n/fa.php
index 98dd415969a..4ec6aa1bbb4 100644
--- a/apps/files_versions/l10n/fa.php
+++ b/apps/files_versions/l10n/fa.php
@@ -1,3 +1,8 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "انقضای تمامی نسخه‌ها"
+"Could not revert: %s" => "بازگردانی امکان ناپذیر است: %s",
+"success" => "موفقیت",
+"failure" => "شکست",
+"No old versions available" => "هیچ نسخه قدیمی در دسترس نیست",
+"No path specified" => "هیچ مسیری مشخص نشده است",
+"Revert a file to a previous version by clicking on its revert button" => "بازگردانی یک پرورنده به نسخه قدیمی اش از طریق دکمه بازگردانی امکان پذیر است"
);
diff --git a/apps/files_versions/l10n/fi_FI.php b/apps/files_versions/l10n/fi_FI.php
index 3cec4c04bfe..0dec7fc2580 100644
--- a/apps/files_versions/l10n/fi_FI.php
+++ b/apps/files_versions/l10n/fi_FI.php
@@ -1,8 +1,10 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Vanhenna kaikki versiot",
-"History" => "Historia",
-"Versions" => "Versiot",
-"This will delete all existing backup versions of your files" => "Tämä poistaa kaikki tiedostojesi olemassa olevat varmuuskopioversiot",
-"Files Versioning" => "Tiedostojen versiointi",
-"Enable" => "Käytä"
+"Could not revert: %s" => "Palautus epäonnistui: %s",
+"success" => "onnistui",
+"File %s was reverted to version %s" => "Tiedosto %s palautettiin versioon %s",
+"failure" => "epäonnistui",
+"File %s could not be reverted to version %s" => "Tiedoston %s palautus versioon %s epäonnistui",
+"No old versions available" => "Vanhoja ei ole saatavilla",
+"No path specified" => "Polkua ei ole määritetty",
+"Revert a file to a previous version by clicking on its revert button" => "Palauta tiedoston edellinen versio napsauttamalla palautuspainiketta"
);
diff --git a/apps/files_versions/l10n/fr.php b/apps/files_versions/l10n/fr.php
index e6dbc274456..e2698c5c4aa 100644
--- a/apps/files_versions/l10n/fr.php
+++ b/apps/files_versions/l10n/fr.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Supprimer les versions intermédiaires",
-"History" => "Historique",
+"Could not revert: %s" => "Impossible de restaurer %s",
+"success" => "succès",
+"File %s was reverted to version %s" => "Le fichier %s a été restauré dans sa version %s",
+"failure" => "échec",
+"File %s could not be reverted to version %s" => "Le fichier %s ne peut être restauré dans sa version %s",
+"No old versions available" => "Aucune ancienne version n'est disponible",
+"No path specified" => "Aucun chemin spécifié",
"Versions" => "Versions",
-"This will delete all existing backup versions of your files" => "Cette opération va effacer toutes les versions intermédiaires de vos fichiers (et ne garder que la dernière version en date).",
-"Files Versioning" => "Versionnage des fichiers",
-"Enable" => "Activer"
+"Revert a file to a previous version by clicking on its revert button" => "Restaurez un fichier dans une version antérieure en cliquant sur son bouton de restauration"
);
diff --git a/apps/files_versions/l10n/gl.php b/apps/files_versions/l10n/gl.php
index c0d5937e1b1..586ef8c3a68 100644
--- a/apps/files_versions/l10n/gl.php
+++ b/apps/files_versions/l10n/gl.php
@@ -1,7 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Caducar todas as versións",
+"Could not revert: %s" => "Non foi posíbel reverter: %s",
+"success" => "feito",
+"File %s was reverted to version %s" => "O ficheiro %s foi revertido á versión %s",
+"failure" => "produciuse un fallo",
+"File %s could not be reverted to version %s" => "Non foi posíbel reverter o ficheiro %s á versión %s",
+"No old versions available" => "Non hai versións antigas dispoñíbeis",
+"No path specified" => "Non foi indicada a ruta",
"Versions" => "Versións",
-"This will delete all existing backup versions of your files" => "Esto eliminará todas as copias de respaldo existentes dos seus ficheiros",
-"Files Versioning" => "Versionado de ficheiros",
-"Enable" => "Habilitar"
+"Revert a file to a previous version by clicking on its revert button" => "Reverta un ficheiro a unha versión anterior premendo no botón reversión"
);
diff --git a/apps/files_versions/l10n/he.php b/apps/files_versions/l10n/he.php
index 09a013f45a8..9eb4df64857 100644
--- a/apps/files_versions/l10n/he.php
+++ b/apps/files_versions/l10n/he.php
@@ -1,5 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "הפגת תוקף כל הגרסאות",
-"Versions" => "גרסאות",
-"This will delete all existing backup versions of your files" => "פעולה זו תמחק את כל גיבויי הגרסאות הקיימים של הקבצים שלך"
+"History" => "היסטוריה",
+"Files Versioning" => "שמירת הבדלי גרסאות של קבצים",
+"Enable" => "הפעלה"
);
diff --git a/apps/files_versions/l10n/hu_HU.php b/apps/files_versions/l10n/hu_HU.php
new file mode 100644
index 00000000000..9f7420157e1
--- /dev/null
+++ b/apps/files_versions/l10n/hu_HU.php
@@ -0,0 +1,11 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Nem sikerült átállni a változatra: %s",
+"success" => "sikerült",
+"File %s was reverted to version %s" => "%s állományt átállítottuk erre a változatra: %s",
+"failure" => "nem sikerült",
+"File %s could not be reverted to version %s" => "%s állományt nem sikerült átállítani erre a változatra: %s",
+"No old versions available" => "Nincs régebbi változat",
+"No path specified" => "Nincs megadva az útvonal",
+"Versions" => "Az állományok korábbi változatai",
+"Revert a file to a previous version by clicking on its revert button" => "Az állomány átállítható egy régebbi változatra, ha a gombra kattint"
+);
diff --git a/apps/files_versions/l10n/id.php b/apps/files_versions/l10n/id.php
new file mode 100644
index 00000000000..4662aa86432
--- /dev/null
+++ b/apps/files_versions/l10n/id.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Tidak dapat mengembalikan: %s",
+"success" => "sukses",
+"File %s was reverted to version %s" => "Berkas %s telah dikembalikan ke versi %s",
+"failure" => "gagal",
+"File %s could not be reverted to version %s" => "Berkas %s gagal dikembalikan ke versi %s",
+"No old versions available" => "Versi lama tidak tersedia",
+"No path specified" => "Lokasi tidak ditentukan",
+"Revert a file to a previous version by clicking on its revert button" => "Kembalikan berkas ke versi sebelumnya dengan mengklik tombol kembalikan"
+);
diff --git a/apps/files_versions/l10n/is.php b/apps/files_versions/l10n/is.php
new file mode 100644
index 00000000000..d165a78c31e
--- /dev/null
+++ b/apps/files_versions/l10n/is.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Versions" => "Útgáfur"
+);
diff --git a/apps/files_versions/l10n/it.php b/apps/files_versions/l10n/it.php
index 0b1e70823d5..bca00879993 100644
--- a/apps/files_versions/l10n/it.php
+++ b/apps/files_versions/l10n/it.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Scadenza di tutte le versioni",
-"History" => "Cronologia",
+"Could not revert: %s" => "Impossibild ripristinare: %s",
+"success" => "completata",
+"File %s was reverted to version %s" => "Il file %s è stato ripristinato alla versione %s",
+"failure" => "non riuscita",
+"File %s could not be reverted to version %s" => "Il file %s non può essere ripristinato alla versione %s",
+"No old versions available" => "Non sono disponibili versioni precedenti",
+"No path specified" => "Nessun percorso specificato",
"Versions" => "Versioni",
-"This will delete all existing backup versions of your files" => "Ciò eliminerà tutte le versioni esistenti dei tuoi file",
-"Files Versioning" => "Controllo di versione dei file",
-"Enable" => "Abilita"
+"Revert a file to a previous version by clicking on its revert button" => "Ripristina un file a una versione precedente facendo clic sul rispettivo pulsante di ripristino"
);
diff --git a/apps/files_versions/l10n/ja_JP.php b/apps/files_versions/l10n/ja_JP.php
index 367152c0743..0c2dbd401c4 100644
--- a/apps/files_versions/l10n/ja_JP.php
+++ b/apps/files_versions/l10n/ja_JP.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "すべてのバージョンを削除する",
-"History" => "履歴",
+"Could not revert: %s" => "元に戻せませんでした: %s",
+"success" => "成功",
+"File %s was reverted to version %s" => "ファイル %s をバージョン %s に戻しました",
+"failure" => "失敗",
+"File %s could not be reverted to version %s" => "ファイル %s をバージョン %s に戻せませんでした",
+"No old versions available" => "利用可能な古いバージョンはありません",
+"No path specified" => "パスが指定されていません",
"Versions" => "バージョン",
-"This will delete all existing backup versions of your files" => "これは、あなたのファイルのすべてのバックアップバージョンを削除します",
-"Files Versioning" => "ファイルのバージョン管理",
-"Enable" => "有効化"
+"Revert a file to a previous version by clicking on its revert button" => "もとに戻すボタンをクリックすると、ファイルを過去のバージョンに戻します"
);
diff --git a/apps/files_versions/l10n/ko.php b/apps/files_versions/l10n/ko.php
new file mode 100644
index 00000000000..f40925e1be2
--- /dev/null
+++ b/apps/files_versions/l10n/ko.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "역사",
+"Files Versioning" => "파일 버전 관리",
+"Enable" => "사용함"
+);
diff --git a/apps/files_versions/l10n/ku_IQ.php b/apps/files_versions/l10n/ku_IQ.php
index 5fa3b9080d7..db5dbad49fc 100644
--- a/apps/files_versions/l10n/ku_IQ.php
+++ b/apps/files_versions/l10n/ku_IQ.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "وه‌شانه‌کان گشتیان به‌سه‌رده‌چن",
"History" => "مێژوو",
-"Versions" => "وه‌شان",
-"This will delete all existing backup versions of your files" => "ئه‌مه‌ سه‌رجه‌م پاڵپشتی وه‌شانه‌ هه‌بووه‌کانی په‌ڕگه‌کانت ده‌سڕینته‌وه",
"Files Versioning" => "وه‌شانی په‌ڕگه",
"Enable" => "چالاککردن"
);
diff --git a/apps/files_versions/l10n/lb.php b/apps/files_versions/l10n/lb.php
new file mode 100644
index 00000000000..3aa625ffc97
--- /dev/null
+++ b/apps/files_versions/l10n/lb.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "Historique",
+"Files Versioning" => "Fichier's Versionéierung ",
+"Enable" => "Aschalten"
+);
diff --git a/apps/files_versions/l10n/lt_LT.php b/apps/files_versions/l10n/lt_LT.php
index b3810d06ec7..adf4893020e 100644
--- a/apps/files_versions/l10n/lt_LT.php
+++ b/apps/files_versions/l10n/lt_LT.php
@@ -1,3 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Panaikinti visų versijų galiojimą"
+"History" => "Istorija",
+"Files Versioning" => "Failų versijos",
+"Enable" => "Įjungti"
);
diff --git a/apps/files_versions/l10n/lv.php b/apps/files_versions/l10n/lv.php
new file mode 100644
index 00000000000..bf8d40fa0f0
--- /dev/null
+++ b/apps/files_versions/l10n/lv.php
@@ -0,0 +1,11 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Nevarēja atgriezt — %s",
+"success" => "veiksme",
+"File %s was reverted to version %s" => "Datne %s tika atgriezt uz versiju %s",
+"failure" => "neveiksme",
+"File %s could not be reverted to version %s" => "Datni %s nevarēja atgriezt uz versiju %s",
+"No old versions available" => "Nav pieejamu vecāku versiju",
+"No path specified" => "Nav norādīts ceļš",
+"Versions" => "Versijas",
+"Revert a file to a previous version by clicking on its revert button" => "Atgriez datni uz iepriekšēju versiju, spiežot uz tās atgriešanas pogu"
+);
diff --git a/apps/files_versions/l10n/mk.php b/apps/files_versions/l10n/mk.php
new file mode 100644
index 00000000000..6a1882c2bfd
--- /dev/null
+++ b/apps/files_versions/l10n/mk.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Versions" => "Версии"
+);
diff --git a/apps/files_versions/l10n/nb_NO.php b/apps/files_versions/l10n/nb_NO.php
index 55cc12113d7..18c72506102 100644
--- a/apps/files_versions/l10n/nb_NO.php
+++ b/apps/files_versions/l10n/nb_NO.php
@@ -1,3 +1,5 @@
<?php $TRANSLATIONS = array(
-"Enable Files Versioning" => "Slå på versjonering"
+"History" => "Historie",
+"Files Versioning" => "Fil versjonering",
+"Enable" => "Aktiver"
);
diff --git a/apps/files_versions/l10n/nl.php b/apps/files_versions/l10n/nl.php
index 1cf875048d7..92088522121 100644
--- a/apps/files_versions/l10n/nl.php
+++ b/apps/files_versions/l10n/nl.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Alle versies laten verlopen",
-"History" => "Geschiedenis",
+"Could not revert: %s" => "Kon niet terugdraaien: %s",
+"success" => "succes",
+"File %s was reverted to version %s" => "Bestand %s is teruggedraaid naar versie %s",
+"failure" => "mislukking",
+"File %s could not be reverted to version %s" => "Bestand %s kon niet worden teruggedraaid naar versie %s",
+"No old versions available" => "Geen oudere versies beschikbaar",
+"No path specified" => "Geen pad opgegeven",
"Versions" => "Versies",
-"This will delete all existing backup versions of your files" => "Dit zal alle bestaande backup versies van uw bestanden verwijderen",
-"Files Versioning" => "Bestand versies",
-"Enable" => "Zet aan"
+"Revert a file to a previous version by clicking on its revert button" => "Draai een bestand terug naar een voorgaande versie door te klikken op de terugdraai knop"
);
diff --git a/apps/files_versions/l10n/pl.php b/apps/files_versions/l10n/pl.php
index 46c28d4590a..68944e86760 100644
--- a/apps/files_versions/l10n/pl.php
+++ b/apps/files_versions/l10n/pl.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Wygasają wszystkie wersje",
-"History" => "Historia",
+"Could not revert: %s" => "Nie można było przywrócić: %s",
+"success" => "sukces",
+"File %s was reverted to version %s" => "Plik %s został przywrócony do wersji %s",
+"failure" => "porażka",
+"File %s could not be reverted to version %s" => "Plik %s nie mógł być przywrócony do wersji %s",
+"No old versions available" => "Nie są dostępne żadne starsze wersje",
+"No path specified" => "Nie podano ścieżki",
"Versions" => "Wersje",
-"This will delete all existing backup versions of your files" => "Spowoduje to usunięcie wszystkich istniejących wersji kopii zapasowych plików",
-"Files Versioning" => "Wersjonowanie plików",
-"Enable" => "Włącz"
+"Revert a file to a previous version by clicking on its revert button" => "Przywróć plik do poprzedniej wersji klikając w jego przycisk przywrócenia"
);
diff --git a/apps/files_versions/l10n/pt_BR.php b/apps/files_versions/l10n/pt_BR.php
index 3d39a533d65..f13b7931d4b 100644
--- a/apps/files_versions/l10n/pt_BR.php
+++ b/apps/files_versions/l10n/pt_BR.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expirar todas as versões",
-"History" => "Histórico",
+"Could not revert: %s" => "Impossível reverter: %s",
+"success" => "sucesso",
+"File %s was reverted to version %s" => "Arquivo %s revertido à versão %s",
+"failure" => "falha",
+"File %s could not be reverted to version %s" => "Arquivo %s não pôde ser revertido à versão %s",
+"No old versions available" => "Nenhuma versão antiga disponível",
+"No path specified" => "Nenhum caminho especificado",
"Versions" => "Versões",
-"This will delete all existing backup versions of your files" => "Isso removerá todas as versões de backup existentes dos seus arquivos",
-"Files Versioning" => "Versionamento de Arquivos",
-"Enable" => "Habilitar"
+"Revert a file to a previous version by clicking on its revert button" => "Reverta um arquivo a uma versão anterior clicando no botão reverter"
);
diff --git a/apps/files_versions/l10n/pt_PT.php b/apps/files_versions/l10n/pt_PT.php
index 2ddf70cc6c5..93379547295 100644
--- a/apps/files_versions/l10n/pt_PT.php
+++ b/apps/files_versions/l10n/pt_PT.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expirar todas as versões",
-"History" => "Histórico",
+"Could not revert: %s" => "Não foi possível reverter: %s",
+"success" => "Sucesso",
+"File %s was reverted to version %s" => "O ficheiro %s foi revertido para a versão %s",
+"failure" => "Falha",
+"File %s could not be reverted to version %s" => "Não foi possível reverter o ficheiro %s para a versão %s",
+"No old versions available" => "Não existem versões mais antigas",
+"No path specified" => "Nenhum caminho especificado",
"Versions" => "Versões",
-"This will delete all existing backup versions of your files" => "Isto irá apagar todas as versões de backup do seus ficheiros",
-"Files Versioning" => "Versionamento de Ficheiros",
-"Enable" => "Activar"
+"Revert a file to a previous version by clicking on its revert button" => "Reverter um ficheiro para uma versão anterior clicando no seu botão reverter."
);
diff --git a/apps/files_versions/l10n/ro.php b/apps/files_versions/l10n/ro.php
index e23e771e392..7dfaee3672b 100644
--- a/apps/files_versions/l10n/ro.php
+++ b/apps/files_versions/l10n/ro.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expiră toate versiunile",
"History" => "Istoric",
-"Versions" => "Versiuni",
-"This will delete all existing backup versions of your files" => "Această acțiune va șterge toate versiunile salvate ale fișierelor tale",
"Files Versioning" => "Versionare fișiere",
"Enable" => "Activare"
);
diff --git a/apps/files_versions/l10n/ru.php b/apps/files_versions/l10n/ru.php
index f91cae90a14..7377fbb5382 100644
--- a/apps/files_versions/l10n/ru.php
+++ b/apps/files_versions/l10n/ru.php
@@ -1,5 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Просрочить все версии",
+"Could not revert: %s" => "Не может быть возвращён: %s",
+"success" => "успех",
+"File %s was reverted to version %s" => "Файл %s был возвращён к версии %s",
+"failure" => "провал",
+"File %s could not be reverted to version %s" => "Файл %s не может быть возвращён к версии %s",
+"No old versions available" => "Нет доступных старых версий",
+"No path specified" => "Путь не указан",
"Versions" => "Версии",
-"This will delete all existing backup versions of your files" => "Очистить список версий ваших файлов"
+"Revert a file to a previous version by clicking on its revert button" => "Вернуть файл к предыдущей версии нажатием на кнопку возврата"
);
diff --git a/apps/files_versions/l10n/ru_RU.php b/apps/files_versions/l10n/ru_RU.php
index a14258eea87..8656e346eb6 100644
--- a/apps/files_versions/l10n/ru_RU.php
+++ b/apps/files_versions/l10n/ru_RU.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Срок действия всех версий истекает",
"History" => "История",
-"Versions" => "Версии",
-"This will delete all existing backup versions of your files" => "Это приведет к удалению всех существующих версий резервной копии ваших файлов",
"Files Versioning" => "Файлы управления версиями",
"Enable" => "Включить"
);
diff --git a/apps/files_versions/l10n/si_LK.php b/apps/files_versions/l10n/si_LK.php
new file mode 100644
index 00000000000..37debf869bc
--- /dev/null
+++ b/apps/files_versions/l10n/si_LK.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "ඉතිහාසය",
+"Files Versioning" => "ගොනු අනුවාදයන්",
+"Enable" => "සක්‍රිය කරන්න"
+);
diff --git a/apps/files_versions/l10n/sk_SK.php b/apps/files_versions/l10n/sk_SK.php
index 132c6c09682..50e4af4d964 100644
--- a/apps/files_versions/l10n/sk_SK.php
+++ b/apps/files_versions/l10n/sk_SK.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Expirovať všetky verzie",
-"History" => "História",
+"Could not revert: %s" => "Nemožno obnoviť: %s",
+"success" => "úspech",
+"File %s was reverted to version %s" => "Súbor %s bol obnovený na verziu %s",
+"failure" => "chyba",
+"File %s could not be reverted to version %s" => "Súbor %s nemohol byť obnovený na verziu %s",
+"No old versions available" => "Nie sú dostupné žiadne staršie verzie",
+"No path specified" => "Nevybrali ste cestu",
"Versions" => "Verzie",
-"This will delete all existing backup versions of your files" => "Budú zmazané všetky zálohované verzie vašich súborov",
-"Files Versioning" => "Vytváranie verzií súborov",
-"Enable" => "Zapnúť"
+"Revert a file to a previous version by clicking on its revert button" => "Obnovte súbor do predošlej verzie kliknutím na tlačítko obnoviť"
);
diff --git a/apps/files_versions/l10n/sl.php b/apps/files_versions/l10n/sl.php
index 056a17decfe..2df00fc8264 100644
--- a/apps/files_versions/l10n/sl.php
+++ b/apps/files_versions/l10n/sl.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Zastaraj vse različice",
-"History" => "Zgodovina",
+"Could not revert: %s" => "Ni mogoče povrniti: %s",
+"success" => "uspešno",
+"File %s was reverted to version %s" => "Datoteka %s je povrnjena na različico %s.",
+"failure" => "spodletelo",
+"File %s could not be reverted to version %s" => "Datoteke %s ni mogoče povrniti na različico %s.",
+"No old versions available" => "Ni starejših različic.",
+"No path specified" => "Ni določene poti",
"Versions" => "Različice",
-"This will delete all existing backup versions of your files" => "To bo izbrisalo vse obstoječe različice varnostnih kopij vaših datotek",
-"Files Versioning" => "Sledenje različicam",
-"Enable" => "Omogoči"
+"Revert a file to a previous version by clicking on its revert button" => "Povrni datoteko na predhodno različico s klikom na gumb za povrnitev."
);
diff --git a/apps/files_versions/l10n/sr.php b/apps/files_versions/l10n/sr.php
new file mode 100644
index 00000000000..0195f84567f
--- /dev/null
+++ b/apps/files_versions/l10n/sr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "Историја",
+"Files Versioning" => "Прављење верзија датотека",
+"Enable" => "Омогући"
+);
diff --git a/apps/files_versions/l10n/sv.php b/apps/files_versions/l10n/sv.php
index e36164e30ab..46e2c0f8bcf 100644
--- a/apps/files_versions/l10n/sv.php
+++ b/apps/files_versions/l10n/sv.php
@@ -1,8 +1,10 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Upphör alla versioner",
-"History" => "Historik",
-"Versions" => "Versioner",
-"This will delete all existing backup versions of your files" => "Detta kommer att radera alla befintliga säkerhetskopior av dina filer",
-"Files Versioning" => "Versionshantering av filer",
-"Enable" => "Aktivera"
+"Could not revert: %s" => "Kunde inte återställa: %s",
+"success" => "lyckades",
+"File %s was reverted to version %s" => "Filen %s återställdes till version %s",
+"failure" => "misslyckades",
+"File %s could not be reverted to version %s" => "Filen %s kunde inte återställas till version %s",
+"No old versions available" => "Inga gamla versioner finns tillgängliga",
+"No path specified" => "Ingen sökväg angiven",
+"Revert a file to a previous version by clicking on its revert button" => "Återställ en fil till en tidigare version genom att klicka på knappen"
);
diff --git a/apps/files_versions/l10n/ta_LK.php b/apps/files_versions/l10n/ta_LK.php
new file mode 100644
index 00000000000..aca76dcc262
--- /dev/null
+++ b/apps/files_versions/l10n/ta_LK.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "வரலாறு",
+"Files Versioning" => "கோப்பு பதிப்புகள்",
+"Enable" => "இயலுமைப்படுத்துக"
+);
diff --git a/apps/files_versions/l10n/th_TH.php b/apps/files_versions/l10n/th_TH.php
index 89b9f626911..e1e996903ae 100644
--- a/apps/files_versions/l10n/th_TH.php
+++ b/apps/files_versions/l10n/th_TH.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "หมดอายุทุกรุ่น",
"History" => "ประวัติ",
-"Versions" => "รุ่น",
-"This will delete all existing backup versions of your files" => "นี่จะเป็นลบทิ้งไฟล์รุ่นที่ทำการสำรองข้อมูลทั้งหมดที่มีอยู่ของคุณทิ้งไป",
"Files Versioning" => "การกำหนดเวอร์ชั่นของไฟล์",
"Enable" => "เปิดใช้งาน"
);
diff --git a/apps/files_versions/l10n/tr.php b/apps/files_versions/l10n/tr.php
new file mode 100644
index 00000000000..745400d331c
--- /dev/null
+++ b/apps/files_versions/l10n/tr.php
@@ -0,0 +1,10 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Geri alınamıyor: %s",
+"success" => "Başarılı.",
+"File %s was reverted to version %s" => "Dosya %s, %s versiyonuna döndürüldü",
+"failure" => "hata",
+"File %s could not be reverted to version %s" => "Dosya %s, %s versiyonuna döndürülemedi.",
+"No old versions available" => "Eski versiyonlar mevcut değil.",
+"No path specified" => "Yama belirtilmemiş",
+"Versions" => "Sürümler"
+);
diff --git a/apps/files_versions/l10n/uk.php b/apps/files_versions/l10n/uk.php
new file mode 100644
index 00000000000..e722d95497b
--- /dev/null
+++ b/apps/files_versions/l10n/uk.php
@@ -0,0 +1,11 @@
+<?php $TRANSLATIONS = array(
+"Could not revert: %s" => "Не вдалося відновити: %s",
+"success" => "успішно",
+"File %s was reverted to version %s" => "Файл %s був відновлений до версії %s",
+"failure" => "неуспішно",
+"File %s could not be reverted to version %s" => "Файл %s не може бути відновлений до версії %s",
+"No old versions available" => "Старі версії недоступні",
+"No path specified" => "Шлях не вказаний",
+"Versions" => "Версії",
+"Revert a file to a previous version by clicking on its revert button" => "Відновити файл на попередню версію, натиснувши на кнопку Відновити"
+);
diff --git a/apps/files_versions/l10n/vi.php b/apps/files_versions/l10n/vi.php
index 992c0751d0a..f2499e7bf35 100644
--- a/apps/files_versions/l10n/vi.php
+++ b/apps/files_versions/l10n/vi.php
@@ -1,5 +1,10 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "Hết hạn tất cả các phiên bản",
-"Versions" => "Phiên bản",
-"This will delete all existing backup versions of your files" => "Điều này sẽ xóa tất cả các phiên bản sao lưu hiện có "
+"Could not revert: %s" => "Không thể khôi phục: %s",
+"success" => "thành công",
+"File %s was reverted to version %s" => "File %s đã được khôi phục về phiên bản %s",
+"failure" => "Thất bại",
+"File %s could not be reverted to version %s" => "File %s không thể khôi phục về phiên bản %s",
+"No old versions available" => "Không có phiên bản cũ nào",
+"No path specified" => "Không chỉ ra đường dẫn rõ ràng",
+"Revert a file to a previous version by clicking on its revert button" => "Khôi phục một file về phiên bản trước đó bằng cách click vào nút Khôi phục tương ứng"
);
diff --git a/apps/files_versions/l10n/zh_CN.GB2312.php b/apps/files_versions/l10n/zh_CN.GB2312.php
index 107805221b8..d9e788033aa 100644
--- a/apps/files_versions/l10n/zh_CN.GB2312.php
+++ b/apps/files_versions/l10n/zh_CN.GB2312.php
@@ -1,8 +1,5 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "作废所有版本",
"History" => "历史",
-"Versions" => "版本",
-"This will delete all existing backup versions of your files" => "这将删除所有您现有文件的备份版本",
"Files Versioning" => "文件版本",
"Enable" => "启用"
);
diff --git a/apps/files_versions/l10n/zh_CN.php b/apps/files_versions/l10n/zh_CN.php
index 48e7157c98f..65d0d284a07 100644
--- a/apps/files_versions/l10n/zh_CN.php
+++ b/apps/files_versions/l10n/zh_CN.php
@@ -1,8 +1,11 @@
<?php $TRANSLATIONS = array(
-"Expire all versions" => "过期所有版本",
-"History" => "历史",
+"Could not revert: %s" => "无法恢复: %s",
+"success" => "成功",
+"File %s was reverted to version %s" => "文件 %s 已被恢复到历史版本 %s",
+"failure" => "失败",
+"File %s could not be reverted to version %s" => "文件 %s 无法被恢复到历史版本 %s",
+"No old versions available" => "该文件没有历史版本",
+"No path specified" => "未指定路径",
"Versions" => "版本",
-"This will delete all existing backup versions of your files" => "将会删除您的文件的所有备份版本",
-"Files Versioning" => "文件版本",
-"Enable" => "开启"
+"Revert a file to a previous version by clicking on its revert button" => "点击恢复按钮可将文件恢复到之前的版本"
);
diff --git a/apps/files_versions/l10n/zh_HK.php b/apps/files_versions/l10n/zh_HK.php
new file mode 100644
index 00000000000..71bd3bbbd94
--- /dev/null
+++ b/apps/files_versions/l10n/zh_HK.php
@@ -0,0 +1,6 @@
+<?php $TRANSLATIONS = array(
+"success" => "成功",
+"failure" => "失敗",
+"No old versions available" => "沒有以往版本",
+"Versions" => "版本"
+);
diff --git a/apps/files_versions/l10n/zh_TW.php b/apps/files_versions/l10n/zh_TW.php
new file mode 100644
index 00000000000..a7b496b37db
--- /dev/null
+++ b/apps/files_versions/l10n/zh_TW.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"History" => "歷史",
+"Files Versioning" => "檔案版本化中...",
+"Enable" => "啟用"
+);
diff --git a/apps/files_versions/lib/capabilities.php b/apps/files_versions/lib/capabilities.php
new file mode 100644
index 00000000000..3251a07b6ae
--- /dev/null
+++ b/apps/files_versions/lib/capabilities.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2013 Tom Needham <tom@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCA\Files_Versions;
+
+class Capabilities {
+
+ public static function getCapabilities() {
+ return new \OC_OCS_Result(array(
+ 'capabilities' => array(
+ 'files' => array(
+ 'versioning' => true,
+ ),
+ ),
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php
index 500ce0ef064..7891b20e92f 100644
--- a/apps/files_versions/lib/hooks.php
+++ b/apps/files_versions/lib/hooks.php
@@ -10,7 +10,7 @@
* This class contains all hooks.
*/
-namespace OCA_Versions;
+namespace OCA\Files_Versions;
class Hooks {
@@ -20,13 +20,10 @@ class Hooks {
public static function write_hook( $params ) {
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
-
- $versions = new Storage( new \OC_FilesystemView('') );
-
- $path = $params[\OC_Filesystem::signal_param_path];
-
- if($path<>'') $versions->store( $path );
-
+ $path = $params[\OC\Files\Filesystem::signal_param_path];
+ if($path<>'') {
+ Storage::store($path);
+ }
}
}
@@ -39,14 +36,12 @@ class Hooks {
* cleanup the versions directory if the actual file gets deleted
*/
public static function remove_hook($params) {
- $versions_fileview = \OCP\Files::getStorage('files_versions');
- $rel_path = $params['path'];
- $abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_path.'.v';
- if(Storage::isversioned($rel_path)) {
- $versions = Storage::getVersions($rel_path);
- foreach ($versions as $v) {
- unlink($abs_path . $v['version']);
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $path = $params[\OC\Files\Filesystem::signal_param_path];
+ if($path<>'') {
+ Storage::delete($path);
}
+
}
}
@@ -58,17 +53,13 @@ class Hooks {
* of the stored versions along the actual file
*/
public static function rename_hook($params) {
- $versions_fileview = \OCP\Files::getStorage('files_versions');
- $rel_oldpath = $params['oldpath'];
- $abs_oldpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_oldpath.'.v';
- $abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$params['newpath'].'.v';
- if(Storage::isversioned($rel_oldpath)) {
- $info=pathinfo($abs_newpath);
- if(!file_exists($info['dirname'])) mkdir($info['dirname'],0750,true);
- $versions = Storage::getVersions($rel_oldpath);
- foreach ($versions as $v) {
- rename($abs_oldpath.$v['version'], $abs_newpath.$v['version']);
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $oldpath = $params['oldpath'];
+ $newpath = $params['newpath'];
+ if($oldpath<>'' && $newpath<>'') {
+ Storage::rename( $oldpath, $newpath );
}
+
}
}
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index b68fa28a1ff..6da0e434541 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -1,6 +1,7 @@
<?php
/**
* Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
+ * 2013 Bjoern Schiessle <schiessle@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
@@ -12,268 +13,465 @@
* A class to handle the versioning of files.
*/
-namespace OCA_Versions;
+namespace OCA\Files_Versions;
class Storage {
-
- // config.php configuration:
- // - files_versions
- // - files_versionsfolder
- // - files_versionsblacklist
- // - files_versionsmaxfilesize
- // - files_versionsinterval
- // - files_versionmaxversions
- //
- // todo:
- // - finish porting to OC_FilesystemView to enable network transparency
- // - add transparent compression. first test if it´s worth it.
-
const DEFAULTENABLED=true;
- const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp';
- const DEFAULTMAXFILESIZE=1048576; // 10MB
- const DEFAULTMININTERVAL=60; // 1 min
- const DEFAULTMAXVERSIONS=50;
-
- private static function getUidAndFilename($filename)
- {
- if (\OCP\App::isEnabled('files_sharing')
- && substr($filename, 0, 7) == '/Shared'
- && $source = \OCP\Share::getItemSharedWith('file',
- substr($filename, 7),
- \OC_Share_Backend_File::FORMAT_SHARED_STORAGE)) {
- $filename = $source['path'];
- $pos = strpos($filename, '/files', 1);
- $uid = substr($filename, 1, $pos - 1);
- $filename = substr($filename, $pos + 6);
- } else {
- $uid = \OCP\User::getUser();
+ const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
+
+ private static $max_versions_per_interval = array(
+ //first 10sec, one version every 2sec
+ 1 => array('intervalEndsAfter' => 10, 'step' => 2),
+ //next minute, one version every 10sec
+ 2 => array('intervalEndsAfter' => 60, 'step' => 10),
+ //next hour, one version every minute
+ 3 => array('intervalEndsAfter' => 3600, 'step' => 60),
+ //next 24h, one version every hour
+ 4 => array('intervalEndsAfter' => 86400, 'step' => 3600),
+ //next 30days, one version per day
+ 5 => array('intervalEndsAfter' => 2592000, 'step' => 86400),
+ //until the end one version per week
+ 6 => array('intervalEndsAfter' => -1, 'step' => 604800),
+ );
+
+ public static function getUidAndFilename($filename) {
+ $uid = \OC\Files\Filesystem::getOwner($filename);
+ \OC\Files\Filesystem::initMountPoints($uid);
+ if ( $uid != \OCP\User::getUser() ) {
+ $info = \OC\Files\Filesystem::getFileInfo($filename);
+ $ownerView = new \OC\Files\View('/'.$uid.'/files');
+ $filename = $ownerView->getPath($info['fileid']);
}
return array($uid, $filename);
}
/**
+ * get current size of all versions from a given user
+ *
+ * @param $user user who owns the versions
+ * @return mixed versions size or false if no versions size is stored
+ */
+ private static function getVersionsSize($user) {
+ $query = \OC_DB::prepare('SELECT `size` FROM *PREFIX*files_versions WHERE `user`=?');
+ $result = $query->execute(array($user))->fetchAll();
+
+ if ($result) {
+ return $result[0]['size'];
+ }
+ return false;
+ }
+
+ /**
+ * write to the database how much space is in use for versions
+ *
+ * @param $user owner of the versions
+ * @param $size size of the versions
+ */
+ private static function setVersionsSize($user, $size) {
+ if ( self::getVersionsSize($user) === false) {
+ $query = \OC_DB::prepare('INSERT INTO *PREFIX*files_versions (`size`, `user`) VALUES (?, ?)');
+ }else {
+ $query = \OC_DB::prepare('UPDATE *PREFIX*files_versions SET `size`=? WHERE `user`=?');
+ }
+ $query->execute(array($size, $user));
+ }
+
+ /**
* store a new version of a file.
*/
- public function store($filename) {
+ public static function store($filename) {
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+
+ // if the file gets streamed we need to remove the .part extension
+ // to get the right target
+ $ext = pathinfo($filename, PATHINFO_EXTENSION);
+ if ($ext === 'part') {
+ $filename = substr($filename, 0, strlen($filename)-5);
+ }
+
list($uid, $filename) = self::getUidAndFilename($filename);
- $files_view = new \OC_FilesystemView('/'.$uid.'/files');
- $users_view = new \OC_FilesystemView('/'.$uid);
- //check if source file already exist as version to avoid recursions.
- // todo does this check work?
- if ($users_view->file_exists($filename)) {
- return false;
- }
+ $files_view = new \OC\Files\View('/'.$uid .'/files');
+ $users_view = new \OC\Files\View('/'.$uid);
+ $versions_view = new \OC\Files\View('/'.$uid.'/files_versions');
// check if filename is a directory
if($files_view->is_dir($filename)) {
return false;
}
- // check filetype blacklist
- $blacklist=explode(' ',\OCP\Config::getSystemValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST));
- foreach($blacklist as $bl) {
- $parts=explode('.', $filename);
- $ext=end($parts);
- if(strtolower($ext)==$bl) {
- return false;
- }
- }
// we should have a source file to work with
if (!$files_view->file_exists($filename)) {
return false;
}
- // check filesize
- if($files_view->filesize($filename)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)) {
- return false;
- }
-
-
- // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval)
- if ($uid == \OCP\User::getUser()) {
- $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions');
- $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
- $matches=glob($versionsFolderName.'/'.$filename.'.v*');
- sort($matches);
- $parts=explode('.v',end($matches));
- if((end($parts)+Storage::DEFAULTMININTERVAL)>time()) {
- return false;
- }
- }
-
-
// create all parent folders
- $info=pathinfo($filename);
+ $info=pathinfo($filename);
+ $versionsFolderName=$versions_view->getLocalFolder('');
if(!file_exists($versionsFolderName.'/'.$info['dirname'])) {
- mkdir($versionsFolderName.'/'.$info['dirname'],0750,true);
+ mkdir($versionsFolderName.'/'.$info['dirname'], 0750, true);
}
// store a new version of a file
- $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.time());
+ $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
+ $versionsSize = self::getVersionsSize($uid);
+ if ( $versionsSize === false || $versionsSize < 0 ) {
+ $versionsSize = self::calculateSize($uid);
+ }
+
+ $versionsSize += $users_view->filesize('files'.$filename);
// expire old revisions if necessary
- Storage::expire($filename);
+ $newSize = self::expire($filename, $versionsSize);
+
+ if ( $newSize != $versionsSize ) {
+ self::setVersionsSize($uid, $newSize);
+ }
}
}
/**
- * rollback to an old version of a file.
+ * Delete versions of a file
*/
- public static function rollback($filename,$revision) {
-
- if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
- list($uid, $filename) = self::getUidAndFilename($filename);
- $users_view = new \OC_FilesystemView('/'.$uid);
-
- // rollback
- if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) {
-
- return true;
-
- }else{
-
- return false;
-
+ public static function delete($filename) {
+ list($uid, $filename) = self::getUidAndFilename($filename);
+ $versions_fileview = new \OC\Files\View('/'.$uid .'/files_versions');
+
+ $abs_path = $versions_fileview->getLocalFile($filename.'.v');
+ if( ($versions = self::getVersions($uid, $filename)) ) {
+ $versionsSize = self::getVersionsSize($uid);
+ if ( $versionsSize === false || $versionsSize < 0 ) {
+ $versionsSize = self::calculateSize($uid);
}
-
+ foreach ($versions as $v) {
+ unlink($abs_path . $v['version']);
+ $versionsSize -= $v['size'];
+ }
+ self::setVersionsSize($uid, $versionsSize);
}
+ }
+ /**
+ * rename versions of a file
+ */
+ public static function rename($old_path, $new_path) {
+ list($uid, $oldpath) = self::getUidAndFilename($old_path);
+ list($uidn, $newpath) = self::getUidAndFilename($new_path);
+ $versions_view = new \OC\Files\View('/'.$uid .'/files_versions');
+ $files_view = new \OC\Files\View('/'.$uid .'/files');
+
+ // if the file already exists than it was a upload of a existing file
+ // over the web interface -> store() is the right function we need here
+ if ($files_view->file_exists($newpath)) {
+ return self::store($new_path);
+ }
+
+ $abs_newpath = $versions_view->getLocalFile($newpath);
+
+ if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) {
+ $versions_view->rename($oldpath, $newpath);
+ } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) {
+ $info=pathinfo($abs_newpath);
+ if(!file_exists($info['dirname'])) mkdir($info['dirname'], 0750, true);
+ foreach ($versions as $v) {
+ $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']);
+ }
+ }
}
/**
- * check if old versions of a file exist.
+ * rollback to an old version of a file.
*/
- public static function isversioned($filename) {
+ public static function rollback($filename, $revision) {
+
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
list($uid, $filename) = self::getUidAndFilename($filename);
- $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions');
-
- $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+ $users_view = new \OC\Files\View('/'.$uid);
+ $versionCreated = false;
+
+ //first create a new version
+ $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
+ if ( !$users_view->file_exists($version)) {
+ $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
+ $versionCreated = true;
+ }
- // check for old versions
- $matches=glob($versionsFolderName.$filename.'.v*');
- if(count($matches)>0) {
+ // rollback
+ if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) {
+ $users_view->touch('files'.$filename, $revision);
+ Storage::expire($filename);
return true;
- }else{
- return false;
+
+ }else if ( $versionCreated ) {
+ $users_view->unlink($version);
}
- }else{
- return(false);
}
- }
+ return false;
+ }
/**
* @brief get a list of all available versions of a file in descending chronological order
+ * @param $uid user id from the owner of the file
* @param $filename file to find versions of, relative to the user files dir
* @param $count number of versions to return
* @returns array
*/
- public static function getVersions( $filename, $count = 0 ) {
-
+ public static function getVersions($uid, $filename, $count = 0 ) {
if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
- list($uid, $filename) = self::getUidAndFilename($filename);
- $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions');
-
- $versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+ $versions_fileview = new \OC\Files\View('/' . $uid . '/files_versions');
+ $versionsName = $versions_fileview->getLocalFile($filename);
+
$versions = array();
-
// fetch for old versions
- $matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
+ $matches = glob(preg_quote($versionsName).'.v*' );
- sort( $matches );
+ if ( !$matches ) {
+ return $versions;
+ }
- $i = 0;
+ sort( $matches );
- $files_view = new \OC_FilesystemView('/'.$uid.'/files');
+ $files_view = new \OC\Files\View('/'.$uid.'/files');
$local_file = $files_view->getLocalFile($filename);
- foreach( $matches as $ma ) {
+ $local_file_md5 = \md5_file( $local_file );
- $i++;
- $versions[$i]['cur'] = 0;
+ foreach( $matches as $ma ) {
$parts = explode( '.v', $ma );
- $versions[$i]['version'] = ( end( $parts ) );
+ $version = ( end( $parts ) );
+ $key = $version.'#'.$filename;
+ $versions[$key]['cur'] = 0;
+ $versions[$key]['version'] = $version;
+ $versions[$key]['path'] = $filename;
+ $versions[$key]['size'] = $versions_fileview->filesize($filename.'.v'.$version);
// if file with modified date exists, flag it in array as currently enabled version
- ( \md5_file( $ma ) == \md5_file( $local_file ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 );
+ ( \md5_file( $ma ) == $local_file_md5 ? $versions[$key]['fileMatch'] = 1 : $versions[$key]['fileMatch'] = 0 );
}
$versions = array_reverse( $versions );
foreach( $versions as $key => $value ) {
-
// flag the first matched file in array (which will have latest modification date) as current version
if ( $value['fileMatch'] ) {
-
$value['cur'] = 1;
break;
-
}
-
}
$versions = array_reverse( $versions );
// only show the newest commits
if( $count != 0 and ( count( $versions )>$count ) ) {
-
$versions = array_slice( $versions, count( $versions ) - $count );
-
}
return( $versions );
-
} else {
-
// if versioning isn't enabled then return an empty array
return( array() );
-
}
}
/**
- * @brief Erase a file's versions which exceed the set quota
+ * @brief get the size of all stored versions from a given user
+ * @param $uid id from the user
+ * @return size of vesions
*/
- public static function expire($filename) {
- if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
- list($uid, $filename) = self::getUidAndFilename($filename);
- $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions');
+ private static function calculateSize($uid) {
+ if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
+ $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions');
+ $versionsRoot = $versions_fileview->getLocalFolder('');
- $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+ $iterator = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($versionsRoot),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
- // check for old versions
- $matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
+ $size = 0;
- if( count( $matches ) > \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ) {
+ foreach ($iterator as $path) {
+ if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) {
+ $relpath = substr($path, strlen($versionsRoot)-1);
+ $size += $versions_fileview->filesize($relpath);
+ }
+ }
- $numberToDelete = count($matches) - \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS );
+ return $size;
+ }
+ }
- // delete old versions of a file
- $deleteItems = array_slice( $matches, 0, $numberToDelete );
+ /**
+ * @brief returns all stored file versions from a given user
+ * @param $uid id to the user
+ * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
+ */
+ private static function getAllVersions($uid) {
+ if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
+ $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions');
+ $versionsRoot = $versions_fileview->getLocalFolder('');
- foreach( $deleteItems as $de ) {
+ $iterator = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($versionsRoot),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
- unlink( $versionsFolderName.'/'.$filename.'.v'.$de );
+ $versions = array();
+ foreach ($iterator as $path) {
+ if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) {
+ $relpath = substr($path, strlen($versionsRoot)-1);
+ $versions[$match[1].'#'.$relpath] = array('path' => $relpath, 'timestamp' => $match[1]);
}
}
+
+ ksort($versions);
+
+ $i = 0;
+
+ $result = array();
+
+ foreach( $versions as $key => $value ) {
+ $i++;
+ $size = $versions_fileview->filesize($value['path']);
+ $filename = substr($value['path'], 0, -strlen($value['timestamp'])-2);
+
+ $result['all'][$key]['version'] = $value['timestamp'];
+ $result['all'][$key]['path'] = $filename;
+ $result['all'][$key]['size'] = $size;
+
+ $filename = substr($value['path'], 0, -strlen($value['timestamp'])-2);
+ $result['by_file'][$filename][$key]['version'] = $value['timestamp'];
+ $result['by_file'][$filename][$key]['path'] = $filename;
+ $result['by_file'][$filename][$key]['size'] = $size;
+
+ }
+
+ return $result;
}
}
/**
- * @brief Erase all old versions of all user files
- * @return true/false
+ * @brief Erase a file's versions which exceed the set quota
*/
- public function expireAll() {
- $view = \OCP\Files::getStorage('files_versions');
- return $view->deleteAll('', true);
+ private static function expire($filename, $versionsSize = null) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ list($uid, $filename) = self::getUidAndFilename($filename);
+ $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions');
+
+ // get available disk space for user
+ $quota = \OC_Preferences::getValue($uid, 'files', 'quota');
+ if ( $quota === null || $quota === 'default') {
+ $quota = \OC_Appconfig::getValue('files', 'default_quota');
+ }
+ if ( $quota === null || $quota === 'none' ) {
+ $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers());
+ } else {
+ $quota = \OCP\Util::computerFileSize($quota);
+ }
+
+ // make sure that we have the current size of the version history
+ if ( $versionsSize === null ) {
+ $versionsSize = self::getVersionsSize($uid);
+ if ( $versionsSize === false || $versionsSize < 0 ) {
+ $versionsSize = self::calculateSize($uid);
+ }
+ }
+
+ // calculate available space for version history
+ $files_view = new \OC\Files\View('/'.$uid.'/files');
+ $rootInfo = $files_view->getFileInfo('/');
+ $free = $quota-$rootInfo['size']; // remaining free space for user
+ if ( $free > 0 ) {
+ $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
+ } else {
+ $availableSpace = $free-$versionsSize;
+ }
+
+ // after every 1000s run reduce the number of all versions not only for the current file
+ $random = rand(0, 1000);
+ if ($random == 0) {
+ $result = Storage::getAllVersions($uid);
+ $versions_by_file = $result['by_file'];
+ $all_versions = $result['all'];
+ } else {
+ $all_versions = Storage::getVersions($uid, $filename);
+ $versions_by_file[$filename] = $all_versions;
+ }
+
+ $time = time();
+
+ // it is possible to expire versions from more than one file
+ // iterate through all given files
+ foreach ($versions_by_file as $filename => $versions) {
+ $versions = array_reverse($versions); // newest version first
+
+ $interval = 1;
+ $step = Storage::$max_versions_per_interval[$interval]['step'];
+ if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) {
+ $nextInterval = -1;
+ } else {
+ $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
+ }
+
+ $firstVersion = reset($versions);
+ $firstKey = key($versions);
+ $prevTimestamp = $firstVersion['version'];
+ $nextVersion = $firstVersion['version'] - $step;
+ $remaining_versions[$firstKey] = $firstVersion;
+ unset($versions[$firstKey]);
+
+ foreach ($versions as $key => $version) {
+ $newInterval = true;
+ while ( $newInterval ) {
+ if ( $nextInterval == -1 || $version['version'] >= $nextInterval ) {
+ if ( $version['version'] > $nextVersion ) {
+ //distance between two version too small, delete version
+ $versions_fileview->unlink($version['path'].'.v'.$version['version']);
+ $availableSpace += $version['size'];
+ $versionsSize -= $version['size'];
+ unset($all_versions[$key]); // update array with all versions
+ } else {
+ $nextVersion = $version['version'] - $step;
+ }
+ $newInterval = false; // version checked so we can move to the next one
+ } else { // time to move on to the next interval
+ $interval++;
+ $step = Storage::$max_versions_per_interval[$interval]['step'];
+ $nextVersion = $prevTimestamp - $step;
+ if ( Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1 ) {
+ $nextInterval = -1;
+ } else {
+ $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
+ }
+ $newInterval = true; // we changed the interval -> check same version with new interval
+ }
+ }
+ $prevTimestamp = $version['version'];
+ }
+ }
+
+ // Check if enough space is available after versions are rearranged.
+ // If not we delete the oldest versions until we meet the size limit for versions,
+ // but always keep the two latest versions
+ $numOfVersions = count($all_versions) -2 ;
+ $i = 0;
+ while ($availableSpace < 0 && $i < $numOfVersions) {
+ $versions_fileview->unlink($all_versions[$i]['path'].'.v'.$all_versions[$i]['version']);
+ $versionsSize -= $all_versions[$i]['size'];
+ $availableSpace += $all_versions[$i]['size'];
+ $i++;
+ }
+
+ return $versionsSize; // finally return the new size of the version history
+ }
+
+ return false;
}
}
diff --git a/apps/files_versions/settings-personal.php b/apps/files_versions/settings-personal.php
deleted file mode 100644
index 4fb866bd999..00000000000
--- a/apps/files_versions/settings-personal.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-$tmpl = new OCP\Template( 'files_versions', 'settings-personal');
-
-OCP\Util::addscript('files_versions','settings-personal');
-
-return $tmpl->fetchPage();
diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php
deleted file mode 100644
index f2873b8f7c2..00000000000
--- a/apps/files_versions/settings.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-OCP\User::checkAdminUser();
-
-OCP\Util::addscript( 'files_versions', 'versions' );
-
-$tmpl = new OCP\Template( 'files_versions', 'settings');
-
-return $tmpl->fetchPage();
diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php
index 854d032da62..f7284439041 100644
--- a/apps/files_versions/templates/history.php
+++ b/apps/files_versions/templates/history.php
@@ -5,26 +5,29 @@
if( isset( $_['message'] ) ) {
- if( isset($_['path'] ) ) echo('<strong>File: '.$_['path'] ).'</strong><br>';
- echo('<strong>'.$_['message'] ).'</strong><br>';
+ if( isset($_['path'] ) ) print_unescaped('<strong>File: '.OC_Util::sanitizeHTML($_['path'])).'</strong><br>';
+ print_unescaped('<strong>'.OC_Util::sanitizeHTML($_['message']) ).'</strong><br>';
}else{
if( isset( $_['outcome_stat'] ) ) {
- echo( '<div id="feedback-messages" class="'.$_['outcome_stat'].'"><h3>'.$_['outcome_msg'] ).'</h3></div><br>';
+ print_unescaped( '<div id="feedback-messages" class="'.OC_Util::sanitizeHTML($_['outcome_stat']).'"><h3>'.OC_Util::sanitizeHTML($_['outcome_msg']) ).'</h3></div><br>';
}
- echo( '<strong>Versions of '.$_['path'] ).'</strong><br>';
- echo('<p><em>Revert a file to a previous version by clicking on its revert button</em></p><br />');
+ print_unescaped( '<strong>Versions of '.OC_Util::sanitizeHTML($_['path']) ).'</strong><br>';
+ print_unescaped('<p><em>'.OC_Util::sanitizeHTML($l->t('Revert a file to a previous version by clicking on its revert button')).'</em></p><br />');
foreach ( $_['versions'] as $v ) {
- echo ' ';
- echo OCP\Util::formatDate( doubleval($v['version']) );
- echo ' <a href="'.OCP\Util::linkTo('files_versions', 'history.php', array('path' => $_['path'], 'revert' => $v['version'])) .'" class="button">Revert</a><br /><br />';
- if ( $v['cur'] ) { echo ' (<b>Current</b>)'; }
- echo '<br /><br />';
+ p(' ');
+ p(OCP\Util::formatDate( doubleval($v['version'])));
+ print_unescaped(' <a href="'.OCP\Util::linkTo('files_versions', 'history.php',
+ array('path' => $_['path'], 'revert' => $v['version'])) .'" class="button">Revert</a><br /><br />');
+ if ( $v['cur'] ) {
+ print_unescaped(' (<b>Current</b>)');
+ }
+ print_unescaped('<br /><br />');
}
}
diff --git a/apps/files_versions/templates/settings-personal.php b/apps/files_versions/templates/settings-personal.php
deleted file mode 100644
index 6d97cea46c3..00000000000
--- a/apps/files_versions/templates/settings-personal.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<form id="versions">
- <fieldset class="personalblock">
- <legend>
- <strong><?php echo $l->t('Versions'); ?></strong>
- </legend>
- <p>
- <?php echo $l->t('This will delete all existing backup versions of your files'); ?>
- </p>
- <button id="expireAllBtn">
- <?php echo $l->t('Expire all versions'); ?>
- <img style="display: none;" class="expireAllLoading" src="<?php echo OCP\Util::imagePath('core', 'loading.gif'); ?>" />
- </button>
- </fieldset>
-</form>
diff --git a/apps/files_versions/templates/settings.php b/apps/files_versions/templates/settings.php
deleted file mode 100644
index 88063cb075b..00000000000
--- a/apps/files_versions/templates/settings.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<form id="versionssettings">
- <fieldset class="personalblock">
- <legend><strong><?php echo $l->t('Files Versioning');?></strong></legend>
- <input type="checkbox" name="versions" id="versions" value="1" <?php if (OCP\Config::getSystemValue('versions', 'true')=='true') echo ' checked="checked"'; ?> /> <label for="versions"><?php echo $l->t('Enable'); ?></label> <br/>
- </fieldset>
-</form>
diff --git a/apps/files_versions/ajax/expireAll.php b/apps/user_ldap/ajax/deleteConfiguration.php
index 5c95885ffbd..ade57110d34 100644
--- a/apps/files_versions/ajax/expireAll.php
+++ b/apps/user_ldap/ajax/deleteConfiguration.php
@@ -1,10 +1,10 @@
<?php
/**
- * ownCloud - user_migrate
+ * ownCloud - user_ldap
*
- * @author Sam Tuke
- * @copyright 2012 Sam Tuke samtuke@owncloud.com
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -21,24 +21,15 @@
*
*/
-// TODO: Allow admins to expire versions of any user
-// TODO: Provide feedback as to how many versions were deleted
-
// Check user and app status
-OCP\JSON::checkLoggedIn();
-OCP\App::checkAppEnabled('files_versions');
+OCP\JSON::checkAdminUser();
+OCP\JSON::checkAppEnabled('user_ldap');
OCP\JSON::callCheck();
-$versions = new OCA_Versions\Storage();
-
-if( $versions->expireAll() ) {
-
+$prefix = $_POST['ldap_serverconfig_chooser'];
+if(\OCA\user_ldap\lib\Helper::deleteServerConfiguration($prefix)) {
OCP\JSON::success();
- die();
-
} else {
-
- OCP\JSON::error();
- die();
-
-} \ No newline at end of file
+ $l=OC_L10N::get('user_ldap');
+ OCP\JSON::error(array('message' => $l->t('Failed to delete the server configuration')));
+}
diff --git a/apps/user_ldap/ajax/getConfiguration.php b/apps/user_ldap/ajax/getConfiguration.php
new file mode 100644
index 00000000000..dfae68d2dc9
--- /dev/null
+++ b/apps/user_ldap/ajax/getConfiguration.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * ownCloud - user_ldap
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Check user and app status
+OCP\JSON::checkAdminUser();
+OCP\JSON::checkAppEnabled('user_ldap');
+OCP\JSON::callCheck();
+
+$prefix = $_POST['ldap_serverconfig_chooser'];
+$connection = new \OCA\user_ldap\lib\Connection($prefix);
+OCP\JSON::success(array('configuration' => $connection->getConfiguration())); \ No newline at end of file
diff --git a/apps/user_ldap/ajax/getNewServerConfigPrefix.php b/apps/user_ldap/ajax/getNewServerConfigPrefix.php
new file mode 100644
index 00000000000..17e78f87072
--- /dev/null
+++ b/apps/user_ldap/ajax/getNewServerConfigPrefix.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * ownCloud - user_ldap
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Check user and app status
+OCP\JSON::checkAdminUser();
+OCP\JSON::checkAppEnabled('user_ldap');
+OCP\JSON::callCheck();
+
+$serverConnections = \OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes();
+sort($serverConnections);
+$lk = array_pop($serverConnections);
+$ln = intval(str_replace('s', '', $lk));
+$nk = 's'.str_pad($ln+1, 2, '0', STR_PAD_LEFT);
+OCP\JSON::success(array('configPrefix' => $nk)); \ No newline at end of file
diff --git a/apps/user_ldap/ajax/setConfiguration.php b/apps/user_ldap/ajax/setConfiguration.php
new file mode 100644
index 00000000000..206487c7e0a
--- /dev/null
+++ b/apps/user_ldap/ajax/setConfiguration.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * ownCloud - user_ldap
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Check user and app status
+OCP\JSON::checkAdminUser();
+OCP\JSON::checkAppEnabled('user_ldap');
+OCP\JSON::callCheck();
+
+$prefix = $_POST['ldap_serverconfig_chooser'];
+$connection = new \OCA\user_ldap\lib\Connection($prefix);
+$connection->setConfiguration($_POST);
+$connection->saveConfiguration();
+OCP\JSON::success(); \ No newline at end of file
diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php
index a82f7e4c17b..7ce1258a796 100644
--- a/apps/user_ldap/ajax/testConfiguration.php
+++ b/apps/user_ldap/ajax/testConfiguration.php
@@ -4,7 +4,7 @@
* ownCloud - user_ldap
*
* @author Arthur Schiwon
- * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
+ * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -26,14 +26,19 @@ OCP\JSON::checkAdminUser();
OCP\JSON::checkAppEnabled('user_ldap');
OCP\JSON::callCheck();
-$connection = new \OCA\user_ldap\lib\Connection(null);
+$l=OC_L10N::get('user_ldap');
+
+$connection = new \OCA\user_ldap\lib\Connection('', null);
if($connection->setConfiguration($_POST)) {
//Configuration is okay
if($connection->bind()) {
- OCP\JSON::success(array('message' => 'The configuration is valid and the connection could be established!'));
+ OCP\JSON::success(array('message'
+ => $l->t('The configuration is valid and the connection could be established!')));
} else {
- OCP\JSON::error(array('message' => 'The configuration is valid, but the Bind failed. Please check the server settings and credentials.'));
+ OCP\JSON::error(array('message'
+ => $l->t('The configuration is valid, but the Bind failed. Please check the server settings and credentials.')));
}
} else {
- OCP\JSON::error(array('message' => 'The configuration is invalid. Please look in the ownCloud log for further details.'));
+ OCP\JSON::error(array('message'
+ => $l->t('The configuration is invalid. Please look in the ownCloud log for further details.')));
}
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
index 0eec7829a4a..89410b5ef07 100644
--- a/apps/user_ldap/appinfo/app.php
+++ b/apps/user_ldap/appinfo/app.php
@@ -23,15 +23,23 @@
OCP\App::registerAdmin('user_ldap', 'settings');
-$connector = new OCA\user_ldap\lib\Connection('user_ldap');
-$userBackend = new OCA\user_ldap\USER_LDAP();
-$userBackend->setConnector($connector);
-$groupBackend = new OCA\user_ldap\GROUP_LDAP();
-$groupBackend->setConnector($connector);
+$configPrefixes = OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(true);
+if(count($configPrefixes) == 1) {
+ $connector = new OCA\user_ldap\lib\Connection($configPrefixes[0]);
+ $userBackend = new OCA\user_ldap\USER_LDAP();
+ $userBackend->setConnector($connector);
+ $groupBackend = new OCA\user_ldap\GROUP_LDAP();
+ $groupBackend->setConnector($connector);
+} else {
+ $userBackend = new OCA\user_ldap\User_Proxy($configPrefixes);
+ $groupBackend = new OCA\user_ldap\Group_Proxy($configPrefixes);
+}
-// register user backend
-OC_User::useBackend($userBackend);
-OC_Group::useBackend($groupBackend);
+if(count($configPrefixes) > 0) {
+ // register user backend
+ OC_User::useBackend($userBackend);
+ OC_Group::useBackend($groupBackend);
+}
// add settings page to navigation
$entry = array(
@@ -42,3 +50,8 @@ $entry = array(
);
OCP\Backgroundjob::addRegularTask('OCA\user_ldap\lib\Jobs', 'updateGroups');
+if(OCP\App::isEnabled('user_webdavauth')) {
+ OCP\Util::writeLog('user_ldap',
+ 'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour',
+ OCP\Util::WARN);
+}
diff --git a/apps/user_ldap/appinfo/database.xml b/apps/user_ldap/appinfo/database.xml
index a785bbf4221..812e450dde7 100644
--- a/apps/user_ldap/appinfo/database.xml
+++ b/apps/user_ldap/appinfo/database.xml
@@ -130,7 +130,7 @@
</field>
<index>
- <name>ldap_group_members</name>
+ <name>ldap_group_members_index</name>
<unique>true</unique>
<field>
<name>owncloudname</name>
diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml
index 30fbf687dbe..148a72cecbb 100644
--- a/apps/user_ldap/appinfo/info.xml
+++ b/apps/user_ldap/appinfo/info.xml
@@ -2,10 +2,14 @@
<info>
<id>user_ldap</id>
<name>LDAP user and group backend</name>
- <description>Authenticate Users by LDAP</description>
+ <description>Authenticate users and groups by LDAP respectively Active
+ Directory.
+
+ This app is not compatible with the WebDAV user backend.
+ </description>
<licence>AGPL</licence>
<author>Dominik Schmidt and Arthur Schiwon</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
<types>
<authentication/>
diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php
index f23285a0dc6..2fcbf1902ac 100644
--- a/apps/user_ldap/appinfo/update.php
+++ b/apps/user_ldap/appinfo/update.php
@@ -5,7 +5,7 @@
//ATTENTION
//Upgrade from ownCloud 3 (LDAP backend 0.1) to ownCloud 4.5 (LDAP backend 0.3) is not supported!!
//You must do upgrade to ownCloud 4.0 first!
-//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad efffects.
+//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad effects.
//settings
$pw = OCP\Config::getAppValue('user_ldap', 'ldap_password');
@@ -22,34 +22,75 @@ if($state == 'unset') {
OCP\Config::setSystemValue('ldapIgnoreNamingRules', false);
}
-// ### SUPPORTED upgrade path starts here ###
-
//from version 0.2 to 0.3 (0.2.0.x dev version)
$objects = array('user', 'group');
-$connector = new \OCA\user_ldap\lib\Connection('user_ldap');
+$connector = new \OCA\user_ldap\lib\Connection();
$userBE = new \OCA\user_ldap\USER_LDAP();
$userBE->setConnector($connector);
$groupBE = new \OCA\user_ldap\GROUP_LDAP();
$groupBE->setConnector($connector);
foreach($objects as $object) {
- $fetchDNSql = 'SELECT `ldap_dn`, `owncloud_name` FROM `*PREFIX*ldap_'.$object.'_mapping` WHERE `directory_uuid` = ""';
- $updateSql = 'UPDATE `*PREFIX*ldap_'.$object.'_mapping` SET `ldap_DN` = ?, `directory_uuid` = ? WHERE `ldap_dn` = ?';
+ $fetchDNSql = '
+ SELECT `ldap_dn`, `owncloud_name`, `directory_uuid`
+ FROM `*PREFIX*ldap_'.$object.'_mapping`';
+ $updateSql = '
+ UPDATE `*PREFIX*ldap_'.$object.'_mapping`
+ SET `ldap_DN` = ?, `directory_uuid` = ?
+ WHERE `ldap_dn` = ?';
$query = OCP\DB::prepare($fetchDNSql);
$res = $query->execute();
$DNs = $res->fetchAll();
$updateQuery = OCP\DB::prepare($updateSql);
foreach($DNs as $dn) {
- $newDN = mb_strtolower($dn['ldap_dn'], 'UTF-8');
- if($object == 'user') {
+ $newDN = escapeDN(mb_strtolower($dn['ldap_dn'], 'UTF-8'));
+ if(!empty($dn['directory_uuid'])) {
+ $uuid = $dn['directory_uuid'];
+ } elseif($object == 'user') {
$uuid = $userBE->getUUID($newDN);
//fix home folder to avoid new ones depending on the configuration
$userBE->getHome($dn['owncloud_name']);
} else {
$uuid = $groupBE->getUUID($newDN);
}
- $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn']));
+ try {
+ $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn']));
+ } catch(Exception $e) {
+ \OCP\Util::writeLog('user_ldap',
+ 'Could not update '.$object.' '.$dn['ldap_dn'].' in the mappings table. ',
+ \OCP\Util::WARN);
+ }
+
+ }
+}
+
+function escapeDN($dn) {
+ $aDN = ldap_explode_dn($dn, false);
+ unset($aDN['count']);
+ foreach($aDN as $key => $part) {
+ $value = substr($part, strpos($part, '=')+1);
+ $escapedValue = strtr($value, Array(','=>'\2c', '='=>'\3d', '+'=>'\2b',
+ '<'=>'\3c', '>'=>'\3e', ';'=>'\3b', '\\'=>'\5c',
+ '"'=>'\22', '#'=>'\23'));
+ $part = str_replace($part, $value, $escapedValue);
}
+ $dn = implode(',', $aDN);
+
+ return $dn;
+}
+
+
+// SUPPORTED UPGRADE FROM Version 0.3 (ownCloud 4.5) to 0.4 (ownCloud 5)
+
+if(!isset($connector)) {
+ $connector = new \OCA\user_ldap\lib\Connection();
}
+//it is required, that connections do have ldap_configuration_active setting stored in the database
+$connector->getConfiguration();
+$connector->saveConfiguration();
+
+// we don't save it anymore, was a well-meant bad idea. Clean up database.
+$query = OC_DB::prepare('DELETE FROM `*PREFIX*preferences` WHERE `appid` = ? AND `configkey` = ?');
+$query->execute(array('user_ldap' , 'homedir'));
diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version
index 73082a89b35..60a2d3e96c8 100644
--- a/apps/user_ldap/appinfo/version
+++ b/apps/user_ldap/appinfo/version
@@ -1 +1 @@
-0.3.0.0 \ No newline at end of file
+0.4.0 \ No newline at end of file
diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css
index 30c5c175c9b..84ada0832ab 100644
--- a/apps/user_ldap/css/settings.css
+++ b/apps/user_ldap/css/settings.css
@@ -2,9 +2,16 @@
width: 20%;
max-width: 200px;
display: inline-block;
+ vertical-align: top;
+ padding-top: 9px;
}
-#ldap fieldset input {
+#ldap fieldset input, #ldap fieldset textarea {
width: 70%;
display: inline-block;
+}
+
+.ldapwarning {
+ margin-left: 1.4em;
+ color: #FF3B3B;
} \ No newline at end of file
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index bd9f7e0c552..efa5f8b4fe3 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -28,10 +28,11 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
public function setConnector(lib\Connection &$connection) {
parent::setConnector($connection);
- if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) {
- $this->enabled = false;
+ $filter = $this->connection->ldapGroupFilter;
+ $gassoc = $this->connection->ldapGroupMemberAssocAttr;
+ if(!empty($filter) && !empty($gassoc)) {
+ $this->enabled = true;
}
- $this->enabled = true;
}
/**
@@ -96,12 +97,13 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
if(!$this->enabled) {
return array();
}
- if($this->connection->isCached('getUserGroups'.$uid)) {
- return $this->connection->getFromCache('getUserGroups'.$uid);
+ $cacheKey = 'getUserGroups'.$uid;
+ if($this->connection->isCached($cacheKey)) {
+ return $this->connection->getFromCache($cacheKey);
}
$userDN = $this->username2dn($uid);
if(!$userDN) {
- $this->connection->writeToCache('getUserGroups'.$uid, array());
+ $this->connection->writeToCache($cacheKey, array());
return array();
}
@@ -122,9 +124,9 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
$this->connection->ldapGroupFilter,
$this->connection->ldapGroupMemberAssocAttr.'='.$uid
));
- $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn'));
+ $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'));
$groups = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
- $this->connection->writeToCache('getUserGroups'.$uid, $groups);
+ $this->connection->writeToCache($cacheKey, $groups);
return $groups;
}
@@ -137,61 +139,87 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
if(!$this->enabled) {
return array();
}
- $this->groupSearch = $search;
- if($this->connection->isCached('usersInGroup'.$gid)) {
- $groupUsers = $this->connection->getFromCache('usersInGroup'.$gid);
- if(!empty($this->groupSearch)) {
- $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter'));
- }
- if($limit == -1) {
- $limit = null;
- }
- return array_slice($groupUsers, $offset, $limit);
+ $cachekey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
+ // check for cache of the exact query
+ $groupUsers = $this->connection->getFromCache($cachekey);
+ if(!is_null($groupUsers)) {
+ return $groupUsers;
+ }
+
+ // check for cache of the query without limit and offset
+ $groupUsers = $this->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
+ if(!is_null($groupUsers)) {
+ $groupUsers = array_slice($groupUsers, $offset, $limit);
+ $this->connection->writeToCache($cachekey, $groupUsers);
+ return $groupUsers;
}
+ if($limit == -1) {
+ $limit = null;
+ }
$groupDN = $this->groupname2dn($gid);
if(!$groupDN) {
- $this->connection->writeToCache('usersInGroup'.$gid, array());
+ // group couldn't be found, return empty resultset
+ $this->connection->writeToCache($cachekey, array());
return array();
}
$members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr);
if(!$members) {
- $this->connection->writeToCache('usersInGroup'.$gid, array());
+ //in case users could not be retrieved, return empty resultset
+ $this->connection->writeToCache($cachekey, array());
return array();
}
- $result = array();
+ $groupUsers = array();
$isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid');
foreach($members as $member) {
if($isMemberUid) {
- $filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8');
+ //we got uids, need to get their DNs to 'tranlsate' them to usernames
+ $filter = $this->combineFilterWithAnd(array(
+ \OCP\Util::mb_str_replace('%uid', $member,
+ $this->connection>ldapLoginFilter, 'UTF-8'),
+ $this->getFilterPartForUserSearch($search)
+ ));
$ldap_users = $this->fetchListOfUsers($filter, 'dn');
if(count($ldap_users) < 1) {
continue;
}
- $result[] = $this->dn2username($ldap_users[0]);
- continue;
+ $groupUsers[] = $this->dn2username($ldap_users[0]);
} else {
+ //we got DNs, check if we need to filter by search or we can give back all of them
+ if(!empty($search)) {
+ if(!$this->readAttribute($member,
+ $this->connection->ldapUserDisplayName,
+ $this->getFilterPartForUserSearch($search))) {
+ continue;
+ }
+ }
+ // dn2username will also check if the users belong to the allowed base
if($ocname = $this->dn2username($member)) {
- $result[] = $ocname;
+ $groupUsers[] = $ocname;
}
}
}
- if(!$isMemberUid) {
- $result = array_intersect($result, \OCP\User::getUsers());
- }
- $groupUsers = array_unique($result, SORT_LOCALE_STRING);
- $this->connection->writeToCache('usersInGroup'.$gid, $groupUsers);
+ natsort($groupUsers);
+ $this->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
+ $groupUsers = array_slice($groupUsers, $offset, $limit);
+ $this->connection->writeToCache($cachekey, $groupUsers);
- if(!empty($this->groupSearch)) {
- $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter'));
- }
- if($limit == -1) {
- $limit = null;
- }
- return array_slice($groupUsers, $offset, $limit);
+ return $groupUsers;
+ }
+ /**
+ * @brief get a list of all display names in a group
+ * @returns array with display names (value) and user ids(key)
+ */
+ public function displayNamesInGroup($gid, $search, $limit, $offset) {
+ $users = $this->usersInGroup($gid, $search, $limit, $offset);
+ $displayNames = array();
+ foreach($users as $user) {
+ $displayNames[$user] = \OC_User::getDisplayName($user);
+ }
+ return $displayNames;
}
/**
@@ -204,22 +232,31 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
if(!$this->enabled) {
return array();
}
+ $cachekey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
- if($this->connection->isCached('getGroups')) {
- $ldap_groups = $this->connection->getFromCache('getGroups');
- } else {
- $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn'));
- $ldap_groups = $this->ownCloudGroupNames($ldap_groups);
- $this->connection->writeToCache('getGroups', $ldap_groups);
+ //Check cache before driving unnecessary searches
+ \OCP\Util::writeLog('user_ldap', 'getGroups '.$cachekey, \OCP\Util::DEBUG);
+ $ldap_groups = $this->connection->getFromCache($cachekey);
+ if(!is_null($ldap_groups)) {
+ return $ldap_groups;
}
- $this->groupSearch = $search;
- if(!empty($this->groupSearch)) {
- $ldap_groups = array_filter($ldap_groups, array($this, 'groupMatchesFilter'));
- }
- if($limit = -1) {
+
+ // if we'd pass -1 to LDAP search, we'd end up in a Protocol
+ // error. With a limit of 0, we get 0 results. So we pass null.
+ if($limit <= 0) {
$limit = null;
}
- return array_slice($ldap_groups, $offset, $limit);
+ $filter = $this->combineFilterWithAnd(array(
+ $this->connection->ldapGroupFilter,
+ $this->getFilterPartForGroupSearch($search)
+ ));
+ \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
+ $ldap_groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'),
+ $limit, $offset);
+ $ldap_groups = $this->ownCloudGroupNames($ldap_groups);
+
+ $this->connection->writeToCache($cachekey, $ldap_groups);
+ return $ldap_groups;
}
public function groupMatchesFilter($group) {
@@ -263,7 +300,6 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
* compared with OC_USER_BACKEND_CREATE_USER etc.
*/
public function implementsActions($actions) {
- //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now.
- return false;
+ return (bool)(OC_GROUP_BACKEND_GET_DISPLAYNAME & $actions);
}
-} \ No newline at end of file
+}
diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php
new file mode 100644
index 00000000000..68d2efe3871
--- /dev/null
+++ b/apps/user_ldap/group_proxy.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Artuhr Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap;
+
+class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface {
+ private $backends = array();
+ private $refBackend = null;
+
+ /**
+ * @brief Constructor
+ * @param $serverConfigPrefixes array containing the config Prefixes
+ */
+ public function __construct($serverConfigPrefixes) {
+ parent::__construct();
+ foreach($serverConfigPrefixes as $configPrefix) {
+ $this->backends[$configPrefix] = new \OCA\user_ldap\GROUP_LDAP();
+ $connector = $this->getConnector($configPrefix);
+ $this->backends[$configPrefix]->setConnector($connector);
+ if(is_null($this->refBackend)) {
+ $this->refBackend = &$this->backends[$configPrefix];
+ }
+ }
+ }
+
+ /**
+ * @brief Tries the backends one after the other until a positive result is returned from the specified method
+ * @param $gid string, the gid connected to the request
+ * @param $method string, the method of the group backend that shall be called
+ * @param $parameters an array of parameters to be passed
+ * @return mixed, the result of the method or false
+ */
+ protected function walkBackends($gid, $method, $parameters) {
+ $cacheKey = $this->getGroupCacheKey($gid);
+ foreach($this->backends as $configPrefix => $backend) {
+ if($result = call_user_func_array(array($backend, $method), $parameters)) {
+ $this->writeToCache($cacheKey, $configPrefix);
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Asks the backend connected to the server that supposely takes care of the gid from the request.
+ * @param $gid string, the gid connected to the request
+ * @param $method string, the method of the group backend that shall be called
+ * @param $parameters an array of parameters to be passed
+ * @return mixed, the result of the method or false
+ */
+ protected function callOnLastSeenOn($gid, $method, $parameters) {
+ $cacheKey = $this->getGroupCacheKey($gid);;
+ $prefix = $this->getFromCache($cacheKey);
+ //in case the uid has been found in the past, try this stored connection first
+ if(!is_null($prefix)) {
+ if(isset($this->backends[$prefix])) {
+ $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters);
+ if(!$result) {
+ //not found here, reset cache to null
+ $this->writeToCache($cacheKey, null);
+ }
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief is user in group?
+ * @param $uid uid of the user
+ * @param $gid gid of the group
+ * @returns true/false
+ *
+ * Checks whether the user is member of a group or not.
+ */
+ public function inGroup($uid, $gid) {
+ return $this->handleRequest($gid, 'inGroup', array($uid, $gid));
+ }
+
+ /**
+ * @brief Get all groups a user belongs to
+ * @param $uid Name of the user
+ * @returns array with group names
+ *
+ * This function fetches all groups a user belongs to. It does not check
+ * if the user exists at all.
+ */
+ public function getUserGroups($uid) {
+ $groups = array();
+
+ foreach($this->backends as $backend) {
+ $backendGroups = $backend->getUserGroups($uid);
+ if (is_array($backendGroups)) {
+ $groups = array_merge($groups, $backendGroups);
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * @brief get a list of all users in a group
+ * @returns array with user ids
+ */
+ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ $users = array();
+
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->usersInGroup($gid, $search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = array_merge($users, $backendUsers);
+ }
+ }
+
+ return $users;
+ }
+
+ /**
+ * @brief get a list of all display names in a group
+ * @returns array with display names (value) and user ids(key)
+ */
+ public function displayNamesInGroup($gid, $search, $limit, $offset) {
+ $displayNames = array();
+
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->displayNamesInGroup($gid, $search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $displayNames = array_merge($displayNames, $backendUsers);
+ }
+ }
+ return $displayNames;
+ }
+
+ /**
+ * @brief get a list of all groups
+ * @returns array with group names
+ *
+ * Returns a list with all groups
+ */
+ public function getGroups($search = '', $limit = -1, $offset = 0) {
+ $groups = array();
+
+ foreach($this->backends as $backend) {
+ $backendGroups = $backend->getGroups($search, $limit, $offset);
+ if (is_array($backendGroups)) {
+ $groups = array_merge($groups, $backendGroups);
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * check if a group exists
+ * @param string $gid
+ * @return bool
+ */
+ public function groupExists($gid) {
+ return $this->handleRequest($gid, 'groupExists', array($gid));
+ }
+
+ /**
+ * @brief Check if backend implements actions
+ * @param $actions bitwise-or'ed actions
+ * @returns boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ //it's the same across all our user backends obviously
+ return $this->refBackend->implementsActions($actions);
+ }
+} \ No newline at end of file
diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js
index 7063eead96a..e34849ec887 100644
--- a/apps/user_ldap/js/settings.js
+++ b/apps/user_ldap/js/settings.js
@@ -1,6 +1,114 @@
+var LdapConfiguration = {
+ refreshConfig: function() {
+ if($('#ldap_serverconfig_chooser option').length < 2) {
+ LdapConfiguration.addConfiguration(true);
+ return;
+ }
+ $.post(
+ OC.filePath('user_ldap','ajax','getConfiguration.php'),
+ $('#ldap_serverconfig_chooser').serialize(),
+ function (result) {
+ if(result.status == 'success') {
+ $.each(result.configuration, function(configkey, configvalue) {
+ elementID = '#'+configkey;
+
+ //deal with Checkboxes
+ if($(elementID).is('input[type=checkbox]')) {
+ if(configvalue == 1) {
+ $(elementID).attr('checked', 'checked');
+ } else {
+ $(elementID).removeAttr('checked');
+ }
+ return;
+ }
+
+ //On Textareas, Multi-Line Settings come as array
+ if($(elementID).is('textarea') && $.isArray(configvalue)) {
+ configvalue = configvalue.join("\n");
+ }
+
+ // assign the value
+ $('#'+configkey).val(configvalue);
+ });
+ }
+ }
+ );
+ },
+
+ resetDefaults: function() {
+ $('#ldap').find('input[type=text], input[type=number], input[type=password], textarea, select').each(function() {
+ if($(this).attr('id') == 'ldap_serverconfig_chooser') {
+ return;
+ }
+ $(this).val($(this).attr('data-default'));
+ });
+ $('#ldap').find('input[type=checkbox]').each(function() {
+ if($(this).attr('data-default') == 1) {
+ $(this).attr('checked', 'checked');
+ } else {
+ $(this).removeAttr('checked');
+ }
+ });
+ },
+
+ deleteConfiguration: function() {
+ $.post(
+ OC.filePath('user_ldap','ajax','deleteConfiguration.php'),
+ $('#ldap_serverconfig_chooser').serialize(),
+ function (result) {
+ if(result.status == 'success') {
+ $('#ldap_serverconfig_chooser option:selected').remove();
+ $('#ldap_serverconfig_chooser option:first').select();
+ LdapConfiguration.refreshConfig();
+ } else {
+ OC.dialogs.alert(
+ result.message,
+ t('user_ldap', 'Deletion failed')
+ );
+ }
+ }
+ );
+ },
+
+ addConfiguration: function(doNotAsk) {
+ $.post(
+ OC.filePath('user_ldap','ajax','getNewServerConfigPrefix.php'),
+ function (result) {
+ if(result.status == 'success') {
+ if(doNotAsk) {
+ LdapConfiguration.resetDefaults();
+ } else {
+ OC.dialogs.confirm(
+ t('user_ldap', 'Take over settings from recent server configuration?'),
+ t('user_ldap', 'Keep settings?'),
+ function(keep) {
+ if(!keep) {
+ LdapConfiguration.resetDefaults();
+ }
+ }
+ );
+ }
+ $('#ldap_serverconfig_chooser option:selected').removeAttr('selected');
+ var html = '<option value="'+result.configPrefix+'" selected="selected">'+$('#ldap_serverconfig_chooser option').length+'. Server</option>';
+ $('#ldap_serverconfig_chooser option:last').before(html);
+ } else {
+ OC.dialogs.alert(
+ result.message,
+ t('user_ldap', 'Cannot add server configuration')
+ );
+ }
+ }
+ );
+ }
+}
+
$(document).ready(function() {
+ $('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'});
$('#ldapSettings').tabs();
+ $('#ldap_submit').button();
$('#ldap_action_test_connection').button();
+ $('#ldap_action_delete_configuration').button();
+ LdapConfiguration.refreshConfig();
$('#ldap_action_test_connection').click(function(event){
event.preventDefault();
$.post(
@@ -10,15 +118,60 @@ $(document).ready(function() {
if (result.status == 'success') {
OC.dialogs.alert(
result.message,
- 'Connection test succeeded'
+ t('user_ldap', 'Connection test succeeded')
);
} else {
OC.dialogs.alert(
result.message,
- 'Connection test failed'
+ t('user_ldap', 'Connection test failed')
);
}
}
);
});
+
+ $('#ldap_action_delete_configuration').click(function(event) {
+ event.preventDefault();
+ OC.dialogs.confirm(
+ t('user_ldap', 'Do you really want to delete the current Server Configuration?'),
+ t('user_ldap', 'Confirm Deletion'),
+ function(deleteConfiguration) {
+ if(deleteConfiguration) {
+ LdapConfiguration.deleteConfiguration();
+ }
+ }
+ );
+ });
+
+ $('#ldap_submit').click(function(event) {
+ event.preventDefault();
+ $.post(
+ OC.filePath('user_ldap','ajax','setConfiguration.php'),
+ $('#ldap').serialize(),
+ function (result) {
+ bgcolor = $('#ldap_submit').css('background');
+ if (result.status == 'success') {
+ //the dealing with colors is a but ugly, but the jQuery version in use has issues with rgba colors
+ $('#ldap_submit').css('background', '#fff');
+ $('#ldap_submit').effect('highlight', {'color':'#A8FA87'}, 5000, function() {
+ $('#ldap_submit').css('background', bgcolor);
+ });
+ } else {
+ $('#ldap_submit').css('background', '#fff');
+ $('#ldap_submit').effect('highlight', {'color':'#E97'}, 5000, function() {
+ $('#ldap_submit').css('background', bgcolor);
+ });
+ }
+ }
+ );
+ });
+
+ $('#ldap_serverconfig_chooser').change(function(event) {
+ value = $('#ldap_serverconfig_chooser option:selected:first').attr('value');
+ if(value == 'NEW') {
+ LdapConfiguration.addConfiguration(false);
+ } else {
+ LdapConfiguration.refreshConfig();
+ }
+ });
}); \ No newline at end of file
diff --git a/apps/user_ldap/l10n/af_ZA.php b/apps/user_ldap/l10n/af_ZA.php
new file mode 100644
index 00000000000..944495f3869
--- /dev/null
+++ b/apps/user_ldap/l10n/af_ZA.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Wagwoord",
+"Help" => "Hulp"
+);
diff --git a/apps/user_ldap/l10n/ar.php b/apps/user_ldap/l10n/ar.php
new file mode 100644
index 00000000000..4d7b7ac4ade
--- /dev/null
+++ b/apps/user_ldap/l10n/ar.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "فشل الحذف",
+"Password" => "كلمة المرور",
+"Help" => "المساعدة"
+);
diff --git a/apps/user_ldap/l10n/bg_BG.php b/apps/user_ldap/l10n/bg_BG.php
new file mode 100644
index 00000000000..c064534a6b8
--- /dev/null
+++ b/apps/user_ldap/l10n/bg_BG.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "Парола",
+"Help" => "Помощ"
+);
diff --git a/apps/user_ldap/l10n/bn_BD.php b/apps/user_ldap/l10n/bn_BD.php
new file mode 100644
index 00000000000..69dfc896179
--- /dev/null
+++ b/apps/user_ldap/l10n/bn_BD.php
@@ -0,0 +1,36 @@
+<?php $TRANSLATIONS = array(
+"Host" => "হোস্ট",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL আবশ্যক না হলে আপনি এই প্রটোকলটি মুছে ফেলতে পারেন । এরপর শুরু করুন এটা দিয়ে ldaps://",
+"Base DN" => "ভিত্তি DN",
+"You can specify Base DN for users and groups in the Advanced tab" => "সুচারু ট্যঅবে গিয়ে আপনি ব্যবহারকারি এবং গোষ্ঠীসমূহের জন্য ভিত্তি DN নির্ধারণ করতে পারেন।",
+"User DN" => "ব্যবহারকারি DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. পরিচয় গোপন রেখে অধিগমনের জন্য DN এবং কূটশব্দটি ফাঁকা রাখুন।",
+"Password" => "কূটশব্দ",
+"For anonymous access, leave DN and Password empty." => "অজ্ঞাতকুলশীল অধিগমনের জন্য DN এবং কূটশব্দটি ফাঁকা রাখুন।",
+"User Login Filter" => "ব্যবহারকারির প্রবেশ ছাঁকনী",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "প্রবেশের চেষ্টা করার সময় প্রযোজ্য ছাঁকনীটি নির্ধারণ করবে। প্রবেশের সময় ব্যবহারকারী নামটি %%uid দিয়ে প্রতিস্থাপিত হবে।",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid স্থানধারক ব্যবহার করুন, উদাহরণঃ \"uid=%%uid\"",
+"User List Filter" => "ব্যবহারকারী তালিকা ছাঁকনী",
+"Defines the filter to apply, when retrieving users." => "ব্যবহারকারী উদ্ধার করার সময় প্রয়োগের জন্য ছাঁকনী নির্ধারণ করবে।",
+"without any placeholder, e.g. \"objectClass=person\"." => "কোন স্থানধারক ব্যতীত, যেমনঃ \"objectClass=person\"।",
+"Group Filter" => "গোষ্ঠী ছাঁকনী",
+"Defines the filter to apply, when retrieving groups." => "গোষ্ঠীসমূহ উদ্ধার করার সময় প্রয়োগের জন্য ছাঁকনী নির্ধারণ করবে।",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "কোন স্থান ধারক ব্যতীত, উদাহরণঃ\"objectClass=posixGroup\"।",
+"Port" => "পোর্ট",
+"Use TLS" => "TLS ব্যবহার কর",
+"Case insensitve LDAP server (Windows)" => "বর্ণ অসংবেদী LDAP সার্ভার (উইন্ডোজ)",
+"Turn off SSL certificate validation." => "SSL সনদপত্র যাচাইকরণ বন্ধ রাক।",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "শুধুমাত্র যদি এই বিকল্পটি ব্যবহার করেই সংযোগ কার্যকরী হয় তবে আপনার ownCloud সার্ভারে LDAP সার্ভারের SSL সনদপত্রটি আমদানি করুন।",
+"Not recommended, use for testing only." => "অনুমোদিত নয়, শুধুমাত্র পরীক্ষামূলক ব্যবহারের জন্য।",
+"in seconds. A change empties the cache." => "সেকেন্ডে। কোন পরিবর্তন ক্যাসে খালি করবে।",
+"User Display Name Field" => "ব্যবহারকারীর প্রদর্শিতব্য নামের ক্ষেত্র",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "ব্যবহারকারীর ownCloud নাম তৈরি করার জন্য ব্যভহৃত LDAP বৈশিষ্ট্য।",
+"Base User Tree" => "ভিত্তি ব্যবহারকারি বৃক্ষাকারে",
+"Group Display Name Field" => "গোষ্ঠীর প্রদর্শিতব্য নামের ক্ষেত্র",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "গোষ্ঠীর ownCloud নাম তৈরি করার জন্য ব্যভহৃত LDAP বৈশিষ্ট্য।",
+"Base Group Tree" => "ভিত্তি গোষ্ঠী বৃক্ষাকারে",
+"Group-Member association" => "গোষ্ঠী-সদস্য সংস্থাপন",
+"in bytes" => "বাইটে",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ব্যবহারকারী নামের জন্য ফাঁকা রাখুন (পূর্বনির্ধারিত)। অন্যথায়, LDAP/AD বৈশিষ্ট্য নির্ধারণ করুন।",
+"Help" => "সহায়িকা"
+);
diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php
index be72912040d..abdecb164e8 100644
--- a/apps/user_ldap/l10n/ca.php
+++ b/apps/user_ldap/l10n/ca.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Ha fallat en eliminar la configuració del servidor",
+"The configuration is valid and the connection could be established!" => "La configuració és vàlida i s'ha pogut establir la comunicació!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuració és vàlida, però ha fallat el Bind. Comproveu les credencials i l'arranjament del servidor.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuració no és vàlida. Per més detalls mireu al registre d'ownCloud.",
+"Deletion failed" => "Eliminació fallida",
+"Take over settings from recent server configuration?" => "Voleu prendre l'arranjament de la configuració actual del servidor?",
+"Keep settings?" => "Voleu mantenir la configuració?",
+"Cannot add server configuration" => "No es pot afegir la configuració del servidor",
+"Connection test succeeded" => "La prova de connexió ha reeixit",
+"Connection test failed" => "La prova de connexió ha fallat",
+"Do you really want to delete the current Server Configuration?" => "Voleu eliminar la configuració actual del servidor?",
+"Confirm Deletion" => "Confirma l'eliminació",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Avís:</b> Les aplicacions user_ldap i user_webdavauth són incompatibles. Podeu experimentar comportaments no desitjats. Demaneu a l'administrador del sistema que en desactivi una.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Avís:</b> El mòdul PHP LDAP no està instal·lat, el dorsal no funcionarà. Demaneu a l'administrador del sistema que l'instal·li.",
+"Server configuration" => "Configuració del servidor",
+"Add Server Configuration" => "Afegeix la configuració del servidor",
"Host" => "Màquina",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podeu ometre el protocol, excepte si requeriu SSL. Llavors comenceu amb ldaps://",
"Base DN" => "DN Base",
+"One Base DN per line" => "Una DN Base per línia",
"You can specify Base DN for users and groups in the Advanced tab" => "Podeu especificar DN Base per usuaris i grups a la pestanya Avançat",
"User DN" => "DN Usuari",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "La DN de l'usuari client amb la que s'haurà de fer, per exemple uid=agent,dc=exemple,dc=com. Per un accés anònim, deixeu la DN i la contrasenya en blanc.",
@@ -16,22 +33,43 @@
"Group Filter" => "Filtre de grup",
"Defines the filter to apply, when retrieving groups." => "Defineix el filtre a aplicar quan es mostren grups.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sense cap paràmetre de substitució, per exemple \"objectClass=grupPosix\".",
+"Connection Settings" => "Arranjaments de connexió",
+"Configuration Active" => "Configuració activa",
+"When unchecked, this configuration will be skipped." => "Si està desmarcat, aquesta configuració s'ometrà.",
"Port" => "Port",
-"Base User Tree" => "Arbre base d'usuaris",
-"Base Group Tree" => "Arbre base de grups",
-"Group-Member association" => "Associació membres-grup",
+"Backup (Replica) Host" => "Màquina de còpia de serguretat (rèplica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Afegiu una màquina de còpia de seguretat opcional. Ha de ser una rèplica del servidor LDAP/AD principal.",
+"Backup (Replica) Port" => "Port de la còpia de seguretat (rèplica)",
+"Disable Main Server" => "Desactiva el servidor principal",
+"When switched on, ownCloud will only connect to the replica server." => "Quan està connectat, ownCloud només es connecta al servidor de la rèplica.",
"Use TLS" => "Usa TLS",
-"Do not use it for SSL connections, it will fail." => "No ho useu en connexions SSL, fallarà.",
+"Do not use it additionally for LDAPS connections, it will fail." => "No ho useu adicionalment per a conexions LDAPS, fallarà.",
"Case insensitve LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)",
"Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexió només funciona amb aquesta opció, importeu el certificat SSL del servidor LDAP en el vostre servidor ownCloud.",
"Not recommended, use for testing only." => "No recomanat, ús només per proves.",
+"Cache Time-To-Live" => "Memòria de cau Time-To-Live",
+"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.",
+"Directory Settings" => "Arranjaments de carpetes",
"User Display Name Field" => "Camp per mostrar el nom d'usuari",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP a usar per generar el nom d'usuari ownCloud.",
+"Base User Tree" => "Arbre base d'usuaris",
+"One User Base DN per line" => "Una DN Base d'Usuari per línia",
+"User Search Attributes" => "Atributs de cerca d'usuari",
+"Optional; one attribute per line" => "Opcional; Un atribut per línia",
"Group Display Name Field" => "Camp per mostrar el nom del grup",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP a usar per generar el nom de grup ownCloud.",
+"Base Group Tree" => "Arbre base de grups",
+"One Group Base DN per line" => "Una DN Base de Grup per línia",
+"Group Search Attributes" => "Atributs de cerca de grup",
+"Group-Member association" => "Associació membres-grup",
+"Special Attributes" => "Atributs especials",
+"Quota Field" => "Camp de quota",
+"Quota Default" => "Quota per defecte",
"in bytes" => "en bytes",
-"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.",
+"Email Field" => "Camp de correu electrònic",
+"User Home Folder Naming Rule" => "Norma per anomenar la carpeta arrel d'usuari",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixeu-ho buit pel nom d'usuari (per defecte). Altrament, especifiqueu un atribut LDAP/AD.",
+"Test Configuration" => "Comprovació de la configuració",
"Help" => "Ajuda"
);
diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php
index c90dc9ed568..c5d77026b9e 100644
--- a/apps/user_ldap/l10n/cs_CZ.php
+++ b/apps/user_ldap/l10n/cs_CZ.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Selhalo smazání nastavení serveru",
+"The configuration is valid and the connection could be established!" => "Nastavení je v pořádku a spojení bylo navázáno.",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurace je v pořádku, ale spojení selhalo. Zkontrolujte, prosím, nastavení serveru a přihlašovací údaje.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Nastavení je neplatné. Zkontrolujte, prosím, záznam ownCloud pro další podrobnosti.",
+"Deletion failed" => "Mazání selhalo.",
+"Take over settings from recent server configuration?" => "Převzít nastavení z nedávného nastavení serveru?",
+"Keep settings?" => "Ponechat nastavení?",
+"Cannot add server configuration" => "Nelze přidat nastavení serveru",
+"Connection test succeeded" => "Test spojení byl úspěšný",
+"Connection test failed" => "Test spojení selhal",
+"Do you really want to delete the current Server Configuration?" => "Opravdu si přejete smazat současné nastavení serveru?",
+"Confirm Deletion" => "Potvrdit smazání",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Varování:</b> Aplikace user_ldap a user_webdavauth nejsou kompatibilní. Může nastávat neočekávané chování. Požádejte, prosím, správce systému aby jednu z nich zakázal.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Varování:</b> není nainstalován LDAP modul pro PHP, podpůrná vrstva nebude fungovat. Požádejte, prosím, správce systému aby jej nainstaloval.",
+"Server configuration" => "Nastavení serveru",
+"Add Server Configuration" => "Přidat nastavení serveru",
"Host" => "Počítač",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Můžete vynechat protokol, vyjma pokud požadujete SSL. Tehdy začněte s ldaps://",
"Base DN" => "Základní DN",
+"One Base DN per line" => "Jedna základní DN na řádku",
"You can specify Base DN for users and groups in the Advanced tab" => "V rozšířeném nastavení můžete určit základní DN pro uživatele a skupiny",
"User DN" => "Uživatelské DN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klentského uživatele ke kterému tvoříte vazbu, např. uid=agent,dc=example,dc=com. Pro anonymní přístup ponechte údaje DN and Heslo prázdné.",
@@ -16,22 +33,43 @@
"Group Filter" => "Filtr skupin",
"Defines the filter to apply, when retrieving groups." => "Určuje použitý filtr, pro získávaní skupin.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez zástupných znaků, např. \"objectClass=posixGroup\".",
+"Connection Settings" => "Nastavení spojení",
+"Configuration Active" => "Nastavení aktivní",
+"When unchecked, this configuration will be skipped." => "Pokud není zaškrtnuto, bude nastavení přeskočeno.",
"Port" => "Port",
-"Base User Tree" => "Základní uživatelský strom",
-"Base Group Tree" => "Základní skupinový strom",
-"Group-Member association" => "Asociace člena skupiny",
+"Backup (Replica) Host" => "Záložní (kopie) hostitel",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Zadejte volitelného záložního hostitele. Musí to být kopie hlavního serveru LDAP/AD.",
+"Backup (Replica) Port" => "Záložní (kopie) port",
+"Disable Main Server" => "Zakázat hlavní serveru",
+"When switched on, ownCloud will only connect to the replica server." => "Při zapnutí se ownCloud připojí pouze k záložnímu serveru",
"Use TLS" => "Použít TLS",
-"Do not use it for SSL connections, it will fail." => "Nepoužívejte pro připojení pomocí SSL, připojení selže.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Nepoužívejte pro spojení LDAP, selže.",
"Case insensitve LDAP server (Windows)" => "LDAP server nerozlišující velikost znaků (Windows)",
"Turn off SSL certificate validation." => "Vypnout ověřování SSL certifikátu.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Pokud připojení pracuje pouze s touto možností, tak importujte SSL certifikát SSL serveru do Vašeho serveru ownCloud",
"Not recommended, use for testing only." => "Není doporučeno, pouze pro testovací účely.",
+"Cache Time-To-Live" => "TTL vyrovnávací paměti",
+"in seconds. A change empties the cache." => "ve vteřinách. Změna vyprázdní vyrovnávací paměť.",
+"Directory Settings" => "Nastavení adresáře",
"User Display Name Field" => "Pole pro zobrazované jméno uživatele",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména uživatele ownCloud",
+"Base User Tree" => "Základní uživatelský strom",
+"One User Base DN per line" => "Jedna uživatelská základní DN na řádku",
+"User Search Attributes" => "Atributy vyhledávání uživatelů",
+"Optional; one attribute per line" => "Volitelné, atribut na řádku",
"Group Display Name Field" => "Pole pro zobrazení jména skupiny",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména skupiny ownCloud",
+"Base Group Tree" => "Základní skupinový strom",
+"One Group Base DN per line" => "Jedna skupinová základní DN na řádku",
+"Group Search Attributes" => "Atributy vyhledávání skupin",
+"Group-Member association" => "Asociace člena skupiny",
+"Special Attributes" => "Speciální atributy",
+"Quota Field" => "Pole pro kvótu",
+"Quota Default" => "Výchozí kvóta",
"in bytes" => "v bajtech",
-"in seconds. A change empties the cache." => "ve vteřinách. Změna vyprázdní vyrovnávací paměť.",
+"Email Field" => "Pole e-mailu",
+"User Home Folder Naming Rule" => "Pravidlo pojmenování domovské složky uživatele",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ponechte prázdné pro uživatelské jméno (výchozí). Jinak uveďte LDAP/AD parametr.",
+"Test Configuration" => "Vyzkoušet nastavení",
"Help" => "Nápověda"
);
diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php
index f0589b33a93..9329c4e8a24 100644
--- a/apps/user_ldap/l10n/da.php
+++ b/apps/user_ldap/l10n/da.php
@@ -1,10 +1,25 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Fejl ved sletning",
"Host" => "Host",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kan udelade protokollen, medmindre du skal bruge SSL. Start i så fald med ldaps://",
"Base DN" => "Base DN",
+"You can specify Base DN for users and groups in the Advanced tab" => "You can specify Base DN for users and groups in the Advanced tab",
+"User DN" => "Bruger DN",
"Password" => "Kodeord",
+"For anonymous access, leave DN and Password empty." => "For anonym adgang, skal du lade DN og Adgangskode tomme.",
+"User Login Filter" => "Bruger Login Filter",
+"User List Filter" => "Brugerliste Filter",
+"Defines the filter to apply, when retrieving users." => "Definere filteret der bruges ved indlæsning af brugere.",
+"Group Filter" => "Gruppe Filter",
+"Defines the filter to apply, when retrieving groups." => "Definere filteret der bruges når der indlæses grupper.",
"Port" => "Port",
"Use TLS" => "Brug TLS",
+"Turn off SSL certificate validation." => "Deaktiver SSL certifikat validering",
"Not recommended, use for testing only." => "Anbefales ikke, brug kun for at teste.",
+"User Display Name Field" => "User Display Name Field",
+"Base User Tree" => "Base Bruger Træ",
+"Base Group Tree" => "Base Group Tree",
+"Group-Member association" => "Group-Member association",
+"in bytes" => "i bytes",
"Help" => "Hjælp"
);
diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php
index 97debcbab60..399bfdc037a 100644
--- a/apps/user_ldap/l10n/de.php
+++ b/apps/user_ldap/l10n/de.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Löschen der Serverkonfiguration fehlgeschlagen",
+"The configuration is valid and the connection could be established!" => "Die Konfiguration war erfolgreich, die Verbindung konnte hergestellt werden!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig aber die Verbindung ist fehlgeschlagen. Bitte überprüfen Sie die Servereinstellungen und die Anmeldeinformationen.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig, bitte sehen Sie für weitere Details im ownCloud Log nach",
+"Deletion failed" => "Löschen fehlgeschlagen",
+"Take over settings from recent server configuration?" => "Einstellungen von letzter Konfiguration übernehmen?",
+"Keep settings?" => "Einstellungen beibehalten?",
+"Cannot add server configuration" => "Serverkonfiguration konnte nicht hinzugefügt werden.",
+"Connection test succeeded" => "Verbindungstest erfolgreich",
+"Connection test failed" => "Verbindungstest fehlgeschlagen",
+"Do you really want to delete the current Server Configuration?" => "Wollen Sie die aktuelle Serverkonfiguration wirklich löschen?",
+"Confirm Deletion" => "Löschung bestätigen",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Warnung:</b> Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitte Deinen Systemadministator eine der beiden Anwendungen zu deaktivieren.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Warnung:</b> Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitte Deinen Systemadministrator das Modul zu installieren.",
+"Server configuration" => "Serverkonfiguration",
+"Add Server Configuration" => "Serverkonfiguration hinzufügen",
"Host" => "Host",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kannst das Protokoll auslassen, außer wenn Du SSL benötigst. Beginne dann mit ldaps://",
"Base DN" => "Basis-DN",
+"One Base DN per line" => "Ein Base DN pro Zeile",
"You can specify Base DN for users and groups in the Advanced tab" => "Du kannst Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren",
"User DN" => "Benutzer-DN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lasse DN und Passwort leer.",
@@ -16,22 +33,43 @@
"Group Filter" => "Gruppen-Filter",
"Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"",
+"Connection Settings" => "Verbindungseinstellungen",
+"Configuration Active" => "Konfiguration aktiv",
+"When unchecked, this configuration will be skipped." => "Konfiguration wird übersprungen wenn deaktiviert",
"Port" => "Port",
-"Base User Tree" => "Basis-Benutzerbaum",
-"Base Group Tree" => "Basis-Gruppenbaum",
-"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer",
+"Backup (Replica) Host" => "Backup Host (Kopie)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Gib einen optionalen Backup Host an. Es muss sich um eine Kopie des Haupt LDAP/AD Servers handeln.",
+"Backup (Replica) Port" => "Backup Port",
+"Disable Main Server" => "Hauptserver deaktivieren",
+"When switched on, ownCloud will only connect to the replica server." => "Wenn aktiviert, wird ownCloud ausschließlich den Backupserver verwenden.",
"Use TLS" => "Nutze TLS",
-"Do not use it for SSL connections, it will fail." => "Verwende dies nicht für SSL-Verbindungen, es wird fehlschlagen.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Benutze es nicht zusammen mit LDAPS Verbindungen, es wird fehlschlagen.",
"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)",
"Turn off SSL certificate validation." => "Schalte die SSL-Zertifikatsprüfung aus.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.",
"Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.",
+"Cache Time-To-Live" => "Speichere Time-To-Live zwischen",
+"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.",
+"Directory Settings" => "Ordnereinstellungen",
"User Display Name Field" => "Feld für den Anzeigenamen des Benutzers",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ",
+"Base User Tree" => "Basis-Benutzerbaum",
+"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile",
+"User Search Attributes" => "Benutzersucheigenschaften",
+"Optional; one attribute per line" => "Optional; eine Eigenschaft pro Zeile",
"Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ",
+"Base Group Tree" => "Basis-Gruppenbaum",
+"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile",
+"Group Search Attributes" => "Gruppensucheigenschaften",
+"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer",
+"Special Attributes" => "Spezielle Eigenschaften",
+"Quota Field" => "Kontingent Feld",
+"Quota Default" => "Kontingent Standard",
"in bytes" => "in Bytes",
-"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.",
+"Email Field" => "E-Mail Feld",
+"User Home Folder Naming Rule" => "Benennungsregel für das Heimatverzeichnis des Benutzers",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.",
+"Test Configuration" => "Testkonfiguration",
"Help" => "Hilfe"
);
diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php
new file mode 100644
index 00000000000..bff7b0312c7
--- /dev/null
+++ b/apps/user_ldap/l10n/de_DE.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Das Löschen der Server-Konfiguration schlug fehl",
+"The configuration is valid and the connection could be established!" => "Die Konfiguration ist gültig und die Verbindung konnte hergestellt werden!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig, aber das Herstellen der Verbindung schlug fehl. Bitte überprüfen Sie die Server-Einstellungen und Zertifikate.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig. Weitere Details können Sie im ownCloud-Log nachlesen.",
+"Deletion failed" => "Löschen fehlgeschlagen",
+"Take over settings from recent server configuration?" => "Sollen die Einstellungen der letzten Serverkonfiguration übernommen werden?",
+"Keep settings?" => "Einstellungen behalten?",
+"Cannot add server configuration" => "Das Hinzufügen der Serverkonfiguration schlug fehl",
+"Connection test succeeded" => "Verbindungstest erfolgreich",
+"Connection test failed" => "Verbindungstest fehlgeschlagen",
+"Do you really want to delete the current Server Configuration?" => "Möchten Sie die Serverkonfiguration wirklich löschen?",
+"Confirm Deletion" => "Löschung bestätigen",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Warnung:</b> Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitten Sie Ihren Systemadministator eine der beiden Anwendungen zu deaktivieren.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Warnung:</b> Da das PHP-Modul für LDAP ist nicht installiert, das Backend wird nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.",
+"Server configuration" => "Serverkonfiguration",
+"Add Server Configuration" => "Serverkonfiguration hinzufügen",
+"Host" => "Host",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://",
+"Base DN" => "Basis-DN",
+"One Base DN per line" => "Ein Base DN pro Zeile",
+"You can specify Base DN for users and groups in the Advanced tab" => "Sie können Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren",
+"User DN" => "Benutzer-DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lassen Sie DN und Passwort leer.",
+"Password" => "Passwort",
+"For anonymous access, leave DN and Password empty." => "Lassen Sie die Felder von DN und Passwort für anonymen Zugang leer.",
+"User Login Filter" => "Benutzer-Login-Filter",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung versucht wird. %%uid ersetzt den Benutzernamen bei dem Anmeldeversuch.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "verwenden Sie %%uid Platzhalter, z. B. \"uid=%%uid\"",
+"User List Filter" => "Benutzer-Filter-Liste",
+"Defines the filter to apply, when retrieving users." => "Definiert den Filter für die Anfrage der Benutzer.",
+"without any placeholder, e.g. \"objectClass=person\"." => "ohne Platzhalter, z.B.: \"objectClass=person\"",
+"Group Filter" => "Gruppen-Filter",
+"Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"",
+"Connection Settings" => "Verbindungseinstellungen",
+"Configuration Active" => "Konfiguration aktiv",
+"When unchecked, this configuration will be skipped." => "Wenn nicht angehakt, wird diese Konfiguration übersprungen.",
+"Port" => "Port",
+"Backup (Replica) Host" => "Back-Up (Replikation) Host",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Geben Sie einen optionalen Backup Host an. Es muss ein Replikat des Haupt- LDAP/AD Servers sein.",
+"Backup (Replica) Port" => "Back-Up (Replikation) Port",
+"Disable Main Server" => "Hauptserver deaktivieren",
+"When switched on, ownCloud will only connect to the replica server." => "Wenn eingeschaltet wird sich die ownCloud nur mit dem Replikat-Server verbinden.",
+"Use TLS" => "Nutze TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Benutzen Sie es nicht in Verbindung mit LDAPS Verbindungen, es wird fehlschlagen.",
+"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)",
+"Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.",
+"Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.",
+"Cache Time-To-Live" => "Speichere Time-To-Live zwischen",
+"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.",
+"Directory Settings" => "Verzeichniseinstellungen",
+"User Display Name Field" => "Feld für den Anzeigenamen des Benutzers",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ",
+"Base User Tree" => "Basis-Benutzerbaum",
+"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile",
+"User Search Attributes" => "Benutzer-Suche Eigenschaften",
+"Optional; one attribute per line" => "Optional; ein Attribut pro Zeile",
+"Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ",
+"Base Group Tree" => "Basis-Gruppenbaum",
+"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile",
+"Group Search Attributes" => "Gruppen-Suche Eigenschaften",
+"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer",
+"Special Attributes" => "Besondere Eigenschaften",
+"Quota Field" => "Kontingent Feld",
+"Quota Default" => "Kontingent Standard",
+"in bytes" => "in Bytes",
+"Email Field" => "E-Mail Feld",
+"User Home Folder Naming Rule" => "Benennungsregel für das Heimatverzeichnis des Benutzers",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.",
+"Test Configuration" => "Testkonfiguration",
+"Help" => "Hilfe"
+);
diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php
index e973eaac0a9..96ec8180437 100644
--- a/apps/user_ldap/l10n/el.php
+++ b/apps/user_ldap/l10n/el.php
@@ -1,18 +1,53 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Αποτυχία διαγραφής ρυθμίσεων διακομιστή",
+"The configuration is valid and the connection could be established!" => "Οι ρυθμίσεις είναι έγκυρες και η σύνδεση μπορεί να πραγματοποιηθεί!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Οι ρυθμίσεις είναι έγκυρες, αλλά απέτυχε η σύνδεση. Παρακαλώ ελέγξτε τις ρυθμίσεις του διακομιστή και τα διαπιστευτήρια.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Μη έγκυρες ρυθμίσεις. Παρακαλώ ελέγξτε τις καταγραφές του ownCloud για περισσότερες λεπτομέρειες.",
+"Deletion failed" => "Η διαγραφή απέτυχε",
+"Keep settings?" => "Διατήρηση ρυθμίσεων;",
+"Cannot add server configuration" => "Αδυναμία προσθήκης ρυθμίσεων διακομιστή",
+"Connection test succeeded" => "Επιτυχημένη δοκιμαστική σύνδεση",
+"Connection test failed" => "Αποτυχημένη δοκιμαστική σύνδεσης.",
+"Do you really want to delete the current Server Configuration?" => "Θέλετε να διαγράψετε τις τρέχουσες ρυθμίσεις του διακομιστή;",
+"Confirm Deletion" => "Επιβεβαίωση Διαγραφής",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Προσοχή:</b> Οι εφαρμογές user_ldap και user_webdavauth είναι ασύμβατες. Μπορεί να αντιμετωπίσετε απρόβλεπτη συμπεριφορά. Παρακαλώ ζητήστε από τον διαχειριστή συστήματος να απενεργοποιήσει μία από αυτές.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Προσοχή:</b> Το άρθρωμα PHP LDAP δεν είναι εγκατεστημένο και το σύστημα υποστήριξης δεν θα δουλέψει. Παρακαλώ ζητήστε από τον διαχειριστή συστήματος να το εγκαταστήσει.",
+"Server configuration" => "Ρυθμίσεις Διακομιστή",
+"Add Server Configuration" => "Προσθήκη Ρυθμίσεων Διακομιστή",
+"Host" => "Διακομιστής",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Μπορείτε να παραλείψετε το πρωτόκολλο, εκτός αν απαιτείται SSL. Σε αυτή την περίπτωση ξεκινήστε με ldaps://",
"Base DN" => "Base DN",
+"You can specify Base DN for users and groups in the Advanced tab" => "Μπορείτε να καθορίσετε το Base DN για χρήστες και ομάδες από την καρτέλα Προηγμένες ρυθμίσεις",
"User DN" => "User DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Το DN του χρήστη πελάτη με το οποίο θα πρέπει να γίνει η σύνδεση, π.χ. uid=agent,dc=example,dc=com. Για χρήση χωρίς πιστοποίηση, αφήστε το DN και τον Κωδικό κενά.",
"Password" => "Συνθηματικό",
+"For anonymous access, leave DN and Password empty." => "Για ανώνυμη πρόσβαση, αφήστε κενά τα πεδία DN και Pasword.",
"User Login Filter" => "User Login Filter",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Καθορίζει το φίλτρο που θα ισχύει κατά την προσπάθεια σύνδεσης χρήστη. %%uid αντικαθιστά το όνομα χρήστη κατά τη σύνδεση. ",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "χρησιμοποιήστε τη μεταβλητή %%uid, π.χ. \"uid=%%uid\"",
"User List Filter" => "User List Filter",
+"Defines the filter to apply, when retrieving users." => "Καθορίζει το φίλτρο που θα ισχύει κατά την ανάκτηση επαφών.",
+"without any placeholder, e.g. \"objectClass=person\"." => "χωρίς κάποια μεταβλητή, π.χ. \"objectClass=άτομο\".",
"Group Filter" => "Group Filter",
+"Defines the filter to apply, when retrieving groups." => "Καθορίζει το φίλτρο που θα ισχύει κατά την ανάκτηση ομάδων.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "χωρίς κάποια μεταβλητή, π.χ. \"objectClass=ΟμάδαPosix\".",
+"Connection Settings" => "Ρυθμίσεις Σύνδεσης",
"Port" => "Θύρα",
-"Base User Tree" => "Base User Tree",
-"Base Group Tree" => "Base Group Tree",
-"Group-Member association" => "Group-Member association",
"Use TLS" => "Χρήση TLS",
+"Case insensitve LDAP server (Windows)" => "LDAP server (Windows) με διάκριση πεζών-ΚΕΦΑΛΑΙΩΝ",
+"Turn off SSL certificate validation." => "Απενεργοποίηση επικύρωσης πιστοποιητικού SSL.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Εάν η σύνδεση δουλεύει μόνο με αυτή την επιλογή, εισάγετε το LDAP SSL πιστοποιητικό του διακομιστή στον ownCloud server σας.",
"Not recommended, use for testing only." => "Δεν προτείνεται, χρήση μόνο για δοκιμές.",
+"in seconds. A change empties the cache." => "σε δευτερόλεπτα. Μια αλλαγή αδειάζει την μνήμη cache.",
+"Directory Settings" => "Ρυθμίσεις Καταλόγου",
"User Display Name Field" => "Πεδίο Ονόματος Χρήστη",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος χρήστη του ownCloud.",
+"Base User Tree" => "Base User Tree",
"Group Display Name Field" => "Group Display Name Field",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος ομάδας του ownCloud.",
+"Base Group Tree" => "Base Group Tree",
+"Group-Member association" => "Group-Member association",
"in bytes" => "σε bytes",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Αφήστε το κενό για το όνομα χρήστη (προεπιλογή). Διαφορετικά, συμπληρώστε μία ιδιότητα LDAP/AD.",
"Help" => "Βοήθεια"
);
diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php
index ef8aff8a390..3ffcbddb3e3 100644
--- a/apps/user_ldap/l10n/eo.php
+++ b/apps/user_ldap/l10n/eo.php
@@ -1,7 +1,8 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Forigo malsukcesis",
"Host" => "Gastigo",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vi povas neglekti la protokolon, escepte se vi bezonas SSL-on. Tiuokaze, komencu per ldaps://",
-"Base DN" => "Baz-DN",
+"Base DN" => "Bazo-DN",
"User DN" => "Uzanto-DN",
"Password" => "Pasvorto",
"For anonymous access, leave DN and Password empty." => "Por sennoman aliron, lasu DN-on kaj Pasvorton malplenaj.",
@@ -15,21 +16,20 @@
"Defines the filter to apply, when retrieving groups." => "Ĝi difinas la filtrilon aplikotan, kiam veniĝas grupoj.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ajna referencilo, ekz.: \"objectClass=posixGroup\".",
"Port" => "Pordo",
-"Base User Tree" => "Baza uzantarbo",
-"Base Group Tree" => "Baza gruparbo",
-"Group-Member association" => "Asocio de grupo kaj membro",
"Use TLS" => "Uzi TLS-on",
-"Do not use it for SSL connections, it will fail." => "Ne uzu ĝin por SSL-konektoj, ĝi malsukcesos.",
"Case insensitve LDAP server (Windows)" => "LDAP-servilo blinda je litergrandeco (Vindozo)",
"Turn off SSL certificate validation." => "Malkapabligi validkontrolon de SSL-atestiloj.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la konekto nur funkcias kun ĉi tiu malnepro, enportu la SSL-atestilo de la LDAP-servilo en via ownCloud-servilo.",
"Not recommended, use for testing only." => "Ne rekomendata, uzu ĝin nur por testoj.",
+"in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.",
"User Display Name Field" => "Kampo de vidignomo de uzanto",
"The LDAP attribute to use to generate the user`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la uzanto.",
+"Base User Tree" => "Baza uzantarbo",
"Group Display Name Field" => "Kampo de vidignomo de grupo",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la grupo.",
+"Base Group Tree" => "Baza gruparbo",
+"Group-Member association" => "Asocio de grupo kaj membro",
"in bytes" => "duumoke",
-"in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lasu malplena por uzantonomo (defaŭlto). Alie, specifu LDAP/AD-atributon.",
"Help" => "Helpo"
);
diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php
index c89ceb8eee2..1f0f92e7ac4 100644
--- a/apps/user_ldap/l10n/es.php
+++ b/apps/user_ldap/l10n/es.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "No se pudo borrar la configuración del servidor",
+"The configuration is valid and the connection could be established!" => "La configuración es válida y la conexión puede establecerse!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuración es válida, pero falló el Enlace. Por favor, compruebe la configuración del servidor y las credenciales.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuración no es válida. Por favor, busque en el log de ownCloud para más detalles.",
+"Deletion failed" => "Falló el borrado",
+"Take over settings from recent server configuration?" => "Hacerse cargo de los ajustes de configuración del servidor reciente?",
+"Keep settings?" => "Mantener la configuración?",
+"Cannot add server configuration" => "No se puede añadir la configuración del servidor",
+"Connection test succeeded" => "La prueba de conexión fue exitosa",
+"Connection test failed" => "La prueba de conexión falló",
+"Do you really want to delete the current Server Configuration?" => "¿Realmente desea eliminar la configuración actual del servidor?",
+"Confirm Deletion" => "Confirmar eliminación",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Advertencia:</b> Los Apps user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Advertencia:</b> El módulo LDAP de PHP no está instalado, el sistema no funcionará. Por favor consulte al administrador del sistema para instalarlo.",
+"Server configuration" => "Configuración del Servidor",
+"Add Server Configuration" => "Agregar configuracion del servidor",
"Host" => "Servidor",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Puede omitir el protocolo, excepto si requiere SSL. En ese caso, empiece con ldaps://",
"Base DN" => "DN base",
+"One Base DN per line" => "Un DN Base por línea",
"You can specify Base DN for users and groups in the Advanced tab" => "Puede especificar el DN base para usuarios y grupos en la pestaña Avanzado",
"User DN" => "DN usuario",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "El DN del usuario cliente con el que se hará la asociación, p.ej. uid=agente,dc=ejemplo,dc=com. Para acceso anónimo, deje DN y contraseña vacíos.",
@@ -16,22 +33,42 @@
"Group Filter" => "Filtro de grupo",
"Defines the filter to apply, when retrieving groups." => "Define el filtro a aplicar, cuando se obtienen grupos.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Con cualquier placeholder, ej: \"objectClass=posixGroup\".",
+"Connection Settings" => "Configuracion de coneccion",
+"Configuration Active" => "Configuracion activa",
+"When unchecked, this configuration will be skipped." => "Cuando deseleccione, esta configuracion sera omitida.",
"Port" => "Puerto",
-"Base User Tree" => "Árbol base de usuario",
-"Base Group Tree" => "Árbol base de grupo",
-"Group-Member association" => "Asociación Grupo-Miembro",
+"Backup (Replica) Host" => "Host para backup (Replica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dar un host de copia de seguridad opcional. Debe ser una réplica del servidor principal LDAP / AD.",
+"Backup (Replica) Port" => "Puerto para backup (Replica)",
+"Disable Main Server" => "Deshabilitar servidor principal",
+"When switched on, ownCloud will only connect to the replica server." => "Cuando se inicie, ownCloud unicamente estara conectado al servidor replica",
"Use TLS" => "Usar TLS",
-"Do not use it for SSL connections, it will fail." => "No usarlo para SSL, habrá error.",
+"Do not use it additionally for LDAPS connections, it will fail." => "No usar adicionalmente para conecciones LDAPS, estas fallaran",
"Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)",
"Turn off SSL certificate validation." => "Apagar la validación por certificado SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importe el certificado SSL del servidor LDAP en su servidor ownCloud.",
"Not recommended, use for testing only." => "No recomendado, sólo para pruebas.",
+"Cache Time-To-Live" => "Cache TTL",
+"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la cache.",
+"Directory Settings" => "Configuracion de directorio",
"User Display Name Field" => "Campo de nombre de usuario a mostrar",
"The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.",
+"Base User Tree" => "Árbol base de usuario",
+"One User Base DN per line" => "Un DN Base de Usuario por línea",
+"User Search Attributes" => "Atributos de la busqueda de usuario",
+"Optional; one attribute per line" => "Opcional; un atributo por linea",
"Group Display Name Field" => "Campo de nombre de grupo a mostrar",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de los grupos de ownCloud.",
+"Base Group Tree" => "Árbol base de grupo",
+"One Group Base DN per line" => "Un DN Base de Grupo por línea",
+"Group Search Attributes" => "Atributos de busqueda de grupo",
+"Group-Member association" => "Asociación Grupo-Miembro",
+"Special Attributes" => "Atributos especiales",
+"Quota Field" => "Cuota",
+"Quota Default" => "Cuota por defecto",
"in bytes" => "en bytes",
-"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la cache.",
+"Email Field" => "E-mail",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Vacío para el nombre de usuario (por defecto). En otro caso, especifique un atributo LDAP/AD.",
+"Test Configuration" => "Configuración de prueba",
"Help" => "Ayuda"
);
diff --git a/apps/user_ldap/l10n/es_AR.php b/apps/user_ldap/l10n/es_AR.php
index 6bd452e9d90..c8aec0cd41b 100644
--- a/apps/user_ldap/l10n/es_AR.php
+++ b/apps/user_ldap/l10n/es_AR.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Fallo al borrar la configuración del servidor",
+"The configuration is valid and the connection could be established!" => "La configuración es valida y la conexión pudo ser establecida.",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuración es válida, pero el enlace falló. Por favor, comprobá la configuración del servidor y las credenciales.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuración no es válida. Por favor, buscá en el log de ownCloud más detalles.",
+"Deletion failed" => "Error al borrar",
+"Take over settings from recent server configuration?" => "Tomar los valores de la anterior configuración de servidor?",
+"Keep settings?" => "¿Mantener preferencias?",
+"Cannot add server configuration" => "No se pudo añadir la configuración del servidor",
+"Connection test succeeded" => "El este de conexión ha sido completado satisfactoriamente",
+"Connection test failed" => "Falló es test de conexión",
+"Do you really want to delete the current Server Configuration?" => "¿Realmente desea borrar la configuración actual del servidor?",
+"Confirm Deletion" => "Confirmar borrado",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Advertencia:</b> Los Apps user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Atención:</b> El módulo PHP LDAP no está instalado, este elemento no va a funcionar. Por favor, pedile al administrador que lo instale.",
+"Server configuration" => "Configuración del Servidor",
+"Add Server Configuration" => "Añadir Configuración del Servidor",
"Host" => "Servidor",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podés omitir el protocolo, excepto si SSL es requerido. En ese caso, empezá con ldaps://",
"Base DN" => "DN base",
+"One Base DN per line" => "Una DN base por línea",
"You can specify Base DN for users and groups in the Advanced tab" => "Podés especificar el DN base para usuarios y grupos en la pestaña \"Avanzado\"",
"User DN" => "DN usuario",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "El DN del usuario cliente con el que se hará la asociación, p.ej. uid=agente,dc=ejemplo,dc=com. Para acceso anónimo, dejá DN y contraseña vacíos.",
@@ -16,22 +33,43 @@
"Group Filter" => "Filtro de grupo",
"Defines the filter to apply, when retrieving groups." => "Define el filtro a aplicar cuando se obtienen grupos.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Sin ninguna plantilla, p. ej.: \"objectClass=posixGroup\".",
+"Connection Settings" => "Configuración de Conección",
+"Configuration Active" => "Configuración activa",
+"When unchecked, this configuration will be skipped." => "Si no está seleccionada, esta configuración será omitida.",
"Port" => "Puerto",
-"Base User Tree" => "Árbol base de usuario",
-"Base Group Tree" => "Árbol base de grupo",
-"Group-Member association" => "Asociación Grupo-Miembro",
+"Backup (Replica) Host" => "Host para copia de seguridad (réplica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dar un servidor de copia de seguridad opcional. Debe ser una réplica del servidor principal LDAP/AD.",
+"Backup (Replica) Port" => "Puerto para copia de seguridad (réplica)",
+"Disable Main Server" => "Deshabilitar el Servidor Principal",
+"When switched on, ownCloud will only connect to the replica server." => "Al comenzar, ownCloud se conectará únicamente al servidor réplica",
"Use TLS" => "Usar TLS",
-"Do not use it for SSL connections, it will fail." => "No usarlo para SSL, dará error.",
+"Do not use it additionally for LDAPS connections, it will fail." => "No usar adicionalmente para conexiones LDAPS, las mismas fallarán",
"Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)",
"Turn off SSL certificate validation." => "Desactivar la validación por certificado SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importá el certificado SSL del servidor LDAP en tu servidor ownCloud.",
"Not recommended, use for testing only." => "No recomendado, sólo para pruebas.",
+"Cache Time-To-Live" => "Tiempo de vida del caché",
+"in seconds. A change empties the cache." => "en segundos. Cambiarlo vacía la cache.",
+"Directory Settings" => "Configuración de Directorio",
"User Display Name Field" => "Campo de nombre de usuario a mostrar",
"The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.",
+"Base User Tree" => "Árbol base de usuario",
+"One User Base DN per line" => "Una DN base de usuario por línea",
+"User Search Attributes" => "Atributos de la búsqueda de usuario",
+"Optional; one attribute per line" => "Opcional; un atributo por linea",
"Group Display Name Field" => "Campo de nombre de grupo a mostrar",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de los grupos de ownCloud.",
+"Base Group Tree" => "Árbol base de grupo",
+"One Group Base DN per line" => "Una DN base de grupo por línea",
+"Group Search Attributes" => "Atributos de búsqueda de grupo",
+"Group-Member association" => "Asociación Grupo-Miembro",
+"Special Attributes" => "Atributos Especiales",
+"Quota Field" => "Campo de cuota",
+"Quota Default" => "Cuota por defecto",
"in bytes" => "en bytes",
-"in seconds. A change empties the cache." => "en segundos. Cambiarlo vacía la cache.",
+"Email Field" => "Campo de e-mail",
+"User Home Folder Naming Rule" => "Regla de nombre de los directorios de usuario",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Vacío para el nombre de usuario (por defecto). En otro caso, especificá un atributo LDAP/AD.",
+"Test Configuration" => "Probar configuración",
"Help" => "Ayuda"
);
diff --git a/apps/user_ldap/l10n/et_EE.php b/apps/user_ldap/l10n/et_EE.php
index f83142225e2..91eb38c7c5f 100644
--- a/apps/user_ldap/l10n/et_EE.php
+++ b/apps/user_ldap/l10n/et_EE.php
@@ -1,9 +1,15 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Kustutamine ebaõnnestus",
"Host" => "Host",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sa ei saa protokolli ära jätta, välja arvatud siis, kui sa nõuad SSL-ühendust. Sel juhul alusta eesliitega ldaps://",
"Base DN" => "Baas DN",
+"You can specify Base DN for users and groups in the Advanced tab" => "Sa saad kasutajate ja gruppide baas DN-i määrata lisavalikute vahekaardilt",
"User DN" => "Kasutaja DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Klientkasutaja DN, kellega seotakse, nt. uid=agent,dc=näidis,dc=com. Anonüümseks ligipääsuks jäta DN ja parool tühjaks.",
"Password" => "Parool",
+"For anonymous access, leave DN and Password empty." => "Anonüümseks ligipääsuks jäta DN ja parool tühjaks.",
"User Login Filter" => "Kasutajanime filter",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Määrab sisselogimisel kasutatava filtri. %%uid asendab sisselogimistegevuses kasutajanime.",
"use %%uid placeholder, e.g. \"uid=%%uid\"" => "kasuta %%uid kohatäitjat, nt. \"uid=%%uid\"",
"User List Filter" => "Kasutajate nimekirja filter",
"Defines the filter to apply, when retrieving users." => "Määrab kasutajaid hankides filtri, mida rakendatakse.",
@@ -12,20 +18,20 @@
"Defines the filter to apply, when retrieving groups." => "Määrab gruppe hankides filtri, mida rakendatakse.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilma ühegi kohatäitjata, nt. \"objectClass=posixGroup\".",
"Port" => "Port",
-"Base User Tree" => "Baaskasutaja puu",
-"Base Group Tree" => "Baasgrupi puu",
-"Group-Member association" => "Grupiliikme seotus",
"Use TLS" => "Kasutaja TLS",
-"Do not use it for SSL connections, it will fail." => "Ära kasuta seda SSL ühenduse jaoks, see ei toimi.",
"Case insensitve LDAP server (Windows)" => "Mittetõstutundlik LDAP server (Windows)",
"Turn off SSL certificate validation." => "Lülita SSL sertifikaadi kontrollimine välja.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Kui ühendus toimib ainult selle valikuga, siis impordi LDAP serveri SSL sertifikaat oma ownCloud serverisse.",
"Not recommended, use for testing only." => "Pole soovitatav, kasuta ainult testimiseks.",
+"in seconds. A change empties the cache." => "sekundites. Muudatus tühjendab vahemälu.",
"User Display Name Field" => "Kasutaja näidatava nime väli",
"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP omadus, mida kasutatakse kasutaja ownCloudi nime loomiseks.",
+"Base User Tree" => "Baaskasutaja puu",
"Group Display Name Field" => "Grupi näidatava nime väli",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP omadus, mida kasutatakse ownCloudi grupi nime loomiseks.",
+"Base Group Tree" => "Baasgrupi puu",
+"Group-Member association" => "Grupiliikme seotus",
"in bytes" => "baitides",
-"in seconds. A change empties the cache." => "sekundites. Muudatus tühjendab vahemälu.",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kasutajanime (vaikeväärtus) kasutamiseks jäta tühjaks. Vastasel juhul määra LDAP/AD omadus.",
"Help" => "Abiinfo"
);
diff --git a/apps/user_ldap/l10n/eu.php b/apps/user_ldap/l10n/eu.php
index 35dacef3f2f..5e9fd014c64 100644
--- a/apps/user_ldap/l10n/eu.php
+++ b/apps/user_ldap/l10n/eu.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Zerbitzariaren konfigurazioa ezabatzeak huts egin du",
+"The configuration is valid and the connection could be established!" => "Konfigurazioa egokia da eta konexioa ezarri daiteke!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurazioa ongi dago, baina Bind-ek huts egin du. Mesedez egiaztatu zerbitzariaren ezarpenak eta kredentzialak.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurazioa ez dago ongi. Mesedez ikusi ownCloud-en egunerokoa informazio gehiago eskuratzeko.",
+"Deletion failed" => "Ezabaketak huts egin du",
+"Take over settings from recent server configuration?" => "oraintsuko zerbitzariaren konfigurazioaren ezarpenen ardura hartu?",
+"Keep settings?" => "Mantendu ezarpenak?",
+"Cannot add server configuration" => "Ezin da zerbitzariaren konfigurazioa gehitu",
+"Connection test succeeded" => "Konexio froga ongi burutu da",
+"Connection test failed" => "Konexio frogak huts egin du",
+"Do you really want to delete the current Server Configuration?" => "Ziur zaude Zerbitzariaren Konfigurazioa ezabatu nahi duzula?",
+"Confirm Deletion" => "Baieztatu Ezabatzea",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Abisua:</b> user_ldap eta user_webdavauth aplikazioak bateraezinak dira. Portaera berezia izan dezakezu. Mesedez eskatu zure sistema kudeatzaileari bietako bat desgaitzeko.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Abisua:</b> PHPk behar duen LDAP modulua ez dago instalaturik, motorrak ez du funtzionatuko. Mesedez eskatu zure sistema kudeatzaileari instala dezan.",
+"Server configuration" => "Zerbitzariaren konfigurazioa",
+"Add Server Configuration" => "Gehitu Zerbitzariaren Konfigurazioa",
"Host" => "Hostalaria",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokoloa ez da beharrezkoa, SSL behar baldin ez baduzu. Honela bada hasi ldaps://",
"Base DN" => "Oinarrizko DN",
+"One Base DN per line" => "DN Oinarri bat lerroko",
"You can specify Base DN for users and groups in the Advanced tab" => "Erabiltzaile eta taldeentzako Oinarrizko DN zehaztu dezakezu Aurreratu fitxan",
"User DN" => "Erabiltzaile DN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Lotura egingo den bezero erabiltzailearen DNa, adb. uid=agent,dc=example,dc=com. Sarrera anonimoak gaitzeko utzi DN eta Pasahitza hutsik.",
@@ -16,22 +33,43 @@
"Group Filter" => "Taldeen iragazkia",
"Defines the filter to apply, when retrieving groups." => "Taldeak jasotzen direnean ezarriko den iragazkia zehazten du.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "txantiloirik gabe, adb. \"objectClass=posixGroup\".",
+"Connection Settings" => "Konexio Ezarpenak",
+"Configuration Active" => "Konfigurazio Aktiboa",
+"When unchecked, this configuration will be skipped." => "Markatuta ez dagoenean, konfigurazio hau ez da kontutan hartuko.",
"Port" => "Portua",
-"Base User Tree" => "Oinarrizko Erabiltzaile Zuhaitza",
-"Base Group Tree" => "Oinarrizko Talde Zuhaitza",
-"Group-Member association" => "Talde-Kide elkarketak",
+"Backup (Replica) Host" => "Babeskopia (Replica) Ostalaria",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Eman babeskopia ostalari gehigarri bat. LDAP/AD zerbitzari nagusiaren replica bat izan behar da.",
+"Backup (Replica) Port" => "Babeskopia (Replica) Ataka",
+"Disable Main Server" => "Desgaitu Zerbitzari Nagusia",
+"When switched on, ownCloud will only connect to the replica server." => "Markatuta dagoenean, ownCloud bakarrik replica zerbitzarira konektatuko da.",
"Use TLS" => "Erabili TLS",
-"Do not use it for SSL connections, it will fail." => "Ez erabili SSL konexioetan, huts egingo du.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Ez erabili LDAPS konexioetarako, huts egingo du.",
"Case insensitve LDAP server (Windows)" => "Maiuskulak eta minuskulak ezberditzen ez dituen LDAP zerbitzaria (windows)",
"Turn off SSL certificate validation." => "Ezgaitu SSL ziurtagirien egiaztapena.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Konexioa aukera hau ezinbestekoa badu, inportatu LDAP zerbitzariaren SSL ziurtagiria zure ownCloud zerbitzarian.",
"Not recommended, use for testing only." => "Ez da aholkatzen, erabili bakarrik frogak egiteko.",
+"Cache Time-To-Live" => "Katxearen Bizi-Iraupena",
+"in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.",
+"Directory Settings" => "Karpetaren Ezarpenak",
"User Display Name Field" => "Erabiltzaileen bistaratzeko izena duen eremua",
"The LDAP attribute to use to generate the user`s ownCloud name." => "ownCloud erabiltzailearen izena sortzeko erabiliko den LDAP atributua",
+"Base User Tree" => "Oinarrizko Erabiltzaile Zuhaitza",
+"One User Base DN per line" => "Erabiltzaile DN Oinarri bat lerroko",
+"User Search Attributes" => "Erabili Bilaketa Atributuak ",
+"Optional; one attribute per line" => "Aukerakoa; atributu bat lerro bakoitzeko",
"Group Display Name Field" => "Taldeen bistaratzeko izena duen eremua",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "ownCloud taldearen izena sortzeko erabiliko den LDAP atributua",
+"Base Group Tree" => "Oinarrizko Talde Zuhaitza",
+"One Group Base DN per line" => "Talde DN Oinarri bat lerroko",
+"Group Search Attributes" => "Taldekatu Bilaketa Atributuak ",
+"Group-Member association" => "Talde-Kide elkarketak",
+"Special Attributes" => "Atributu Bereziak",
+"Quota Field" => "Kuota Eremua",
+"Quota Default" => "Kuota Lehenetsia",
"in bytes" => "bytetan",
-"in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.",
+"Email Field" => "Eposta eremua",
+"User Home Folder Naming Rule" => "Erabiltzailearen Karpeta Nagusia Izendatzeko Patroia",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Utzi hutsik erabiltzaile izenarako (lehentsia). Bestela zehaztu LDAP/AD atributua.",
+"Test Configuration" => "Egiaztatu Konfigurazioa",
"Help" => "Laguntza"
);
diff --git a/apps/user_ldap/l10n/fa.php b/apps/user_ldap/l10n/fa.php
index 44324221168..7816ef7c6f7 100644
--- a/apps/user_ldap/l10n/fa.php
+++ b/apps/user_ldap/l10n/fa.php
@@ -1,5 +1,17 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "عملیات حذف پیکربندی سرور ناموفق ماند",
+"The configuration is valid and the connection could be established!" => "پیکربندی معتبر است و ارتباط می تواند برقرار شود",
+"Deletion failed" => "حذف کردن انجام نشد",
+"Keep settings?" => "آیا تنظیمات ذخیره شود ؟",
+"Connection test succeeded" => "تست اتصال با موفقیت انجام گردید",
+"Connection test failed" => "تست اتصال ناموفق بود",
+"Do you really want to delete the current Server Configuration?" => "آیا واقعا می خواهید پیکربندی کنونی سرور را حذف کنید؟",
+"Confirm Deletion" => "تایید حذف",
+"Server configuration" => "پیکربندی سرور",
+"Add Server Configuration" => "افزودن پیکربندی سرور",
"Host" => "میزبانی",
"Password" => "رمز عبور",
+"Port" => "درگاه",
+"in bytes" => "در بایت",
"Help" => "راه‌نما"
);
diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php
index 24195649a64..bfbd6c78564 100644
--- a/apps/user_ldap/l10n/fi_FI.php
+++ b/apps/user_ldap/l10n/fi_FI.php
@@ -1,4 +1,10 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Poisto epäonnistui",
+"Keep settings?" => "Säilytetäänkö asetukset?",
+"Cannot add server configuration" => "Palvelinasetusten lisäys epäonnistui",
+"Connection test succeeded" => "Yhteystesti onnistui",
+"Connection test failed" => "Yhteystesti epäonnistui",
+"Confirm Deletion" => "Vahvista poisto",
"Host" => "Isäntä",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Voit jättää protokollan määrittämättä, paitsi kun vaadit SSL:ää. Aloita silloin ldaps://",
"Base DN" => "Oletus DN",
@@ -16,22 +22,24 @@
"Group Filter" => "Ryhmien suodatus",
"Defines the filter to apply, when retrieving groups." => "Määrittelee käytettävän suodattimen, kun ryhmiä haetaan. ",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilman paikanvaraustermiä, ts. \"objectClass=posixGroup\".",
+"Connection Settings" => "Yhteysasetukset",
"Port" => "Portti",
-"Base User Tree" => "Oletuskäyttäjäpuu",
-"Base Group Tree" => "Ryhmien juuri",
-"Group-Member association" => "Ryhmän ja jäsenen assosiaatio (yhteys)",
+"Disable Main Server" => "Poista pääpalvelin käytöstä",
"Use TLS" => "Käytä TLS:ää",
-"Do not use it for SSL connections, it will fail." => "Älä käytä SSL-yhteyttä varten, se epäonnistuu. ",
"Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)",
"Turn off SSL certificate validation." => "Poista käytöstä SSL-varmenteen vahvistus",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jos yhteys toimii vain tällä valinnalla, siirrä LDAP-palvelimen SSL-varmenne ownCloud-palvelimellesi.",
"Not recommended, use for testing only." => "Ei suositella, käytä vain testausta varten.",
+"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.",
+"Directory Settings" => "Hakemistoasetukset",
"User Display Name Field" => "Käyttäjän näytettävän nimen kenttä",
"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP-attribuutti, jota käytetään käyttäjän ownCloud-käyttäjänimenä ",
+"Base User Tree" => "Oletuskäyttäjäpuu",
"Group Display Name Field" => "Ryhmän \"näytettävä nimi\"-kenttä",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP-attribuutti, jota käytetään luomaan ryhmän ownCloud-nimi",
+"Base Group Tree" => "Ryhmien juuri",
+"Group-Member association" => "Ryhmän ja jäsenen assosiaatio (yhteys)",
"in bytes" => "tavuissa",
-"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Jätä tyhjäksi käyttäjänimi (oletusasetus). Muutoin anna LDAP/AD-atribuutti.",
"Help" => "Ohje"
);
diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php
index a0b1c6b7d9c..990658e147e 100644
--- a/apps/user_ldap/l10n/fr.php
+++ b/apps/user_ldap/l10n/fr.php
@@ -1,10 +1,27 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Échec de la suppression de la configuration du serveur",
+"The configuration is valid and the connection could be established!" => "La configuration est valide est la connexion peut être établie !",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuration est valide, mais le lien ne peut être établi. Veuillez vérifier les paramètres du serveur ainsi que vos identifiants de connexion.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuration est invalide. Veuillez vous référer aux fichiers de journaux ownCloud pour plus d'information.",
+"Deletion failed" => "La suppression a échoué",
+"Take over settings from recent server configuration?" => "Récupérer les paramètres depuis une configuration récente du serveur ?",
+"Keep settings?" => "Garder ces paramètres ?",
+"Cannot add server configuration" => "Impossible d'ajouter la configuration du serveur.",
+"Connection test succeeded" => "Test de connexion réussi",
+"Connection test failed" => "Le test de connexion a échoué",
+"Do you really want to delete the current Server Configuration?" => "Êtes-vous vraiment sûr de vouloir effacer la configuration actuelle du serveur ?",
+"Confirm Deletion" => "Confirmer la suppression",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Avertissement:</b> Les applications user_ldap et user_webdavauth sont incompatibles. Des disfonctionnements peuvent survenir. Contactez votre administrateur système pour qu'il désactive l'une d'elles.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Attention :</b> Le module php LDAP n'est pas installé, par conséquent cette extension ne pourra fonctionner. Veuillez contacter votre administrateur système afin qu'il l'installe.",
+"Server configuration" => "Configuration du serveur",
+"Add Server Configuration" => "Ajouter une configuration du serveur",
"Host" => "Hôte",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas préfixez avec ldaps://",
"Base DN" => "DN Racine",
-"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez détailler les DN Racines de vos utilisateurs et groupes dans l'onglet Avancé",
+"One Base DN per line" => "Un DN racine par ligne",
+"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez spécifier les DN Racines de vos utilisateurs et groupes via l'onglet Avancé",
"User DN" => "DN Utilisateur (Autorisé à consulter l'annuaire)",
-"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Le DN de l'utilisateur client avec lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour l'accès anonyme, laisser le DN et le mot de passe vides.",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN de l'utilisateur client pour lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour un accès anonyme, laisser le DN et le mot de passe vides.",
"Password" => "Mot de passe",
"For anonymous access, leave DN and Password empty." => "Pour un accès anonyme, laisser le DN Utilisateur et le mot de passe vides.",
"User Login Filter" => "Modèle d'authentification utilisateurs",
@@ -16,22 +33,43 @@
"Group Filter" => "Filtre de groupes",
"Defines the filter to apply, when retrieving groups." => "Définit le filtre à appliquer lors de la récupération des groupes.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sans élément de substitution, par exemple \"objectClass=posixGroup\".",
+"Connection Settings" => "Paramètres de connexion",
+"Configuration Active" => "Configuration active",
+"When unchecked, this configuration will be skipped." => "Lorsque non cochée, la configuration sera ignorée.",
"Port" => "Port",
-"Base User Tree" => "DN racine de l'arbre utilisateurs",
-"Base Group Tree" => "DN racine de l'arbre groupes",
-"Group-Member association" => "Association groupe-membre",
+"Backup (Replica) Host" => "Serveur de backup (réplique)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Fournir un serveur de backup optionnel. Il doit s'agir d'une réplique du serveur LDAP/AD principal.",
+"Backup (Replica) Port" => "Port du serveur de backup (réplique)",
+"Disable Main Server" => "Désactiver le serveur principal",
+"When switched on, ownCloud will only connect to the replica server." => "Lorsqu'activé, ownCloud ne se connectera qu'au serveur répliqué.",
"Use TLS" => "Utiliser TLS",
-"Do not use it for SSL connections, it will fail." => "Ne pas utiliser pour les connexions SSL, car cela échouera.",
+"Do not use it additionally for LDAPS connections, it will fail." => "À ne pas utiliser pour les connexions LDAPS (cela échouera).",
"Case insensitve LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)",
"Turn off SSL certificate validation." => "Désactiver la validation du certificat SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur ownCloud.",
"Not recommended, use for testing only." => "Non recommandé, utilisation pour tests uniquement.",
+"Cache Time-To-Live" => "Durée de vie du cache",
+"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.",
+"Directory Settings" => "Paramètres du répertoire",
"User Display Name Field" => "Champ \"nom d'affichage\" de l'utilisateur",
"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms d'utilisateurs d'ownCloud.",
+"Base User Tree" => "DN racine de l'arbre utilisateurs",
+"One User Base DN per line" => "Un DN racine utilisateur par ligne",
+"User Search Attributes" => "Recherche des attributs utilisateur",
+"Optional; one attribute per line" => "Optionnel, un attribut par ligne",
"Group Display Name Field" => "Champ \"nom d'affichage\" du groupe",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms de groupes d'ownCloud.",
+"Base Group Tree" => "DN racine de l'arbre groupes",
+"One Group Base DN per line" => "Un DN racine groupe par ligne",
+"Group Search Attributes" => "Recherche des attributs du groupe",
+"Group-Member association" => "Association groupe-membre",
+"Special Attributes" => "Attributs spéciaux",
+"Quota Field" => "Champ du quota",
+"Quota Default" => "Quota par défaut",
"in bytes" => "en octets",
-"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.",
+"Email Field" => "Champ Email",
+"User Home Folder Naming Rule" => "Convention de nommage du répertoire utilisateur",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laisser vide ",
+"Test Configuration" => "Tester la configuration",
"Help" => "Aide"
);
diff --git a/apps/user_ldap/l10n/gl.php b/apps/user_ldap/l10n/gl.php
new file mode 100644
index 00000000000..deb6dbb5553
--- /dev/null
+++ b/apps/user_ldap/l10n/gl.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Non foi posíbel eliminar a configuración do servidor",
+"The configuration is valid and the connection could be established!" => "A configuración é correcta e pode estabelecerse a conexión.",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuración é correcta, mais a ligazón non. Comprobe a configuración do servidor e as credenciais.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuración non é correcta. Vexa o rexistro de ownCloud para máis detalles",
+"Deletion failed" => "Fallou o borrado",
+"Take over settings from recent server configuration?" => "Tomar os recentes axustes de configuración do servidor?",
+"Keep settings?" => "Manter os axustes?",
+"Cannot add server configuration" => "Non é posíbel engadir a configuración do servidor",
+"Connection test succeeded" => "A proba de conexión foi satisfactoria",
+"Connection test failed" => "A proba de conexión fracasou",
+"Do you really want to delete the current Server Configuration?" => "Confirma que quere eliminar a configuración actual do servidor?",
+"Confirm Deletion" => "Confirmar a eliminación",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Aviso:</b> Os aplicativos user_ldap e user_webdavauth son incompatíbeis. Pode acontecer un comportamento estraño. Consulte co administrador do sistema para desactivar un deles.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Aviso:</b> O módulo PHP LDAP non está instalado, o servidor non funcionará. Consulte co administrador do sistema para instalalo.",
+"Server configuration" => "Configuración do servidor",
+"Add Server Configuration" => "Engadir a configuración do servidor",
+"Host" => "Servidor",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Pode omitir o protocolo agás que precise de SSL. Nese caso comece con ldaps://",
+"Base DN" => "DN base",
+"One Base DN per line" => "Un DN base por liña",
+"You can specify Base DN for users and groups in the Advanced tab" => "Pode especificar a DN base para usuarios e grupos na lapela de «Avanzado»",
+"User DN" => "DN do usuario",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN do cliente do usuario co que hai que estabelecer unha conexión, p.ex uid=axente, dc=exemplo, dc=com. Para o acceso anónimo deixe o DN e o contrasinal baleiros.",
+"Password" => "Contrasinal",
+"For anonymous access, leave DN and Password empty." => "Para o acceso anónimo deixe o DN e o contrasinal baleiros.",
+"User Login Filter" => "Filtro de acceso de usuarios",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Define o filtro que se aplica cando se intenta o acceso. %%uid substitúe o nome de usuario e a acción de acceso.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "usar a marca de posición %%uid, p.ex «uid=%%uid»",
+"User List Filter" => "Filtro da lista de usuarios",
+"Defines the filter to apply, when retrieving users." => "Define o filtro a aplicar cando se recompilan os usuarios.",
+"without any placeholder, e.g. \"objectClass=person\"." => "sen ningunha marca de posición, como p.ex «objectClass=persoa».",
+"Group Filter" => "Filtro de grupo",
+"Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar cando se recompilan os grupos.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ningunha marca de posición, como p.ex «objectClass=grupoPosix».",
+"Connection Settings" => "Axustes da conexión",
+"Configuration Active" => "Configuración activa",
+"When unchecked, this configuration will be skipped." => "Se está sen marcar, omítese esta configuración.",
+"Port" => "Porto",
+"Backup (Replica) Host" => "Servidor da copia de seguranza (Réplica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Indicar un servidor de copia de seguranza opcional. Debe ser unha réplica do servidor principal LDAP/AD.",
+"Backup (Replica) Port" => "Porto da copia de seguranza (Réplica)",
+"Disable Main Server" => "Desactivar o servidor principal",
+"When switched on, ownCloud will only connect to the replica server." => "Cando está activado, ownCloud só se conectará ao servidor de réplica.",
+"Use TLS" => "Usar TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Non utilizalo ademais para conexións LDAPS xa que fallará.",
+"Case insensitve LDAP server (Windows)" => "Servidor LDAP que non distingue entre maiúsculas e minúsculas (Windows)",
+"Turn off SSL certificate validation." => "Desactiva a validación do certificado SSL.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexión só funciona con esta opción importe o certificado SSL do servidor LDAP no seu servidor ownCloud.",
+"Not recommended, use for testing only." => "Non se recomenda. Só para probas.",
+"Cache Time-To-Live" => "Tempo de persistencia da caché",
+"in seconds. A change empties the cache." => "en segundos. Calquera cambio baleira a caché.",
+"Directory Settings" => "Axustes do directorio",
+"User Display Name Field" => "Campo de mostra do nome de usuario",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP a empregar para xerar o nome de usuario de ownCloud.",
+"Base User Tree" => "Base da árbore de usuarios",
+"One User Base DN per line" => "Un DN base de usuario por liña",
+"User Search Attributes" => "Atributos de busca do usuario",
+"Optional; one attribute per line" => "Opcional; un atributo por liña",
+"Group Display Name Field" => "Campo de mostra do nome de grupo",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP úsase para xerar os nomes dos grupos de ownCloud.",
+"Base Group Tree" => "Base da árbore de grupo",
+"One Group Base DN per line" => "Un DN base de grupo por liña",
+"Group Search Attributes" => "Atributos de busca do grupo",
+"Group-Member association" => "Asociación de grupos e membros",
+"Special Attributes" => "Atributos especiais",
+"Quota Field" => "Campo de cota",
+"Quota Default" => "Cota predeterminada",
+"in bytes" => "en bytes",
+"Email Field" => "Campo do correo",
+"User Home Folder Naming Rule" => "Regra de nomeado do cartafol do usuario",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixar baleiro para o nome de usuario (predeterminado). Noutro caso, especifique un atributo LDAP/AD.",
+"Test Configuration" => "Probar a configuración",
+"Help" => "Axuda"
+);
diff --git a/apps/user_ldap/l10n/he.php b/apps/user_ldap/l10n/he.php
new file mode 100644
index 00000000000..c9b0e282f1d
--- /dev/null
+++ b/apps/user_ldap/l10n/he.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "מחיקה נכשלה",
+"Host" => "מארח",
+"User DN" => "DN משתמש",
+"Password" => "סיסמא",
+"For anonymous access, leave DN and Password empty." => "לגישה אנונימית, השאר את הDM והסיסמא ריקים.",
+"User Login Filter" => "סנן כניסת משתמש",
+"User List Filter" => "סנן רשימת משתמשים",
+"Group Filter" => "סנן קבוצה",
+"Port" => "פורט",
+"in seconds. A change empties the cache." => "בשניות. שינוי מרוקן את המטמון.",
+"in bytes" => "בבתים",
+"Help" => "עזרה"
+);
diff --git a/apps/user_ldap/l10n/hi.php b/apps/user_ldap/l10n/hi.php
new file mode 100644
index 00000000000..60d4ea98e84
--- /dev/null
+++ b/apps/user_ldap/l10n/hi.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "सहयोग"
+);
diff --git a/apps/user_ldap/l10n/hr.php b/apps/user_ldap/l10n/hr.php
new file mode 100644
index 00000000000..91503315066
--- /dev/null
+++ b/apps/user_ldap/l10n/hr.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "Pomoć"
+);
diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php
new file mode 100644
index 00000000000..a82a64ab32f
--- /dev/null
+++ b/apps/user_ldap/l10n/hu_HU.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Nem sikerült törölni a kiszolgáló konfigurációját",
+"The configuration is valid and the connection could be established!" => "A konfiguráció érvényes, és a kapcsolat létrehozható!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A konfiguráció érvényes, de a kapcsolat nem hozható létre. Kérem ellenőrizze a kiszolgáló beállításait, és az elérési adatokat.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Érvénytelen konfiguráció. További információkért nézze meg az ownCloud naplófájlját.",
+"Deletion failed" => "A törlés nem sikerült",
+"Take over settings from recent server configuration?" => "Vegyük át a beállításokat az előző konfigurációból?",
+"Keep settings?" => "Tartsuk meg a beállításokat?",
+"Cannot add server configuration" => "Az új kiszolgáló konfigurációja nem hozható létre",
+"Connection test succeeded" => "A kapcsolatellenőrzés eredménye: sikerült",
+"Connection test failed" => "A kapcsolatellenőrzés eredménye: nem sikerült",
+"Do you really want to delete the current Server Configuration?" => "Tényleg törölni szeretné a kiszolgáló beállításait?",
+"Confirm Deletion" => "A törlés megerősítése",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Figyelem:</b> a user_ldap és user_webdavauth alkalmazások nem kompatibilisek. Együttes használatuk váratlan eredményekhez vezethet. Kérje meg a rendszergazdát, hogy a kettő közül kapcsolja ki az egyiket.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Figyelmeztetés:</b> Az LDAP PHP modul nincs telepítve, ezért ez az alrendszer nem fog működni. Kérje meg a rendszergazdát, hogy telepítse!",
+"Server configuration" => "A kiszolgálók beállításai",
+"Add Server Configuration" => "Új kiszolgáló beállításának hozzáadása",
+"Host" => "Kiszolgáló",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "A protokoll előtag elhagyható, kivéve, ha SSL-t kíván használni. Ebben az esetben kezdje így: ldaps://",
+"Base DN" => "DN-gyökér",
+"One Base DN per line" => "Soronként egy DN-gyökér",
+"You can specify Base DN for users and groups in the Advanced tab" => "A Haladó fülre kattintva külön DN-gyökér állítható be a felhasználók és a csoportok számára",
+"User DN" => "A kapcsolódó felhasználó DN-je",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Annak a felhasználónak a DN-je, akinek a nevében bejelentkezve kapcsolódunk a kiszolgálóhoz, pl. uid=agent,dc=example,dc=com. Bejelentkezés nélküli eléréshez ne töltse ki a DN és Jelszó mezőket!",
+"Password" => "Jelszó",
+"For anonymous access, leave DN and Password empty." => "Bejelentkezés nélküli eléréshez ne töltse ki a DN és Jelszó mezőket!",
+"User Login Filter" => "Szűrő a bejelentkezéshez",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Ez a szűrő érvényes a bejelentkezés megkísérlésekor. Ekkor az %%uid változó helyére a bejelentkezési név kerül.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "használja az %%uid változót, pl. \"uid=%%uid\"",
+"User List Filter" => "A felhasználók szűrője",
+"Defines the filter to apply, when retrieving users." => "Ez a szűrő érvényes a felhasználók listázásakor.",
+"without any placeholder, e.g. \"objectClass=person\"." => "itt ne használjon változót, pl. \"objectClass=person\".",
+"Group Filter" => "A csoportok szűrője",
+"Defines the filter to apply, when retrieving groups." => "Ez a szűrő érvényes a csoportok listázásakor.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "itt ne használjunk változót, pl. \"objectClass=posixGroup\".",
+"Connection Settings" => "Kapcsolati beállítások",
+"Configuration Active" => "A beállítás aktív",
+"When unchecked, this configuration will be skipped." => "Ha nincs kipipálva, ez a beállítás kihagyódik.",
+"Port" => "Port",
+"Backup (Replica) Host" => "Másodkiszolgáló (replika)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Adjon meg egy opcionális másodkiszolgálót. Ez a fő LDAP/AD kiszolgáló szinkron másolata (replikája) kell legyen.",
+"Backup (Replica) Port" => "A másodkiszolgáló (replika) portszáma",
+"Disable Main Server" => "A fő szerver kihagyása",
+"When switched on, ownCloud will only connect to the replica server." => "Ha ezt bekapcsoljuk, akkor az ownCloud csak a másodszerverekhez kapcsolódik.",
+"Use TLS" => "Használjunk TLS-t",
+"Do not use it additionally for LDAPS connections, it will fail." => "LDAPS kapcsolatok esetén ne kapcsoljuk be, mert nem fog működni.",
+"Case insensitve LDAP server (Windows)" => "Az LDAP-kiszolgáló nem tesz különbséget a kis- és nagybetűk között (Windows)",
+"Turn off SSL certificate validation." => "Ne ellenőrizzük az SSL-tanúsítvány érvényességét",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát az ownCloud kiszolgálóra!",
+"Not recommended, use for testing only." => "Nem javasolt, csak tesztelésre érdemes használni.",
+"Cache Time-To-Live" => "A gyorsítótár tárolási időtartama",
+"in seconds. A change empties the cache." => "másodpercben. A változtatás törli a cache tartalmát.",
+"Directory Settings" => "Címtár beállítások",
+"User Display Name Field" => "A felhasználónév mezője",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Ebből az LDAP attribútumból képződik a felhasználó elnevezése, ami megjelenik az ownCloudban.",
+"Base User Tree" => "A felhasználói fa gyökere",
+"One User Base DN per line" => "Soronként egy felhasználói fa gyökerét adhatjuk meg",
+"User Search Attributes" => "A felhasználók lekérdezett attribútumai",
+"Optional; one attribute per line" => "Nem kötelező megadni, soronként egy attribútum",
+"Group Display Name Field" => "A csoport nevének mezője",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Ebből az LDAP attribútumból képződik a csoport elnevezése, ami megjelenik az ownCloudban.",
+"Base Group Tree" => "A csoportfa gyökere",
+"One Group Base DN per line" => "Soronként egy csoportfa gyökerét adhatjuk meg",
+"Group Search Attributes" => "A csoportok lekérdezett attribútumai",
+"Group-Member association" => "A csoporttagság attribútuma",
+"Special Attributes" => "Különleges attribútumok",
+"Quota Field" => "Kvóta mező",
+"Quota Default" => "Alapértelmezett kvóta",
+"in bytes" => "bájtban",
+"Email Field" => "Email mező",
+"User Home Folder Naming Rule" => "A home könyvtár elérési útvonala",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Hagyja üresen, ha a felhasználónevet kívánja használni. Ellenkező esetben adjon meg egy LDAP/AD attribútumot!",
+"Test Configuration" => "A beállítások tesztelése",
+"Help" => "Súgó"
+);
diff --git a/apps/user_ldap/l10n/ia.php b/apps/user_ldap/l10n/ia.php
new file mode 100644
index 00000000000..3586bf5a2e7
--- /dev/null
+++ b/apps/user_ldap/l10n/ia.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "Adjuta"
+);
diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php
new file mode 100644
index 00000000000..5912789c856
--- /dev/null
+++ b/apps/user_ldap/l10n/id.php
@@ -0,0 +1,69 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Gagal menghapus konfigurasi server",
+"The configuration is valid and the connection could be established!" => "Konfigurasi valid dan koneksi dapat dilakukan!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurasi valid, tetapi Bind gagal. Silakan cek pengaturan server dan keamanan.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurasi salah. Silakan lihat log ownCloud untuk lengkapnya.",
+"Deletion failed" => "penghapusan gagal",
+"Take over settings from recent server configuration?" => "Ambil alih pengaturan dari konfigurasi server saat ini?",
+"Keep settings?" => "Biarkan pengaturan?",
+"Cannot add server configuration" => "Gagal menambah konfigurasi server",
+"Connection test succeeded" => "Tes koneksi sukses",
+"Connection test failed" => "Tes koneksi gagal",
+"Do you really want to delete the current Server Configuration?" => "Anda ingin menghapus Konfigurasi Server saat ini?",
+"Confirm Deletion" => "Konfirmasi Penghapusan",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Peringatan:/b> Aplikasi user_ldap dan user_webdavauth tidak kompatibel. Anda mungkin akan mengalami kejadian yang tidak diharapkan. Silakan minta administrator sistem untuk menonaktifkan salah satunya.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Peringatan:</b> Modul LDAP PHP tidak terpasang, perangkat tidak akan bekerja. Silakan minta administrator sistem untuk memasangnya.",
+"Server configuration" => "Konfigurasi server",
+"Add Server Configuration" => "Tambah Konfigurasi Server",
+"Host" => "host",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol dapat tidak ditulis, kecuali anda menggunakan SSL. Lalu jalankan dengan ldaps://",
+"Base DN" => "Base DN",
+"One Base DN per line" => "Satu Base DN per baris",
+"You can specify Base DN for users and groups in the Advanced tab" => "Anda dapat menetapkan Base DN untuk pengguna dan grup dalam tab Lanjutan",
+"User DN" => "User DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN dari klien pengguna yang dengannya tautan akan diterapkan, mis. uid=agen,dc=contoh,dc=com. Untuk akses anonim, biarkan DN dan kata sandi kosong.",
+"Password" => "kata kunci",
+"For anonymous access, leave DN and Password empty." => "Untuk akses anonim, biarkan DN dan Kata sandi kosong.",
+"User Login Filter" => "gunakan saringan login",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definisikan filter untuk diterapkan, saat login dilakukan. %%uid menggantikan username saat login.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "gunakan pengganti %%uid, mis. \"uid=%%uid\"",
+"User List Filter" => "Daftar Filter Pengguna",
+"Defines the filter to apply, when retrieving users." => "Definisikan filter untuk diterapkan saat menerima pengguna.",
+"without any placeholder, e.g. \"objectClass=person\"." => "tanpa pengganti apapun, mis. \"objectClass=seseorang\".",
+"Group Filter" => "saringan grup",
+"Defines the filter to apply, when retrieving groups." => "Definisikan filter untuk diterapkan saat menerima grup.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "tanpa pengganti apapaun, mis. \"objectClass=posixGroup\".",
+"Connection Settings" => "Pengaturan Koneksi",
+"Configuration Active" => "Konfigurasi Aktif",
+"When unchecked, this configuration will be skipped." => "Jika tidak dicentang, konfigurasi ini dilewati.",
+"Port" => "port",
+"Backup (Replica) Host" => "Host Cadangan (Replika)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Berikan pilihan host cadangan. Harus merupakan replika dari server LDAP/AD utama.",
+"Backup (Replica) Port" => "Port Cadangan (Replika)",
+"Disable Main Server" => "Nonaktifkan Server Utama",
+"When switched on, ownCloud will only connect to the replica server." => "Saat diaktifkan, ownCloud hanya akan terhubung ke server replika.",
+"Use TLS" => "gunakan TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Jangan gunakan utamanya untuk koneksi LDAPS, koneksi akan gagal.",
+"Case insensitve LDAP server (Windows)" => "Server LDAP dengan kapitalisasi tidak sensitif (Windows)",
+"Turn off SSL certificate validation." => "matikan validasi sertivikat SSL",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jika koneksi hanya bekerja dengan opsi ini, impor sertifikat SSL server LDAP dari server ownCloud anda.",
+"Not recommended, use for testing only." => "tidak disarankan, gunakan hanya untuk pengujian.",
+"in seconds. A change empties the cache." => "dalam detik. perubahan mengosongkan cache",
+"Directory Settings" => "Pengaturan Direktori",
+"User Display Name Field" => "Bidang Tampilan Nama Pengguna",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP yang digunakan untuk menghasilkan nama pengguna ownCloud.",
+"Base User Tree" => "Pohon Pengguna Dasar",
+"One User Base DN per line" => "Satu Pengguna Base DN per baris",
+"User Search Attributes" => "Atribut Pencarian Pengguna",
+"Optional; one attribute per line" => "Pilihan; satu atribut per baris",
+"Group Display Name Field" => "Bidang Tampilan Nama Grup",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP yang digunakan untuk menghasilkan nama grup ownCloud.",
+"Base Group Tree" => "Pohon Grup Dasar",
+"One Group Base DN per line" => "Satu Grup Base DN per baris",
+"Group Search Attributes" => "Atribut Pencarian Grup",
+"Group-Member association" => "asosiasi Anggota-Grup",
+"Special Attributes" => "Atribut Khusus",
+"in bytes" => "dalam bytes",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Biarkan nama pengguna kosong (default). Atau tetapkan atribut LDAP/AD.",
+"Help" => "bantuan"
+);
diff --git a/apps/user_ldap/l10n/is.php b/apps/user_ldap/l10n/is.php
new file mode 100644
index 00000000000..29bc7692795
--- /dev/null
+++ b/apps/user_ldap/l10n/is.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Host" => "Netþjónn",
+"Password" => "Lykilorð",
+"Help" => "Hjálp"
+);
diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php
index f07f0aa5a42..a2790fd1dec 100644
--- a/apps/user_ldap/l10n/it.php
+++ b/apps/user_ldap/l10n/it.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Eliminazione della configurazione del server non riuscita",
+"The configuration is valid and the connection could be established!" => "La configurazione è valida e la connessione può essere stabilita.",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configurazione è valida, ma il Bind non è riuscito. Controlla le impostazioni del server e le credenziali.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "La configurazione non è valida. Controlla il log di ownCloud per ulteriori dettagli.",
+"Deletion failed" => "Eliminazione non riuscita",
+"Take over settings from recent server configuration?" => "Vuoi recuperare le impostazioni dalla configurazione recente del server?",
+"Keep settings?" => "Vuoi mantenere le impostazioni?",
+"Cannot add server configuration" => "Impossibile aggiungere la configurazione del server",
+"Connection test succeeded" => "Prova di connessione riuscita",
+"Connection test failed" => "Prova di connessione non riuscita",
+"Do you really want to delete the current Server Configuration?" => "Vuoi davvero eliminare la configurazione attuale del server?",
+"Confirm Deletion" => "Conferma l'eliminazione",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Avviso:</b> le applicazioni user_ldap e user_webdavauth sono incompatibili. Potresti riscontrare un comportamento inatteso. Chiedi al tuo amministratore di sistema di disabilitarne uno.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Avviso:</b> il modulo PHP LDAP non è installato, il motore non funzionerà. Chiedi al tuo amministratore di sistema di installarlo.",
+"Server configuration" => "Configurazione del server",
+"Add Server Configuration" => "Aggiungi configurazione del server",
"Host" => "Host",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "È possibile omettere il protocollo, ad eccezione se è necessario SSL. Quindi inizia con ldaps://",
"Base DN" => "DN base",
+"One Base DN per line" => "Un DN base per riga",
"You can specify Base DN for users and groups in the Advanced tab" => "Puoi specificare una DN base per gli utenti ed i gruppi nella scheda Avanzate",
"User DN" => "DN utente",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Il DN per il client dell'utente con cui deve essere associato, ad esempio uid=agent,dc=example,dc=com. Per l'accesso anonimo, lasciare vuoti i campi DN e Password",
@@ -16,22 +33,43 @@
"Group Filter" => "Filtro per il gruppo",
"Defines the filter to apply, when retrieving groups." => "Specifica quale filtro utilizzare durante il recupero dei gruppi.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "senza nessun segnaposto, per esempio \"objectClass=posixGroup\".",
+"Connection Settings" => "Impostazioni di connessione",
+"Configuration Active" => "Configurazione attiva",
+"When unchecked, this configuration will be skipped." => "Se deselezionata, questa configurazione sarà saltata.",
"Port" => "Porta",
-"Base User Tree" => "Struttura base dell'utente",
-"Base Group Tree" => "Struttura base del gruppo",
-"Group-Member association" => "Associazione gruppo-utente ",
+"Backup (Replica) Host" => "Host di backup (Replica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Fornisci un host di backup opzionale. Deve essere una replica del server AD/LDAP principale.",
+"Backup (Replica) Port" => "Porta di backup (Replica)",
+"Disable Main Server" => "Disabilita server principale",
+"When switched on, ownCloud will only connect to the replica server." => "Se abilitata, ownCloud si collegherà solo al server di replica.",
"Use TLS" => "Usa TLS",
-"Do not use it for SSL connections, it will fail." => "Non utilizzare per le connessioni SSL, fallirà.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Da non utilizzare per le connessioni LDAPS, non funzionerà.",
"Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)",
"Turn off SSL certificate validation." => "Disattiva il controllo del certificato SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la connessione funziona esclusivamente con questa opzione, importa il certificato SSL del server LDAP nel tuo server ownCloud.",
"Not recommended, use for testing only." => "Non consigliato, utilizzare solo per test.",
+"Cache Time-To-Live" => "Tempo di vita della cache",
+"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.",
+"Directory Settings" => "Impostazioni delle cartelle",
"User Display Name Field" => "Campo per la visualizzazione del nome utente",
"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attributo LDAP da usare per generare il nome dell'utente ownCloud.",
+"Base User Tree" => "Struttura base dell'utente",
+"One User Base DN per line" => "Un DN base utente per riga",
+"User Search Attributes" => "Attributi di ricerca utente",
+"Optional; one attribute per line" => "Opzionale; un attributo per riga",
"Group Display Name Field" => "Campo per la visualizzazione del nome del gruppo",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attributo LDAP da usare per generare il nome del gruppo ownCloud.",
+"Base Group Tree" => "Struttura base del gruppo",
+"One Group Base DN per line" => "Un DN base gruppo per riga",
+"Group Search Attributes" => "Attributi di ricerca gruppo",
+"Group-Member association" => "Associazione gruppo-utente ",
+"Special Attributes" => "Attributi speciali",
+"Quota Field" => "Campo Quota",
+"Quota Default" => "Quota predefinita",
"in bytes" => "in byte",
-"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.",
+"Email Field" => "Campo Email",
+"User Home Folder Naming Rule" => "Regola di assegnazione del nome della cartella utente",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lascia vuoto per il nome utente (predefinito). Altrimenti, specifica un attributo LDAP/AD.",
+"Test Configuration" => "Prova configurazione",
"Help" => "Aiuto"
);
diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php
index ffaae4e9bd2..3ae7d2e6392 100644
--- a/apps/user_ldap/l10n/ja_JP.php
+++ b/apps/user_ldap/l10n/ja_JP.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "サーバ設定の削除に失敗しました",
+"The configuration is valid and the connection could be established!" => "設定は有効であり、接続を確立しました!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "設定は有効ですが、接続に失敗しました。サーバ設定と資格情報を確認して下さい。",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "設定は無効です。詳細は ownCloud のログを見て下さい。",
+"Deletion failed" => "削除に失敗しました",
+"Take over settings from recent server configuration?" => "最近のサーバ設定から設定を引き継ぎますか?",
+"Keep settings?" => "設定を保持しますか?",
+"Cannot add server configuration" => "サーバ設定を追加できません",
+"Connection test succeeded" => "接続テストに成功しました",
+"Connection test failed" => "接続テストに失敗しました",
+"Do you really want to delete the current Server Configuration?" => "現在のサーバ設定を本当に削除してもよろしいですか?",
+"Confirm Deletion" => "削除の確認",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>警告:</b> user_ldap と user_webdavauth のアプリには互換性がありません。予期せぬ動作をする可能姓があります。システム管理者にどちらかを無効にするよう問い合わせてください。",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>警告:</b> PHP LDAP モジュールがインストールされていません。バックエンドが正しく動作しません。システム管理者にインストールするよう問い合わせてください。",
+"Server configuration" => "サーバ設定",
+"Add Server Configuration" => "サーバ設定を追加",
"Host" => "ホスト",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL通信しない場合には、プロトコル名を省略することができます。そうでない場合には、ldaps:// から始めてください。",
"Base DN" => "ベースDN",
+"One Base DN per line" => "1行に1つのベースDN",
"You can specify Base DN for users and groups in the Advanced tab" => "拡張タブでユーザとグループのベースDNを指定することができます。",
"User DN" => "ユーザDN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "クライアントユーザーのDNは、特定のものに結びつけることはしません。 例えば uid=agent,dc=example,dc=com. だと匿名アクセスの場合、DNとパスワードは空のままです。",
@@ -16,22 +33,43 @@
"Group Filter" => "グループフィルタ",
"Defines the filter to apply, when retrieving groups." => "グループを取得するときに適用するフィルターを定義する。",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "プレースホルダーを利用しないでください。例 \"objectClass=posixGroup\"",
+"Connection Settings" => "接続設定",
+"Configuration Active" => "設定はアクティブです",
+"When unchecked, this configuration will be skipped." => "チェックを外すと、この設定はスキップされます。",
"Port" => "ポート",
-"Base User Tree" => "ベースユーザツリー",
-"Base Group Tree" => "ベースグループツリー",
-"Group-Member association" => "グループとメンバーの関連付け",
+"Backup (Replica) Host" => "バックアップ(レプリカ)ホスト",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "バックアップホストをオプションで指定することができます。メインのLDAP/ADサーバのレプリカである必要があります。",
+"Backup (Replica) Port" => "バックアップ(レプリカ)ポート",
+"Disable Main Server" => "メインサーバを無効にする",
+"When switched on, ownCloud will only connect to the replica server." => "有効にすると、ownCloudはレプリカサーバにのみ接続します。",
"Use TLS" => "TLSを利用",
-"Do not use it for SSL connections, it will fail." => "SSL接続に利用しないでください、失敗します。",
+"Do not use it additionally for LDAPS connections, it will fail." => "LDAPS接続のために追加でそれを利用しないで下さい。失敗します。",
"Case insensitve LDAP server (Windows)" => "大文字/小文字を区別しないLDAPサーバ(Windows)",
"Turn off SSL certificate validation." => "SSL証明書の確認を無効にする。",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "接続がこのオプションでのみ動作する場合は、LDAPサーバのSSL証明書をownCloudサーバにインポートしてください。",
"Not recommended, use for testing only." => "推奨しません、テスト目的でのみ利用してください。",
+"Cache Time-To-Live" => "キャッシュのTTL",
+"in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。",
+"Directory Settings" => "ディレクトリ設定",
"User Display Name Field" => "ユーザ表示名のフィールド",
"The LDAP attribute to use to generate the user`s ownCloud name." => "ユーザのownCloud名の生成に利用するLDAP属性。",
+"Base User Tree" => "ベースユーザツリー",
+"One User Base DN per line" => "1行に1つのユーザベースDN",
+"User Search Attributes" => "ユーザ検索属性",
+"Optional; one attribute per line" => "オプション:1行に1属性",
"Group Display Name Field" => "グループ表示名のフィールド",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "グループのownCloud名の生成に利用するLDAP属性。",
+"Base Group Tree" => "ベースグループツリー",
+"One Group Base DN per line" => "1行に1つのグループベースDN",
+"Group Search Attributes" => "グループ検索属性",
+"Group-Member association" => "グループとメンバーの関連付け",
+"Special Attributes" => "特殊属性",
+"Quota Field" => "クォータフィールド",
+"Quota Default" => "クォータのデフォルト",
"in bytes" => "バイト",
-"in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。",
+"Email Field" => "メールフィールド",
+"User Home Folder Naming Rule" => "ユーザのホームフォルダ命名規則",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ユーザ名を空のままにしてください(デフォルト)。そうでない場合は、LDAPもしくはADの属性を指定してください。",
+"Test Configuration" => "テスト設定",
"Help" => "ヘルプ"
);
diff --git a/apps/user_ldap/l10n/ka.php b/apps/user_ldap/l10n/ka.php
new file mode 100644
index 00000000000..169926283e9
--- /dev/null
+++ b/apps/user_ldap/l10n/ka.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "პაროლი",
+"Help" => "შველა"
+);
diff --git a/apps/user_ldap/l10n/ka_GE.php b/apps/user_ldap/l10n/ka_GE.php
new file mode 100644
index 00000000000..b31767fe935
--- /dev/null
+++ b/apps/user_ldap/l10n/ka_GE.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "წაშლის ველი",
+"Help" => "დახმარება"
+);
diff --git a/apps/user_ldap/l10n/ko.php b/apps/user_ldap/l10n/ko.php
new file mode 100644
index 00000000000..8aa9fe74b3d
--- /dev/null
+++ b/apps/user_ldap/l10n/ko.php
@@ -0,0 +1,53 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "삭제 실패",
+"Keep settings?" => "설정을 유지합니까?",
+"Connection test succeeded" => "연결 시험 성공",
+"Connection test failed" => "연결 시험 실패",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>경고:</b> user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여 둘 중 하나만 사용하도록 하십시오.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>경고:</b> PHP LDAP 모듈이 비활성화되어 있거나 설치되어 있지 않습니다. 백엔드를 사용할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.",
+"Host" => "호스트",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL을 사용하는 경우가 아니라면 프로토콜을 입력하지 않아도 됩니다. SSL을 사용하려면 ldaps://를 입력하십시오.",
+"Base DN" => "기본 DN",
+"One Base DN per line" => "기본 DN을 한 줄에 하나씩 입력하십시오",
+"You can specify Base DN for users and groups in the Advanced tab" => "고급 탭에서 사용자 및 그룹에 대한 기본 DN을 지정할 수 있습니다.",
+"User DN" => "사용자 DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "바인딩 작업을 수행할 클라이언트 사용자 DN입니다. 예를 들어서 uid=agent,dc=example,dc=com입니다. 익명 접근을 허용하려면 DN과 암호를 비워 두십시오.",
+"Password" => "암호",
+"For anonymous access, leave DN and Password empty." => "익명 접근을 허용하려면 DN과 암호를 비워 두십시오.",
+"User Login Filter" => "사용자 로그인 필터",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "로그인을 시도할 때 적용할 필터입니다. %%uid는 로그인 작업에서의 사용자 이름으로 대체됩니다.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid 자리 비움자를 사용하십시오. 예제: \"uid=%%uid\"\"",
+"User List Filter" => "사용자 목록 필터",
+"Defines the filter to apply, when retrieving users." => "사용자를 검색할 때 적용할 필터를 정의합니다.",
+"without any placeholder, e.g. \"objectClass=person\"." => "자리 비움자를 사용할 수 없습니다. 예제: \"objectClass=person\"",
+"Group Filter" => "그룹 필터",
+"Defines the filter to apply, when retrieving groups." => "그룹을 검색할 때 적용할 필터를 정의합니다.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "자리 비움자를 사용할 수 없습니다. 예제: \"objectClass=posixGroup\"",
+"Connection Settings" => "연결 설정",
+"Configuration Active" => "구성 활성화",
+"Port" => "포트",
+"Backup (Replica) Host" => "백업 (복제) 포트",
+"Backup (Replica) Port" => "백업 (복제) 포트",
+"Disable Main Server" => "주 서버 비활성화",
+"Use TLS" => "TLS 사용",
+"Case insensitve LDAP server (Windows)" => "서버에서 대소문자를 구분하지 않음 (Windows)",
+"Turn off SSL certificate validation." => "SSL 인증서 유효성 검사를 해제합니다.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "이 옵션을 사용해야 연결할 수 있는 경우에는 LDAP 서버의 SSL 인증서를 ownCloud로 가져올 수 있습니다.",
+"Not recommended, use for testing only." => "추천하지 않음, 테스트로만 사용하십시오.",
+"in seconds. A change empties the cache." => "초. 항목 변경 시 캐시가 갱신됩니다.",
+"Directory Settings" => "디렉토리 설정",
+"User Display Name Field" => "사용자의 표시 이름 필드",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP 속성은 사용자의 ownCloud 이름을 생성하기 위해 사용합니다.",
+"Base User Tree" => "기본 사용자 트리",
+"One User Base DN per line" => "사용자 DN을 한 줄에 하나씩 입력하십시오",
+"User Search Attributes" => "사용자 검색 속성",
+"Group Display Name Field" => "그룹의 표시 이름 필드",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP 속성은 그룹의 ownCloud 이름을 생성하기 위해 사용합니다.",
+"Base Group Tree" => "기본 그룹 트리",
+"One Group Base DN per line" => "그룹 기본 DN을 한 줄에 하나씩 입력하십시오",
+"Group Search Attributes" => "그룹 검색 속성",
+"Group-Member association" => "그룹-회원 연결",
+"in bytes" => "바이트",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "사용자 이름을 사용하려면 비워 두십시오(기본값). 기타 경우 LDAP/AD 속성을 지정하십시오.",
+"Help" => "도움말"
+);
diff --git a/apps/user_ldap/l10n/ku_IQ.php b/apps/user_ldap/l10n/ku_IQ.php
new file mode 100644
index 00000000000..1ae808ddd91
--- /dev/null
+++ b/apps/user_ldap/l10n/ku_IQ.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "یارمەتی"
+);
diff --git a/apps/user_ldap/l10n/lb.php b/apps/user_ldap/l10n/lb.php
new file mode 100644
index 00000000000..39ed627ce2c
--- /dev/null
+++ b/apps/user_ldap/l10n/lb.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Konnt net läschen",
+"Password" => "Passwuert",
+"Help" => "Hëllef"
+);
diff --git a/apps/user_ldap/l10n/lt_LT.php b/apps/user_ldap/l10n/lt_LT.php
index 809ed571bd0..aa21dd2d3c1 100644
--- a/apps/user_ldap/l10n/lt_LT.php
+++ b/apps/user_ldap/l10n/lt_LT.php
@@ -1,4 +1,5 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Ištrinti nepavyko",
"Password" => "Slaptažodis",
"Group Filter" => "Grupės filtras",
"Port" => "Prievadas",
diff --git a/apps/user_ldap/l10n/lv.php b/apps/user_ldap/l10n/lv.php
new file mode 100644
index 00000000000..50126664e5b
--- /dev/null
+++ b/apps/user_ldap/l10n/lv.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Neizdevās izdzēst servera konfigurāciju",
+"The configuration is valid and the connection could be established!" => "Konfigurācija ir derīga un varēja izveidot savienojumu!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurācija ir derīga, bet sasaiste neizdevās. Lūdzu, pārbaudiet servera iestatījumus un akreditācijas datus.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurācija ir nederīga. Lūdzu, apskatiet ownCloud žurnālu, lai uzzinātu vairāk.",
+"Deletion failed" => "Neizdevās izdzēst",
+"Take over settings from recent server configuration?" => "Paņemt iestatījumus no nesenas servera konfigurācijas?",
+"Keep settings?" => "Paturēt iestatījumus?",
+"Cannot add server configuration" => "Nevar pievienot servera konfigurāciju",
+"Connection test succeeded" => "Savienojuma tests ir veiksmīgs",
+"Connection test failed" => "Savienojuma tests cieta neveiksmi",
+"Do you really want to delete the current Server Configuration?" => "Vai tiešām vēlaties dzēst pašreizējo servera konfigurāciju?",
+"Confirm Deletion" => "Apstiprināt dzēšanu",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Brīdinājums:</b> lietotnes user_ldap un user_webdavauth ir nesavietojamas. Tās var izraisīt negaidītu uzvedību. Lūdzu, prasiet savam sistēmas administratoram kādu no tām deaktivēt.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Brīdinājums:</b> PHP LDAP modulis nav uzinstalēts, aizmugure nedarbosies. Lūdzu, prasiet savam sistēmas administratoram kādu no tām deaktivēt.",
+"Server configuration" => "Servera konfigurācija",
+"Add Server Configuration" => "Pievienot servera konfigurāciju",
+"Host" => "Resursdators",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Var neiekļaut protokolu, izņemot, ja vajag SSL. Tad sākums ir ldaps://",
+"Base DN" => "Bāzes DN",
+"One Base DN per line" => "Viena bāzes DN rindā",
+"You can specify Base DN for users and groups in the Advanced tab" => "Lietotājiem un grupām bāzes DN var norādīt cilnē “Paplašināti”",
+"User DN" => "Lietotāja DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Klienta lietotāja DN, ar ko veiks sasaisti, piemēram, uid=agent,dc=example,dc=com. Lai piekļūtu anonīmi, atstājiet DN un paroli tukšu.",
+"Password" => "Parole",
+"For anonymous access, leave DN and Password empty." => "Lai piekļūtu anonīmi, atstājiet DN un paroli tukšu.",
+"User Login Filter" => "Lietotāja ierakstīšanās filtrs",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definē filtru, ko izmantot, kad mēģina ierakstīties. %%uid ierakstīšanās darbībā aizstāj lietotājvārdu.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "lieto %%uid vietturi, piemēram, \"uid=%%uid\"",
+"User List Filter" => "Lietotāju saraksta filtrs",
+"Defines the filter to apply, when retrieving users." => "Definē filtru, ko izmantot, kad saņem lietotāju sarakstu.",
+"without any placeholder, e.g. \"objectClass=person\"." => "bez jebkādiem vietturiem, piemēram, \"objectClass=person\".",
+"Group Filter" => "Grupu filtrs",
+"Defines the filter to apply, when retrieving groups." => "Definē filtru, ko izmantot, kad saņem grupu sarakstu.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez jebkādiem vietturiem, piemēram, \"objectClass=posixGroup\".",
+"Connection Settings" => "Savienojuma iestatījumi",
+"Configuration Active" => "Konfigurācija ir aktīva",
+"When unchecked, this configuration will be skipped." => "Ja nav atzīmēts, šī konfigurācija tiks izlaista.",
+"Port" => "Ports",
+"Backup (Replica) Host" => "Rezerves (kopija) serveris",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Norādi rezerves serveri (nav obligāti). Tam ir jābūt galvenā LDAP/AD servera kopijai.",
+"Backup (Replica) Port" => "Rezerves (kopijas) ports",
+"Disable Main Server" => "Deaktivēt galveno serveri",
+"When switched on, ownCloud will only connect to the replica server." => "Kad ieslēgts, ownCloud savienosies tikai ar kopijas serveri.",
+"Use TLS" => "Lietot TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Neizmanto papildu LDAPS savienojumus! Tas nestrādās.",
+"Case insensitve LDAP server (Windows)" => "Reģistrnejutīgs LDAP serveris (Windows)",
+"Turn off SSL certificate validation." => "Izslēgt SSL sertifikātu validēšanu.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ja savienojums darbojas ar šo opciju, importē LDAP serveru SSL sertifikātu savā ownCloud serverī.",
+"Not recommended, use for testing only." => "Nav ieteicams, izmanto tikai testēšanai!",
+"Cache Time-To-Live" => "Kešatmiņas dzīvlaiks",
+"in seconds. A change empties the cache." => "sekundēs. Izmaiņas iztukšos kešatmiņu.",
+"Directory Settings" => "Direktorijas iestatījumi",
+"User Display Name Field" => "Lietotāja redzamā vārda lauks",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP atribūts, ko izmantot lietotāja ownCloud vārda veidošanai.",
+"Base User Tree" => "Bāzes lietotāju koks",
+"One User Base DN per line" => "Viena lietotāju bāzes DN rindā",
+"User Search Attributes" => "Lietotāju meklēšanas atribūts",
+"Optional; one attribute per line" => "Neobligāti; viens atribūts rindā",
+"Group Display Name Field" => "Grupas redzamā nosaukuma lauks",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP atribūts, ko izmantot grupas ownCloud nosaukuma veidošanai.",
+"Base Group Tree" => "Bāzes grupu koks",
+"One Group Base DN per line" => "Viena grupu bāzes DN rindā",
+"Group Search Attributes" => "Grupu meklēšanas atribūts",
+"Group-Member association" => "Grupu piederības asociācija",
+"Special Attributes" => "Īpašie atribūti",
+"Quota Field" => "Kvotu lauks",
+"Quota Default" => "Kvotas noklusējums",
+"in bytes" => "baitos",
+"Email Field" => "E-pasta lauks",
+"User Home Folder Naming Rule" => "Lietotāja mājas mapes nosaukšanas kārtula",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Atstāt tukšu lietotāja vārdam (noklusējuma). Citādi, norādi LDAP/AD atribūtu.",
+"Test Configuration" => "Testa konfigurācija",
+"Help" => "Palīdzība"
+);
diff --git a/apps/user_ldap/l10n/mk.php b/apps/user_ldap/l10n/mk.php
new file mode 100644
index 00000000000..7d34ff49492
--- /dev/null
+++ b/apps/user_ldap/l10n/mk.php
@@ -0,0 +1,7 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Бришењето е неуспешно",
+"Host" => "Домаќин",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Може да го скокнете протколот освен ако не ви треба SSL. Тогаш ставете ldaps://",
+"Password" => "Лозинка",
+"Help" => "Помош"
+);
diff --git a/apps/user_ldap/l10n/ms_MY.php b/apps/user_ldap/l10n/ms_MY.php
new file mode 100644
index 00000000000..17a6cbe2cb6
--- /dev/null
+++ b/apps/user_ldap/l10n/ms_MY.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Pemadaman gagal",
+"Help" => "Bantuan"
+);
diff --git a/apps/user_ldap/l10n/my_MM.php b/apps/user_ldap/l10n/my_MM.php
new file mode 100644
index 00000000000..ee8d3dd26fa
--- /dev/null
+++ b/apps/user_ldap/l10n/my_MM.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "စကားဝှက်",
+"Help" => "အကူအညီ"
+);
diff --git a/apps/user_ldap/l10n/nb_NO.php b/apps/user_ldap/l10n/nb_NO.php
new file mode 100644
index 00000000000..c4700245f24
--- /dev/null
+++ b/apps/user_ldap/l10n/nb_NO.php
@@ -0,0 +1,58 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Klarte ikke å slette tjener-konfigurasjonen.",
+"The configuration is valid and the connection could be established!" => "Konfigurasjonen er i orden og tilkoblingen skal være etablert!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurasjonen er i orden, men Bind mislyktes. Vennligst sjekk tjener-konfigurasjonen og påloggingsinformasjonen.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurasjonen er ikke i orden. Vennligst se ownClouds logfil for flere detaljer.",
+"Deletion failed" => "Sletting mislyktes",
+"Take over settings from recent server configuration?" => "Hent innstillinger fra tidligere tjener-konfigurasjon?",
+"Keep settings?" => "Behold innstillinger?",
+"Cannot add server configuration" => "Kan ikke legge til tjener-konfigurasjon",
+"Connection test succeeded" => "Tilkoblingstest lyktes",
+"Connection test failed" => "Tilkoblingstest mislyktes",
+"Do you really want to delete the current Server Configuration?" => "Er du sikker på at du vil slette aktiv tjener-konfigurasjon?",
+"Confirm Deletion" => "Bekreft sletting",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Advarsel:</b>Apps user_ldap og user_webdavauth er ikke kompatible. Du kan oppleve uventet atferd fra systemet. Vennligst spør din system-administrator om å deaktivere en av dem.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Warning:</b> PHP LDAP modulen er ikke installert, hjelperen vil ikke virke. Vennligst be din system-administrator om å installere den.",
+"Server configuration" => "Tjener-konfigurasjon",
+"Add Server Configuration" => "Legg til tjener-konfigurasjon",
+"Host" => "Tjener",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kan utelate protokollen, men du er påkrevd å bruke SSL. Deretter starte med ldaps://",
+"Base DN" => "Base DN",
+"One Base DN per line" => "En hoved DN pr. linje",
+"You can specify Base DN for users and groups in the Advanced tab" => "Du kan spesifisere Base DN for brukere og grupper under Avansert fanen",
+"User DN" => "Bruker DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN nummeret til klienten som skal bindes til, f.eks. uid=agent,dc=example,dc=com. For anonym tilgang, la DN- og passord-feltet stå tomt.",
+"Password" => "Passord",
+"For anonymous access, leave DN and Password empty." => "For anonym tilgang, la DN- og passord-feltet stå tomt.",
+"User Login Filter" => "Brukerpålogging filter",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definerer filteret som skal brukes når et påloggingsforsøk blir utført. %%uid erstatter brukernavnet i innloggingshandlingen.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "bruk %%uid plassholder, f.eks. \"uid=%%uid\"",
+"User List Filter" => "Brukerliste filter",
+"Defines the filter to apply, when retrieving users." => "Definerer filteret som skal brukes, når systemet innhenter brukere.",
+"without any placeholder, e.g. \"objectClass=person\"." => "uten noe plassholder, f.eks. \"objectClass=person\".",
+"Group Filter" => "Gruppefilter",
+"Defines the filter to apply, when retrieving groups." => "Definerer filteret som skal brukes, når systemet innhenter grupper.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "uten noe plassholder, f.eks. \"objectClass=posixGroup\".",
+"Configuration Active" => "Konfigurasjon aktiv",
+"When unchecked, this configuration will be skipped." => "Når ikke huket av så vil denne konfigurasjonen bli hoppet over.",
+"Port" => "Port",
+"Backup (Replica) Host" => "Sikkerhetskopierings (Replica) vert",
+"Use TLS" => "Bruk TLS",
+"Case insensitve LDAP server (Windows)" => "Case-insensitiv LDAP tjener (Windows)",
+"Turn off SSL certificate validation." => "Slå av SSL-sertifikat validering",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Hvis tilgang kun fungerer med dette alternativet, importer LDAP-tjenerens SSL-sertifikat til din egen ownCloud tjener.",
+"Not recommended, use for testing only." => "Ikke anbefalt, bruk kun for testing",
+"in seconds. A change empties the cache." => "i sekunder. En endring tømmer bufferen.",
+"User Display Name Field" => "Vis brukerens navnfelt",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP-attributen å bruke for å generere brukers ownCloud navn.",
+"Base User Tree" => "Hovedbruker tre",
+"One User Base DN per line" => "En Bruker Base DN pr. linje",
+"Group Display Name Field" => "Vis gruppens navnfelt",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP-attributen å bruke for å generere gruppens ownCloud navn.",
+"Base Group Tree" => "Hovedgruppe tre",
+"One Group Base DN per line" => "En gruppe hoved-DN pr. linje",
+"Group-Member association" => "gruppe-medlem assosiasjon",
+"in bytes" => "i bytes",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "La stå tom for brukernavn (standard). Ellers, spesifiser en LDAP/AD attributt.",
+"Help" => "Hjelp"
+);
diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php
new file mode 100644
index 00000000000..7973c66cd10
--- /dev/null
+++ b/apps/user_ldap/l10n/nl.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Verwijderen serverconfiguratie mislukt",
+"The configuration is valid and the connection could be established!" => "De configuratie is geldig en de verbinding is geslaagd!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "De configuratie is geldig, maar Bind mislukte. Controleer de serverinstellingen en inloggegevens.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "De configuratie is ongeldig. Controleer de ownCloud log voor meer details.",
+"Deletion failed" => "Verwijderen mislukt",
+"Take over settings from recent server configuration?" => "Overnemen instellingen van de recente serverconfiguratie?",
+"Keep settings?" => "Instellingen bewaren?",
+"Cannot add server configuration" => "Kon de serverconfiguratie niet toevoegen",
+"Connection test succeeded" => "Verbindingstest geslaagd",
+"Connection test failed" => "Verbindingstest mislukt",
+"Do you really want to delete the current Server Configuration?" => "Wilt u werkelijk de huidige Serverconfiguratie verwijderen?",
+"Confirm Deletion" => "Bevestig verwijderen",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Waarschuwing:</b> De Apps user_ldap en user_webdavauth zijn incompatible. U kunt onverwacht gedrag ervaren. Vraag uw beheerder om een van beide apps de deactiveren.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Waarschuwing:</b> De PHP LDAP module is niet geïnstalleerd, het backend zal niet werken. Vraag uw systeembeheerder om de module te installeren.",
+"Server configuration" => "Serverconfiguratie",
+"Add Server Configuration" => "Toevoegen serverconfiguratie",
+"Host" => "Host",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Je kunt het protocol weglaten, tenzij je SSL vereist. Start in dat geval met ldaps://",
+"Base DN" => "Base DN",
+"One Base DN per line" => "Een Base DN per regel",
+"You can specify Base DN for users and groups in the Advanced tab" => "Je kunt het Base DN voor gebruikers en groepen specificeren in het tab Geavanceerd.",
+"User DN" => "User DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "De DN van de client gebruiker waarmee de verbinding zal worden gemaakt, bijv. uid=agent,dc=example,dc=com. Voor anonieme toegang laat je het DN en het wachtwoord leeg.",
+"Password" => "Wachtwoord",
+"For anonymous access, leave DN and Password empty." => "Voor anonieme toegang, laat de DN en het wachtwoord leeg.",
+"User Login Filter" => "Gebruikers Login Filter",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definiëerd de toe te passen filter indien er geprobeerd wordt in te loggen. %%uid vervangt de gebruikersnaam in de login actie.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "gebruik %%uid placeholder, bijv. \"uid=%%uid\"",
+"User List Filter" => "Gebruikers Lijst Filter",
+"Defines the filter to apply, when retrieving users." => "Definiëerd de toe te passen filter voor het ophalen van gebruikers.",
+"without any placeholder, e.g. \"objectClass=person\"." => "zonder een placeholder, bijv. \"objectClass=person\"",
+"Group Filter" => "Groep Filter",
+"Defines the filter to apply, when retrieving groups." => "Definiëerd de toe te passen filter voor het ophalen van groepen.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "zonder een placeholder, bijv. \"objectClass=posixGroup\"",
+"Connection Settings" => "Verbindingsinstellingen",
+"Configuration Active" => "Configuratie actief",
+"When unchecked, this configuration will be skipped." => "Als dit niet is ingeschakeld wordt deze configuratie overgeslagen.",
+"Port" => "Poort",
+"Backup (Replica) Host" => "Backup (Replica) Host",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Opgeven optionele backup host. Het moet een replica van de hoofd LDAP/AD server.",
+"Backup (Replica) Port" => "Backup (Replica) Poort",
+"Disable Main Server" => "Deactiveren hoofdserver",
+"When switched on, ownCloud will only connect to the replica server." => "Wanneer ingeschakeld, zal ownCloud allen verbinden met de replicaserver.",
+"Use TLS" => "Gebruik TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Gebruik het niet voor LDAPS verbindingen, dat gaat niet lukken.",
+"Case insensitve LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)",
+"Turn off SSL certificate validation." => "Schakel SSL certificaat validatie uit.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Als de connectie alleen werkt met deze optie, importeer dan het LDAP server SSL certificaat naar je ownCloud server.",
+"Not recommended, use for testing only." => "Niet aangeraden, gebruik alleen voor test doeleinden.",
+"Cache Time-To-Live" => "Cache time-to-live",
+"in seconds. A change empties the cache." => "in seconden. Een verandering maakt de cache leeg.",
+"Directory Settings" => "Mapinstellingen",
+"User Display Name Field" => "Gebruikers Schermnaam Veld",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Het te gebruiken LDAP attribuut voor het genereren van de ownCloud naam voor de gebruikers.",
+"Base User Tree" => "Basis Gebruikers Structuur",
+"One User Base DN per line" => "Een User Base DN per regel",
+"User Search Attributes" => "Attributen voor gebruikerszoekopdrachten",
+"Optional; one attribute per line" => "Optioneel; één attribuut per regel",
+"Group Display Name Field" => "Groep Schermnaam Veld",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Het te gebruiken LDAP attribuut voor het genereren van de ownCloud naam voor de groepen.",
+"Base Group Tree" => "Basis Groupen Structuur",
+"One Group Base DN per line" => "Een Group Base DN per regel",
+"Group Search Attributes" => "Attributen voor groepszoekopdrachten",
+"Group-Member association" => "Groepslid associatie",
+"Special Attributes" => "Speciale attributen",
+"Quota Field" => "Quota veld",
+"Quota Default" => "Quota standaard",
+"in bytes" => "in bytes",
+"Email Field" => "E-mailveld",
+"User Home Folder Naming Rule" => "Gebruikers Home map naamgevingsregel",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laat leeg voor de gebruikersnaam (standaard). Of, specificeer een LDAP/AD attribuut.",
+"Test Configuration" => "Test configuratie",
+"Help" => "Help"
+);
diff --git a/apps/user_ldap/l10n/nn_NO.php b/apps/user_ldap/l10n/nn_NO.php
new file mode 100644
index 00000000000..54d1f158f65
--- /dev/null
+++ b/apps/user_ldap/l10n/nn_NO.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "Hjelp"
+);
diff --git a/apps/user_ldap/l10n/oc.php b/apps/user_ldap/l10n/oc.php
new file mode 100644
index 00000000000..a128638172a
--- /dev/null
+++ b/apps/user_ldap/l10n/oc.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Fracàs d'escafatge",
+"Help" => "Ajuda"
+);
diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php
index 7ebebe1e073..776aa445e4e 100644
--- a/apps/user_ldap/l10n/pl.php
+++ b/apps/user_ldap/l10n/pl.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Nie można usunąć konfiguracji serwera",
+"The configuration is valid and the connection could be established!" => "Konfiguracja jest prawidłowa i można ustanowić połączenie!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfiguracja jest prawidłowa, ale Bind nie. Sprawdź ustawienia serwera i poświadczenia.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfiguracja jest nieprawidłowa. Proszę przejrzeć logi dziennika ownCloud ",
+"Deletion failed" => "Skasowanie nie powiodło się",
+"Take over settings from recent server configuration?" => "Przejmij ustawienia z ostatnich konfiguracji serwera?",
+"Keep settings?" => "Zachować ustawienia?",
+"Cannot add server configuration" => "Nie można dodać konfiguracji serwera",
+"Connection test succeeded" => "Test połączenia udany",
+"Connection test failed" => "Test połączenia nie udany",
+"Do you really want to delete the current Server Configuration?" => "Czy chcesz usunąć bieżącą konfigurację serwera?",
+"Confirm Deletion" => "Potwierdź usunięcie",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Ostrzeżenie:</b> Aplikacje user_ldap i user_webdavauth nie są kompatybilne. Mogą powodować nieoczekiwane zachowanie. Poproś administratora o wyłączenie jednej z nich.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Ostrzeżenie:</b> Moduł PHP LDAP nie jest zainstalowany i nie będzie działał. Poproś administratora o włączenie go.",
+"Server configuration" => "Konfiguracja servera",
+"Add Server Configuration" => "Dodaj konfigurację servera",
"Host" => "Host",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Można pominąć protokół, z wyjątkiem wymaganego protokołu SSL. Następnie uruchom z ldaps://",
"Base DN" => "Baza DN",
+"One Base DN per line" => "Jedna baza DN na linię",
"You can specify Base DN for users and groups in the Advanced tab" => "Bazę DN można określić dla użytkowników i grup w karcie Zaawansowane",
"User DN" => "Użytkownik DN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN użytkownika klienta, z którym powiązanie wykonuje się, np. uid=agent,dc=example,dc=com. Dla dostępu anonimowego pozostawić DN i hasło puste",
@@ -16,22 +33,43 @@
"Group Filter" => "Grupa filtrów",
"Defines the filter to apply, when retrieving groups." => "Definiuje filtry do zastosowania, podczas pobierania grup.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez żadnych symboli zastępczych np. \"objectClass=posixGroup\".",
+"Connection Settings" => "Konfiguracja połączeń",
+"Configuration Active" => "Konfiguracja archiwum",
+"When unchecked, this configuration will be skipped." => "Gdy niezaznaczone, ta konfiguracja zostanie pominięta.",
"Port" => "Port",
-"Base User Tree" => "Drzewo bazy użytkowników",
-"Base Group Tree" => "Drzewo bazy grup",
-"Group-Member association" => "Członek grupy stowarzyszenia",
+"Backup (Replica) Host" => "Kopia zapasowa (repliki) host",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dać opcjonalnie hosta kopii zapasowej . To musi być repliką głównego serwera LDAP/AD.",
+"Backup (Replica) Port" => "Kopia zapasowa (repliki) Port",
+"Disable Main Server" => "Wyłącz serwer główny",
+"When switched on, ownCloud will only connect to the replica server." => "Po włączeniu, ownCloud tylko połączy się z serwerem repliki.",
"Use TLS" => "Użyj TLS",
-"Do not use it for SSL connections, it will fail." => "Nie używaj SSL dla połączeń, jeśli się nie powiedzie.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Nie używaj go dodatkowo dla połączeń protokołu LDAPS, zakończy się niepowodzeniem.",
"Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)",
"Turn off SSL certificate validation." => "Wyłączyć sprawdzanie poprawności certyfikatu SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jeśli połączenie działa tylko z tą opcją, zaimportuj certyfikat SSL serwera LDAP w serwerze ownCloud.",
"Not recommended, use for testing only." => "Niezalecane, użyj tylko testowo.",
+"Cache Time-To-Live" => "Przechowuj czas życia",
+"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.",
+"Directory Settings" => "Ustawienia katalogów",
"User Display Name Field" => "Pole wyświetlanej nazwy użytkownika",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy użytkownika ownCloud.",
+"Base User Tree" => "Drzewo bazy użytkowników",
+"One User Base DN per line" => "Jeden użytkownik Bazy DN na linię",
+"User Search Attributes" => "Szukaj atrybutów",
+"Optional; one attribute per line" => "Opcjonalnie; jeden atrybut w wierszu",
"Group Display Name Field" => "Pole wyświetlanej nazwy grupy",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy grup ownCloud.",
+"Base Group Tree" => "Drzewo bazy grup",
+"One Group Base DN per line" => "Jedna grupa bazy DN na linię",
+"Group Search Attributes" => "Grupa atrybutów wyszukaj",
+"Group-Member association" => "Członek grupy stowarzyszenia",
+"Special Attributes" => "Specjalne atrybuty",
+"Quota Field" => "Pole przydziału",
+"Quota Default" => "Przydział domyślny",
"in bytes" => "w bajtach",
-"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.",
+"Email Field" => "Pole email",
+"User Home Folder Naming Rule" => "Reguły nazewnictwa folderu domowego użytkownika",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pozostaw puste dla user name (domyślnie). W przeciwnym razie podaj atrybut LDAP/AD.",
+"Test Configuration" => "Konfiguracja testowa",
"Help" => "Pomoc"
);
diff --git a/apps/user_ldap/l10n/pt_BR.php b/apps/user_ldap/l10n/pt_BR.php
index 18eed6d0142..a728ea15fde 100644
--- a/apps/user_ldap/l10n/pt_BR.php
+++ b/apps/user_ldap/l10n/pt_BR.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
-"Host" => "Host",
+"Failed to delete the server configuration" => "Falha ao deletar a configuração do servidor",
+"The configuration is valid and the connection could be established!" => "A configuração é válida e a conexão foi estabelecida!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuração é válida, mas o Bind falhou. Confira as configurações do servidor e as credenciais.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuração é inválida. Leia o log do ownCloud para mais detalhes.",
+"Deletion failed" => "Remoção falhou",
+"Take over settings from recent server configuration?" => "Tomar parámetros de recente configuração de servidor?",
+"Keep settings?" => "Manter ajustes?",
+"Cannot add server configuration" => "Impossível adicionar a configuração do servidor",
+"Connection test succeeded" => "Teste de conexão bem sucedida",
+"Connection test failed" => "Teste de conexão falhou",
+"Do you really want to delete the current Server Configuration?" => "Você quer realmente deletar as atuais Configurações de Servidor?",
+"Confirm Deletion" => "Confirmar Exclusão",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Aviso:</b> Os aplicativos user_ldap e user_webdavauth são incompatíveis. Você deverá experienciar comportamento inesperado. Por favor, peça ao seu administrador do sistema para desabilitar um deles.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Aviso:</b> O módulo PHP LDAP não está instalado, o backend não funcionará. Por favor, peça ao seu administrador do sistema para instalá-lo.",
+"Server configuration" => "Configuração de servidor",
+"Add Server Configuration" => "Adicionar Configuração de Servidor",
+"Host" => "Servidor",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Você pode omitir o protocolo, exceto quando requerer SSL. Então inicie com ldaps://",
"Base DN" => "DN Base",
+"One Base DN per line" => "Uma base DN por linha",
"You can specify Base DN for users and groups in the Advanced tab" => "Você pode especificar DN Base para usuários e grupos na guia Avançada",
"User DN" => "DN Usuário",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN do cliente usuário com qual a ligação deverá ser feita, ex. uid=agent,dc=example,dc=com. Para acesso anônimo, deixe DN e Senha vazios.",
@@ -11,27 +28,48 @@
"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Define o filtro pra aplicar ao efetuar uma tentativa de login. %%uuid substitui o nome de usuário na ação de login.",
"use %%uid placeholder, e.g. \"uid=%%uid\"" => "use %%uid placeholder, ex. \"uid=%%uid\"",
"User List Filter" => "Filtro de Lista de Usuário",
-"Defines the filter to apply, when retrieving users." => "Define filtro a aplicar ao obter usuários.",
+"Defines the filter to apply, when retrieving users." => "Define filtro a ser aplicado ao obter usuários.",
"without any placeholder, e.g. \"objectClass=person\"." => "sem nenhum espaço reservado, ex. \"objectClass=person\".",
"Group Filter" => "Filtro de Grupo",
"Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar ao obter grupos.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sem nenhum espaço reservado, ex. \"objectClass=posixGroup\"",
+"Connection Settings" => "Configurações de Conexão",
+"Configuration Active" => "Configuração ativa",
+"When unchecked, this configuration will be skipped." => "Quando não marcada, esta configuração será ignorada.",
"Port" => "Porta",
-"Base User Tree" => "Árvore de Usuário Base",
-"Base Group Tree" => "Árvore de Grupo Base",
-"Group-Member association" => "Associação Grupo-Membro",
+"Backup (Replica) Host" => "Servidor de Backup (Réplica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Defina um servidor de backup opcional. Ele deverá ser uma réplica do servidor LDAP/AD principal.",
+"Backup (Replica) Port" => "Porta do Backup (Réplica)",
+"Disable Main Server" => "Desativar Servidor Principal",
+"When switched on, ownCloud will only connect to the replica server." => "Quando ativado, ownCloud somente se conectará ao servidor de réplica.",
"Use TLS" => "Usar TLS",
-"Do not use it for SSL connections, it will fail." => "Não use-o para conexões SSL, pois falhará.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Não use adicionalmente para conexões LDAPS, pois falhará.",
"Case insensitve LDAP server (Windows)" => "Servidor LDAP sensível à caixa alta (Windows)",
"Turn off SSL certificate validation." => "Desligar validação de certificado SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexão só funciona com essa opção, importe o certificado SSL do servidor LDAP no seu servidor ownCloud.",
"Not recommended, use for testing only." => "Não recomendado, use somente para testes.",
+"Cache Time-To-Live" => "Cache Time-To-Live",
+"in seconds. A change empties the cache." => "em segundos. Uma mudança esvaziará o cache.",
+"Directory Settings" => "Configurações de Diretório",
"User Display Name Field" => "Campo Nome de Exibição de Usuário",
"The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do usuário.",
+"Base User Tree" => "Árvore de Usuário Base",
+"One User Base DN per line" => "Um usuário-base DN por linha",
+"User Search Attributes" => "Atributos de Busca de Usuário",
+"Optional; one attribute per line" => "Opcional; um atributo por linha",
"Group Display Name Field" => "Campo Nome de Exibição de Grupo",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do grupo.",
+"Base Group Tree" => "Árvore de Grupo Base",
+"One Group Base DN per line" => "Um grupo-base DN por linha",
+"Group Search Attributes" => "Atributos de Busca de Grupo",
+"Group-Member association" => "Associação Grupo-Membro",
+"Special Attributes" => "Atributos Especiais",
+"Quota Field" => "Campo de Cota",
+"Quota Default" => "Cota Padrão",
"in bytes" => "em bytes",
-"in seconds. A change empties the cache." => "em segundos. Uma mudança esvaziará o cache.",
+"Email Field" => "Campo de Email",
+"User Home Folder Naming Rule" => "Regra para Nome da Pasta Pessoal do Usuário",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de usuário (padrão). Caso contrário, especifique um atributo LDAP/AD.",
+"Test Configuration" => "Teste de Configuração",
"Help" => "Ajuda"
);
diff --git a/apps/user_ldap/l10n/pt_PT.php b/apps/user_ldap/l10n/pt_PT.php
index bf32a295371..3092d061437 100644
--- a/apps/user_ldap/l10n/pt_PT.php
+++ b/apps/user_ldap/l10n/pt_PT.php
@@ -1,10 +1,75 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Erro ao eliminar as configurações do servidor",
+"The configuration is valid and the connection could be established!" => "A configuração está correcta e foi possível estabelecer a ligação!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuração está correcta, mas não foi possível estabelecer o \"laço\", por favor, verifique as configurações do servidor e as credenciais.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuração é inválida. Por favor, veja o log do ownCloud para mais detalhes.",
+"Deletion failed" => "Erro ao apagar",
+"Take over settings from recent server configuration?" => "Assumir as configurações da configuração do servidor mais recente?",
+"Keep settings?" => "Manter as definições?",
+"Cannot add server configuration" => "Não foi possível adicionar as configurações do servidor.",
+"Connection test succeeded" => "Teste de conecção passado com sucesso.",
+"Connection test failed" => "Erro no teste de conecção.",
+"Do you really want to delete the current Server Configuration?" => "Deseja realmente apagar as configurações de servidor actuais?",
+"Confirm Deletion" => "Confirmar a operação de apagar",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Aviso:</b> A aplicação user_ldap e user_webdavauth são incompativeis. A aplicação pode tornar-se instável. Por favor, peça ao seu administrador para desactivar uma das aplicações.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Aviso:</b> O módulo PHP LDAP não está instalado, logo não irá funcionar. Por favor peça ao administrador para o instalar.",
+"Server configuration" => "Configurações do servidor",
+"Add Server Configuration" => "Adicionar configurações do servidor",
"Host" => "Anfitrião",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Pode omitir o protocolo, excepto se necessitar de SSL. Neste caso, comece com ldaps://",
+"Base DN" => "DN base",
+"One Base DN per line" => "Uma base DN por linho",
"You can specify Base DN for users and groups in the Advanced tab" => "Pode especificar o ND Base para utilizadores e grupos no separador Avançado",
+"User DN" => "DN do utilizador",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN to cliente ",
"Password" => "Palavra-passe",
+"For anonymous access, leave DN and Password empty." => "Para acesso anónimo, deixe DN e a Palavra-passe vazios.",
+"User Login Filter" => "Filtro de login de utilizador",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Define o filtro a aplicar, para aquando de uma tentativa de login. %%uid substitui o nome de utilizador utilizado.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Use a variável %%uid , exemplo: \"uid=%%uid\"",
+"User List Filter" => "Utilizar filtro",
+"Defines the filter to apply, when retrieving users." => "Defina o filtro a aplicar, ao recuperar utilizadores.",
+"without any placeholder, e.g. \"objectClass=person\"." => "Sem variável. Exemplo: \"objectClass=pessoa\".",
"Group Filter" => "Filtrar por grupo",
+"Defines the filter to apply, when retrieving groups." => "Defina o filtro a aplicar, ao recuperar grupos.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Sem nenhuma variável. Exemplo: \"objectClass=posixGroup\".",
+"Connection Settings" => "Definições de ligação",
+"Configuration Active" => "Configuração activa",
+"When unchecked, this configuration will be skipped." => "Se não estiver marcada, esta definição não será tida em conta.",
"Port" => "Porto",
-"in bytes" => "em bytes",
+"Backup (Replica) Host" => "Servidor de Backup (Réplica)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Forneça um servidor (anfitrião) de backup. Deve ser uma réplica do servidor principal de LDAP/AD ",
+"Backup (Replica) Port" => "Porta do servidor de backup (Replica)",
+"Disable Main Server" => "Desactivar servidor principal",
+"When switched on, ownCloud will only connect to the replica server." => "Se estiver ligado, o ownCloud vai somente ligar-se a este servidor de réplicas.",
+"Use TLS" => "Usar TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Não utilize para adicionar ligações LDAP, irá falhar!",
+"Case insensitve LDAP server (Windows)" => "Servidor LDAP (Windows) não sensível a maiúsculas.",
+"Turn off SSL certificate validation." => "Desligar a validação de certificado SSL.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a ligação apenas funcionar com está opção, importe o certificado SSL do servidor LDAP para o seu servidor do ownCloud.",
+"Not recommended, use for testing only." => "Não recomendado, utilizado apenas para testes!",
+"Cache Time-To-Live" => "Cache do tempo de vida dos objetos no servidor",
"in seconds. A change empties the cache." => "em segundos. Uma alteração esvazia a cache.",
+"Directory Settings" => "Definições de directorias",
+"User Display Name Field" => "Mostrador do nome de utilizador.",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Atributo LDAP para gerar o nome de utilizador do ownCloud.",
+"Base User Tree" => "Base da árvore de utilizadores.",
+"One User Base DN per line" => "Uma base de utilizador DN por linha",
+"User Search Attributes" => "Utilizar atributos de pesquisa",
+"Optional; one attribute per line" => "Opcional; Um atributo por linha",
+"Group Display Name Field" => "Mostrador do nome do grupo.",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributo LDAP para gerar o nome do grupo do ownCloud.",
+"Base Group Tree" => "Base da árvore de grupos.",
+"One Group Base DN per line" => "Uma base de grupo DN por linha",
+"Group Search Attributes" => "Atributos de pesquisa de grupo",
+"Group-Member association" => "Associar utilizador ao grupo.",
+"Special Attributes" => "Atributos especiais",
+"Quota Field" => "Quota",
+"Quota Default" => "Quota padrão",
+"in bytes" => "em bytes",
+"Email Field" => "Campo de email",
+"User Home Folder Naming Rule" => "Regra da pasta inicial do utilizador",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de utilizador (padrão). De outro modo, especifique um atributo LDAP/AD.",
+"Test Configuration" => "Testar a configuração",
"Help" => "Ajuda"
);
diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php
index beeed857455..8f55a35b491 100644
--- a/apps/user_ldap/l10n/ro.php
+++ b/apps/user_ldap/l10n/ro.php
@@ -1,7 +1,11 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Ștergerea a eșuat",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Atentie:</b> Apps user_ldap si user_webdavauth sunt incompatibile. Este posibil sa experimentati un comportament neasteptat. Vă rugăm să întrebați administratorul de sistem pentru a dezactiva una dintre ele.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Atenție</b> Modulul PHP LDAP nu este instalat, infrastructura nu va funcționa. Contactează administratorul sistemului pentru al instala.",
"Host" => "Gazdă",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Puteți omite protocolul, decât dacă folosiți SSL. Atunci se începe cu ldaps://",
"Base DN" => "DN de bază",
+"One Base DN per line" => "Un Base DN pe linie",
"You can specify Base DN for users and groups in the Advanced tab" => "Puteți să specificați DN de bază pentru utilizatori și grupuri în fila Avansat",
"User DN" => "DN al utilizatorului",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-ul clientului utilizator cu care se va efectua conectarea, d.e. uid=agent,dc=example,dc=com. Pentru acces anonim, lăsăți DN și Parolă libere.",
@@ -17,21 +21,22 @@
"Defines the filter to apply, when retrieving groups." => "Definește filtrele care se aplică, când se preiau grupurile.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "fără substituenți, d.e. \"objectClass=posixGroup\"",
"Port" => "Portul",
-"Base User Tree" => "Arborele de bază al Utilizatorilor",
-"Base Group Tree" => "Arborele de bază al Grupurilor",
-"Group-Member association" => "Asocierea Grup-Membru",
"Use TLS" => "Utilizează TLS",
-"Do not use it for SSL connections, it will fail." => "A nu se utiliza pentru conexiuni SSL, va eșua.",
"Case insensitve LDAP server (Windows)" => "Server LDAP insensibil la majuscule (Windows)",
"Turn off SSL certificate validation." => "Oprește validarea certificatelor SSL ",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Dacă conexiunea lucrează doar cu această opțiune, importează certificatul SSL al serverului LDAP în serverul ownCloud.",
"Not recommended, use for testing only." => "Nu este recomandat, a se utiliza doar pentru testare.",
+"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.",
"User Display Name Field" => "Câmpul cu numele vizibil al utilizatorului",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele de utilizator din ownCloud.",
+"Base User Tree" => "Arborele de bază al Utilizatorilor",
+"One User Base DN per line" => "Un User Base DN pe linie",
"Group Display Name Field" => "Câmpul cu numele grupului",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele grupurilor din ownCloud",
+"Base Group Tree" => "Arborele de bază al Grupurilor",
+"One Group Base DN per line" => "Un Group Base DN pe linie",
+"Group-Member association" => "Asocierea Grup-Membru",
"in bytes" => "în octeți",
-"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lăsați gol pentru numele de utilizator (implicit). În caz contrar, specificați un atribut LDAP / AD.",
"Help" => "Ajutor"
);
diff --git a/apps/user_ldap/l10n/ru.php b/apps/user_ldap/l10n/ru.php
index 92982d868b8..0746e1e8929 100644
--- a/apps/user_ldap/l10n/ru.php
+++ b/apps/user_ldap/l10n/ru.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Не удалось удалить конфигурацию сервера",
+"The configuration is valid and the connection could be established!" => "Конфигурация правильная и подключение может быть установлено!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Конфигурация верна, но операция подключения завершилась неудачно. Пожалуйста, проверьте настройки сервера и учетные данные.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Конфигурация не верна. Пожалуйста, посмотрите в журнале ownCloud детали.",
+"Deletion failed" => "Удаление не удалось",
+"Take over settings from recent server configuration?" => "Принять настройки из последней конфигурации сервера?",
+"Keep settings?" => "Сохранить настройки?",
+"Cannot add server configuration" => "Не получилось добавить конфигурацию сервера",
+"Connection test succeeded" => "Проверка соединения удалась",
+"Connection test failed" => "Проверка соединения не удалась",
+"Do you really want to delete the current Server Configuration?" => "Вы действительно хотите удалить существующую конфигурацию сервера?",
+"Confirm Deletion" => "Подтверждение удаления",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Внимание:</b>Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением. Пожалуйста, обратитесь к системному администратору, чтобы отключить одно из них.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Внимание:</b> Модуль LDAP для PHP не установлен, бэкенд не будет работать. Пожалуйста, попросите вашего системного администратора его установить. ",
+"Server configuration" => "Конфигурация сервера",
+"Add Server Configuration" => "Добавить конфигурацию сервера",
"Host" => "Сервер",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можно опустить протокол, за исключением того, когда вам требуется SSL. Тогда начните с ldaps :/ /",
"Base DN" => "Базовый DN",
+"One Base DN per line" => "По одному базовому DN в строке.",
"You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп на вкладке \"Расширенное\"",
"User DN" => "DN пользователя",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-клиента пользователя, с которым связывают должно быть заполнено, например, uid=агент, dc=пример, dc=com. Для анонимного доступа, оставьте DN и пароль пустыми.",
@@ -16,22 +33,43 @@
"Group Filter" => "Фильтр группы",
"Defines the filter to apply, when retrieving groups." => "Определяет фильтр для применения при получении группы.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "без заполнения, например \"objectClass=posixGroup\".",
+"Connection Settings" => "Настройки подключения",
+"Configuration Active" => "Конфигурация активна",
+"When unchecked, this configuration will be skipped." => "Когда галочка снята, эта конфигурация будет пропущена.",
"Port" => "Порт",
-"Base User Tree" => "База пользовательского дерева",
-"Base Group Tree" => "База группового дерева",
-"Group-Member association" => "Ассоциация Группа-Участник",
+"Backup (Replica) Host" => "Адрес резервного сервера",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Укажите дополнительный резервный сервер. Он должен быть репликой главного LDAP/AD сервера.",
+"Backup (Replica) Port" => "Порт резервного сервера",
+"Disable Main Server" => "Отключение главного сервера",
+"When switched on, ownCloud will only connect to the replica server." => "Когда включено, ownCloud будет соединяться только с резервным сервером.",
"Use TLS" => "Использовать TLS",
-"Do not use it for SSL connections, it will fail." => "Не используйте для соединений SSL",
+"Do not use it additionally for LDAPS connections, it will fail." => "Не используйте совместно с безопасными подключениями (LDAPS), это не сработает.",
"Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру сервер LDAP (Windows)",
"Turn off SSL certificate validation." => "Отключить проверку сертификата SSL.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте на ваш сервер ownCloud сертификат SSL сервера LDAP.",
"Not recommended, use for testing only." => "Не рекомендуется, используйте только для тестирования.",
+"Cache Time-To-Live" => "Кэш времени жизни",
+"in seconds. A change empties the cache." => "в секундах. Изменение очистит кэш.",
+"Directory Settings" => "Настройки каталога",
"User Display Name Field" => "Поле отображаемого имени пользователя",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP для генерации имени пользователя ownCloud.",
+"Base User Tree" => "База пользовательского дерева",
+"One User Base DN per line" => "По одной базовому DN пользователей в строке.",
+"User Search Attributes" => "Поисковые атрибуты пользователя",
+"Optional; one attribute per line" => "Опционально; один атрибут на линию",
"Group Display Name Field" => "Поле отображаемого имени группы",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP для генерации имени группы ownCloud.",
+"Base Group Tree" => "База группового дерева",
+"One Group Base DN per line" => "По одной базовому DN групп в строке.",
+"Group Search Attributes" => "Атрибуты поиска для группы",
+"Group-Member association" => "Ассоциация Группа-Участник",
+"Special Attributes" => "Специальные атрибуты",
+"Quota Field" => "Поле квота",
+"Quota Default" => "Квота по умолчанию",
"in bytes" => "в байтах",
-"in seconds. A change empties the cache." => "в секундах. Изменение очистит кэш.",
+"Email Field" => "Поле адресса эллектронной почты",
+"User Home Folder Naming Rule" => "Правило именования Домашней Папки Пользователя",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте имя пользователя пустым (по умолчанию). Иначе укажите атрибут LDAP/AD.",
+"Test Configuration" => "Тестовая конфигурация",
"Help" => "Помощь"
);
diff --git a/apps/user_ldap/l10n/ru_RU.php b/apps/user_ldap/l10n/ru_RU.php
index 8433f39e83c..a4ed503b1d1 100644
--- a/apps/user_ldap/l10n/ru_RU.php
+++ b/apps/user_ldap/l10n/ru_RU.php
@@ -1,29 +1,42 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Удаление не удалось",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Предупреждение:</b> Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением системы. Пожалуйста, обратитесь к системному администратору для отключения одного из них.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Предупреждение:</b> Модуль PHP LDAP не установлен, бэкэнд не будет работать. Пожалуйста, обратитесь к Вашему системному администратору, чтобы установить его.",
"Host" => "Хост",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Вы можете пропустить протокол, если Вам не требуется SSL. Затем начните с ldaps://",
"Base DN" => "База DN",
+"One Base DN per line" => "Одно базовое DN на линию",
"You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп во вкладке «Дополнительно»",
"User DN" => "DN пользователя",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN клиентского пользователя, с которого должна осуществляться привязка, например, uid=agent,dc=example,dc=com. Для анонимного доступа оставьте поля DN и Пароль пустыми.",
"Password" => "Пароль",
"For anonymous access, leave DN and Password empty." => "Для анонимного доступа оставьте поля DN и пароль пустыми.",
"User Login Filter" => "Фильтр имен пользователей",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Задает фильтр, применяемый при загрузке пользователя. %%uid заменяет имя пользователя при входе.",
"use %%uid placeholder, e.g. \"uid=%%uid\"" => "используйте %%uid заполнитель, например, \"uid=%%uid\"",
+"User List Filter" => "Фильтр списка пользователей",
+"Defines the filter to apply, when retrieving users." => "Задает фильтр, применяемый при получении пользователей.",
"without any placeholder, e.g. \"objectClass=person\"." => "без каких-либо заполнителей, например, \"objectClass=person\".",
"Group Filter" => "Групповой фильтр",
+"Defines the filter to apply, when retrieving groups." => "Задает фильтр, применяемый при получении групп.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "без каких-либо заполнителей, например, \"objectClass=posixGroup\".",
"Port" => "Порт",
-"Base User Tree" => "Базовое дерево пользователей",
-"Group-Member association" => "Связь член-группа",
"Use TLS" => "Использовать TLS",
-"Do not use it for SSL connections, it will fail." => "Не используйте это SSL-соединений, это не будет выполнено.",
"Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру LDAP-сервер (Windows)",
"Turn off SSL certificate validation." => "Выключить проверку сертификата SSL.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте SSL-сертификат LDAP сервера в ваш ownCloud сервер.",
"Not recommended, use for testing only." => "Не рекомендовано, используйте только для тестирования.",
+"in seconds. A change empties the cache." => "в секундах. Изменение очищает кэш.",
"User Display Name Field" => "Поле, отображаемое как имя пользователя",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, используемый для создания имени пользователя в ownCloud.",
+"Base User Tree" => "Базовое дерево пользователей",
+"One User Base DN per line" => "Одно пользовательское базовое DN на линию",
+"Group Display Name Field" => "Поле, отображаемое как имя группы",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, используемый для создания группового имени в ownCloud.",
+"Base Group Tree" => "Базовое дерево групп",
+"One Group Base DN per line" => "Одно групповое базовое DN на линию",
+"Group-Member association" => "Связь член-группа",
"in bytes" => "в байтах",
-"in seconds. A change empties the cache." => "в секундах. Изменение очищает кэш.",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте пустым под имя пользователя (по умолчанию). В противном случае задайте LDAP/AD атрибут.",
"Help" => "Помощь"
);
diff --git a/apps/user_ldap/l10n/si_LK.php b/apps/user_ldap/l10n/si_LK.php
new file mode 100644
index 00000000000..50124e4d54f
--- /dev/null
+++ b/apps/user_ldap/l10n/si_LK.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "මකාදැමීම අසාර්ථකයි",
+"Host" => "සත්කාරකය",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL අවශ්‍යය වන විට පමණක් හැර, අන් අවස්ථාවන්හිදී ප්‍රොටොකෝලය අත් හැරිය හැක. භාවිතා කරන විට ldaps:// ලෙස ආරම්භ කරන්න",
+"Password" => "මුර පදය",
+"User Login Filter" => "පරිශීලක පිවිසුම් පෙරහන",
+"User List Filter" => "පරිශීලක ලැයිස්තු පෙරහන",
+"Group Filter" => "කණ්ඩායම් පෙරහන",
+"Defines the filter to apply, when retrieving groups." => "කණ්ඩායම් සොයා ලබාගන්නා විට, යොදන පෙරහන නියම කරයි",
+"Port" => "තොට",
+"Use TLS" => "TLS භාවිතා කරන්න",
+"Not recommended, use for testing only." => "නිර්දේශ කළ නොහැක. පරීක්ෂණ සඳහා පමණක් භාවිත කරන්න",
+"Help" => "උදව්"
+);
diff --git a/apps/user_ldap/l10n/sk_SK.php b/apps/user_ldap/l10n/sk_SK.php
new file mode 100644
index 00000000000..cb55762e64f
--- /dev/null
+++ b/apps/user_ldap/l10n/sk_SK.php
@@ -0,0 +1,75 @@
+<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Zlyhalo zmazanie nastavenia servera.",
+"The configuration is valid and the connection could be established!" => "Nastavenie je v poriadku a pripojenie je stabilné.",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Nastavenie je v poriadku, ale pripojenie zlyhalo. Skontrolujte nastavenia servera a prihlasovacie údaje.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Nastavenia sú neplatné. Podrobnosti hľadajte v logu ownCloud.",
+"Deletion failed" => "Odstránenie zlyhalo",
+"Take over settings from recent server configuration?" => "Prebrať nastavenia z nedávneho nastavenia servera?",
+"Keep settings?" => "Ponechať nastavenia?",
+"Cannot add server configuration" => "Nemožno pridať nastavenie servera",
+"Connection test succeeded" => "Test pripojenia bol úspešný",
+"Connection test failed" => "Test pripojenia zlyhal",
+"Do you really want to delete the current Server Configuration?" => "Naozaj chcete zmazať súčasné nastavenie servera?",
+"Confirm Deletion" => "Potvrdiť vymazanie",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Upozornenie:</b> Aplikácie user_ldap a user_webdavauth nie sú kompatibilné. Môže nastávať neočakávané správanie. Požiadajte administrátora systému aby jednu z nich zakázal.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Upozornenie:</b> nie je nainštalovaný LDAP modul pre PHP, backend vrstva nebude fungovať. Požádejte administrátora systému aby ho nainštaloval.",
+"Server configuration" => "Nastavenia servera",
+"Add Server Configuration" => "Pridať nastavenia servera.",
+"Host" => "Hostiteľ",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Môžete vynechať protokol, s výnimkou požadovania SSL. Vtedy začnite s ldaps://",
+"Base DN" => "Základné DN",
+"One Base DN per line" => "Jedno základné DN na riadok",
+"You can specify Base DN for users and groups in the Advanced tab" => "V rozšírenom nastavení môžete zadať základné DN pre používateľov a skupiny",
+"User DN" => "Používateľské DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klientského používateľa, ku ktorému tvoríte väzbu, napr. uid=agent,dc=example,dc=com. Pre anonymný prístup ponechajte údaje DN a Heslo prázdne.",
+"Password" => "Heslo",
+"For anonymous access, leave DN and Password empty." => "Pre anonymný prístup ponechajte údaje DN a Heslo prázdne.",
+"User Login Filter" => "Filter prihlásenia používateľov",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Určuje použitý filter, pri pokuse o prihlásenie. %%uid nahradzuje používateľské meno v činnosti prihlásenia.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "použite zástupný vzor %%uid, napr. \\\"uid=%%uid\\\"",
+"User List Filter" => "Filter zoznamov používateľov",
+"Defines the filter to apply, when retrieving users." => "Definuje použitý filter, pre získanie používateľov.",
+"without any placeholder, e.g. \"objectClass=person\"." => "bez zástupných znakov, napr. \"objectClass=person\"",
+"Group Filter" => "Filter skupiny",
+"Defines the filter to apply, when retrieving groups." => "Definuje použitý filter, pre získanie skupín.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez zástupných znakov, napr. \"objectClass=posixGroup\"",
+"Connection Settings" => "Nastavenie pripojenia",
+"Configuration Active" => "Nastavenia sú aktívne ",
+"When unchecked, this configuration will be skipped." => "Ak nie je zaškrtnuté, nastavenie bude preskočené.",
+"Port" => "Port",
+"Backup (Replica) Host" => "Záložný server (kópia) hosť",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Zadajte záložný LDAP/AD. Musí to byť kópia hlavného LDAP/AD servera.",
+"Backup (Replica) Port" => "Záložný server (kópia) port",
+"Disable Main Server" => "Zakázať hlavný server",
+"When switched on, ownCloud will only connect to the replica server." => "Pri zapnutí sa ownCloud pripojí len k záložnému serveru.",
+"Use TLS" => "Použi TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Nepoužívajte pre pripojenie LDAPS, zlyhá.",
+"Case insensitve LDAP server (Windows)" => "LDAP server nerozlišuje veľkosť znakov (Windows)",
+"Turn off SSL certificate validation." => "Vypnúť overovanie SSL certifikátu.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ak pripojenie pracuje len s touto možnosťou, tak importujte SSL certifikát LDAP serveru do vášho servera ownCloud.",
+"Not recommended, use for testing only." => "Nie je doporučované, len pre testovacie účely.",
+"Cache Time-To-Live" => "Životnosť objektov v cache",
+"in seconds. A change empties the cache." => "v sekundách. Zmena vyprázdni vyrovnávaciu pamäť.",
+"Directory Settings" => "Nastavenie priečinka",
+"User Display Name Field" => "Pole pre zobrazenia mena používateľa",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribút LDAP použitý na vygenerovanie mena používateľa ownCloud ",
+"Base User Tree" => "Základný používateľský strom",
+"One User Base DN per line" => "Jedna používateľská základná DN na riadok",
+"User Search Attributes" => "Atribúty vyhľadávania používateľov",
+"Optional; one attribute per line" => "Voliteľné, jeden atribút na jeden riadok",
+"Group Display Name Field" => "Pole pre zobrazenie mena skupiny",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribút LDAP použitý na vygenerovanie mena skupiny ownCloud ",
+"Base Group Tree" => "Základný skupinový strom",
+"One Group Base DN per line" => "Jedna skupinová základná DN na riadok",
+"Group Search Attributes" => "Atribúty vyhľadávania skupín",
+"Group-Member association" => "Priradenie člena skupiny",
+"Special Attributes" => "Špeciálne atribúty",
+"Quota Field" => "Pole kvóty",
+"Quota Default" => "Predvolená kvóta",
+"in bytes" => "v bajtoch",
+"Email Field" => "Pole email",
+"User Home Folder Naming Rule" => "Pravidlo pre nastavenie mena používateľského priečinka dát",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Nechajte prázdne pre používateľské meno (predvolené). Inak uveďte atribút LDAP/AD.",
+"Test Configuration" => "Test nastavenia",
+"Help" => "Pomoc"
+);
diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php
index fd28b640156..8ff1fd53440 100644
--- a/apps/user_ldap/l10n/sl.php
+++ b/apps/user_ldap/l10n/sl.php
@@ -1,37 +1,75 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Brisanje nastavitev strežnika je spodletelo.",
+"The configuration is valid and the connection could be established!" => "Nastavitev je veljavna, zato je povezavo mogoče vzpostaviti!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Nastavitev je veljavna, vendar pa je vez Bind spodletela. Preveriti je treba nastavitve strežnika in ustreznost poveril.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Nastavitev je veljavna. Več podrobnosti je zapisanih v dnevniku ownCloud.",
+"Deletion failed" => "Brisanje je spodletelo.",
+"Take over settings from recent server configuration?" => "Ali naj se prevzame nastavitve nedavne nastavitve strežnika?",
+"Keep settings?" => "Ali nas se nastavitve ohranijo?",
+"Cannot add server configuration" => "Ni mogoče dodati nastavitev strežnika",
+"Connection test succeeded" => "Preizkus povezave je uspešno končan.",
+"Connection test failed" => "Preizkus povezave je spodletel.",
+"Do you really want to delete the current Server Configuration?" => "Ali res želite izbrisati trenutne nastavitve strežnika?",
+"Confirm Deletion" => "Potrdi brisanje",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Opozorilo:</b> možnosti user_ldap in user_webdavauth nista združljivi. Pri uporabi je mogoče nepričakovano obnašanje sistema. Eno izmed možnosti je priporočeno onemgočiti.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Opozorilo:</b> modul PHP LDAP mora biti nameščen, sicer vmesnik ne bo deloval. Paket je treba namestiti.",
+"Server configuration" => "Nastavitev strežnika",
+"Add Server Configuration" => "Dodaj nastavitve strežnika",
"Host" => "Gostitelj",
-"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol lahko izpustite, razen če zahtevate SSL. V tem primeru začnite z ldaps://",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol je lahko izpuščen, če ni posebej zahtevan SSL. V tem primeru se mora naslov začeti z ldaps://",
"Base DN" => "Osnovni DN",
-"You can specify Base DN for users and groups in the Advanced tab" => "Osnovni DN za uporabnike in skupine lahko določite v zavihku Napredno",
+"One Base DN per line" => "En osnovni DN na vrstico",
+"You can specify Base DN for users and groups in the Advanced tab" => "Osnovni DN za uporabnike in skupine lahko določite v zavihku naprednih možnosti.",
"User DN" => "Uporabnik DN",
-"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN uporabnikovega odjemalca, s katerim naj se opravi vezava, npr. uid=agent,dc=example,dc=com. Za anonimni dostop pustite polji DN in geslo prazni.",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN uporabnikovega odjemalca, s katerim naj se opravi vezava, npr. uid=agent,dc=example,dc=com. Za brezimni dostop sta polji DN in geslo prazni.",
"Password" => "Geslo",
-"For anonymous access, leave DN and Password empty." => "Za anonimni dostop pustite polji DN in geslo prazni.",
+"For anonymous access, leave DN and Password empty." => "Za brezimni dostop sta polji DN in geslo prazni.",
"User Login Filter" => "Filter prijav uporabnikov",
-"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Določi filter uporabljen pri prijavi. %%uid nadomesti uporaniško ime pri prijavi.",
-"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Uporabite ogrado %%uid, npr. \"uid=%%uid\".",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Določi filter, uporabljen pri prijavi. %%uid nadomesti uporabniško ime v postopku prijave.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Uporabite vsebnik %%uid, npr. \"uid=%%uid\".",
"User List Filter" => "Filter seznama uporabnikov",
"Defines the filter to apply, when retrieving users." => "Določi filter za uporabo med pridobivanjem uporabnikov.",
-"without any placeholder, e.g. \"objectClass=person\"." => "Brez katerekoli ograde, npr. \"objectClass=person\".",
+"without any placeholder, e.g. \"objectClass=person\"." => "Brez kateregakoli vsebnika, npr. \"objectClass=person\".",
"Group Filter" => "Filter skupin",
"Defines the filter to apply, when retrieving groups." => "Določi filter za uporabo med pridobivanjem skupin.",
-"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Brez katerekoli ograde, npr. \"objectClass=posixGroup\".",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Brez katerekoli vsebnika, npr. \"objectClass=posixGroup\".",
+"Connection Settings" => "Nastavitve povezave",
+"Configuration Active" => "Dejavna nastavitev",
+"When unchecked, this configuration will be skipped." => "Neizbrana možnost preskoči nastavitev.",
"Port" => "Vrata",
-"Base User Tree" => "Osnovno uporabniško drevo",
-"Base Group Tree" => "Osnovno drevo skupine",
-"Group-Member association" => "Povezava člana skupine",
+"Backup (Replica) Host" => "Varnostna kopija (replika) podatkov gostitelja",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Podati je treba izbirno varnostno kopijo gostitelja. Ta mora biti natančna replika strežnika LDAP/AD.",
+"Backup (Replica) Port" => "Varnostna kopija (replika) podatka vrat",
+"Disable Main Server" => "Onemogoči glavni strežnik",
+"When switched on, ownCloud will only connect to the replica server." => "Ob priklopu bo strežnik ownCloud povezan le s kopijo (repliko) strežnika.",
"Use TLS" => "Uporabi TLS",
-"Do not use it for SSL connections, it will fail." => "Ne uporabljajte ga za SSL povezave, saj ne bo delovalo.",
-"Case insensitve LDAP server (Windows)" => "LDAP strežnik je neobčutljiv na velikost črk (Windows)",
-"Turn off SSL certificate validation." => "Onemogoči potrditev veljavnosti SSL certifikata.",
-"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Če povezava deluje samo s to možnostjo, uvozite SSL potrdilo iz LDAP strežnika v vaš ownCloud strežnik.",
-"Not recommended, use for testing only." => "Odsvetovano, uporabite le v namene preizkušanja.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Strežnika ni priporočljivo uporabljati za povezave LDAPS. Povezava bo spodletela.",
+"Case insensitve LDAP server (Windows)" => "Strežnik LDAP ne upošteva velikosti črk (Windows)",
+"Turn off SSL certificate validation." => "Onemogoči določanje veljavnosti potrdila SSL.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Kadar deluje povezava le s to možnostjo, uvozite potrdilo SSL iz strežnika LDAP na vaš strežnik ownCloud.",
+"Not recommended, use for testing only." => "Dejanje ni priporočeno; uporabljeno naj bo le za preizkušanje delovanja.",
+"Cache Time-To-Live" => "Predpomni podatke TTL",
+"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.",
+"Directory Settings" => "Nastavitve mape",
"User Display Name Field" => "Polje za uporabnikovo prikazano ime",
-"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud uporabniških imen.",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP, uporabljen pri ustvarjanju uporabniških imen ownCloud.",
+"Base User Tree" => "Osnovno uporabniško drevo",
+"One User Base DN per line" => "Eno osnovno uporabniško ime DN na vrstico",
+"User Search Attributes" => "Uporabi atribute iskanja",
+"Optional; one attribute per line" => "Izbirno; en atribut na vrstico",
"Group Display Name Field" => "Polje za prikazano ime skupine",
-"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud imen skupin.",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP, uporabljen pri ustvarjanju imen skupin ownCloud.",
+"Base Group Tree" => "Osnovno drevo skupine",
+"One Group Base DN per line" => "Eno osnovno ime skupine DN na vrstico",
+"Group Search Attributes" => "Atributi iskanja skupine",
+"Group-Member association" => "Povezava član-skupina",
+"Special Attributes" => "Posebni atributi",
+"Quota Field" => "Polje količinske omejitve",
+"Quota Default" => "Privzeta količinska omejitev",
"in bytes" => "v bajtih",
-"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.",
-"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pustite prazno za uporabniško ime (privzeto). V nasprotnem primeru navedite LDAP/AD atribut.",
+"Email Field" => "Polje elektronske pošte",
+"User Home Folder Naming Rule" => "Pravila poimenovanja uporabniške osebne mape",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pustite prazno za uporabniško ime (privzeto), sicer navedite atribut LDAP/AD.",
+"Test Configuration" => "Preizkusne nastavitve",
"Help" => "Pomoč"
);
diff --git a/apps/user_ldap/l10n/sr.php b/apps/user_ldap/l10n/sr.php
new file mode 100644
index 00000000000..52569a08ef8
--- /dev/null
+++ b/apps/user_ldap/l10n/sr.php
@@ -0,0 +1,35 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Брисање није успело",
+"Host" => "Домаћин",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можете да изоставите протокол, осим ако захтевате SSL. У том случају почните са ldaps://.",
+"Base DN" => "База DN",
+"User DN" => "Корисник DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN корисника клијента са којим треба да се успостави веза, нпр. uid=agent,dc=example,dc=com. За анониман приступ, оставите поља DN и лозинка празним.",
+"Password" => "Лозинка",
+"For anonymous access, leave DN and Password empty." => "За анониман приступ, оставите поља DN и лозинка празним.",
+"User Login Filter" => "Филтер за пријаву корисника",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Одређује филтер за примењивање при покушају пријаве. %%uid замењује корисничко име.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "користите чувар места %%uid, нпр. „uid=%%uid\"",
+"User List Filter" => "Филтер за списак корисника",
+"Defines the filter to apply, when retrieving users." => "Одређује филтер за примењивање при прибављању корисника.",
+"without any placeholder, e.g. \"objectClass=person\"." => "без икаквог чувара места, нпр. „objectClass=person“.",
+"Group Filter" => "Филтер групе",
+"Defines the filter to apply, when retrieving groups." => "Одређује филтер за примењивање при прибављању група.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "без икаквог чувара места, нпр. „objectClass=posixGroup“.",
+"Port" => "Порт",
+"Use TLS" => "Користи TLS",
+"Case insensitve LDAP server (Windows)" => "LDAP сервер осетљив на велика и мала слова (Windows)",
+"Turn off SSL certificate validation." => "Искључите потврду SSL сертификата.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Увезите SSL сертификат LDAP сервера у свој ownCloud ако веза ради само са овом опцијом.",
+"Not recommended, use for testing only." => "Не препоручује се; користите само за тестирање.",
+"in seconds. A change empties the cache." => "у секундама. Промена испражњава кеш меморију.",
+"User Display Name Field" => "Име приказа корисника",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP атрибут за стварање имена ownCloud-а корисника.",
+"Base User Tree" => "Основно стабло корисника",
+"Group Display Name Field" => "Име приказа групе",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP атрибут за стварање имена ownCloud-а групе.",
+"Base Group Tree" => "Основна стабло група",
+"Group-Member association" => "Придруживање чланова у групу",
+"in bytes" => "у бајтовима",
+"Help" => "Помоћ"
+);
diff --git a/apps/user_ldap/l10n/sr@latin.php b/apps/user_ldap/l10n/sr@latin.php
new file mode 100644
index 00000000000..91503315066
--- /dev/null
+++ b/apps/user_ldap/l10n/sr@latin.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"Help" => "Pomoć"
+);
diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php
index 2a0abb92967..12ecc7b1633 100644
--- a/apps/user_ldap/l10n/sv.php
+++ b/apps/user_ldap/l10n/sv.php
@@ -1,7 +1,24 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Misslyckades med att radera serverinställningen",
+"The configuration is valid and the connection could be established!" => "Inställningen är giltig och anslutningen kunde upprättas!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurationen är riktig, men Bind felade. Var vänlig och kontrollera serverinställningar och logininformation.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Inställningen är ogiltig. Vänligen se ownCloud-loggen för fler detaljer.",
+"Deletion failed" => "Raderingen misslyckades",
+"Take over settings from recent server configuration?" => "Ta över inställningar från tidigare serverkonfiguration?",
+"Keep settings?" => "Behåll inställningarna?",
+"Cannot add server configuration" => "Kunde inte lägga till serverinställning",
+"Connection test succeeded" => "Anslutningstestet lyckades",
+"Connection test failed" => "Anslutningstestet misslyckades",
+"Do you really want to delete the current Server Configuration?" => "Vill du verkligen radera den nuvarande serverinställningen?",
+"Confirm Deletion" => "Bekräfta radering",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Varning:</b> Apps user_ldap och user_webdavauth är inkompatibla. Oväntade problem kan uppstå. Be din systemadministratör att inaktivera en av dom.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Varning:</b> PHP LDAP - modulen är inte installerad, serversidan kommer inte att fungera. Kontakta din systemadministratör för installation.",
+"Server configuration" => "Serverinställning",
+"Add Server Configuration" => "Lägg till serverinställning",
"Host" => "Server",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du behöver inte ange protokoll förutom om du använder SSL. Starta då med ldaps://",
"Base DN" => "Start DN",
+"One Base DN per line" => "Ett Start DN per rad",
"You can specify Base DN for users and groups in the Advanced tab" => "Du kan ange start DN för användare och grupper under fliken Avancerat",
"User DN" => "Användare DN",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN för användaren som skall användas, t.ex. uid=agent, dc=example, dc=com. För anonym åtkomst, lämna DN och lösenord tomt.",
@@ -16,22 +33,39 @@
"Group Filter" => "Gruppfilter",
"Defines the filter to apply, when retrieving groups." => "Definierar filter att tillämpa vid listning av grupper.",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "utan platshållare, t.ex. \"objectClass=posixGroup\".",
+"Connection Settings" => "Uppkopplingsinställningar",
+"Configuration Active" => "Konfiguration aktiv",
+"When unchecked, this configuration will be skipped." => "Ifall denna är avbockad så kommer konfigurationen att skippas.",
"Port" => "Port",
-"Base User Tree" => "Bas för användare i katalogtjänst",
-"Base Group Tree" => "Bas för grupper i katalogtjänst",
-"Group-Member association" => "Attribut för gruppmedlemmar",
+"Backup (Replica) Host" => "Säkerhetskopierings-värd (Replika)",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Ange en valfri värd för säkerhetskopiering. Den måste vara en replika av den huvudsakliga LDAP/AD-servern",
+"Backup (Replica) Port" => "Säkerhetskopierins-port (Replika)",
+"Disable Main Server" => "Inaktivera huvudserver",
+"When switched on, ownCloud will only connect to the replica server." => "När denna är påkopplad kommer ownCloud att koppla upp till replika-servern, endast.",
"Use TLS" => "Använd TLS",
-"Do not use it for SSL connections, it will fail." => "Använd inte för SSL-anslutningar, det kommer inte att fungera.",
+"Do not use it additionally for LDAPS connections, it will fail." => "Använd inte för LDAPS-anslutningar, det kommer inte att fungera.",
"Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)",
"Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Om anslutningen bara fungerar med det här alternativet, importera LDAP-serverns SSL-certifikat i din ownCloud-server.",
"Not recommended, use for testing only." => "Rekommenderas inte, använd bara för test. ",
+"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.",
+"Directory Settings" => "Mappinställningar",
"User Display Name Field" => "Attribut för användarnamn",
"The LDAP attribute to use to generate the user`s ownCloud name." => "Attribut som används för att generera användarnamn i ownCloud.",
+"Base User Tree" => "Bas för användare i katalogtjänst",
+"One User Base DN per line" => "En Användare start DN per rad",
+"User Search Attributes" => "Användarsökningsattribut",
+"Optional; one attribute per line" => "Valfritt; ett attribut per rad",
"Group Display Name Field" => "Attribut för gruppnamn",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "Attribut som används för att generera gruppnamn i ownCloud.",
+"Base Group Tree" => "Bas för grupper i katalogtjänst",
+"One Group Base DN per line" => "En Grupp start DN per rad",
+"Group Search Attributes" => "Gruppsökningsattribut",
+"Group-Member association" => "Attribut för gruppmedlemmar",
+"Special Attributes" => "Specialattribut",
+"Quota Field" => "Kvotfält",
"in bytes" => "i bytes",
-"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.",
+"Email Field" => "E-postfält",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lämnas tomt för användarnamn (standard). Ange annars ett LDAP/AD-attribut.",
"Help" => "Hjälp"
);
diff --git a/apps/user_ldap/l10n/ta_LK.php b/apps/user_ldap/l10n/ta_LK.php
new file mode 100644
index 00000000000..f6beb3c4863
--- /dev/null
+++ b/apps/user_ldap/l10n/ta_LK.php
@@ -0,0 +1,27 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "நீக்கம் தோல்வியடைந்தது",
+"Host" => "ஓம்புனர்",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "நீங்கள் SSL சேவையை தவிர உடன்படு வரைமுறையை தவிர்க்க முடியும். பிறகு ldaps:.// உடன் ஆரம்பிக்கவும்",
+"Base DN" => "தள DN",
+"You can specify Base DN for users and groups in the Advanced tab" => "நீங்கள் பயனாளர்களுக்கும் மேன்மை தத்தலில் உள்ள குழுவிற்கும் தள DN ஐ குறிப்பிடலாம் ",
+"User DN" => "பயனாளர் DN",
+"Password" => "கடவுச்சொல்",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "எந்த ஒதுக்கீடும் இல்லாமல், உதாரணம். \"objectClass=posixGroup\".",
+"Port" => "துறை ",
+"Use TLS" => "TLS ஐ பயன்படுத்தவும்",
+"Case insensitve LDAP server (Windows)" => "உணர்ச்சியான LDAP சேவையகம் (சாளரங்கள்)",
+"Turn off SSL certificate validation." => "SSL சான்றிதழின் செல்லுபடியை நிறுத்திவிடவும்",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "இந்த தெரிவுகளில் மட்டும் இணைப்பு வேலைசெய்தால், உங்களுடைய owncloud சேவையகத்திலிருந்து LDAP சேவையகத்தின் SSL சான்றிதழை இறக்குமதி செய்யவும்",
+"Not recommended, use for testing only." => "பரிந்துரைக்கப்படவில்லை, சோதனைக்காக மட்டும் பயன்படுத்தவும்.",
+"in seconds. A change empties the cache." => "செக்கன்களில். ஒரு மாற்றம் இடைமாற்றுநினைவகத்தை வெற்றிடமாக்கும்.",
+"User Display Name Field" => "பயனாளர் காட்சிப்பெயர் புலம்",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "பயனாளரின் ownCloud பெயரை உருவாக்க LDAP பண்புக்கூறை பயன்படுத்தவும்.",
+"Base User Tree" => "தள பயனாளர் மரம்",
+"Group Display Name Field" => "குழுவின் காட்சி பெயர் புலம் ",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "ownCloud குழுக்களின் பெயர்களை உருவாக்க LDAP பண்புக்கூறை பயன்படுத்தவும்.",
+"Base Group Tree" => "தள குழு மரம்",
+"Group-Member association" => "குழு உறுப்பினர் சங்கம்",
+"in bytes" => "bytes களில் ",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "பயனாளர் பெயரிற்கு வெற்றிடமாக விடவும் (பொது இருப்பு). இல்லாவிடின் LDAP/AD பண்புக்கூறை குறிப்பிடவும்.",
+"Help" => "உதவி"
+);
diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php
index acc7a4936bc..802badb2f03 100644
--- a/apps/user_ldap/l10n/th_TH.php
+++ b/apps/user_ldap/l10n/th_TH.php
@@ -1,7 +1,23 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "การลบการกำหนดค่าเซิร์ฟเวอร์ล้มเหลว",
+"The configuration is valid and the connection could be established!" => "การกำหนดค่าถูกต้องและการเชื่อมต่อสามารถเชื่อมต่อได้!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "การกำหนดค่าถูกต้อง, แต่การผูกข้อมูลล้มเหลว, กรุณาตรวจสอบการตั้งค่าเซิร์ฟเวอร์และข้อมูลการเข้าใช้งาน",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "การกำหนดค่าไม่ถูกต้อง กรุณาดูรายละเอียดจากบันทึกการเปลี่ยนแปลงของ ownCloud สำหรับรายละเอียดเพิ่มเติม",
+"Deletion failed" => "การลบทิ้งล้มเหลว",
+"Keep settings?" => "รักษาการตั้งค่าไว้?",
+"Cannot add server configuration" => "ไม่สามารถเพิ่มค่ากำหนดเซิร์ฟเวอร์ได้",
+"Connection test succeeded" => "ทดสอบการเชื่อมต่อสำเร็จ",
+"Connection test failed" => "ทดสอบการเชื่อมต่อล้มเหลว",
+"Do you really want to delete the current Server Configuration?" => "คุณแน่ใจแล้วหรือว่าต้องการลบการกำหนดค่าเซิร์ฟเวอร์ปัจจุบันทิ้งไป?",
+"Confirm Deletion" => "ยืนยันการลบทิ้ง",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>คำเตือน:</b> แอปฯ user_ldap และ user_webdavauth ไม่สามารถใช้งานร่วมกันได้. คุณอาจประสพปัญหาที่ไม่คาดคิดจากเหตุการณ์ดังกล่าว กรุณาติดต่อผู้ดูแลระบบของคุณเพื่อระงับการใช้งานแอปฯ ตัวใดตัวหนึ่งข้างต้น",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>คำเตือน:</b> โมดูล PHP LDAP ยังไม่ได้ถูกติดตั้ง, ระบบด้านหลังจะไม่สามารถทำงานได้ กรุณาติดต่อผู้ดูแลระบบของคุณเพื่อทำการติดตั้งโมดูลดังกล่าว",
+"Server configuration" => "การกำหนดค่าเซิร์ฟเวอร์",
+"Add Server Configuration" => "เพิ่มการกำหนดค่าเซิร์ฟเวอร์",
"Host" => "โฮสต์",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "คุณสามารถปล่อยช่องโปรโตคอลเว้นไว้ได้, ยกเว้นกรณีที่คุณต้องการใช้ SSL จากนั้นเริ่มต้นด้วย ldaps://",
"Base DN" => "DN ฐาน",
+"One Base DN per line" => "หนึ่ง Base DN ต่อบรรทัด",
"You can specify Base DN for users and groups in the Advanced tab" => "คุณสามารถระบุ DN หลักสำหรับผู้ใช้งานและกลุ่มต่างๆในแท็บขั้นสูงได้",
"User DN" => "DN ของผู้ใช้งาน",
"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN ของผู้ใช้งานที่เป็นลูกค้าอะไรก็ตามที่ผูกอยู่ด้วย เช่น uid=agent, dc=example, dc=com, สำหรับการเข้าถึงโดยบุคคลนิรนาม, ให้เว้นว่าง DN และ รหัสผ่านเอาไว้",
@@ -16,22 +32,30 @@
"Group Filter" => "ตัวกรองข้อมูลกลุ่ม",
"Defines the filter to apply, when retrieving groups." => "ระบุตัวกรองข้อมูลที่ต้องการนำไปใช้งาน, เมื่อดึงข้อมูลกลุ่ม",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "โดยไม่ต้องมีตัวยึดใดๆ, เช่น \"objectClass=posixGroup\",",
+"Connection Settings" => "ตั้งค่าการเชื่อมต่อ",
"Port" => "พอร์ต",
-"Base User Tree" => "รายการผู้ใช้งานหลักแบบ Tree",
-"Base Group Tree" => "รายการกลุ่มหลักแบบ Tree",
-"Group-Member association" => "ความสัมพันธ์ของสมาชิกในกลุ่ม",
+"Disable Main Server" => "ปิดใช้งานเซิร์ฟเวอร์หลัก",
"Use TLS" => "ใช้ TLS",
-"Do not use it for SSL connections, it will fail." => "กรุณาอย่าใช้การเชื่อมต่อแบบ SSL การเชื่อมต่อจะเกิดการล้มเหลว",
"Case insensitve LDAP server (Windows)" => "เซิร์ฟเวอร์ LDAP ประเภท Case insensitive (วินโดวส์)",
"Turn off SSL certificate validation." => "ปิดใช้งานการตรวจสอบความถูกต้องของใบรับรองความปลอดภัย SSL",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "หากการเชื่อมต่อสามารถทำงานได้เฉพาะกับตัวเลือกนี้เท่านั้น, ให้นำเข้าข้อมูลใบรับรองความปลอดภัยแบบ SSL ของเซิร์ฟเวอร์ LDAP ดังกล่าวเข้าไปไว้ในเซิร์ฟเวอร์ ownCloud",
"Not recommended, use for testing only." => "ไม่แนะนำให้ใช้งาน, ใช้สำหรับการทดสอบเท่านั้น",
+"in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า",
+"Directory Settings" => "ตั้งค่าไดเร็กทอรี่",
"User Display Name Field" => "ช่องแสดงชื่อผู้ใช้งานที่ต้องการ",
"The LDAP attribute to use to generate the user`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สำหรับสร้างชื่อของผู้ใช้งาน ownCloud",
+"Base User Tree" => "รายการผู้ใช้งานหลักแบบ Tree",
+"One User Base DN per line" => "หนึ่ง User Base DN ต่อบรรทัด",
+"User Search Attributes" => "คุณลักษณะการค้นหาชื่อผู้ใช้",
+"Optional; one attribute per line" => "ตัวเลือกเพิ่มเติม; หนึ่งคุณลักษณะต่อบรรทัด",
"Group Display Name Field" => "ช่องแสดงชื่อกลุ่มที่ต้องการ",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สร้างชื่อกลุ่มของ ownCloud",
+"Base Group Tree" => "รายการกลุ่มหลักแบบ Tree",
+"One Group Base DN per line" => "หนึ่ง Group Base DN ต่อบรรทัด",
+"Group Search Attributes" => "คุณลักษณะการค้นหาแบบกลุ่ม",
+"Group-Member association" => "ความสัมพันธ์ของสมาชิกในกลุ่ม",
+"Special Attributes" => "คุณลักษณะพิเศษ",
"in bytes" => "ในหน่วยไบต์",
-"in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "เว้นว่างไว้สำหรับ ชื่อผู้ใช้ (ค่าเริ่มต้น) หรือไม่กรุณาระบุคุณลักษณะของ LDAP/AD",
"Help" => "ช่วยเหลือ"
);
diff --git a/apps/user_ldap/l10n/tr.php b/apps/user_ldap/l10n/tr.php
new file mode 100644
index 00000000000..7bcabb0448a
--- /dev/null
+++ b/apps/user_ldap/l10n/tr.php
@@ -0,0 +1,30 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "Silme başarısız oldu",
+"Keep settings?" => "Ayarları kalsınmı?",
+"Connection test succeeded" => "Bağlantı testi başarılı oldu",
+"Connection test failed" => "Bağlantı testi başarısız oldu",
+"Confirm Deletion" => "Silmeyi onayla",
+"Host" => "Sunucu",
+"Base DN" => "Ana DN",
+"User DN" => "Kullanıcı DN",
+"Password" => "Parola",
+"For anonymous access, leave DN and Password empty." => "Anonim erişim için DN ve Parola alanlarını boş bırakın.",
+"User Login Filter" => "Kullanıcı Oturum Filtresi",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid yer tutucusunu kullanın, örneğin \"uid=%%uid\"",
+"User List Filter" => "Kullanıcı Liste Filtresi",
+"without any placeholder, e.g. \"objectClass=person\"." => "bir yer tutucusu olmadan, örneğin \"objectClass=person\"",
+"Group Filter" => "Grup Süzgeci",
+"Connection Settings" => "Bağlantı ayarları",
+"Port" => "Port",
+"Disable Main Server" => "Ana sunucuyu devredışı birak",
+"Use TLS" => "TLS kullan",
+"Turn off SSL certificate validation." => "SSL sertifika doğrulamasını kapat.",
+"Not recommended, use for testing only." => "Önerilmez, sadece test için kullanın.",
+"in seconds. A change empties the cache." => "saniye cinsinden. Bir değişiklik önbelleği temizleyecektir.",
+"Base User Tree" => "Temel Kullanıcı Ağacı",
+"Base Group Tree" => "Temel Grup Ağacı",
+"Group-Member association" => "Grup-Üye işbirliği",
+"in bytes" => "byte cinsinden",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kullanıcı adı bölümünü boş bırakın (varsayılan). ",
+"Help" => "Yardım"
+);
diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php
index fd6a88d2372..623d34c98e6 100644
--- a/apps/user_ldap/l10n/uk.php
+++ b/apps/user_ldap/l10n/uk.php
@@ -1,4 +1,75 @@
<?php $TRANSLATIONS = array(
+"Failed to delete the server configuration" => "Не вдалося видалити конфігурацію сервера",
+"The configuration is valid and the connection could be established!" => "Конфігурація вірна і зв'язок може бути встановлений ​​!",
+"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Конфігурація вірна, але встановити зв'язок не вдалося. Будь ласка, перевірте налаштування сервера і облікові дані.",
+"The configuration is invalid. Please look in the ownCloud log for further details." => "Конфігурація невірна. Подробиці подивіться, будь ласка, в журналі ownCloud.",
+"Deletion failed" => "Видалення не було виконано",
+"Take over settings from recent server configuration?" => "Застосувати налаштування з останньої конфігурації сервера ?",
+"Keep settings?" => "Зберегти налаштування ?",
+"Cannot add server configuration" => "Неможливо додати конфігурацію сервера",
+"Connection test succeeded" => "Перевірка з'єднання пройшла успішно",
+"Connection test failed" => "Перевірка з'єднання завершилась неуспішно",
+"Do you really want to delete the current Server Configuration?" => "Ви дійсно бажаєте видалити поточну конфігурацію сервера ?",
+"Confirm Deletion" => "Підтвердіть Видалення",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Увага:</b> Застосунки user_ldap та user_webdavauth не сумісні. Ви можете зіткнутися з несподіваною поведінкою. Будь ласка, зверніться до системного адміністратора, щоб відключити одну з них.",
+"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Увага:</ b> Потрібний модуль PHP LDAP не встановлено, базова програма працювати не буде. Будь ласка, зверніться до системного адміністратора, щоб встановити його.",
+"Server configuration" => "Налаштування Сервера",
+"Add Server Configuration" => "Додати налаштування Сервера",
+"Host" => "Хост",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можна не вказувати протокол, якщо вам не потрібен SSL. Тоді почніть з ldaps://",
+"Base DN" => "Базовий DN",
+"One Base DN per line" => "Один Base DN на одній строчці",
+"You can specify Base DN for users and groups in the Advanced tab" => "Ви можете задати Базовий DN для користувачів і груп на вкладинці Додатково",
+"User DN" => "DN Користувача",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN клієнтського користувача для прив'язки, наприклад: uid=agent,dc=example,dc=com. Для анонімного доступу, залиште DN і Пароль порожніми.",
"Password" => "Пароль",
+"For anonymous access, leave DN and Password empty." => "Для анонімного доступу, залиште DN і Пароль порожніми.",
+"User Login Filter" => "Фільтр Користувачів, що під'єднуються",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Визначає фільтр, який застосовується при спробі входу. %%uid замінює ім'я користувача при вході.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "використовуйте %%uid заповнювач, наприклад: \"uid=%%uid\"",
+"User List Filter" => "Фільтр Списку Користувачів",
+"Defines the filter to apply, when retrieving users." => "Визначає фільтр, який застосовується при отриманні користувачів",
+"without any placeholder, e.g. \"objectClass=person\"." => "без будь-якого заповнювача, наприклад: \"objectClass=person\".",
+"Group Filter" => "Фільтр Груп",
+"Defines the filter to apply, when retrieving groups." => "Визначає фільтр, який застосовується при отриманні груп.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "без будь-якого заповнювача, наприклад: \"objectClass=posixGroup\".",
+"Connection Settings" => "Налаштування З'єднання",
+"Configuration Active" => "Налаштування Активне",
+"When unchecked, this configuration will be skipped." => "Якщо \"галочка\" знята, ця конфігурація буде пропущена.",
+"Port" => "Порт",
+"Backup (Replica) Host" => "Сервер для резервних копій",
+"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Вкажіть додатковий резервний сервер. Він повинен бути копією головного LDAP/AD сервера.",
+"Backup (Replica) Port" => "Порт сервера для резервних копій",
+"Disable Main Server" => "Вимкнути Головний Сервер",
+"When switched on, ownCloud will only connect to the replica server." => "Коли увімкнуто, ownCloud буде приєднуватись лише до сервера з резервними копіями.",
+"Use TLS" => "Використовуйте TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Не використовуйте це додатково для під'єднання до LDAP, бо виконано не буде.",
+"Case insensitve LDAP server (Windows)" => "Нечутливий до регістру LDAP сервер (Windows)",
+"Turn off SSL certificate validation." => "Вимкнути перевірку SSL сертифіката.",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Якщо з'єднання працює лише з цією опцією, імпортуйте SSL сертифікат LDAP сервера у ваший ownCloud сервер.",
+"Not recommended, use for testing only." => "Не рекомендується, використовуйте лише для тестів.",
+"Cache Time-To-Live" => "Час актуальності Кеша",
+"in seconds. A change empties the cache." => "в секундах. Зміна очищує кеш.",
+"Directory Settings" => "Налаштування Каталога",
+"User Display Name Field" => "Поле, яке відображає Ім'я Користувача",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен користувачів ownCloud.",
+"Base User Tree" => "Основне Дерево Користувачів",
+"One User Base DN per line" => "Один Користувач Base DN на одній строчці",
+"User Search Attributes" => "Пошукові Атрибути Користувача",
+"Optional; one attribute per line" => "Додатково; один атрибут на строчку",
+"Group Display Name Field" => "Поле, яке відображає Ім'я Групи",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен груп ownCloud.",
+"Base Group Tree" => "Основне Дерево Груп",
+"One Group Base DN per line" => "Одна Група Base DN на одній строчці",
+"Group Search Attributes" => "Пошукові Атрибути Групи",
+"Group-Member association" => "Асоціація Група-Член",
+"Special Attributes" => "Спеціальні Атрибути",
+"Quota Field" => "Поле Квоти",
+"Quota Default" => "Квота за замовчанням",
+"in bytes" => "в байтах",
+"Email Field" => "Поле Ел. пошти",
+"User Home Folder Naming Rule" => "Правило іменування домашньої теки користувача",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Залиште порожнім для імені користувача (за замовчанням). Інакше, вкажіть атрибут LDAP/AD.",
+"Test Configuration" => "Тестове налаштування",
"Help" => "Допомога"
);
diff --git a/apps/user_ldap/l10n/ur_PK.php b/apps/user_ldap/l10n/ur_PK.php
new file mode 100644
index 00000000000..4c606a13808
--- /dev/null
+++ b/apps/user_ldap/l10n/ur_PK.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"Password" => "پاسورڈ",
+"Help" => "مدد"
+);
diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php
index 7a6ac2665c6..4bbb977f363 100644
--- a/apps/user_ldap/l10n/vi.php
+++ b/apps/user_ldap/l10n/vi.php
@@ -1,12 +1,46 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "Xóa thất bại",
"Host" => "Máy chủ",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Bạn có thể bỏ qua các giao thức, ngoại trừ SSL. Sau đó bắt đầu với ldaps://",
+"Base DN" => "DN cơ bản",
+"You can specify Base DN for users and groups in the Advanced tab" => "Bạn có thể chỉ định DN cơ bản cho người dùng và các nhóm trong tab Advanced",
+"User DN" => "Người dùng DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Các DN của người sử dụng đã được thực hiện, ví dụ như uid =agent , dc = example, dc = com. Để truy cập nặc danh ,DN và mật khẩu trống.",
"Password" => "Mật khẩu",
+"For anonymous access, leave DN and Password empty." => "Cho phép truy cập nặc danh , DN và mật khẩu trống.",
+"User Login Filter" => "Lọc người dùng đăng nhập",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Xác định các bộ lọc để áp dụng, khi đăng nhập . uid%% thay thế tên người dùng trong các lần đăng nhập.",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "use %%uid placeholder, e.g. \"uid=%%uid\"",
+"User List Filter" => "Lọc danh sách thành viên",
+"Defines the filter to apply, when retrieving users." => "Xác định các bộ lọc để áp dụng, khi người dụng sử dụng.",
+"without any placeholder, e.g. \"objectClass=person\"." => "mà không giữ chỗ nào, ví dụ như \"objectClass = person\".",
+"Group Filter" => "Bộ lọc nhóm",
+"Defines the filter to apply, when retrieving groups." => "Xác định các bộ lọc để áp dụng, khi nhóm sử dụng.",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "mà không giữ chỗ nào, ví dụ như \"objectClass = osixGroup\".",
+"Connection Settings" => "Connection Settings",
"Port" => "Cổng",
+"Backup (Replica) Port" => "Cổng sao lưu (Replica)",
+"Disable Main Server" => "Tắt máy chủ chính",
+"When switched on, ownCloud will only connect to the replica server." => "When switched on, ownCloud will only connect to the replica server.",
"Use TLS" => "Sử dụng TLS",
+"Do not use it additionally for LDAPS connections, it will fail." => "Do not use it additionally for LDAPS connections, it will fail.",
+"Case insensitve LDAP server (Windows)" => "Trường hợp insensitve LDAP máy chủ (Windows)",
"Turn off SSL certificate validation." => "Tắt xác thực chứng nhận SSL",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Nếu kết nối chỉ hoạt động với tùy chọn này, vui lòng import LDAP certificate SSL trong máy chủ ownCloud của bạn.",
"Not recommended, use for testing only." => "Không khuyến khích, Chỉ sử dụng để thử nghiệm.",
+"in seconds. A change empties the cache." => "trong vài giây. Một sự thay đổi bộ nhớ cache.",
+"Directory Settings" => "Directory Settings",
"User Display Name Field" => "Hiển thị tên người sử dụng",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo tên người dùng ownCloud.",
+"Base User Tree" => "Cây người dùng cơ bản",
+"User Search Attributes" => "User Search Attributes",
+"Optional; one attribute per line" => "Optional; one attribute per line",
"Group Display Name Field" => "Hiển thị tên nhóm",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo các nhóm ownCloud.",
+"Base Group Tree" => "Cây nhóm cơ bản",
+"Group Search Attributes" => "Group Search Attributes",
+"Group-Member association" => "Nhóm thành viên Cộng đồng",
+"Special Attributes" => "Special Attributes",
"in bytes" => "Theo Byte",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Để trống tên người dùng (mặc định). Nếu không chỉ định thuộc tính LDAP/AD",
"Help" => "Giúp đỡ"
diff --git a/apps/user_ldap/l10n/zh_CN.GB2312.php b/apps/user_ldap/l10n/zh_CN.GB2312.php
index 8b906aea5ce..f5bc41fd46b 100644
--- a/apps/user_ldap/l10n/zh_CN.GB2312.php
+++ b/apps/user_ldap/l10n/zh_CN.GB2312.php
@@ -1,4 +1,5 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "删除失败",
"Host" => "主机",
"You can omit the protocol, except you require SSL. Then start with ldaps://" => "您可以忽略协议,除非您需要 SSL。然后用 ldaps:// 开头",
"Base DN" => "基本判别名",
@@ -17,21 +18,20 @@
"Defines the filter to apply, when retrieving groups." => "定义撷取群组时要应用的过滤器",
"without any placeholder, e.g. \"objectClass=posixGroup\"." => "不能使用占位符,例如 \"objectClass=posixGroup\"。",
"Port" => "端口",
-"Base User Tree" => "基本用户树",
-"Base Group Tree" => "基本群组树",
-"Group-Member association" => "群组-成员组合",
"Use TLS" => "使用 TLS",
-"Do not use it for SSL connections, it will fail." => "不要使用它进行 SSL 连接,会失败的。",
"Case insensitve LDAP server (Windows)" => "大小写不敏感的 LDAP 服务器 (Windows)",
"Turn off SSL certificate validation." => "关闭 SSL 证书校验。",
"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "如果只有使用此选项才能连接,请导入 LDAP 服务器的 SSL 证书到您的 ownCloud 服务器。",
"Not recommended, use for testing only." => "不推荐,仅供测试",
+"in seconds. A change empties the cache." => "以秒计。修改会清空缓存。",
"User Display Name Field" => "用户显示名称字段",
"The LDAP attribute to use to generate the user`s ownCloud name." => "用于生成用户的 ownCloud 名称的 LDAP 属性。",
+"Base User Tree" => "基本用户树",
"Group Display Name Field" => "群组显示名称字段",
"The LDAP attribute to use to generate the groups`s ownCloud name." => "用于生成群组的 ownCloud 名称的 LDAP 属性。",
+"Base Group Tree" => "基本群组树",
+"Group-Member association" => "群组-成员组合",
"in bytes" => "以字节计",
-"in seconds. A change empties the cache." => "以秒计。修改会清空缓存。",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "用户名请留空 (默认)。否则,请指定一个 LDAP/AD 属性。",
"Help" => "帮助"
);
diff --git a/apps/user_ldap/l10n/zh_CN.php b/apps/user_ldap/l10n/zh_CN.php
index 5f6200db404..d494945e2e4 100644
--- a/apps/user_ldap/l10n/zh_CN.php
+++ b/apps/user_ldap/l10n/zh_CN.php
@@ -1,9 +1,38 @@
<?php $TRANSLATIONS = array(
+"Deletion failed" => "删除失败",
+"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>警告:</b>应用 user_ldap 和 user_webdavauth 不兼容。您可能遭遇未预料的行为。请垂询您的系统管理员禁用其中一个。",
"Host" => "主机",
+"You can omit the protocol, except you require SSL. Then start with ldaps://" => "可以忽略协议,但如要使用SSL,则需以ldaps://开头",
"Base DN" => "Base DN",
"You can specify Base DN for users and groups in the Advanced tab" => "您可以在高级选项卡里为用户和组指定Base DN",
"User DN" => "User DN",
+"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "客户端使用的DN必须与绑定的相同,比如uid=agent,dc=example,dc=com\n如需匿名访问,将DN和密码保留为空",
"Password" => "密码",
+"For anonymous access, leave DN and Password empty." => "启用匿名访问,将DN和密码保留为空",
+"User Login Filter" => "用户登录过滤",
+"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "定义当尝试登录时的过滤器。 在登录过程中,%%uid将会被用户名替换",
+"use %%uid placeholder, e.g. \"uid=%%uid\"" => "使用 %%uid作为占位符,例如“uid=%%uid”",
+"User List Filter" => "用户列表过滤",
+"Defines the filter to apply, when retrieving users." => "定义拉取用户时的过滤器",
+"without any placeholder, e.g. \"objectClass=person\"." => "没有任何占位符,如 \"objectClass=person\".",
+"Group Filter" => "组过滤",
+"Defines the filter to apply, when retrieving groups." => "定义拉取组信息时的过滤器",
+"without any placeholder, e.g. \"objectClass=posixGroup\"." => "无需占位符,例如\"objectClass=posixGroup\"",
"Port" => "端口",
+"Use TLS" => "使用TLS",
+"Case insensitve LDAP server (Windows)" => "大小写敏感LDAP服务器(Windows)",
+"Turn off SSL certificate validation." => "关闭SSL证书验证",
+"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "如果链接仅在此选项时可用,在您的ownCloud服务器中导入LDAP服务器的SSL证书。",
+"Not recommended, use for testing only." => "暂不推荐,仅供测试",
+"in seconds. A change empties the cache." => "以秒计。修改将清空缓存。",
+"User Display Name Field" => "用户显示名称字段",
+"The LDAP attribute to use to generate the user`s ownCloud name." => "用来生成用户的ownCloud名称的 LDAP属性",
+"Base User Tree" => "基础用户树",
+"Group Display Name Field" => "组显示名称字段",
+"The LDAP attribute to use to generate the groups`s ownCloud name." => "用来生成组的ownCloud名称的LDAP属性",
+"Base Group Tree" => "基础组树",
+"Group-Member association" => "组成员关联",
+"in bytes" => "字节数",
+"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "将用户名称留空(默认)。否则指定一个LDAP/AD属性",
"Help" => "帮助"
);
diff --git a/apps/user_ldap/l10n/zh_HK.php b/apps/user_ldap/l10n/zh_HK.php
new file mode 100644
index 00000000000..190e4eba798
--- /dev/null
+++ b/apps/user_ldap/l10n/zh_HK.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"Password" => "密碼",
+"Port" => "連接埠",
+"Help" => "幫助"
+);
diff --git a/apps/user_ldap/l10n/zh_TW.php b/apps/user_ldap/l10n/zh_TW.php
new file mode 100644
index 00000000000..9a12bad0747
--- /dev/null
+++ b/apps/user_ldap/l10n/zh_TW.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Deletion failed" => "移除失敗",
+"Host" => "主機",
+"Password" => "密碼",
+"Port" => "連接阜",
+"Use TLS" => "使用TLS",
+"Turn off SSL certificate validation." => "關閉 SSL 憑證驗證",
+"Help" => "說明"
+);
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 66007d09536..90d026962db 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -4,7 +4,7 @@
* ownCloud – LDAP Access
*
* @author Arthur Schiwon
- * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
+ * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -25,6 +25,8 @@ namespace OCA\user_ldap\lib;
abstract class Access {
protected $connection;
+ //never ever check this var directly, always use getPagedSearchResultState
+ protected $pagedSearchedSuccessful;
public function setConnector(Connection &$connection) {
$this->connection = $connection;
@@ -38,13 +40,17 @@ abstract class Access {
* @brief reads a given attribute for an LDAP record identified by a DN
* @param $dn the record in question
* @param $attr the attribute that shall be retrieved
- * @returns the values in an array on success, false otherwise
+ * if empty, just check the record's existence
+ * @returns an array of values on success or an empty
+ * array if $attr is empty, false otherwise
*
- * Reads an attribute from an LDAP entry
+ * Reads an attribute from an LDAP entry or check if entry exists
*/
- public function readAttribute($dn, $attr) {
+ public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
if(!$this->checkConnection()) {
- \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'No LDAP Connector assigned, access impossible for readAttribute.',
+ \OCP\Util::WARN);
return false;
}
$cr = $this->connection->getConnectionResource();
@@ -53,13 +59,22 @@ abstract class Access {
\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
return false;
}
- $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr));
+ $dn = $this->DNasBaseParameter($dn);
+ $rr = @ldap_read($cr, $dn, $filter, array($attr));
if(!is_resource($rr)) {
- \OCP\Util::writeLog('user_ldap', 'readAttribute '.$attr.' failed for DN '.$dn, \OCP\Util::DEBUG);
+ \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
//in case an error occurs , e.g. object does not exist
return false;
}
+ if (empty($attr)) {
+ \OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
+ return array();
+ }
$er = ldap_first_entry($cr, $rr);
+ if(!is_resource($er)) {
+ //did not match the filter, return false
+ return false;
+ }
//LDAP attributes are not case sensitive
$result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
$attr = mb_strtolower($attr, 'UTF-8');
@@ -67,7 +82,13 @@ abstract class Access {
if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
$values = array();
for($i=0;$i<$result[$attr]['count'];$i++) {
- $values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i];
+ if($this->resemblesDN($attr)) {
+ $values[] = $this->sanitizeDN($result[$attr][$i]);
+ } elseif(strtolower($attr) == 'objectguid' || strtolower($attr) == 'guid') {
+ $values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
+ } else {
+ $values[] = $result[$attr][$i];
+ }
}
return $values;
}
@@ -95,12 +116,40 @@ abstract class Access {
* @return the sanitized DN
*/
private function sanitizeDN($dn) {
- //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this!
+ //treating multiple base DNs
+ if(is_array($dn)) {
+ $result = array();
+ foreach($dn as $singleDN) {
+ $result[] = $this->sanitizeDN($singleDN);
+ }
+ return $result;
+ }
+
+ //OID sometimes gives back DNs with whitespace after the comma
+ // a la "uid=foo, cn=bar, dn=..." We need to tackle this!
$dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
//make comparisons and everything work
$dn = mb_strtolower($dn, 'UTF-8');
+ //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
+ //to use the DN in search filters, \ needs to be escaped to \5c additionally
+ //to use them in bases, we convert them back to simple backslashes in readAttribute()
+ $replacements = array(
+ '\,' => '\5c2C',
+ '\=' => '\5c3D',
+ '\+' => '\5c2B',
+ '\<' => '\5c3C',
+ '\>' => '\5c3E',
+ '\;' => '\5c3B',
+ '\"' => '\5c22',
+ '\#' => '\5c23',
+ '(' => '\28',
+ ')' => '\29',
+ '*' => '\2A',
+ );
+ $dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
+
return $dn;
}
@@ -175,12 +224,17 @@ abstract class Access {
* @param $ldapname optional, the display name of the object
* @returns string with with the name to use in ownCloud, false on DN outside of search DN
*
- * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
+ * returns the internal ownCloud name for the given LDAP DN of the
+ * group, false on DN outside of search DN or failure
*/
public function dn2groupname($dn, $ldapname = null) {
- if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseGroups), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseGroups), 'UTF-8'))) {
+ //To avoid bypassing the base DN settings under certain circumstances
+ //with the group support, check whether the provided DN matches one of
+ //the given Bases
+ if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
return false;
}
+
return $this->dn2ocname($dn, $ldapname, false);
}
@@ -193,9 +247,13 @@ abstract class Access {
* returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
*/
public function dn2username($dn, $ldapname = null) {
- if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseUsers), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseUsers), 'UTF-8'))) {
+ //To avoid bypassing the base DN settings under certain circumstances
+ //with the group support, check whether the provided DN matches one of
+ //the given Bases
+ if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseUsers)) {
return false;
}
+
return $this->dn2ocname($dn, $ldapname, true);
}
@@ -209,7 +267,6 @@ abstract class Access {
* returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN
*/
public function dn2ocname($dn, $ldapname = null, $isUser = true) {
- $dn = $this->sanitizeDN($dn);
$table = $this->getMapTable($isUser);
if($isUser) {
$fncFindMappedName = 'findMappedUser';
@@ -243,6 +300,10 @@ abstract class Access {
$query->execute(array($dn, $uuid));
return $component;
}
+ } else {
+ //If the UUID can't be detected something is foul.
+ \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$dn.'. Skipping.', \OCP\Util::INFO);
+ return false;
}
if(is_null($ldapname)) {
@@ -253,21 +314,24 @@ abstract class Access {
}
$ldapname = $ldapname[0];
}
- $ldapname = $this->sanitizeUsername($ldapname);
-
- //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot.
- if(($isUser && !\OCP\User::userExists($ldapname)) || (!$isUser && !\OC_Group::groupExists($ldapname))) {
- if($this->mapComponent($dn, $ldapname, $isUser)) {
- return $ldapname;
+ $intname = $isUser ? $this->sanitizeUsername($uuid) : $this->sanitizeUsername($ldapname);
+
+ //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
+ //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
+ $originalTTL = $this->connection->ldapCacheTTL;
+ $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
+ if(($isUser && !\OCP\User::userExists($intname))
+ || (!$isUser && !\OC_Group::groupExists($intname))) {
+ if($this->mapComponent($dn, $intname, $isUser)) {
+ $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
+ return $intname;
}
}
+ $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
- //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located.
- $oc_name = $this->alternateOwnCloudName($ldapname, $dn);
- if(($isUser && !\OCP\User::userExists($oc_name)) || (!$isUser && !\OC_Group::groupExists($oc_name))) {
- if($this->mapComponent($dn, $oc_name, $isUser)) {
- return $oc_name;
- }
+ $altname = $this->createAltInternalOwnCloudName($intname, $isUser);
+ if($this->mapComponent($dn, $altname, $isUser)) {
+ return $altname;
}
//if everything else did not help..
@@ -314,20 +378,20 @@ abstract class Access {
}
private function findMappedGroup($dn) {
- static $query = null;
+ static $query = null;
if(is_null($query)) {
$query = \OCP\DB::prepare('
- SELECT `owncloud_name`
- FROM `'.$this->getMapTable(false).'`
- WHERE `ldap_dn` = ?'
- );
- }
- $res = $query->execute(array($dn))->fetchOne();
- if($res) {
- return $res;
- }
+ SELECT `owncloud_name`
+ FROM `'.$this->getMapTable(false).'`
+ WHERE `ldap_dn` = ?'
+ );
+ }
+ $res = $query->execute(array($dn))->fetchOne();
+ if($res) {
+ return $res;
+ }
return false;
- }
+ }
private function ldap2ownCloudNames($ldapObjects, $isUsers) {
@@ -339,7 +403,8 @@ abstract class Access {
$ownCloudNames = array();
foreach($ldapObjects as $ldapObject) {
- $ocname = $this->dn2ocname($ldapObject['dn'], $ldapObject[$nameAttribute], $isUsers);
+ $nameByLDAP = isset($ldapObject[$nameAttribute]) ? $ldapObject[$nameAttribute] : null;
+ $ocname = $this->dn2ocname($ldapObject['dn'], $nameByLDAP, $isUsers);
if($ocname) {
$ownCloudNames[] = $ocname;
}
@@ -349,18 +414,92 @@ abstract class Access {
}
/**
- * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
+ * @brief creates a unique name for internal ownCloud use for users. Don't call it directly.
* @param $name the display name of the object
- * @param $dn the dn of the object
- * @returns string with with the name to use in ownCloud
+ * @returns string with with the name to use in ownCloud or false if unsuccessful
*
- * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object
+ * Instead of using this method directly, call
+ * createAltInternalOwnCloudName($name, true)
*/
- private function alternateOwnCloudName($name, $dn) {
- $ufn = ldap_dn2ufn($dn);
- $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8'));
- $name = $this->sanitizeUsername($name);
- return $name;
+ private function _createAltInternalOwnCloudNameForUsers($name) {
+ $attempts = 0;
+ //while loop is just a precaution. If a name is not generated within
+ //20 attempts, something else is very wrong. Avoids infinite loop.
+ while($attempts < 20){
+ $altName = $name . '_' . uniqid();
+ if(\OCP\User::userExists($altName)) {
+ return $altName;
+ }
+ $attempts++;
+ }
+ return false;
+ }
+
+ /**
+ * @brief creates a unique name for internal ownCloud use for groups. Don't call it directly.
+ * @param $name the display name of the object
+ * @returns string with with the name to use in ownCloud or false if unsuccessful.
+ *
+ * Instead of using this method directly, call
+ * createAltInternalOwnCloudName($name, false)
+ *
+ * Group names are also used as display names, so we do a sequential
+ * numbering, e.g. Developers_42 when there are 41 other groups called
+ * "Developers"
+ */
+ private function _createAltInternalOwnCloudNameForGroups($name) {
+ $query = \OCP\DB::prepare('
+ SELECT `owncloud_name`
+ FROM `'.$this->getMapTable(false).'`
+ WHERE `owncloud_name` LIKE ?
+ ');
+
+ $usedNames = array();
+ $res = $query->execute(array($name.'_%'));
+ while($row = $res->fetchRow()) {
+ $usedNames[] = $row['owncloud_name'];
+ }
+ if(!($usedNames) || count($usedNames) == 0) {
+ $lastNo = 1; //will become name_2
+ } else {
+ natsort($usedNames);
+ $lastname = array_pop($usedNames);
+ $lastNo = intval(substr($lastname, strrpos($lastname, '_') + 1));
+ }
+ $altName = $name.'_'.strval($lastNo+1);
+ unset($usedNames);
+
+ $attempts = 1;
+ while($attempts < 21){
+ //Pro forma check to be really sure it is unique
+ //while loop is just a precaution. If a name is not generated within
+ //20 attempts, something else is very wrong. Avoids infinite loop.
+ if(!\OC_Group::groupExists($altName)) {
+ return $altName;
+ }
+ $altName = $name . '_' . $lastNo + $attempts;
+ $attempts++;
+ }
+ return false;
+ }
+
+ /**
+ * @brief creates a unique name for internal ownCloud use.
+ * @param $name the display name of the object
+ * @param $isUser boolean, whether name should be created for a user (true) or a group (false)
+ * @returns string with with the name to use in ownCloud or false if unsuccessful
+ */
+ private function createAltInternalOwnCloudName($name, $isUser) {
+ $originalTTL = $this->connection->ldapCacheTTL;
+ $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
+ if($isUser) {
+ $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
+ } else {
+ $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
+ }
+ $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
+
+ return $altName;
}
/**
@@ -405,7 +544,6 @@ abstract class Access {
*/
private function mapComponent($dn, $ocname, $isUser = true) {
$table = $this->getMapTable($isUser);
- $dn = $this->sanitizeDN($dn);
$sqlAdjustment = '';
$dbtype = \OCP\Config::getSystemValue('dbtype');
@@ -440,12 +578,12 @@ abstract class Access {
return true;
}
- public function fetchListOfUsers($filter, $attr) {
- return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1));
+ public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
+ return $this->fetchList($this->searchUsers($filter, $attr, $limit, $offset), (count($attr) > 1));
}
- public function fetchListOfGroups($filter, $attr) {
- return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1));
+ public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
+ return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
}
private function fetchList($list, $manyAttributes) {
@@ -469,8 +607,8 @@ abstract class Access {
*
* Executes an LDAP search
*/
- public function searchUsers($filter, $attr = null) {
- return $this->search($filter, $this->connection->ldapBaseUsers, $attr);
+ public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
+ return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
}
/**
@@ -481,42 +619,91 @@ abstract class Access {
*
* Executes an LDAP search
*/
- public function searchGroups($filter, $attr = null) {
- return $this->search($filter, $this->connection->ldapBaseGroups, $attr);
+ public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
+ return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
}
/**
* @brief executes an LDAP search
* @param $filter the LDAP filter for the search
- * @param $base the LDAP subtree that shall be searched
- * @param $attr optional, when a certain attribute shall be filtered out
+ * @param $base an array containing the LDAP subtree(s) that shall be searched
+ * @param $attr optional, array, one or more attributes that shall be
+ * retrieved. Results will according to the order in the array.
* @returns array with the search result
*
* Executes an LDAP search
*/
- private function search($filter, $base, $attr = null) {
+ private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
if(!is_null($attr) && !is_array($attr)) {
$attr = array(mb_strtolower($attr, 'UTF-8'));
}
- // See if we have a resource
+ // See if we have a resource, in case not cancel with message
$link_resource = $this->connection->getConnectionResource();
- if(is_resource($link_resource)) {
- $sr = ldap_search($link_resource, $base, $filter, $attr);
- $findings = ldap_get_entries($link_resource, $sr );
-
- // if we're here, probably no connection resource is returned.
- // to make ownCloud behave nicely, we simply give back an empty array.
- if(is_null($findings)) {
- return array();
- }
- } else {
+ if(!is_resource($link_resource)) {
// Seems like we didn't find any resource.
// Return an empty array just like before.
\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
return array();
}
+ //check wether paged search should be attempted
+ $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, $limit, $offset);
+
+ $linkResources = array_pad(array(), count($base), $link_resource);
+ $sr = ldap_search($linkResources, $base, $filter, $attr);
+ $error = ldap_errno($link_resource);
+ if(!is_array($sr) || $error != 0) {
+ \OCP\Util::writeLog('user_ldap',
+ 'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource),
+ \OCP\Util::ERROR);
+ \OCP\Util::writeLog('user_ldap', 'Attempt for Paging? '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
+ return array();
+ }
+
+ // Do the server-side sorting
+ foreach(array_reverse($attr) as $sortAttr){
+ foreach($sr as $searchResource) {
+ ldap_sort($link_resource, $searchResource, $sortAttr);
+ }
+ }
+
+ $findings = array();
+ foreach($sr as $key => $res) {
+ $findings = array_merge($findings, ldap_get_entries($link_resource, $res ));
+ }
+ if($pagedSearchOK) {
+ \OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO);
+ foreach($sr as $key => $res) {
+ $cookie = null;
+ if(ldap_control_paged_result_response($link_resource, $res, $cookie)) {
+ \OCP\Util::writeLog('user_ldap', 'Set paged search cookie', \OCP\Util::INFO);
+ $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
+ }
+ }
+
+ //browsing through prior pages to get the cookie for the new one
+ if($skipHandling) {
+ return;
+ }
+ // if count is bigger, then the server does not support
+ // paged search. Instead, he did a normal search. We set a
+ // flag here, so the callee knows how to deal with it.
+ if($findings['count'] <= $limit) {
+ $this->pagedSearchedSuccessful = true;
+ }
+ } else {
+ if(!is_null($limit)) {
+ \OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO);
+ }
+ }
+
+ // if we're here, probably no connection resource is returned.
+ // to make ownCloud behave nicely, we simply give back an empty array.
+ if(is_null($findings)) {
+ return array();
+ }
+
if(!is_null($attr)) {
$selection = array();
$multiarray = false;
@@ -535,7 +722,9 @@ abstract class Access {
$key = mb_strtolower($key, 'UTF-8');
if(isset($item[$key])) {
if($key != 'dn') {
- $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0];
+ $selection[$i][$key] = $this->resemblesDN($key) ?
+ $this->sanitizeDN($item[$key][0])
+ : $item[$key][0];
} else {
$selection[$i][$key] = $this->sanitizeDN($item[$key]);
}
@@ -556,7 +745,19 @@ abstract class Access {
}
}
}
- return $selection;
+ $findings = $selection;
+ }
+ //we slice the findings, when
+ //a) paged search insuccessful, though attempted
+ //b) no paged search, but limit set
+ if((!$this->pagedSearchedSuccessful
+ && $pagedSearchOK)
+ || (
+ !$pagedSearchOK
+ && !is_null($limit)
+ )
+ ) {
+ $findings = array_slice($findings, intval($offset), $limit);
}
return $findings;
}
@@ -621,7 +822,56 @@ abstract class Access {
return $combinedFilter;
}
+ /**
+ * @brief creates a filter part for to perfrom search for users
+ * @param string $search the search term
+ * @return string the final filter part to use in LDAP searches
+ */
+ public function getFilterPartForUserSearch($search) {
+ return $this->getFilterPartForSearch($search,
+ $this->connection->ldapAttributesForUserSearch,
+ $this->connection->ldapUserDisplayName);
+ }
+
+ /**
+ * @brief creates a filter part for to perfrom search for groups
+ * @param string $search the search term
+ * @return string the final filter part to use in LDAP searches
+ */
+ public function getFilterPartForGroupSearch($search) {
+ return $this->getFilterPartForSearch($search,
+ $this->connection->ldapAttributesForGroupSearch,
+ $this->connection->ldapGroupDisplayName);
+ }
+
+ /**
+ * @brief creates a filter part for searches
+ * @param string $search the search term
+ * @param string $fallbackAttribute a fallback attribute in case the user
+ * did not define search attributes. Typically the display name attribute.
+ * @returns string the final filter part to use in LDAP searches
+ */
+ private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
+ $filter = array();
+ $search = empty($search) ? '*' : '*'.$search.'*';
+ if(!is_array($searchAttributes) || count($searchAttributes) == 0) {
+ if(empty($fallbackAttribute)) {
+ return '';
+ }
+ $filter[] = $fallbackAttribute . '=' . $search;
+ } else {
+ foreach($searchAttributes as $attribute) {
+ $filter[] = $attribute . '=' . $search;
+ }
+ }
+ if(count($filter) == 1) {
+ return '('.$filter[0].')';
+ }
+ return $this->combineFilterWithOr($filter);
+ }
+
public function areCredentialsValid($name, $password) {
+ $name = $this->DNasBaseParameter($name);
$testConnection = clone $this->connection;
$credentials = array(
'ldapAgentName' => $name,
@@ -645,18 +895,20 @@ abstract class Access {
}
//for now, supported (known) attributes are entryUUID, nsuniqueid, objectGUID
- $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid');
+ $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid');
foreach($testAttributes as $attribute) {
\OCP\Util::writeLog('user_ldap', 'Testing '.$attribute.' as UUID attr', \OCP\Util::DEBUG);
- $value = $this->readAttribute($dn, $attribute);
- if(is_array($value) && isset($value[0]) && !empty($value[0])) {
+ $value = $this->readAttribute($dn, $attribute);
+ if(is_array($value) && isset($value[0]) && !empty($value[0])) {
\OCP\Util::writeLog('user_ldap', 'Setting '.$attribute.' as UUID attr', \OCP\Util::DEBUG);
$this->connection->ldapUuidAttribute = $attribute;
return true;
- }
- \OCP\Util::writeLog('user_ldap', 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value,true), \OCP\Util::DEBUG);
+ }
+ \OCP\Util::writeLog('user_ldap',
+ 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value, true),
+ \OCP\Util::DEBUG);
}
return false;
@@ -664,6 +916,9 @@ abstract class Access {
public function getUUID($dn) {
if($this->detectUuidAttribute($dn)) {
+ \OCP\Util::writeLog('user_ldap',
+ 'UUID Checking \ UUID for '.$dn.' using '. $this->connection->ldapUuidAttribute,
+ \OCP\Util::DEBUG);
$uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute);
if(!is_array($uuid) && $this->connection->ldapOverrideUuidAttribute) {
$this->detectUuidAttribute($dn, true);
@@ -679,4 +934,172 @@ abstract class Access {
}
return $uuid;
}
-} \ No newline at end of file
+
+ /**
+ * @brief converts a binary ObjectGUID into a string representation
+ * @param $oguid the ObjectGUID in it's binary form as retrieved from AD
+ * @returns String
+ *
+ * converts a binary ObjectGUID into a string representation
+ * http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
+ */
+ private function convertObjectGUID2Str($oguid) {
+ $hex_guid = bin2hex($oguid);
+ $hex_guid_to_guid_str = '';
+ for($k = 1; $k <= 4; ++$k) {
+ $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
+ }
+ $hex_guid_to_guid_str .= '-';
+ for($k = 1; $k <= 2; ++$k) {
+ $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
+ }
+ $hex_guid_to_guid_str .= '-';
+ for($k = 1; $k <= 2; ++$k) {
+ $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
+ }
+ $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
+ $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
+
+ return strtoupper($hex_guid_to_guid_str);
+ }
+
+ /**
+ * @brief converts a stored DN so it can be used as base parameter for LDAP queries
+ * @param $dn the DN
+ * @returns String
+ *
+ * converts a stored DN so it can be used as base parameter for LDAP queries
+ * internally we store them for usage in LDAP filters
+ */
+ private function DNasBaseParameter($dn) {
+ return str_replace('\\5c', '\\', $dn);
+ }
+
+ /**
+ * @brief checks if the given DN is part of the given base DN(s)
+ * @param $dn the DN
+ * @param $bases array containing the allowed base DN or DNs
+ * @returns Boolean
+ */
+ private function isDNPartOfBase($dn, $bases) {
+ $bases = $this->sanitizeDN($bases);
+ foreach($bases as $base) {
+ $belongsToBase = true;
+ if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base))) {
+ $belongsToBase = false;
+ }
+ if($belongsToBase) {
+ break;
+ }
+ }
+ return $belongsToBase;
+ }
+
+ /**
+ * @brief get a cookie for the next LDAP paged search
+ * @param $base a string with the base DN for the search
+ * @param $filter the search filter to identify the correct search
+ * @param $limit the limit (or 'pageSize'), to identify the correct search well
+ * @param $offset the offset for the new search to identify the correct search really good
+ * @returns string containing the key or empty if none is cached
+ */
+ private function getPagedResultCookie($base, $filter, $limit, $offset) {
+ if($offset == 0) {
+ return '';
+ }
+ $offset -= $limit;
+ //we work with cache here
+ $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset;
+ $cookie = $this->connection->getFromCache($cachekey);
+ if(is_null($cookie)) {
+ $cookie = '';
+ }
+ return $cookie;
+ }
+
+ /**
+ * @brief set a cookie for LDAP paged search run
+ * @param $base a string with the base DN for the search
+ * @param $filter the search filter to identify the correct search
+ * @param $limit the limit (or 'pageSize'), to identify the correct search well
+ * @param $offset the offset for the run search to identify the correct search really good
+ * @param $cookie string containing the cookie returned by ldap_control_paged_result_response
+ * @return void
+ */
+ private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
+ if(!empty($cookie)) {
+ $cachekey = 'lc' . dechex(crc32($base)) . '-' . dechex(crc32($filter)) . '-' .$limit . '-' . $offset;
+ $cookie = $this->connection->writeToCache($cachekey, $cookie);
+ }
+ }
+
+ /**
+ * @brief check wether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
+ * @return true on success, null or false otherwise
+ */
+ public function getPagedSearchResultState() {
+ $result = $this->pagedSearchedSuccessful;
+ $this->pagedSearchedSuccessful = null;
+ return $result;
+ }
+
+
+ /**
+ * @brief prepares a paged search, if possible
+ * @param $filter the LDAP filter for the search
+ * @param $bases an array containing the LDAP subtree(s) that shall be searched
+ * @param $attr optional, when a certain attribute shall be filtered outside
+ * @param $limit
+ * @param $offset
+ *
+ */
+ private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
+ $pagedSearchOK = false;
+ if($this->connection->hasPagedResultSupport && !is_null($limit)) {
+ $offset = intval($offset); //can be null
+ \OCP\Util::writeLog('user_ldap',
+ 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true)
+ .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
+ \OCP\Util::INFO);
+ //get the cookie from the search for the previous search, required by LDAP
+ foreach($bases as $base) {
+
+ $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
+ if(empty($cookie) && ($offset > 0)) {
+ // no cookie known, although the offset is not 0. Maybe cache run out. We need
+ // to start all over *sigh* (btw, Dear Reader, did you need LDAP paged
+ // searching was designed by MSFT?)
+ $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
+ //a bit recursive, $offset of 0 is the exit
+ \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
+ $this->search($filter, array($base), $attr, $limit, $reOffset, true);
+ $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
+ //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
+ //TODO: remember this, probably does not change in the next request...
+ if(empty($cookie)) {
+ $cookie = null;
+ }
+ }
+ if(!is_null($cookie)) {
+ if($offset > 0) {
+ \OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO);
+ }
+ $pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(),
+ $limit, false, $cookie);
+ if(!$pagedSearchOK) {
+ return false;
+ }
+ \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::INFO);
+ } else {
+ \OCP\Util::writeLog('user_ldap',
+ 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
+ \OCP\Util::INFO);
+ }
+
+ }
+ }
+
+ return $pagedSearchOK;
+ }
+
+}
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index bf65d9ad91c..6643428afe4 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -4,7 +4,7 @@
* ownCloud – LDAP Access
*
* @author Arthur Schiwon
- * @copyright 2012 Arthur Schiwon blizzz@owncloud.com
+ * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -25,6 +25,7 @@ namespace OCA\user_ldap\lib;
class Connection {
private $ldapConnectionRes = null;
+ private $configPrefix;
private $configID;
private $configured = false;
@@ -35,6 +36,8 @@ class Connection {
protected $config = array(
'ldapHost' => null,
'ldapPort' => null,
+ 'ldapBackupHost' => null,
+ 'ldapBackupPort' => null,
'ldapBase' => null,
'ldapBaseUsers' => null,
'ldapBaseGroups' => null,
@@ -48,6 +51,7 @@ class Connection {
'ldapUserFilter' => null,
'ldapGroupFilter' => null,
'ldapGroupDisplayName' => null,
+ 'ldapGroupMemberAssocAttr' => null,
'ldapLoginFilter' => null,
'ldapQuotaAttribute' => null,
'ldapQuotaDefault' => null,
@@ -55,16 +59,31 @@ class Connection {
'ldapCacheTTL' => null,
'ldapUuidAttribute' => null,
'ldapOverrideUuidAttribute' => null,
+ 'ldapOverrideMainServer' => false,
+ 'ldapConfigurationActive' => false,
+ 'ldapAttributesForUserSearch' => null,
+ 'ldapAttributesForGroupSearch' => null,
'homeFolderNamingRule' => null,
+ 'hasPagedResultSupport' => false,
);
- public function __construct($configID = 'user_ldap') {
+ /**
+ * @brief Constructor
+ * @param $configPrefix a string with the prefix for the configkey column (appconfig table)
+ * @param $configID a string with the value for the appid column (appconfig table) or null for on-the-fly connections
+ */
+ public function __construct($configPrefix = '', $configID = 'user_ldap') {
+ $this->configPrefix = $configPrefix;
$this->configID = $configID;
$this->cache = \OC_Cache::getGlobalCache();
+ $this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result')
+ && function_exists('ldap_control_paged_result_response'));
}
public function __destruct() {
- @ldap_unbind($this->ldapConnectionRes);
+ if(is_resource($this->ldapConnectionRes)) {
+ @ldap_unbind($this->ldapConnectionRes);
+ };
}
public function __get($name) {
@@ -79,12 +98,12 @@ class Connection {
public function __set($name, $value) {
$changed = false;
- //omly few options are writable
+ //only few options are writable
if($name == 'ldapUuidAttribute') {
\OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG);
$this->config[$name] = $value;
if(!empty($this->configID)) {
- \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', $value);
+ \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', $value);
}
$changed = true;
}
@@ -121,7 +140,7 @@ class Connection {
}
private function getCacheKey($key) {
- $prefix = 'LDAP-'.$this->configID.'-';
+ $prefix = 'LDAP-'.$this->configID.'-'.$this->configPrefix.'-';
if(is_null($key)) {
return $prefix;
}
@@ -159,7 +178,8 @@ class Connection {
if(!$this->configured) {
$this->readConfiguration();
}
- if(!$this->config['ldapCacheTTL']) {
+ if(!$this->config['ldapCacheTTL']
+ || !$this->config['ldapConfigurationActive']) {
return null;
}
$key = $this->getCacheKey($key);
@@ -171,43 +191,124 @@ class Connection {
$this->cache->clear($this->getCacheKey(null));
}
+ private function getValue($varname) {
+ static $defaults;
+ if(is_null($defaults)) {
+ $defaults = $this->getDefaults();
+ }
+ return \OCP\Config::getAppValue($this->configID,
+ $this->configPrefix.$varname,
+ $defaults[$varname]);
+ }
+
+ private function setValue($varname, $value) {
+ \OCP\Config::setAppValue($this->configID,
+ $this->configPrefix.$varname,
+ $value);
+ }
+
/**
* Caches the general LDAP configuration.
*/
private function readConfiguration($force = false) {
- \OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG);
if((!$this->configured || $force) && !is_null($this->configID)) {
- \OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG);
- $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', '');
- $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389);
- $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn','');
- $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password',''));
- $this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', '');
- $this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']);
- $this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']);
- $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0);
- $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0);
- $this->config['turnOffCertCheck'] = \OCP\Config::getAppValue($this->configID, 'ldap_turn_off_cert_check', 0);
- $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8');
- $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person');
- $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)');
- $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)');
- $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8');
- $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', '');
- $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', '');
- $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', '');
- $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember');
- $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false);
- $this->config['ldapCacheTTL'] = \OCP\Config::getAppValue($this->configID, 'ldap_cache_ttl', 10*60);
- $this->config['ldapUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', 'auto');
- $this->config['ldapOverrideUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_override_uuid_attribute', 0);
- $this->config['homeFolderNamingRule'] = \OCP\Config::getAppValue($this->configID, 'home_folder_naming_rule', 'opt:username');
+ $defaults = $this->getDefaults();
+ $v = 'getValue';
+ $this->config['ldapHost'] = $this->$v('ldap_host');
+ $this->config['ldapBackupHost'] = $this->$v('ldap_backup_host');
+ $this->config['ldapPort'] = $this->$v('ldap_port');
+ $this->config['ldapBackupPort'] = $this->$v('ldap_backup_port');
+ $this->config['ldapOverrideMainServer']
+ = $this->$v('ldap_override_main_server');
+ $this->config['ldapAgentName'] = $this->$v('ldap_dn');
+ $this->config['ldapAgentPassword']
+ = base64_decode($this->$v('ldap_agent_password'));
+ $rawLdapBase = $this->$v('ldap_base');
+ $this->config['ldapBase']
+ = preg_split('/\r\n|\r|\n/', $rawLdapBase);
+ $this->config['ldapBaseUsers']
+ = preg_split('/\r\n|\r|\n/', ($this->$v('ldap_base_users')));
+ $this->config['ldapBaseGroups']
+ = preg_split('/\r\n|\r|\n/', $this->$v('ldap_base_groups'));
+ unset($rawLdapBase);
+ $this->config['ldapTLS'] = $this->$v('ldap_tls');
+ $this->config['ldapNoCase'] = $this->$v('ldap_nocase');
+ $this->config['turnOffCertCheck']
+ = $this->$v('ldap_turn_off_cert_check');
+ $this->config['ldapUserDisplayName']
+ = mb_strtolower($this->$v('ldap_display_name'), 'UTF-8');
+ $this->config['ldapUserFilter']
+ = $this->$v('ldap_userlist_filter');
+ $this->config['ldapGroupFilter'] = $this->$v('ldap_group_filter');
+ $this->config['ldapLoginFilter'] = $this->$v('ldap_login_filter');
+ $this->config['ldapGroupDisplayName']
+ = mb_strtolower($this->$v('ldap_group_display_name'), 'UTF-8');
+ $this->config['ldapQuotaAttribute']
+ = $this->$v('ldap_quota_attr');
+ $this->config['ldapQuotaDefault']
+ = $this->$v('ldap_quota_def');
+ $this->config['ldapEmailAttribute']
+ = $this->$v('ldap_email_attr');
+ $this->config['ldapGroupMemberAssocAttr']
+ = $this->$v('ldap_group_member_assoc_attribute');
+ $this->config['ldapIgnoreNamingRules']
+ = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false);
+ $this->config['ldapCacheTTL'] = $this->$v('ldap_cache_ttl');
+ $this->config['ldapUuidAttribute']
+ = $this->$v('ldap_uuid_attribute');
+ $this->config['ldapOverrideUuidAttribute']
+ = $this->$v('ldap_override_uuid_attribute');
+ $this->config['homeFolderNamingRule']
+ = $this->$v('home_folder_naming_rule');
+ $this->config['ldapConfigurationActive']
+ = $this->$v('ldap_configuration_active');
+ $this->config['ldapAttributesForUserSearch']
+ = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_user_search'));
+ $this->config['ldapAttributesForGroupSearch']
+ = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_group_search'));
$this->configured = $this->validateConfiguration();
}
}
/**
+ * @return returns an array that maps internal variable names to database fields
+ */
+ private function getConfigTranslationArray() {
+ static $array = array(
+ 'ldap_host'=>'ldapHost',
+ 'ldap_port'=>'ldapPort',
+ 'ldap_backup_host'=>'ldapBackupHost',
+ 'ldap_backup_port'=>'ldapBackupPort',
+ 'ldap_override_main_server' => 'ldapOverrideMainServer',
+ 'ldap_dn'=>'ldapAgentName',
+ 'ldap_agent_password'=>'ldapAgentPassword',
+ 'ldap_base'=>'ldapBase',
+ 'ldap_base_users'=>'ldapBaseUsers',
+ 'ldap_base_groups'=>'ldapBaseGroups',
+ 'ldap_userlist_filter'=>'ldapUserFilter',
+ 'ldap_login_filter'=>'ldapLoginFilter',
+ 'ldap_group_filter'=>'ldapGroupFilter',
+ 'ldap_display_name'=>'ldapUserDisplayName',
+ 'ldap_group_display_name'=>'ldapGroupDisplayName',
+
+ 'ldap_tls'=>'ldapTLS',
+ 'ldap_nocase'=>'ldapNoCase',
+ 'ldap_quota_def'=>'ldapQuotaDefault',
+ 'ldap_quota_attr'=>'ldapQuotaAttribute',
+ 'ldap_email_attr'=>'ldapEmailAttribute',
+ 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr',
+ 'ldap_cache_ttl'=>'ldapCacheTTL',
+ 'home_folder_naming_rule' => 'homeFolderNamingRule',
+ 'ldap_turn_off_cert_check' => 'turnOffCertCheck',
+ 'ldap_configuration_active' => 'ldapConfigurationActive',
+ 'ldap_attributes_for_user_search' => 'ldapAttributesForUserSearch',
+ 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch'
+ );
+ return $array;
+ }
+
+ /**
* @brief set LDAP configuration with values delivered by an array, not read from configuration
* @param $config array that holds the config parameters in an associated array
* @param &$setParameters optional; array where the set fields will be given to
@@ -218,11 +319,15 @@ class Connection {
return false;
}
- $params = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName',
-
- 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL', 'home_folder_naming_rule' => 'homeFolderNamingRule');
+ $params = $this->getConfigTranslationArray();
foreach($config as $parameter => $value) {
+ if(($parameter == 'homeFolderNamingRule'
+ || (isset($params[$parameter])
+ && $params[$parameter] == 'homeFolderNamingRule'))
+ && !empty($value)) {
+ $value = 'attr:'.$value;
+ }
if(isset($this->config[$parameter])) {
$this->config[$parameter] = $value;
if(is_array($setParameters)) {
@@ -242,11 +347,74 @@ class Connection {
}
/**
+ * @brief saves the current Configuration in the database
+ */
+ public function saveConfiguration() {
+ $trans = array_flip($this->getConfigTranslationArray());
+ foreach($this->config as $key => $value) {
+ \OCP\Util::writeLog('user_ldap', 'LDAP: storing key '.$key.' value '.$value, \OCP\Util::DEBUG);
+ switch ($key) {
+ case 'ldapAgentPassword':
+ $value = base64_encode($value);
+ break;
+ case 'ldapBase':
+ case 'ldapBaseUsers':
+ case 'ldapBaseGroups':
+ case 'ldapAttributesForUserSearch':
+ case 'ldapAttributesForGroupSearch':
+ if(is_array($value)) {
+ $value = implode("\n", $value);
+ }
+ break;
+ case 'ldapIgnoreNamingRules':
+ case 'ldapOverrideUuidAttribute':
+ case 'ldapUuidAttribute':
+ case 'hasPagedResultSupport':
+ continue 2;
+ }
+ if(is_null($value)) {
+ $value = '';
+ }
+
+ $this->setValue($trans[$key], $value);
+ }
+ $this->clearCache();
+ }
+
+ /**
+ * @brief get the current LDAP configuration
+ * @return array
+ */
+ public function getConfiguration() {
+ $this->readConfiguration();
+ $trans = $this->getConfigTranslationArray();
+ $config = array();
+ foreach($trans as $dbKey => $classKey) {
+ if($classKey == 'homeFolderNamingRule') {
+ if(strpos($this->config[$classKey], 'attr:') === 0) {
+ $config[$dbKey] = substr($this->config[$classKey], 5);
+ } else {
+ $config[$dbKey] = '';
+ }
+ continue;
+ } else if((strpos($classKey, 'ldapBase') !== false)
+ || (strpos($classKey, 'ldapAttributes') !== false)) {
+ $config[$dbKey] = implode("\n", $this->config[$classKey]);
+ continue;
+ }
+ $config[$dbKey] = $this->config[$classKey];
+ }
+
+ return $config;
+ }
+
+ /**
* @brief Validates the user specified configuration
* @returns true if configuration seems OK, false otherwise
*/
private function validateConfiguration() {
- //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message
+ // first step: "soft" checks: settings that are not really
+ // necessary, but advisable. If left empty, give an info message
if(empty($this->config['ldapBaseUsers'])) {
\OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO);
$this->config['ldapBaseUsers'] = $this->config['ldapBase'];
@@ -256,14 +424,38 @@ class Connection {
$this->config['ldapBaseGroups'] = $this->config['ldapBase'];
}
if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) {
- \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'No group filter is specified, LDAP group feature will not be used.',
+ \OCP\Util::INFO);
+ }
+ if(!in_array($this->config['ldapUuidAttribute'], array('auto', 'entryuuid', 'nsuniqueid', 'objectguid'))
+ && (!is_null($this->configID))) {
+ \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', 'auto');
+ \OCP\Util::writeLog('user_ldap',
+ 'Illegal value for the UUID Attribute, reset to autodetect.',
+ \OCP\Util::INFO);
+ }
+ if(empty($this->config['ldapBackupPort'])) {
+ //force default
+ $this->config['ldapBackupPort'] = $this->config['ldapPort'];
+ }
+ foreach(array('ldapAttributesForUserSearch', 'ldapAttributesForGroupSearch') as $key) {
+ if(is_array($this->config[$key])
+ && count($this->config[$key]) == 1
+ && empty($this->config[$key][0])) {
+ $this->config[$key] = array();
+ }
}
- if(!in_array($this->config['ldapUuidAttribute'], array('auto','entryuuid', 'nsuniqueid', 'objectguid')) && (!is_null($this->configID))) {
- \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', 'auto');
- \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO);
+ if((strpos($this->config['ldapHost'], 'ldaps') === 0)
+ && $this->config['ldapTLS']) {
+ $this->config['ldapTLS'] = false;
+ \OCP\Util::writeLog('user_ldap',
+ 'LDAPS (already using secure connection) and TLS do not work together. Switched off TLS.',
+ \OCP\Util::INFO);
}
+
//second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning.
$configurationOK = true;
if(empty($this->config['ldapHost'])) {
@@ -276,20 +468,28 @@ class Connection {
}
if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword']))
|| (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) {
- \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.',
+ \OCP\Util::WARN);
$configurationOK = false;
}
//TODO: check if ldapAgentName is in DN form
- if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) {
+ if(empty($this->config['ldapBase'])
+ && (empty($this->config['ldapBaseUsers'])
+ && empty($this->config['ldapBaseGroups']))) {
\OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN);
$configurationOK = false;
}
if(empty($this->config['ldapUserDisplayName'])) {
- \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'No user display name attribute specified, won`t connect.',
+ \OCP\Util::WARN);
$configurationOK = false;
}
if(empty($this->config['ldapGroupDisplayName'])) {
- \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'No group display name attribute specified, won`t connect.',
+ \OCP\Util::WARN);
$configurationOK = false;
}
if(empty($this->config['ldapLoginFilter'])) {
@@ -297,7 +497,9 @@ class Connection {
$configurationOK = false;
}
if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) {
- \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'Login filter does not contain %uid place holder, won`t connect.',
+ \OCP\Util::WARN);
\OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG);
$configurationOK = false;
}
@@ -306,9 +508,50 @@ class Connection {
}
/**
+ * @returns an associative array with the default values. Keys are correspond
+ * to config-value entries in the database table
+ */
+ public function getDefaults() {
+ return array(
+ 'ldap_host' => '',
+ 'ldap_port' => '389',
+ 'ldap_backup_host' => '',
+ 'ldap_backup_port' => '',
+ 'ldap_override_main_server' => '',
+ 'ldap_dn' => '',
+ 'ldap_agent_password' => '',
+ 'ldap_base' => '',
+ 'ldap_base_users' => '',
+ 'ldap_base_groups' => '',
+ 'ldap_userlist_filter' => 'objectClass=person',
+ 'ldap_login_filter' => 'uid=%uid',
+ 'ldap_group_filter' => 'objectClass=posixGroup',
+ 'ldap_display_name' => 'cn',
+ 'ldap_group_display_name' => 'cn',
+ 'ldap_tls' => 1,
+ 'ldap_nocase' => 0,
+ 'ldap_quota_def' => '',
+ 'ldap_quota_attr' => '',
+ 'ldap_email_attr' => '',
+ 'ldap_group_member_assoc_attribute' => 'uniqueMember',
+ 'ldap_cache_ttl' => 600,
+ 'ldap_uuid_attribute' => 'auto',
+ 'ldap_override_uuid_attribute' => 0,
+ 'home_folder_naming_rule' => '',
+ 'ldap_turn_off_cert_check' => 0,
+ 'ldap_configuration_active' => 1,
+ 'ldap_attributes_for_user_search' => '',
+ 'ldap_attributes_for_group_search' => '',
+ );
+ }
+
+ /**
* Connects and Binds to LDAP
*/
private function establishConnection() {
+ if(!$this->config['ldapConfigurationActive']) {
+ return null;
+ }
static $phpLDAPinstalled = true;
if(!$phpLDAPinstalled) {
return false;
@@ -320,27 +563,58 @@ class Connection {
if(!$this->ldapConnectionRes) {
if(!function_exists('ldap_connect')) {
$phpLDAPinstalled = false;
- \OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', \OCP\Util::ERROR);
+ \OCP\Util::writeLog('user_ldap',
+ 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.',
+ \OCP\Util::ERROR);
return false;
}
if($this->config['turnOffCertCheck']) {
if(putenv('LDAPTLS_REQCERT=never')) {
- \OCP\Util::writeLog('user_ldap', 'Turned off SSL certificate validation successfully.', \OCP\Util::WARN);
+ \OCP\Util::writeLog('user_ldap',
+ 'Turned off SSL certificate validation successfully.',
+ \OCP\Util::WARN);
} else {
\OCP\Util::writeLog('user_ldap', 'Could not turn off SSL certificate validation.', \OCP\Util::WARN);
}
}
- $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']);
- if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
- if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
- if($this->config['ldapTLS']) {
- ldap_start_tls($this->ldapConnectionRes);
- }
+ if(!$this->config['ldapOverrideMainServer'] && !$this->getFromCache('overrideMainServer')) {
+ $this->doConnect($this->config['ldapHost'], $this->config['ldapPort']);
+ $bindStatus = $this->bind();
+ $error = is_resource($this->ldapConnectionRes) ? ldap_errno($this->ldapConnectionRes) : -1;
+ } else {
+ $bindStatus = false;
+ $error = null;
+ }
+
+ $error = null;
+ //if LDAP server is not reachable, try the Backup (Replica!) Server
+ if((!$bindStatus && ($error == -1))
+ || $this->config['ldapOverrideMainServer']
+ || $this->getFromCache('overrideMainServer')) {
+ $this->doConnect($this->config['ldapBackupHost'], $this->config['ldapBackupPort']);
+ $bindStatus = $this->bind();
+ if($bindStatus && $error == -1) {
+ //when bind to backup server succeeded and failed to main server,
+ //skip contacting him until next cache refresh
+ $this->writeToCache('overrideMainServer', true);
}
}
+ return $bindStatus;
+ }
+ }
- return $this->bind();
+ private function doConnect($host, $port) {
+ if(empty($host)) {
+ return false;
+ }
+ $this->ldapConnectionRes = ldap_connect($host, $port);
+ if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+ if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
+ if($this->config['ldapTLS']) {
+ ldap_start_tls($this->ldapConnectionRes);
+ }
+ }
}
}
@@ -348,9 +622,18 @@ class Connection {
* Binds to LDAP
*/
public function bind() {
- $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']);
+ if(!$this->config['ldapConfigurationActive']) {
+ return false;
+ }
+ $cr = $this->getConnectionResource();
+ if(!is_resource($cr)) {
+ return false;
+ }
+ $ldapLogin = @ldap_bind($cr, $this->config['ldapAgentName'], $this->config['ldapAgentPassword']);
if(!$ldapLogin) {
- \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR);
+ \OCP\Util::writeLog('user_ldap',
+ 'Bind failed: ' . ldap_errno($cr) . ': ' . ldap_error($cr),
+ \OCP\Util::ERROR);
$this->ldapConnectionRes = null;
return false;
}
diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php
new file mode 100644
index 00000000000..308da3ef724
--- /dev/null
+++ b/apps/user_ldap/lib/helper.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * ownCloud – LDAP Helper
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+class Helper {
+
+ /**
+ * @brief returns prefixes for each saved LDAP/AD server configuration.
+ * @param bool optional, whether only active configuration shall be
+ * retrieved, defaults to false
+ * @return array with a list of the available prefixes
+ *
+ * Configuration prefixes are used to set up configurations for n LDAP or
+ * AD servers. Since configuration is stored in the database, table
+ * appconfig under appid user_ldap, the common identifiers in column
+ * 'configkey' have a prefix. The prefix for the very first server
+ * configuration is empty.
+ * Configkey Examples:
+ * Server 1: ldap_login_filter
+ * Server 2: s1_ldap_login_filter
+ * Server 3: s2_ldap_login_filter
+ *
+ * The prefix needs to be passed to the constructor of Connection class,
+ * except the default (first) server shall be connected to.
+ *
+ */
+ static public function getServerConfigurationPrefixes($activeConfigurations = false) {
+ $referenceConfigkey = 'ldap_configuration_active';
+
+ $query = '
+ SELECT DISTINCT `configkey`
+ FROM `*PREFIX*appconfig`
+ WHERE `configkey` LIKE ?
+ ';
+ if($activeConfigurations) {
+ $query .= ' AND `configvalue` = \'1\'';
+ }
+ $query = \OCP\DB::prepare($query);
+
+ $serverConfigs = $query->execute(array('%'.$referenceConfigkey))->fetchAll();
+ $prefixes = array();
+
+ foreach($serverConfigs as $serverConfig) {
+ $len = strlen($serverConfig['configkey']) - strlen($referenceConfigkey);
+ $prefixes[] = substr($serverConfig['configkey'], 0, $len);
+ }
+
+ return $prefixes;
+ }
+
+ /**
+ * @brief deletes a given saved LDAP/AD server configuration.
+ * @param string the configuration prefix of the config to delete
+ * @return bool true on success, false otherwise
+ */
+ static public function deleteServerConfiguration($prefix) {
+ //just to be on the safe side
+ \OCP\User::checkAdminUser();
+
+ if(!in_array($prefix, self::getServerConfigurationPrefixes())) {
+ return false;
+ }
+
+ $query = \OCP\DB::prepare('
+ DELETE
+ FROM `*PREFIX*appconfig`
+ WHERE `configkey` LIKE ?
+ AND `appid` = \'user_ldap\'
+ AND `configkey` NOT IN (\'enabled\', \'installed_version\', \'types\', \'bgjUpdateGroupsLastRun\')
+ ');
+ $res = $query->execute(array($prefix.'%'));
+
+ if(\OCP\DB::isError($res)) {
+ return false;
+ }
+
+ if($res->numRows() == 0) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php
index b265a8339ef..094d11db3d5 100644
--- a/apps/user_ldap/lib/jobs.php
+++ b/apps/user_ldap/lib/jobs.php
@@ -42,7 +42,9 @@ class Jobs {
$actualGroups = self::getGroupBE()->getGroups();
if(empty($actualGroups) && empty($knownGroups)) {
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.',
+ \OCP\Util::INFO);
\OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time());
return;
}
@@ -75,19 +77,25 @@ class Jobs {
$hasChanged = false;
foreach(array_diff($knownUsers, $actualUsers) as $removedUser) {
\OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group));
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".',
+ \OCP\Util::INFO);
$hasChanged = true;
}
foreach(array_diff($actualUsers, $knownUsers) as $addedUser) {
\OCP\Util::emitHook('OC_User', 'post_addFromGroup', array('uid' => $addedUser, 'gid' => $group));
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".',
+ \OCP\Util::INFO);
$hasChanged = true;
}
if($hasChanged) {
$query->execute(array(serialize($actualUsers), $group));
}
}
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with known Groups.', \OCP\Util::DEBUG);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – FINISHED dealing with known Groups.',
+ \OCP\Util::DEBUG);
}
static private function handleCreatedGroups($createdGroups) {
@@ -98,11 +106,15 @@ class Jobs {
VALUES (?, ?)
');
foreach($createdGroups as $createdGroup) {
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.',
+ \OCP\Util::INFO);
$users = serialize(self::getGroupBE()->usersInGroup($createdGroup));
$query->execute(array($createdGroup, $users));
}
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with created Groups.', \OCP\Util::DEBUG);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – FINISHED dealing with created Groups.',
+ \OCP\Util::DEBUG);
}
static private function handleRemovedGroups($removedGroups) {
@@ -113,10 +125,14 @@ class Jobs {
WHERE `owncloudname` = ?
');
foreach($removedGroups as $removedGroup) {
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.', \OCP\Util::INFO);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.',
+ \OCP\Util::INFO);
$query->execute(array($removedGroup));
}
- \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with removed groups.', \OCP\Util::DEBUG);
+ \OCP\Util::writeLog('user_ldap',
+ 'bgJ "updateGroups" – FINISHED dealing with removed groups.',
+ \OCP\Util::DEBUG);
}
static private function getConnector() {
@@ -154,4 +170,4 @@ class Jobs {
return self::$groupsFromDB;
}
-} \ No newline at end of file
+}
diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/proxy.php
new file mode 100644
index 00000000000..c80e2163475
--- /dev/null
+++ b/apps/user_ldap/lib/proxy.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * ownCloud – LDAP Backend Proxy
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+abstract class Proxy {
+ static private $connectors = array();
+
+ public function __construct() {
+ $this->cache = \OC_Cache::getGlobalCache();
+ }
+
+ private function addConnector($configPrefix) {
+ self::$connectors[$configPrefix] = new \OCA\user_ldap\lib\Connection($configPrefix);
+ }
+
+ protected function getConnector($configPrefix) {
+ if(!isset(self::$connectors[$configPrefix])) {
+ $this->addConnector($configPrefix);
+ }
+ return self::$connectors[$configPrefix];
+ }
+
+ protected function getConnectors() {
+ return self::$connectors;
+ }
+
+ protected function getUserCacheKey($uid) {
+ return 'user-'.$uid.'-lastSeenOn';
+ }
+
+ protected function getGroupCacheKey($gid) {
+ return 'group-'.$gid.'-lastSeenOn';
+ }
+
+ abstract protected function callOnLastSeenOn($id, $method, $parameters);
+ abstract protected function walkBackends($id, $method, $parameters);
+
+ /**
+ * @brief Takes care of the request to the User backend
+ * @param $uid string, the uid connected to the request
+ * @param $method string, the method of the user backend that shall be called
+ * @param $parameters an array of parameters to be passed
+ * @return mixed, the result of the specified method
+ */
+ protected function handleRequest($id, $method, $parameters) {
+ if(!$result = $this->callOnLastSeenOn($id, $method, $parameters)) {
+ $result = $this->walkBackends($id, $method, $parameters);
+ }
+ return $result;
+ }
+
+ private function getCacheKey($key) {
+ $prefix = 'LDAP-Proxy-';
+ if(is_null($key)) {
+ return $prefix;
+ }
+ return $prefix.md5($key);
+ }
+
+ public function getFromCache($key) {
+ if(!$this->isCached($key)) {
+ return null;
+ }
+ $key = $this->getCacheKey($key);
+
+ return unserialize(base64_decode($this->cache->get($key)));
+ }
+
+ public function isCached($key) {
+ $key = $this->getCacheKey($key);
+ return $this->cache->hasKey($key);
+ }
+
+ public function writeToCache($key, $value) {
+ $key = $this->getCacheKey($key);
+ $value = base64_encode(serialize($value));
+ $this->cache->set($key, $value, '2592000');
+ }
+
+ public function clearCache() {
+ $this->cache->clear($this->getCacheKey(null));
+ }
+} \ No newline at end of file
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
index f765151456a..05497ae8a33 100644
--- a/apps/user_ldap/settings.php
+++ b/apps/user_ldap/settings.php
@@ -20,58 +20,39 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
-$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl', 'home_folder_naming_rule');
+
+OC_Util::checkAdminUser();
+
+$params = array('ldap_host', 'ldap_port', 'ldap_backup_host',
+ 'ldap_backup_port', 'ldap_override_main_server', 'ldap_dn',
+ 'ldap_agent_password', 'ldap_base', 'ldap_base_users',
+ 'ldap_base_groups', 'ldap_userlist_filter',
+ 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name',
+ 'ldap_group_display_name', 'ldap_tls',
+ 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def',
+ 'ldap_quota_attr', 'ldap_email_attr',
+ 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl',
+ 'home_folder_naming_rule'
+ );
OCP\Util::addscript('user_ldap', 'settings');
OCP\Util::addstyle('user_ldap', 'settings');
-if ($_POST) {
- foreach($params as $param) {
- if(isset($_POST[$param])) {
- if('ldap_agent_password' == $param) {
- OCP\Config::setAppValue('user_ldap', $param, base64_encode($_POST[$param]));
- } elseif('ldap_cache_ttl' == $param) {
- if(OCP\Config::getAppValue('user_ldap', $param,'') != $_POST[$param]) {
- $ldap = new \OCA\user_ldap\lib\Connection('user_ldap');
- $ldap->clearCache();
- OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]);
- }
- } elseif('home_folder_naming_rule' == $param) {
- $value = empty($_POST[$param]) ? 'opt:username' : 'attr:'.$_POST[$param];
- OCP\Config::setAppValue('user_ldap', $param, $value);
- } else {
- OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]);
- }
- }
- elseif('ldap_tls' == $param) {
- // unchecked checkboxes are not included in the post paramters
- OCP\Config::setAppValue('user_ldap', $param, 0);
- }
- elseif('ldap_nocase' == $param) {
- OCP\Config::setAppValue('user_ldap', $param, 0);
- }
- elseif('ldap_turn_off_cert_check' == $param) {
- OCP\Config::setAppValue('user_ldap', $param, 0);
- }
- }
-}
-
// fill template
-$tmpl = new OCP\Template( 'user_ldap', 'settings');
-foreach($params as $param) {
- $value = OCP\Config::getAppValue('user_ldap', $param,'');
- $tmpl->assign($param, $value);
+$tmpl = new OCP\Template('user_ldap', 'settings');
+
+$prefixes = \OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes();
+$tmpl->assign('serverConfigurationPrefixes', $prefixes);
+
+// assign default values
+if(!isset($ldap)) {
+ $ldap = new \OCA\user_ldap\lib\Connection();
+}
+$defaults = $ldap->getDefaults();
+foreach($defaults as $key => $default) {
+ $tmpl->assign($key.'_default', $default);
}
-// settings with default values
-$tmpl->assign( 'ldap_port', OCP\Config::getAppValue('user_ldap', 'ldap_port', '389'));
-$tmpl->assign( 'ldap_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid'));
-$tmpl->assign( 'ldap_group_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', 'cn'));
-$tmpl->assign( 'ldap_group_member_assoc_attribute', OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember'));
-$tmpl->assign( 'ldap_agent_password', base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password')));
-$tmpl->assign( 'ldap_cache_ttl', OCP\Config::getAppValue('user_ldap', 'ldap_cache_ttl', '600'));
-$hfnr = OCP\Config::getAppValue('user_ldap', 'home_folder_naming_rule', 'opt:username');
-$hfnr = ($hfnr == 'opt:username') ? '' : substr($hfnr, strlen('attr:'));
-$tmpl->assign( 'home_folder_naming_rule', $hfnr, '');
+// $tmpl->assign();
return $tmpl->fetchPage();
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 3a653ad7208..d3c2c298904 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -4,32 +4,99 @@
<li><a href="#ldapSettings-1">LDAP Basic</a></li>
<li><a href="#ldapSettings-2">Advanced</a></li>
</ul>
+ <?php if(OCP\App::isEnabled('user_webdavauth')) {
+ print_unescaped('<p class="ldapwarning">'.$l->t('<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'</p>');
+ }
+ if(!function_exists('ldap_connect')) {
+ print_unescaped('<p class="ldapwarning">'.$l->t('<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.').'</p>');
+ }
+ ?>
<fieldset id="ldapSettings-1">
- <p><label for="ldap_host"><?php echo $l->t('Host');?></label><input type="text" id="ldap_host" name="ldap_host" value="<?php echo $_['ldap_host']; ?>" title="<?php echo $l->t('You can omit the protocol, except you require SSL. Then start with ldaps://');?>"></p>
- <p><label for="ldap_base"><?php echo $l->t('Base DN');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" title="<?php echo $l->t('You can specify Base DN for users and groups in the Advanced tab');?>" /></p>
- <p><label for="ldap_dn"><?php echo $l->t('User DN');?></label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" title="<?php echo $l->t('The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.');?>" /></p>
- <p><label for="ldap_agent_password"><?php echo $l->t('Password');?></label><input type="password" id="ldap_agent_password" name="ldap_agent_password" value="<?php echo $_['ldap_agent_password']; ?>" title="<?php echo $l->t('For anonymous access, leave DN and Password empty.');?>" /></p>
- <p><label for="ldap_login_filter"><?php echo $l->t('User Login Filter');?></label><input type="text" id="ldap_login_filter" name="ldap_login_filter" value="<?php echo $_['ldap_login_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action.');?>" /><br /><small><?php echo $l->t('use %%uid placeholder, e.g. "uid=%%uid"');?></small></p>
- <p><label for="ldap_userlist_filter"><?php echo $l->t('User List Filter');?></label><input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter" value="<?php echo $_['ldap_userlist_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when retrieving users.');?>" /><br /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=person".');?></small></p>
- <p><label for="ldap_group_filter"><?php echo $l->t('Group Filter');?></label><input type="text" id="ldap_group_filter" name="ldap_group_filter" value="<?php echo $_['ldap_group_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when retrieving groups.');?>" /><br /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=posixGroup".');?></small></p>
+ <p><label for="ldap_serverconfig_chooser"><?php p($l->t('Server configuration'));?></label>
+ <select id="ldap_serverconfig_chooser" name="ldap_serverconfig_chooser">
+ <?php if(count($_['serverConfigurationPrefixes']) == 0 ) {
+ ?>
+ <option value="" selected>1. Server</option>');
+ <?php
+ } else {
+ $i = 1;
+ $sel = ' selected';
+ foreach($_['serverConfigurationPrefixes'] as $prefix) {
+ ?>
+ <option value="<?php p($prefix); ?>"<?php p($sel); ?>><?php p($i++); ?>. Server</option>
+ <?php
+ }
+ }
+ ?>
+ <option value="NEW"><?php p($l->t('Add Server Configuration'));?></option>
+ </select>
+ <button id="ldap_action_delete_configuration"
+ name="ldap_action_delete_configuration">Delete Configuration</button>
+ </p>
+ <p><label for="ldap_host"><?php p($l->t('Host'));?></label>
+ <input type="text" id="ldap_host" name="ldap_host" data-default="<?php p($_['ldap_host_default']); ?>"
+ title="<?php p($l->t('You can omit the protocol, except you require SSL. Then start with ldaps://'));?>"></p>
+ <p><label for="ldap_base"><?php p($l->t('Base DN'));?></label>
+ <textarea id="ldap_base" name="ldap_base" placeholder="<?php p($l->t('One Base DN per line'));?>"
+ title="<?php p($l->t('You can specify Base DN for users and groups in the Advanced tab'));?>"
+ data-default="<?php p($_['ldap_base_default']); ?>" ></textarea></p>
+ <p><label for="ldap_dn"><?php p($l->t('User DN'));?></label>
+ <input type="text" id="ldap_dn" name="ldap_dn" data-default="<?php p($_['ldap_dn_default']); ?>"
+ title="<?php p($l->t('The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.'));?>" /></p>
+ <p><label for="ldap_agent_password"><?php p($l->t('Password'));?></label>
+ <input type="password" id="ldap_agent_password" name="ldap_agent_password"
+ data-default="<?php p($_['ldap_agent_password_default']); ?>"
+ title="<?php p($l->t('For anonymous access, leave DN and Password empty.'));?>" /></p>
+ <p><label for="ldap_login_filter"><?php p($l->t('User Login Filter'));?></label>
+ <input type="text" id="ldap_login_filter" name="ldap_login_filter"
+ data-default="<?php p($_['ldap_login_filter_default']); ?>"
+ title="<?php p($l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action.'));?>" />
+ <br /><small><?php p($l->t('use %%uid placeholder, e.g. "uid=%%uid"'));?></small></p>
+ <p><label for="ldap_userlist_filter"><?php p($l->t('User List Filter'));?></label>
+ <input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter"
+ data-default="<?php p($_['ldap_userlist_filter_default']); ?>"
+ title="<?php p($l->t('Defines the filter to apply, when retrieving users.'));?>" />
+ <br /><small><?php p($l->t('without any placeholder, e.g. "objectClass=person".'));?></small></p>
+ <p><label for="ldap_group_filter"><?php p($l->t('Group Filter'));?></label>
+ <input type="text" id="ldap_group_filter" name="ldap_group_filter"
+ data-default="<?php p($_['ldap_group_filter_default']); ?>"
+ title="<?php p($l->t('Defines the filter to apply, when retrieving groups.'));?>" />
+ <br /><small><?php p($l->t('without any placeholder, e.g. "objectClass=posixGroup".'));?></small></p>
</fieldset>
<fieldset id="ldapSettings-2">
- <p><label for="ldap_port"><?php echo $l->t('Port');?></label><input type="text" id="ldap_port" name="ldap_port" value="<?php echo $_['ldap_port']; ?>" /></p>
- <p><label for="ldap_base_users"><?php echo $l->t('Base User Tree');?></label><input type="text" id="ldap_base_users" name="ldap_base_users" value="<?php echo $_['ldap_base_users']; ?>" /></p>
- <p><label for="ldap_base_groups"><?php echo $l->t('Base Group Tree');?></label><input type="text" id="ldap_base_groups" name="ldap_base_groups" value="<?php echo $_['ldap_base_groups']; ?>" /></p>
- <p><label for="ldap_group_member_assoc_attribute"><?php echo $l->t('Group-Member association');?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute"><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'uniqueMember')) echo ' selected'; ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'memberUid')) echo ' selected'; ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'member')) echo ' selected'; ?>>member (AD)</option></select></p>
- <p><label for="ldap_tls"><?php echo $l->t('Use TLS');?></label><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1"<?php if ($_['ldap_tls']) echo ' checked'; ?> title="<?php echo $l->t('Do not use it for SSL connections, it will fail.');?>" /></p>
- <p><label for="ldap_nocase"><?php echo $l->t('Case insensitve LDAP server (Windows)');?></label> <input type="checkbox" id="ldap_nocase" name="ldap_nocase" value="1"<?php if (isset($_['ldap_nocase']) && ($_['ldap_nocase'])) echo ' checked'; ?>></p>
- <p><label for="ldap_turn_off_cert_check"><?php echo $l->t('Turn off SSL certificate validation.');?></label><input type="checkbox" id="ldap_turn_off_cert_check" name="ldap_turn_off_cert_check" title="<?php echo $l->t('If connection only works with this option, import the LDAP server\'s SSL certificate in your ownCloud server.');?>" value="1"<?php if ($_['ldap_turn_off_cert_check']) echo ' checked'; ?>><br/><small><?php echo $l->t('Not recommended, use for testing only.');?></small></p>
- <p><label for="ldap_display_name"><?php echo $l->t('User Display Name Field');?></label><input type="text" id="ldap_display_name" name="ldap_display_name" value="<?php echo $_['ldap_display_name']; ?>" title="<?php echo $l->t('The LDAP attribute to use to generate the user`s ownCloud name.');?>" /></p>
- <p><label for="ldap_group_display_name"><?php echo $l->t('Group Display Name Field');?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" value="<?php echo $_['ldap_group_display_name']; ?>" title="<?php echo $l->t('The LDAP attribute to use to generate the groups`s ownCloud name.');?>" /></p>
- <p><label for="ldap_quota_attr">Quota Field</label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" value="<?php echo $_['ldap_quota_attr']; ?>" /></p>
- <p><label for="ldap_quota_def">Quota Default</label><input type="text" id="ldap_quota_def" name="ldap_quota_def" value="<?php if (isset($_['ldap_quota_def'])) echo $_['ldap_quota_def']; ?>" title="<?php echo $l->t('in bytes');?>" /></p>
- <p><label for="ldap_email_attr">Email Field</label><input type="text" id="ldap_email_attr" name="ldap_email_attr" value="<?php echo $_['ldap_email_attr']; ?>" /></p>
- <p><label for="ldap_cache_ttl">Cache Time-To-Live</label><input type="text" id="ldap_cache_ttl" name="ldap_cache_ttl" value="<?php echo $_['ldap_cache_ttl']; ?>" title="<?php echo $l->t('in seconds. A change empties the cache.');?>" /></p>
- <p><label for="home_folder_naming_rule">User Home Folder Naming Rule</label><input type="text" id="home_folder_naming_rule" name="home_folder_naming_rule" value="<?php echo $_['home_folder_naming_rule']; ?>" title="<?php echo $l->t('Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute.');?>" /></p>
+ <div id="ldapAdvancedAccordion">
+ <h3><?php p($l->t('Connection Settings'));?></h3>
+ <div>
+ <p><label for="ldap_configuration_active"><?php p($l->t('Configuration Active'));?></label><input type="checkbox" id="ldap_configuration_active" name="ldap_configuration_active" value="1" data-default="<?php p($_['ldap_configuration_active_default']); ?>" title="<?php p($l->t('When unchecked, this configuration will be skipped.'));?>" /></p>
+ <p><label for="ldap_port"><?php p($l->t('Port'));?></label><input type="number" id="ldap_port" name="ldap_port" data-default="<?php p($_['ldap_port_default']); ?>" /></p>
+ <p><label for="ldap_backup_host"><?php p($l->t('Backup (Replica) Host'));?></label><input type="text" id="ldap_backup_host" name="ldap_backup_host" data-default="<?php p($_['ldap_backup_host_default']); ?>" title="<?php p($l->t('Give an optional backup host. It must be a replica of the main LDAP/AD server.'));?>"></p>
+ <p><label for="ldap_backup_port"><?php p($l->t('Backup (Replica) Port'));?></label><input type="number" id="ldap_backup_port" name="ldap_backup_port" data-default="<?php p($_['ldap_backup_port_default']); ?>" /></p>
+ <p><label for="ldap_override_main_server"><?php p($l->t('Disable Main Server'));?></label><input type="checkbox" id="ldap_override_main_server" name="ldap_override_main_server" value="1" data-default="<?php p($_['ldap_override_main_server_default']); ?>" title="<?php p($l->t('When switched on, ownCloud will only connect to the replica server.'));?>" /></p>
+ <p><label for="ldap_tls"><?php p($l->t('Use TLS'));?></label><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1" data-default="<?php p($_['ldap_tls_default']); ?>" title="<?php p($l->t('Do not use it additionally for LDAPS connections, it will fail.'));?>" /></p>
+ <p><label for="ldap_nocase"><?php p($l->t('Case insensitve LDAP server (Windows)'));?></label><input type="checkbox" id="ldap_nocase" name="ldap_nocase" data-default="<?php p($_['ldap_nocase_default']); ?>" value="1"<?php if (isset($_['ldap_nocase']) && ($_['ldap_nocase'])) p(' checked'); ?>></p>
+ <p><label for="ldap_turn_off_cert_check"><?php p($l->t('Turn off SSL certificate validation.'));?></label><input type="checkbox" id="ldap_turn_off_cert_check" name="ldap_turn_off_cert_check" title="<?php p($l->t('If connection only works with this option, import the LDAP server\'s SSL certificate in your ownCloud server.'));?>" data-default="<?php p($_['ldap_turn_off_cert_check_default']); ?>" value="1"><br/><small><?php p($l->t('Not recommended, use for testing only.'));?></small></p>
+ <p><label for="ldap_cache_ttl"><?php p($l->t('Cache Time-To-Live'));?></label><input type="number" id="ldap_cache_ttl" name="ldap_cache_ttl" title="<?php p($l->t('in seconds. A change empties the cache.'));?>" data-default="<?php p($_['ldap_cache_ttl_default']); ?>" /></p>
+ </div>
+ <h3><?php p($l->t('Directory Settings'));?></h3>
+ <div>
+ <p><label for="ldap_display_name"><?php p($l->t('User Display Name Field'));?></label><input type="text" id="ldap_display_name" name="ldap_display_name" data-default="<?php p($_['ldap_display_name_default']); ?>" title="<?php p($l->t('The LDAP attribute to use to generate the user`s ownCloud name.'));?>" /></p>
+ <p><label for="ldap_base_users"><?php p($l->t('Base User Tree'));?></label><textarea id="ldap_base_users" name="ldap_base_users" placeholder="<?php p($l->t('One User Base DN per line'));?>" data-default="<?php p($_['ldap_base_users_default']); ?>" title="<?php p($l->t('Base User Tree'));?>"></textarea></p>
+ <p><label for="ldap_attributes_for_user_search"><?php p($l->t('User Search Attributes'));?></label><textarea id="ldap_attributes_for_user_search" name="ldap_attributes_for_user_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_user_search_default']); ?>" title="<?php p($l->t('User Search Attributes'));?>"></textarea></p>
+ <p><label for="ldap_group_display_name"><?php p($l->t('Group Display Name Field'));?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" data-default="<?php p($_['ldap_group_display_name_default']); ?>" title="<?php p($l->t('The LDAP attribute to use to generate the groups`s ownCloud name.'));?>" /></p>
+ <p><label for="ldap_base_groups"><?php p($l->t('Base Group Tree'));?></label><textarea id="ldap_base_groups" name="ldap_base_groups" placeholder="<?php p($l->t('One Group Base DN per line'));?>" data-default="<?php p($_['ldap_base_groups_default']); ?>" title="<?php p($l->t('Base Group Tree'));?>"></textarea></p>
+ <p><label for="ldap_attributes_for_group_search"><?php p($l->t('Group Search Attributes'));?></label><textarea id="ldap_attributes_for_group_search" name="ldap_attributes_for_group_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_group_search_default']); ?>" title="<?php p($l->t('Group Search Attributes'));?>"></textarea></p>
+ <p><label for="ldap_group_member_assoc_attribute"><?php p($l->t('Group-Member association'));?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute" data-default="<?php p($_['ldap_group_member_assoc_attribute_default']); ?>" ><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'uniqueMember')) p(' selected'); ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'memberUid')) p(' selected'); ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'member')) p(' selected'); ?>>member (AD)</option></select></p>
+ </div>
+ <h3><?php p($l->t('Special Attributes'));?></h3>
+ <div>
+ <p><label for="ldap_quota_attr"><?php p($l->t('Quota Field'));?></label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" data-default="<?php p($_['ldap_quota_attr_default']); ?>"/></p>
+ <p><label for="ldap_quota_def"><?php p($l->t('Quota Default'));?></label><input type="text" id="ldap_quota_def" name="ldap_quota_def" data-default="<?php p($_['ldap_quota_def_default']); ?>" title="<?php p($l->t('in bytes'));?>" /></p>
+ <p><label for="ldap_email_attr"><?php p($l->t('Email Field'));?></label><input type="text" id="ldap_email_attr" name="ldap_email_attr" data-default="<?php p($_['ldap_email_attr_default']); ?>" /></p>
+ <p><label for="home_folder_naming_rule"><?php p($l->t('User Home Folder Naming Rule'));?></label><input type="text" id="home_folder_naming_rule" name="home_folder_naming_rule" title="<?php p($l->t('Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute.'));?>" data-default="<?php p($_['home_folder_naming_rule_default']); ?>" /></p>
+ </div>
+ </div>
</fieldset>
- <input type="submit" value="Save" /> <button id="ldap_action_test_connection" name="ldap_action_test_connection">Test Configuration</button> <a href="http://owncloud.org/support/ldap-backend/" target="_blank"><img src="<?php echo OCP\Util::imagePath('','actions/info.png'); ?>" style="height:1.75ex" /> <?php echo $l->t('Help');?></a>
+ <input id="ldap_submit" type="submit" value="Save" /> <button id="ldap_action_test_connection" name="ldap_action_test_connection"><?php p($l->t('Test Configuration'));?></button> <a href="http://doc.owncloud.org/server/5.0/admin_manual/auth_ldap.html" target="_blank"><img src="<?php print_unescaped(OCP\Util::imagePath('', 'actions/info.png')); ?>" style="height:1.75ex" /> <?php p($l->t('Help'));?></a>
</div>
</form>
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
index b953127d86e..ae635597b71 100644
--- a/apps/user_ldap/tests/group_ldap.php
+++ b/apps/user_ldap/tests/group_ldap.php
@@ -20,7 +20,7 @@
*
*/
-class Test_Group_Ldap extends UnitTestCase {
+class Test_Group_Ldap extends PHPUnit_Framework_TestCase {
function setUp() {
OC_Group::clearBackends();
}
@@ -29,18 +29,18 @@ class Test_Group_Ldap extends UnitTestCase {
OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP());
$group_ldap = new OCA\user_ldap\GROUP_LDAP();
- $this->assertIsA(OC_Group::getGroups(),gettype(array()));
- $this->assertIsA($group_ldap->getGroups(),gettype(array()));
+ $this->assertIsA(OC_Group::getGroups(), gettype(array()));
+ $this->assertIsA($group_ldap->getGroups(), gettype(array()));
- $this->assertFalse(OC_Group::inGroup('john','dosers'),gettype(false));
- $this->assertFalse($group_ldap->inGroup('john','dosers'),gettype(false));
+ $this->assertFalse(OC_Group::inGroup('john', 'dosers'), gettype(false));
+ $this->assertFalse($group_ldap->inGroup('john', 'dosers'), gettype(false));
//TODO: check also for expected true result. This backend won't be able to do any modifications, maybe use a dummy for this.
- $this->assertIsA(OC_Group::getUserGroups('john doe'),gettype(array()));
- $this->assertIsA($group_ldap->getUserGroups('john doe'),gettype(array()));
+ $this->assertIsA(OC_Group::getUserGroups('john doe'), gettype(array()));
+ $this->assertIsA($group_ldap->getUserGroups('john doe'), gettype(array()));
- $this->assertIsA(OC_Group::usersInGroup('campers'),gettype(array()));
- $this->assertIsA($group_ldap->usersInGroup('campers'),gettype(array()));
+ $this->assertIsA(OC_Group::usersInGroup('campers'), gettype(array()));
+ $this->assertIsA($group_ldap->usersInGroup('campers'), gettype(array()));
}
}
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index 53a65129108..44a19478598 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -29,11 +29,13 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
private function updateQuota($dn) {
$quota = null;
- if(!empty($this->connection->ldapQuotaDefault)) {
- $quota = $this->connection->ldapQuotaDefault;
+ $quotaDefault = $this->connection->ldapQuotaDefault;
+ $quotaAttribute = $this->connection->ldapQuotaAttribute;
+ if(!empty($quotaDefault)) {
+ $quota = $quotaDefault;
}
- if(!empty($this->connection->ldapQuotaAttribute)) {
- $aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute);
+ if(!empty($quotaAttribute)) {
+ $aQuota = $this->readAttribute($dn, $quotaAttribute);
if($aQuota && (count($aQuota) > 0)) {
$quota = $aQuota[0];
@@ -46,8 +48,9 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
private function updateEmail($dn) {
$email = null;
- if(!empty($this->connection->ldapEmailAttribute)) {
- $aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute);
+ $emailAttribute = $this->connection->ldapEmailAttribute;
+ if(!empty($emailAttribute)) {
+ $aEmail = $this->readAttribute($dn, $emailAttribute);
if($aEmail && (count($aEmail) > 0)) {
$email = $aEmail[0];
}
@@ -101,24 +104,35 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
* Get a list of all users.
*/
public function getUsers($search = '', $limit = 10, $offset = 0) {
- $ldap_users = $this->connection->getFromCache('getUsers');
- if(is_null($ldap_users)) {
- $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn'));
- $ldap_users = $this->ownCloudUserNames($ldap_users);
- $this->connection->writeToCache('getUsers', $ldap_users);
- }
- $this->userSearch = $search;
- if(!empty($this->userSearch)) {
- $ldap_users = array_filter($ldap_users, array($this, 'userMatchesFilter'));
+ $cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
+
+ //check if users are cached, if so return
+ $ldap_users = $this->connection->getFromCache($cachekey);
+ if(!is_null($ldap_users)) {
+ return $ldap_users;
}
- if($limit == -1) {
+
+ // if we'd pass -1 to LDAP search, we'd end up in a Protocol
+ // error. With a limit of 0, we get 0 results. So we pass null.
+ if($limit <= 0) {
$limit = null;
}
- return array_slice($ldap_users, $offset, $limit);
- }
+ $filter = $this->combineFilterWithAnd(array(
+ $this->connection->ldapUserFilter,
+ $this->getFilterPartForUserSearch($search)
+ ));
+
+ \OCP\Util::writeLog('user_ldap',
+ 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
+ \OCP\Util::DEBUG);
+ //do the search and translate results to owncloud names
+ $ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'),
+ $limit, $offset);
+ $ldap_users = $this->ownCloudUserNames($ldap_users);
+ \OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
- public function userMatchesFilter($user) {
- return (strripos($user, $this->userSearch) !== false);
+ $this->connection->writeToCache($cachekey, $ldap_users);
+ return $ldap_users;
}
/**
@@ -138,14 +152,14 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
return false;
}
- //if user really still exists, we will be able to read his objectclass
- $objcs = $this->readAttribute($dn, 'objectclass');
- if(!$objcs || empty($objcs)) {
+ //check if user really still exists by reading its entry
+ if(!is_array($this->readAttribute($dn, ''))) {
$this->connection->writeToCache('userExists'.$uid, false);
return false;
}
$this->connection->writeToCache('userExists'.$uid, true);
+ $this->updateQuota($dn);
return true;
}
@@ -161,41 +175,84 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
}
/**
- * @brief determine the user's home directory
- * @param string $uid the owncloud username
+ * @brief get the user's home directory
+ * @param string $uid the username
* @return boolean
*/
- private function determineHomeDir($uid) {
+ public function getHome($uid) {
+ $cacheKey = 'getHome'.$uid;
+ if($this->connection->isCached($cacheKey)) {
+ return $this->connection->getFromCache($cacheKey);
+ }
if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
$attr = substr($this->connection->homeFolderNamingRule, strlen('attr:'));
$homedir = $this->readAttribute($this->username2dn($uid), $attr);
- if($homedir) {
- $homedir = \OCP\Config::getSystemValue( "datadirectory", \OC::$SERVERROOT."/data" ) . '/' . $homedir[0];
- \OCP\Config::setUserValue($uid, 'user_ldap', 'homedir', $homedir);
+ if($homedir && isset($homedir[0])) {
+ $path = $homedir[0];
+ //if attribute's value is an absolute path take this, otherwise append it to data dir
+ //check for / at the beginning or pattern c:\ resp. c:/
+ if(
+ '/' == $path[0]
+ || (3 < strlen($path) && ctype_alpha($path[0])
+ && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]))
+ ) {
+ $homedir = $path;
+ } else {
+ $homedir = \OCP\Config::getSystemValue('datadirectory',
+ \OC::$SERVERROOT.'/data' ) . '/' . $homedir[0];
+ }
+ $this->connection->writeToCache($cacheKey, $homedir);
return $homedir;
}
}
- //fallback and default: username
- $homedir = \OCP\Config::getSystemValue( "datadirectory", \OC::$SERVERROOT."/data" ) . '/' . $uid;
- \OCP\Config::setUserValue($uid, 'user_ldap', 'homedir', $homedir);
- return $homedir;
+ //false will apply default behaviour as defined and done by OC_User
+ $this->connection->writeToCache($cacheKey, false);
+ return false;
}
/**
- * @brief get the user's home directory
- * @param string $uid the username
- * @return boolean
- */
- public function getHome($uid) {
- if($this->userExists($uid)) {
- $homedir = \OCP\Config::getUserValue($uid, 'user_ldap', 'homedir', false);
- if(!$homedir) {
- $homedir = $this->determineHomeDir($uid);
- }
- return $homedir;
+ * @brief get display name of the user
+ * @param $uid user ID of the user
+ * @return display name
+ */
+ public function getDisplayName($uid) {
+ $cacheKey = 'getDisplayName'.$uid;
+ if(!is_null($displayName = $this->connection->getFromCache($cacheKey))) {
+ return $displayName;
}
- return false;
+
+ $displayName = $this->readAttribute(
+ $this->username2dn($uid),
+ $this->connection->ldapUserDisplayName);
+
+ if($displayName && (count($displayName) > 0)) {
+ $this->connection->writeToCache($cacheKey, $displayName[0]);
+ return $displayName[0];
+ }
+
+ return null;
+ }
+
+ /**
+ * @brief Get a list of all display names
+ * @returns array with all displayNames (value) and the correspondig uids (key)
+ *
+ * Get a list of all display names and user ids.
+ */
+ public function getDisplayNames($search = '', $limit = null, $offset = null) {
+ $cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
+ if(!is_null($displayNames = $this->connection->getFromCache($cacheKey))) {
+ return $displayNames;
+ }
+
+ $displayNames = array();
+ $users = $this->getUsers($search, $limit, $offset);
+ foreach ($users as $user) {
+ $displayNames[$user] = $this->getDisplayName($user);
+ }
+ $this->connection->writeToCache($cacheKey, $displayNames);
+ return $displayNames;
}
/**
@@ -207,7 +264,16 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
* compared with OC_USER_BACKEND_CREATE_USER etc.
*/
public function implementsActions($actions) {
- return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_GET_HOME) & $actions);
+ return (bool)((OC_USER_BACKEND_CHECK_PASSWORD
+ | OC_USER_BACKEND_GET_HOME
+ | OC_USER_BACKEND_GET_DISPLAYNAME)
+ & $actions);
}
-} \ No newline at end of file
+ /**
+ * @return bool
+ */
+ public function hasUserListings() {
+ return true;
+ }
+}
diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php
new file mode 100644
index 00000000000..6a75bae3815
--- /dev/null
+++ b/apps/user_ldap/user_proxy.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Artuhr Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap;
+
+class User_Proxy extends lib\Proxy implements \OCP\UserInterface {
+ private $backends = array();
+ private $refBackend = null;
+
+ /**
+ * @brief Constructor
+ * @param $serverConfigPrefixes array containing the config Prefixes
+ */
+ public function __construct($serverConfigPrefixes) {
+ parent::__construct();
+ foreach($serverConfigPrefixes as $configPrefix) {
+ $this->backends[$configPrefix] = new \OCA\user_ldap\USER_LDAP();
+ $connector = $this->getConnector($configPrefix);
+ $this->backends[$configPrefix]->setConnector($connector);
+ if(is_null($this->refBackend)) {
+ $this->refBackend = &$this->backends[$configPrefix];
+ }
+ }
+ }
+
+ /**
+ * @brief Tries the backends one after the other until a positive result is returned from the specified method
+ * @param $uid string, the uid connected to the request
+ * @param $method string, the method of the user backend that shall be called
+ * @param $parameters an array of parameters to be passed
+ * @return mixed, the result of the method or false
+ */
+ protected function walkBackends($uid, $method, $parameters) {
+ $cacheKey = $this->getUserCacheKey($uid);
+ foreach($this->backends as $configPrefix => $backend) {
+ if($result = call_user_func_array(array($backend, $method), $parameters)) {
+ $this->writeToCache($cacheKey, $configPrefix);
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Asks the backend connected to the server that supposely takes care of the uid from the request.
+ * @param $uid string, the uid connected to the request
+ * @param $method string, the method of the user backend that shall be called
+ * @param $parameters an array of parameters to be passed
+ * @return mixed, the result of the method or false
+ */
+ protected function callOnLastSeenOn($uid, $method, $parameters) {
+ $cacheKey = $this->getUserCacheKey($uid);
+ $prefix = $this->getFromCache($cacheKey);
+ //in case the uid has been found in the past, try this stored connection first
+ if(!is_null($prefix)) {
+ if(isset($this->backends[$prefix])) {
+ $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters);
+ if(!$result) {
+ //not found here, reset cache to null
+ $this->writeToCache($cacheKey, null);
+ }
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Check if backend implements actions
+ * @param $actions bitwise-or'ed actions
+ * @returns boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ //it's the same across all our user backends obviously
+ return $this->refBackend->implementsActions($actions);
+ }
+
+ /**
+ * @brief Get a list of all users
+ * @returns array with all uids
+ *
+ * Get a list of all users.
+ */
+ public function getUsers($search = '', $limit = 10, $offset = 0) {
+ //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends
+ $users = array();
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->getUsers($search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = array_merge($users, $backendUsers);
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * @brief check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function userExists($uid) {
+ return $this->handleRequest($uid, 'userExists', array($uid));
+ }
+
+ /**
+ * @brief Check if the password is correct
+ * @param $uid The username
+ * @param $password The password
+ * @returns true/false
+ *
+ * Check if the password is correct without logging in the user
+ */
+ public function checkPassword($uid, $password) {
+ return $this->handleRequest($uid, 'checkPassword', array($uid, $password));
+ }
+
+ /**
+ * @brief get the user's home directory
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function getHome($uid) {
+ return $this->handleRequest($uid, 'getHome', array($uid));
+ }
+
+ /**
+ * @brief get display name of the user
+ * @param $uid user ID of the user
+ * @return display name
+ */
+ public function getDisplayName($uid) {
+ return $this->handleRequest($uid, 'getDisplayName', array($uid));
+ }
+
+ /**
+ * @brief Get a list of all display names
+ * @returns array with all displayNames (value) and the corresponding uids (key)
+ *
+ * Get a list of all display names and user ids.
+ */
+ public function getDisplayNames($search = '', $limit = null, $offset = null) {
+ //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends
+ $users = array();
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->getDisplayNames($search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = array_merge($users, $backendUsers);
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * @brief delete a user
+ * @param $uid The username of the user to delete
+ * @returns true/false
+ *
+ * Deletes a user
+ */
+ public function deleteUser($uid) {
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasUserListings() {
+ return $this->refBackend->hasUserListings();
+ }
+
+} \ No newline at end of file
diff --git a/apps/user_webdavauth/appinfo/app.php b/apps/user_webdavauth/appinfo/app.php
index 3ab323becce..3cd227bddbe 100755
--- a/apps/user_webdavauth/appinfo/app.php
+++ b/apps/user_webdavauth/appinfo/app.php
@@ -21,9 +21,9 @@
*
*/
-require_once 'apps/user_webdavauth/user_webdavauth.php';
+require_once OC_App::getAppPath('user_webdavauth').'/user_webdavauth.php';
-OC_APP::registerAdmin('user_webdavauth','settings');
+OC_APP::registerAdmin('user_webdavauth', 'settings');
OC_User::registerBackend("WEBDAVAUTH");
OC_User::useBackend( "WEBDAVAUTH" );
diff --git a/apps/user_webdavauth/appinfo/info.xml b/apps/user_webdavauth/appinfo/info.xml
index 9a8027daee6..76b314e48aa 100755
--- a/apps/user_webdavauth/appinfo/info.xml
+++ b/apps/user_webdavauth/appinfo/info.xml
@@ -2,10 +2,14 @@
<info>
<id>user_webdavauth</id>
<name>WebDAV user backend</name>
- <description>Authenticate Users by a WebDAV call</description>
- <version>1.0</version>
+ <description>Authenticate users by a WebDAV call. You can use any WebDAV server, ownCloud server or other webserver to authenticate. It should return http 200 for right credentials and http 401 for wrong ones.
+
+ This app is not compatible to the LDAP user and group backend.</description>
<licence>AGPL</licence>
<author>Frank Karlitschek</author>
- <require>4.9</require>
+ <require>4.93</require>
<shipped>true</shipped>
+ <types>
+ <authentication/>
+ </types>
</info>
diff --git a/apps/user_webdavauth/appinfo/version b/apps/user_webdavauth/appinfo/version
new file mode 100644
index 00000000000..a6bbdb5ff48
--- /dev/null
+++ b/apps/user_webdavauth/appinfo/version
@@ -0,0 +1 @@
+1.1.0.0
diff --git a/apps/user_webdavauth/l10n/.gitkeep b/apps/user_webdavauth/l10n/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/user_webdavauth/l10n/.gitkeep
diff --git a/apps/user_webdavauth/l10n/ar.php b/apps/user_webdavauth/l10n/ar.php
new file mode 100644
index 00000000000..cf59cd2519e
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ar.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"URL: http://" => "الرابط: http://"
+);
diff --git a/apps/user_webdavauth/l10n/bn_BD.php b/apps/user_webdavauth/l10n/bn_BD.php
new file mode 100644
index 00000000000..5366552efae
--- /dev/null
+++ b/apps/user_webdavauth/l10n/bn_BD.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"URL: http://" => "URL:http://"
+);
diff --git a/apps/user_webdavauth/l10n/ca.php b/apps/user_webdavauth/l10n/ca.php
new file mode 100644
index 00000000000..7ac540f2130
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ca.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticació WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enviarà les credencials d'usuari a aquesta URL. Aquest endollable en comprova la resposta i interpretarà els codis d'estat 401 i 403 com a credencials no vàlides, i qualsevol altra resposta com a credencials vàlides."
+);
diff --git a/apps/user_webdavauth/l10n/cs_CZ.php b/apps/user_webdavauth/l10n/cs_CZ.php
new file mode 100644
index 00000000000..9bd4c96a2bb
--- /dev/null
+++ b/apps/user_webdavauth/l10n/cs_CZ.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Ověření WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud odešle uživatelské údaje na zadanou URL. Plugin zkontroluje odpověď a považuje návratovou hodnotu HTTP 401 a 403 za neplatné údaje a všechny ostatní hodnoty jako platné přihlašovací údaje."
+);
diff --git a/apps/user_webdavauth/l10n/da.php b/apps/user_webdavauth/l10n/da.php
new file mode 100644
index 00000000000..b268d3e15d0
--- /dev/null
+++ b/apps/user_webdavauth/l10n/da.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV-godkendelse",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud vil sende brugerens oplysninger til denne URL. Plugin'et registrerer responsen og fortolker HTTP-statuskoder 401 og 403 som ugyldige oplysninger, men alle andre besvarelser som gyldige oplysninger."
+);
diff --git a/apps/user_webdavauth/l10n/de.php b/apps/user_webdavauth/l10n/de.php
new file mode 100644
index 00000000000..c86ff44e55c
--- /dev/null
+++ b/apps/user_webdavauth/l10n/de.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Authentifikation",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sendet die Benutzerdaten an diese URL. Dieses Plugin prüft die Antwort und wird die Statuscodes 401 und 403 als ungültige Daten und alle anderen Antworten als gültige Daten interpretieren."
+);
diff --git a/apps/user_webdavauth/l10n/de_DE.php b/apps/user_webdavauth/l10n/de_DE.php
new file mode 100644
index 00000000000..bd5d328e477
--- /dev/null
+++ b/apps/user_webdavauth/l10n/de_DE.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Authentifizierung",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sendet die Benutzerdaten an diese URL. Dieses Plugin prüft die Antwort und wird die Statuscodes 401 und 403 als ungültige Daten und alle anderen Antworten als gültige Daten interpretieren."
+);
diff --git a/apps/user_webdavauth/l10n/el.php b/apps/user_webdavauth/l10n/el.php
new file mode 100644
index 00000000000..951709c4d64
--- /dev/null
+++ b/apps/user_webdavauth/l10n/el.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Αυθεντικοποίηση μέσω WebDAV ",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "Το ownCloud θα στείλει τα διαπιστευτήρια χρήστη σε αυτό το URL. Αυτό το plugin ελέγχει την απάντηση και την μετατρέπει σε HTTP κωδικό κατάστασης 401 και 403 για μη έγκυρα, όλες οι υπόλοιπες απαντήσεις είναι έγκυρες."
+);
diff --git a/apps/user_webdavauth/l10n/eo.php b/apps/user_webdavauth/l10n/eo.php
new file mode 100644
index 00000000000..d945f181e6b
--- /dev/null
+++ b/apps/user_webdavauth/l10n/eo.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV-aŭtentigo",
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/es.php b/apps/user_webdavauth/l10n/es.php
new file mode 100644
index 00000000000..103c3738e2d
--- /dev/null
+++ b/apps/user_webdavauth/l10n/es.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticación de WevDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "onwCloud enviará las credenciales de usuario a esta URL. Este complemento verifica la respuesta e interpretará los códigos de respuesta HTTP 401 y 403 como credenciales inválidas y todas las otras respuestas como credenciales válidas."
+);
diff --git a/apps/user_webdavauth/l10n/es_AR.php b/apps/user_webdavauth/l10n/es_AR.php
new file mode 100644
index 00000000000..103c3738e2d
--- /dev/null
+++ b/apps/user_webdavauth/l10n/es_AR.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticación de WevDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "onwCloud enviará las credenciales de usuario a esta URL. Este complemento verifica la respuesta e interpretará los códigos de respuesta HTTP 401 y 403 como credenciales inválidas y todas las otras respuestas como credenciales válidas."
+);
diff --git a/apps/user_webdavauth/l10n/et_EE.php b/apps/user_webdavauth/l10n/et_EE.php
new file mode 100644
index 00000000000..f4f74860358
--- /dev/null
+++ b/apps/user_webdavauth/l10n/et_EE.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV autentimine",
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/eu.php b/apps/user_webdavauth/l10n/eu.php
new file mode 100644
index 00000000000..d792c1588bb
--- /dev/null
+++ b/apps/user_webdavauth/l10n/eu.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Autentikazioa",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloudek erabiltzailearen kredentzialak URL honetara bidaliko ditu. Plugin honek erantzuna aztertzen du eta HTTP 401 eta 403 egoera kodeak baliogabezko kredentzialtzat hartuko ditu, beste erantzunak kredentzial egokitzat hartuko dituelarik."
+);
diff --git a/apps/user_webdavauth/l10n/fi_FI.php b/apps/user_webdavauth/l10n/fi_FI.php
new file mode 100644
index 00000000000..6c67c78c812
--- /dev/null
+++ b/apps/user_webdavauth/l10n/fi_FI.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV-todennus",
+"URL: http://" => "Osoite: http://"
+);
diff --git a/apps/user_webdavauth/l10n/fr.php b/apps/user_webdavauth/l10n/fr.php
new file mode 100644
index 00000000000..9d528a3a9d2
--- /dev/null
+++ b/apps/user_webdavauth/l10n/fr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Authentification WebDAV",
+"URL: http://" => "URL : http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enverra les informations de connexion à cette adresse. Ce module complémentaire analyse le code réponse HTTP et considère tout code différent des codes 401 et 403 comme associé à une authentification correcte."
+);
diff --git a/apps/user_webdavauth/l10n/gl.php b/apps/user_webdavauth/l10n/gl.php
new file mode 100644
index 00000000000..f63a7cb0ce8
--- /dev/null
+++ b/apps/user_webdavauth/l10n/gl.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticación WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enviará as credenciais do usuario a este URL. Este engadido comproba a resposta e interpretará os códigos de estado HTTP 401 e 403 como credenciais incorrectas, e todas as outras respostas como credenciais correctas."
+);
diff --git a/apps/user_webdavauth/l10n/hu_HU.php b/apps/user_webdavauth/l10n/hu_HU.php
new file mode 100644
index 00000000000..64352801142
--- /dev/null
+++ b/apps/user_webdavauth/l10n/hu_HU.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV hitelesítés",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "Az ownCloud elküldi a felhasználói fiók adatai a következő URL-re. Ez a bővítőmodul leellenőrzi a választ és ha a HTTP hibakód nem 401 vagy 403 azaz érvénytelen hitelesítő, akkor minden más válasz érvényes lesz."
+);
diff --git a/apps/user_webdavauth/l10n/id.php b/apps/user_webdavauth/l10n/id.php
new file mode 100644
index 00000000000..4324ee8ff52
--- /dev/null
+++ b/apps/user_webdavauth/l10n/id.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Otentikasi WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud akan mengirimkan informasi pengguna ke URL ini. Pengaya akan mengecek respon dan menginterpretasikan kode status HTTP 401 serta 403 sebagai informasi yang keliru, sedangkan respon lainnya dianggap benar."
+);
diff --git a/apps/user_webdavauth/l10n/is.php b/apps/user_webdavauth/l10n/is.php
new file mode 100644
index 00000000000..8fe0d974b32
--- /dev/null
+++ b/apps/user_webdavauth/l10n/is.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"URL: http://" => "Vefslóð: http://"
+);
diff --git a/apps/user_webdavauth/l10n/it.php b/apps/user_webdavauth/l10n/it.php
new file mode 100644
index 00000000000..a7cd6e8e4b4
--- /dev/null
+++ b/apps/user_webdavauth/l10n/it.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticazione WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud invierà le credenziali dell'utente a questo URL. Questa estensione controlla la risposta e interpreta i codici di stato 401 e 403 come credenziali non valide, e tutte le altre risposte come credenziali valide."
+);
diff --git a/apps/user_webdavauth/l10n/ja_JP.php b/apps/user_webdavauth/l10n/ja_JP.php
new file mode 100644
index 00000000000..1cd14a03c72
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ja_JP.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV 認証",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloudはこのURLにユーザ資格情報を送信します。このプラグインは応答をチェックし、HTTP状態コードが 401 と 403 の場合は無効な資格情報とし、他の応答はすべて有効な資格情報として処理します。"
+);
diff --git a/apps/user_webdavauth/l10n/ko.php b/apps/user_webdavauth/l10n/ko.php
new file mode 100644
index 00000000000..578ff35e721
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ko.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV 인증",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud에서 이 URL로 사용자 인증 정보를 보냅니다. 이 플러그인은 응답을 확인하여 HTTP 상태 코드 401이나 403이 돌아온 경우에 잘못된 인증 정보로 간주합니다. 다른 모든 상태 코드는 올바른 인증 정보로 간주합니다."
+);
diff --git a/apps/user_webdavauth/l10n/lv.php b/apps/user_webdavauth/l10n/lv.php
new file mode 100644
index 00000000000..d0043df9f07
--- /dev/null
+++ b/apps/user_webdavauth/l10n/lv.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV autentifikācija",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sūtīs lietotāja akreditācijas datus uz šo URL. Šis spraudnis pārbauda atbildi un interpretē HTTP statusa kodus 401 un 403 kā nederīgus akreditācijas datus un visas citas atbildes kā derīgus akreditācijas datus."
+);
diff --git a/apps/user_webdavauth/l10n/mk.php b/apps/user_webdavauth/l10n/mk.php
new file mode 100644
index 00000000000..245a5101341
--- /dev/null
+++ b/apps/user_webdavauth/l10n/mk.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/nb_NO.php b/apps/user_webdavauth/l10n/nb_NO.php
new file mode 100644
index 00000000000..245a5101341
--- /dev/null
+++ b/apps/user_webdavauth/l10n/nb_NO.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/nl.php b/apps/user_webdavauth/l10n/nl.php
new file mode 100644
index 00000000000..7d1bb33923e
--- /dev/null
+++ b/apps/user_webdavauth/l10n/nl.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV authenticatie",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud stuurt de inloggegevens naar deze URL. Deze plugin controleert het antwoord en interpreteert de HTTP statuscodes 401 als 403 als ongeldige inloggegevens, maar alle andere antwoorden als geldige inloggegevens."
+);
diff --git a/apps/user_webdavauth/l10n/pl.php b/apps/user_webdavauth/l10n/pl.php
new file mode 100644
index 00000000000..4887e935316
--- /dev/null
+++ b/apps/user_webdavauth/l10n/pl.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Uwierzytelnienie WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud wyśle dane uwierzytelniające do tego URL. Ten plugin sprawdza odpowiedź i zinterpretuje kody HTTP 401 oraz 403 jako nieprawidłowe dane uwierzytelniające, a każdy inny kod odpowiedzi jako poprawne dane."
+);
diff --git a/apps/user_webdavauth/l10n/pt_BR.php b/apps/user_webdavauth/l10n/pt_BR.php
new file mode 100644
index 00000000000..6ddd00ccc3e
--- /dev/null
+++ b/apps/user_webdavauth/l10n/pt_BR.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticação WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "O ownCloud enviará as credenciais do usuário para esta URL. Este plugin verifica a resposta e interpreta o os códigos de status do HTTP 401 e 403 como credenciais inválidas, e todas as outras respostas como credenciais válidas."
+);
diff --git a/apps/user_webdavauth/l10n/pt_PT.php b/apps/user_webdavauth/l10n/pt_PT.php
new file mode 100644
index 00000000000..d7e87b5c8d1
--- /dev/null
+++ b/apps/user_webdavauth/l10n/pt_PT.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autenticação WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "O ownCloud vai enviar as credenciais do utilizador através deste URL. Este plugin verifica a resposta e vai interpretar os códigos de estado HTTP 401 e 403 como credenciais inválidas, e todas as outras como válidas."
+);
diff --git a/apps/user_webdavauth/l10n/ro.php b/apps/user_webdavauth/l10n/ro.php
new file mode 100644
index 00000000000..9df490e81ec
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ro.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Autentificare WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud va trimite datele de autentificare la acest URL. Acest modul verifică răspunsul și va interpreta codurile de status HTTP 401 sau 403 ca fiind date de autentificare invalide, și orice alt răspuns ca fiind date valide."
+);
diff --git a/apps/user_webdavauth/l10n/ru.php b/apps/user_webdavauth/l10n/ru.php
new file mode 100644
index 00000000000..f12982fc406
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ru.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Идентификация WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud отправит пользовательские данные на этот URL. Затем плагин проверит ответ, в случае HTTP ответа 401 или 403 данные будут считаться неверными, при любых других ответах - верными."
+);
diff --git a/apps/user_webdavauth/l10n/ru_RU.php b/apps/user_webdavauth/l10n/ru_RU.php
new file mode 100644
index 00000000000..46f74cb972f
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ru_RU.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV аутентификация",
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/si_LK.php b/apps/user_webdavauth/l10n/si_LK.php
new file mode 100644
index 00000000000..cc5cfb3c9b2
--- /dev/null
+++ b/apps/user_webdavauth/l10n/si_LK.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"WebDAV URL: http://" => "WebDAV යොමුව: http://"
+);
diff --git a/apps/user_webdavauth/l10n/sk_SK.php b/apps/user_webdavauth/l10n/sk_SK.php
new file mode 100644
index 00000000000..c4e6dfddc7b
--- /dev/null
+++ b/apps/user_webdavauth/l10n/sk_SK.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV overenie",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud odošle používateľské údaje na zadanú URL. Plugin skontroluje odpoveď a považuje návratovú hodnotu HTTP 401 a 403 za neplatné údaje a všetky ostatné hodnoty ako platné prihlasovacie údaje."
+);
diff --git a/apps/user_webdavauth/l10n/sl.php b/apps/user_webdavauth/l10n/sl.php
new file mode 100644
index 00000000000..7c592723af6
--- /dev/null
+++ b/apps/user_webdavauth/l10n/sl.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Overitev WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "Sistem ownCloud bo poslal uporabniška poverila na navedeni naslov URL. Ta vstavek preveri odziv in tolmači kode stanja HTTP 401 in HTTP 403 kot spodletel odgovor in vse ostale odzive kot veljavna poverila."
+);
diff --git a/apps/user_webdavauth/l10n/sr.php b/apps/user_webdavauth/l10n/sr.php
new file mode 100644
index 00000000000..518fcbe9be5
--- /dev/null
+++ b/apps/user_webdavauth/l10n/sr.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV провера идентитета",
+"URL: http://" => "Адреса: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud ће послати акредитиве корисника на ову адресу. Овај прикључак проверава одговор и тумачи HTTP статусне кодове 401 и 403 као неисправне акредитиве, а све остале одговоре као исправне."
+);
diff --git a/apps/user_webdavauth/l10n/sv.php b/apps/user_webdavauth/l10n/sv.php
new file mode 100644
index 00000000000..c79b35c27cd
--- /dev/null
+++ b/apps/user_webdavauth/l10n/sv.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Autentisering",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud kommer skicka användaruppgifterna till denna URL. Denna plugin kontrollerar svaret och tolkar HTTP-statuskoderna 401 och 403 som felaktiga uppgifter, och alla andra svar som giltiga uppgifter."
+);
diff --git a/apps/user_webdavauth/l10n/ta_LK.php b/apps/user_webdavauth/l10n/ta_LK.php
new file mode 100644
index 00000000000..9bd32954b05
--- /dev/null
+++ b/apps/user_webdavauth/l10n/ta_LK.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"WebDAV URL: http://" => "WebDAV URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/th_TH.php b/apps/user_webdavauth/l10n/th_TH.php
new file mode 100644
index 00000000000..2bd1f685e65
--- /dev/null
+++ b/apps/user_webdavauth/l10n/th_TH.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Authentication",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud จะส่งข้อมูลการเข้าใช้งานของผู้ใช้งานไปยังที่อยู่ URL ดังกล่าวนี้ ปลั๊กอินดังกล่าวจะทำการตรวจสอบข้อมูลที่โต้ตอบกลับมาและจะทำการแปลรหัส HTTP statuscodes 401 และ 403 ให้เป็นข้อมูลการเข้าใช้งานที่ไม่สามารถใช้งานได้ ส่วนข้อมูลอื่นๆที่เหลือทั้งหมดจะเป็นข้อมูลการเข้าใช้งานที่สามารถใช้งานได้"
+);
diff --git a/apps/user_webdavauth/l10n/tr.php b/apps/user_webdavauth/l10n/tr.php
new file mode 100644
index 00000000000..4a2f6d2403b
--- /dev/null
+++ b/apps/user_webdavauth/l10n/tr.php
@@ -0,0 +1,4 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV Kimlik doğrulaması",
+"URL: http://" => "URL: http://"
+);
diff --git a/apps/user_webdavauth/l10n/uk.php b/apps/user_webdavauth/l10n/uk.php
new file mode 100644
index 00000000000..66887df54b5
--- /dev/null
+++ b/apps/user_webdavauth/l10n/uk.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Аутентифікація WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud надішле облікові дані на цей URL. Цей плагін перевірить відповідь і буде інтерпретувати HTTP коди 401 і 403 як повідомлення про недійсні повноваження, а решту відповідей як дійсні облікові дані."
+);
diff --git a/apps/user_webdavauth/l10n/vi.php b/apps/user_webdavauth/l10n/vi.php
new file mode 100644
index 00000000000..ee2aa089125
--- /dev/null
+++ b/apps/user_webdavauth/l10n/vi.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "Xác thực WebDAV",
+"URL: http://" => "URL: http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sẽ gửi chứng thư người dùng tới URL này. Tính năng này kiểm tra trả lời và sẽ hiểu mã 401 và 403 của giao thức HTTP là chứng thư không hợp lệ, và mọi trả lời khác được coi là hợp lệ."
+);
diff --git a/apps/user_webdavauth/l10n/zh_CN.php b/apps/user_webdavauth/l10n/zh_CN.php
new file mode 100644
index 00000000000..72d2a0c11df
--- /dev/null
+++ b/apps/user_webdavauth/l10n/zh_CN.php
@@ -0,0 +1,5 @@
+<?php $TRANSLATIONS = array(
+"WebDAV Authentication" => "WebDAV 认证",
+"URL: http://" => "URL:http://",
+"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud 将会发送用户的身份到此 URL。这个插件检查返回值并且将 HTTP 状态编码 401 和 403 解释为非法身份,其他所有返回值为合法身份。"
+);
diff --git a/apps/user_webdavauth/l10n/zh_TW.php b/apps/user_webdavauth/l10n/zh_TW.php
new file mode 100644
index 00000000000..79740561e5a
--- /dev/null
+++ b/apps/user_webdavauth/l10n/zh_TW.php
@@ -0,0 +1,3 @@
+<?php $TRANSLATIONS = array(
+"WebDAV URL: http://" => "WebDAV 網址 http://"
+);
diff --git a/apps/user_webdavauth/settings.php b/apps/user_webdavauth/settings.php
index 4f1ddbbefda..ae9cb7e4c92 100755
--- a/apps/user_webdavauth/settings.php
+++ b/apps/user_webdavauth/settings.php
@@ -21,12 +21,15 @@
*
*/
-print_r($_POST);
+OC_Util::checkAdminUser();
+
if($_POST) {
+ // CSRF check
+ OCP\JSON::callCheck();
- if(isset($_POST['webdav_url'])) {
- OC_CONFIG::setValue('user_webdavauth_url', strip_tags($_POST['webdav_url']));
- }
+ if(isset($_POST['webdav_url'])) {
+ OC_CONFIG::setValue('user_webdavauth_url', strip_tags($_POST['webdav_url']));
+ }
}
// fill template
diff --git a/apps/user_webdavauth/templates/settings.php b/apps/user_webdavauth/templates/settings.php
index c00c199632a..ec6524ee4f7 100755
--- a/apps/user_webdavauth/templates/settings.php
+++ b/apps/user_webdavauth/templates/settings.php
@@ -1,7 +1,9 @@
<form id="webdavauth" action="#" method="post">
<fieldset class="personalblock">
- <legend><strong>WebDAV Authentication</strong></legend>
- <p><label for="webdav_url"><?php echo $l->t('webdav_url');?><input type="text" id="webdav_url" name="webdav_url" value="<?php echo $_['webdav_url']; ?>"></label>
+ <legend><strong><?php p($l->t('WebDAV Authentication'));?></strong></legend>
+ <p><label for="webdav_url"><?php p($l->t('URL: http://'));?><input type="text" id="webdav_url" name="webdav_url" value="<?php p($_['webdav_url']); ?>"></label>
+ <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" id="requesttoken">
<input type="submit" value="Save" />
+ <br /><?php p($l->t('ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials.')); ?>
</fieldset>
</form>
diff --git a/apps/user_webdavauth/user_webdavauth.php b/apps/user_webdavauth/user_webdavauth.php
index c36d37c1fa2..6417e45434d 100755
--- a/apps/user_webdavauth/user_webdavauth.php
+++ b/apps/user_webdavauth/user_webdavauth.php
@@ -28,54 +28,54 @@ class OC_USER_WEBDAVAUTH extends OC_User_Backend {
$this->webdavauth_url = OC_Config::getValue( "user_webdavauth_url" );
}
- public function createUser() {
- // Can't create user
- OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to create users from web frontend using WebDAV user backend',3);
- return false;
- }
-
- public function deleteUser() {
+ public function deleteUser($uid) {
// Can't delete user
- OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to delete users from web frontend using WebDAV user backend',3);
+ OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to delete users from web frontend using WebDAV user backend', 3);
return false;
}
public function setPassword ( $uid, $password ) {
// We can't change user password
- OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to change password for users from web frontend using WebDAV user backend',3);
+ OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to change password for users from web frontend using WebDAV user backend', 3);
return false;
}
public function checkPassword( $uid, $password ) {
-
$url= 'http://'.urlencode($uid).':'.urlencode($password).'@'.$this->webdavauth_url;
$headers = get_headers($url);
if($headers==false) {
- OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to connect to WebDAV Url: "'.$this->webdavauth_url.'" ' ,3);
+ OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to connect to WebDAV Url: "'.$this->webdavauth_url.'" ', 3);
return false;
}
$returncode= substr($headers[0], 9, 3);
- if($returncode=='401') {
- return false;
+ if(($returncode=='401') or ($returncode=='403')) {
+ return(false);
}else{
- return true;
+ return($uid);
}
}
/*
- * we don´t know if a user exists without the password. so we have to return false all the time
+ * we don´t know if a user exists without the password. so we have to return true all the time
*/
- public function userExists( $uid ) {
+ public function userExists( $uid ){
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasUserListings() {
return false;
}
/*
* we don´t know the users so all we can do it return an empty array here
*/
- public function getUsers() {
+ public function getUsers($search = '', $limit = 10, $offset = 0) {
$returnArray = array();
return $returnArray;