diff --git a/.htaccess b/.htaccess index 08e2a82facb..4ba5095e144 100755 --- a/.htaccess +++ b/.htaccess @@ -26,7 +26,7 @@ RewriteRule ^.well-known/carddav /remote.php/carddav/ [R] RewriteRule ^.well-known/caldav /remote.php/caldav/ [R] RewriteRule ^apps/calendar/caldav.php remote.php/caldav/ [QSA,L] RewriteRule ^apps/contacts/carddav.php remote.php/carddav/ [QSA,L] -RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L] +RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L] RewriteRule ^remote/(.*) remote.php [QSA,L] @@ -38,3 +38,6 @@ DirectoryIndex index.php index.html AddDefaultCharset utf-8 Options -Indexes + + ModPagespeed Off + diff --git a/3rdparty b/3rdparty index b4db0b302aa..3d42b540641 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit b4db0b302aa8266b067012d0f862fafe70a665e0 +Subproject commit 3d42b540641cb3f9ce563d5a4ec6b385dbaba742 diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index c327d2b9f94..1853098c507 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -53,13 +53,22 @@ $result = array( ); if(trim($filename) === '') { - $result['data'] = array('message' => $l10n->t('File name cannot be empty.')); + $result['data'] = array('message' => (string)$l10n->t('File name cannot be empty.')); OCP\JSON::error($result); exit(); } if(strpos($filename, '/') !== false) { - $result['data'] = array('message' => $l10n->t('File name must not contain "/". Please choose a different name.')); + $result['data'] = array('message' => (string)$l10n->t('File name must not contain "/". Please choose a different name.')); + OCP\JSON::error($result); + exit(); +} + +if (!\OC\Files\Filesystem::file_exists($dir . '/')) { + $result['data'] = array('message' => (string)$l10n->t( + 'The target folder has been moved or deleted.'), + 'code' => 'targetnotfound' + ); OCP\JSON::error($result); exit(); } @@ -68,7 +77,7 @@ if(strpos($filename, '/') !== false) { $target = $dir.'/'.$filename; if (\OC\Files\Filesystem::file_exists($target)) { - $result['data'] = array('message' => $l10n->t( + $result['data'] = array('message' => (string)$l10n->t( 'The name %s is already used in the folder %s. Please choose a different name.', array($filename, $dir)) ); @@ -78,20 +87,32 @@ if (\OC\Files\Filesystem::file_exists($target)) { if($source) { if(substr($source, 0, 8)!='https://' and substr($source, 0, 7)!='http://') { - OCP\JSON::error(array('data' => array( 'message' => $l10n->t('Not a valid source') ))); + OCP\JSON::error(array('data' => array('message' => $l10n->t('Not a valid source')))); + exit(); + } + + if (!ini_get('allow_url_fopen')) { + $eventSource->send('error', array('message' => $l10n->t('Server is not allowed to open URLs, please check the server configuration'))); + $eventSource->close(); exit(); } $ctx = stream_context_create(null, array('notification' =>'progress')); - $sourceStream=fopen($source, 'rb', false, $ctx); - $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream); + $sourceStream=@fopen($source, 'rb', false, $ctx); + $result = 0; + if (is_resource($sourceStream)) { + $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream); + } if($result) { $meta = \OC\Files\Filesystem::getFileInfo($target); $mime=$meta['mimetype']; $id = $meta['fileid']; - $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id, 'etag' => $meta['etag'])); + $eventSource->send('success', array('mime' => $mime, 'size' => \OC\Files\Filesystem::filesize($target), 'id' => $id, 'etag' => $meta['etag'])); } else { - $eventSource->send('error', $l10n->t('Error while downloading %s to %s', array($source, $target))); + $eventSource->send('error', array('message' => $l10n->t('Error while downloading %s to %s', array($source, $target)))); + } + if (is_resource($sourceStream)) { + fclose($sourceStream); } $eventSource->close(); exit(); diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php index 2cbc8cfeba5..4cfcae3090d 100644 --- a/apps/files/ajax/newfolder.php +++ b/apps/files/ajax/newfolder.php @@ -29,6 +29,15 @@ if(strpos($foldername, '/') !== false) { exit(); } +if (!\OC\Files\Filesystem::file_exists($dir . '/')) { + $result['data'] = array('message' => (string)$l10n->t( + 'The target folder has been moved or deleted.'), + 'code' => 'targetnotfound' + ); + OCP\JSON::error($result); + exit(); +} + //TODO why is stripslashes used on foldername here but not in newfile.php? $target = $dir . '/' . stripslashes($foldername); diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 0e905f993ac..8f6c42d6620 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -8,6 +8,7 @@ OCP\JSON::setContentTypeHeader('text/plain'); // If no token is sent along, rely on login only $allowedPermissions = OCP\PERMISSION_ALL; +$errorCode = null; $l = OC_L10N::get('files'); if (empty($_POST['dirToken'])) { @@ -34,6 +35,7 @@ if (empty($_POST['dirToken'])) { // resolve reshares $rootLinkItem = OCP\Share::resolveReShare($linkItem); + OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); // Setup FS with owner OC_Util::tearDownFS(); OC_Util::setupFS($rootLinkItem['uid_owner']); @@ -124,7 +126,8 @@ if (strpos($dir, '..') === false) { $meta = \OC\Files\Filesystem::getFileInfo($target); if ($meta === false) { - $error = $l->t('Upload failed. Could not get file info.'); + $error = $l->t('The target folder has been moved or deleted.'); + $errorCode = 'targetnotfound'; } else { $result[] = array('status' => 'success', 'mime' => $meta['mimetype'], @@ -176,5 +179,5 @@ if ($error === false) { OCP\JSON::encodedPrint($result); exit(); } else { - OCP\JSON::error(array(array('data' => array_merge(array('message' => $error), $storageStats)))); + OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats)))); } diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 9f290796205..ef22fe92188 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -52,6 +52,7 @@ $server->addPlugin(new OC_Connector_Sabre_FilesPlugin()); $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin()); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); +$server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); // And off we go! $server->exec(); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 2fc86ca537d..ca3b8500669 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -3,7 +3,7 @@ See the COPYING-README file. */ /* FILE MENU */ -.actions { padding:.3em; height:2em; width: 100%; } +.actions { padding:5px; height:32px; width: 100%; } .actions input, .actions button, .actions .button { margin:0; float:left; } .actions .button a { color: #555; } .actions .button a:hover, .actions .button a:active { color: #333; } @@ -33,9 +33,9 @@ #new>ul { display: none; position: fixed; - min-width: 7em; + min-width: 112px; z-index: 10; - padding: .5em; + padding: 8px; padding-bottom: 0; margin-top: 14px; margin-left: -1px; @@ -46,7 +46,7 @@ border-top-left-radius: 0; box-shadow:0 2px 7px rgba(170,170,170,.4); } -#new>ul>li { height:36px; margin:.3em; padding-left:3em; padding-bottom:0.1em; +#new>ul>li { height:36px; margin:5px; padding-left:48px; padding-bottom:2px; background-repeat:no-repeat; cursor:pointer; } #new>ul>li>p { cursor:pointer; padding-top: 7px; padding-bottom: 7px;} @@ -65,10 +65,15 @@ top: 44px; width: 100%; } -#filestable, #controls { - min-width: 680px; +/* make sure there's enough room for the file actions */ +#body-user #filestable { + min-width: 750px; } -#filestable tbody tr { background-color:#fff; height:2.5em; } +#body-user #controls { + min-width: 600px; +} + +#filestable tbody tr { background-color:#fff; height:40px; } #filestable tbody tr:hover, tbody tr:active { background-color: rgb(240,240,240); } @@ -83,7 +88,7 @@ span.extension, span.uploading, td.date { color:#999; } span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } tr:hover span.extension { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; color:#777; } table tr.mouseOver td { background-color:#eee; } -table th { height:2em; padding:0 .5em; color:#999; } +table th { height:24px; padding:0 8px; color:#999; } table th .name { position: absolute; left: 55px; @@ -98,7 +103,7 @@ table td { } table th#headerName { position: relative; - width: 100em; /* not really sure why this works better than 100% … table styling */ + width: 9999px; /* not really sure why this works better than 100% … table styling */ padding: 0; } #headerName-container { @@ -106,15 +111,15 @@ table th#headerName { height: 50px; } table th#headerSize, table td.filesize { - min-width: 3em; - padding: 0 1em; + min-width: 48px; + padding: 0 16px; text-align: right; } table th#headerDate, table td.date { -moz-box-sizing: border-box; box-sizing: border-box; position: relative; - min-width: 11em; + min-width: 176px; } /* Multiselect bar */ @@ -140,9 +145,9 @@ table.multiselect thead th { } table.multiselect #headerName { position: relative; - width: 100%; + width: 9999px; /* when we use 100%, the styling breaks on mobile … table styling */ } -table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; } +table td.selection, table th.selection, table td.fileaction { width:32px; text-align:center; } table td.filename a.name { position:relative; /* Firefox needs to explicitly have this default set … */ -moz-box-sizing: border-box; @@ -160,8 +165,8 @@ table td.filename input.filename { margin-left: 2px; 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 .3em; } -table td.filename .nametext, .uploadtext, .modified { float:left; padding:.3em 0; } +table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; } +table td.filename .nametext, .uploadtext, .modified { float:left; padding:14px 0; } #modified { position: absolute; @@ -181,8 +186,8 @@ table td.filename .nametext { text-overflow: ellipsis; max-width: 800px; } -table td.filename .uploadtext { font-weight:normal; margin-left:.5em; } -table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } +table td.filename .uploadtext { font-weight:normal; margin-left:8px; } +table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; } .ie8 input[type="checkbox"]{ padding: 0; @@ -217,6 +222,11 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } width: 50px; z-index: 5; } +#fileList tr td.filename>input[type="checkbox"]{ + /* sometimes checkbox height is bigger (KDE/Qt), so setting to absolute + * to prevent it to increase the height */ + position: absolute; +} #fileList tr td.filename>input[type="checkbox"] + label { left: 0; } @@ -237,7 +247,7 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } #fileList tr td.filename a.name label { position: absolute; - width: 100%; + width: 80%; height: 50px; } @@ -250,11 +260,11 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } right: 0; } -#fileList img.move2trash { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; float:right; } +#fileList img.move2trash { display:inline; margin:-8px 0; padding:16px 8px 16px 8px !important; float:right; } #fileList a.action.delete { position: absolute; right: 0; - padding: 9px 14px 19px !important; + padding: 28px 14px 19px !important; } a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } @@ -272,13 +282,13 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } } .selectedActions a img { position:relative; - top:.3em; + top:5px; } #fileList a.action { display: inline; - margin: -.5em 0; + margin: -8px 0; padding: 18px 8px !important; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); diff --git a/apps/files/css/upload.css b/apps/files/css/upload.css index ef043569094..06b4d4655fe 100644 --- a/apps/files/css/upload.css +++ b/apps/files/css/upload.css @@ -5,7 +5,7 @@ height: 36px; width: 39px; padding: 0 !important; /* override default control bar button padding */ - margin-left: .2em; + margin-left: 3px; overflow: hidden; vertical-align: top; } @@ -18,9 +18,6 @@ margin: -5px -3px; cursor: pointer; z-index: 10; - background-image: url('%webroot%/core/img/actions/upload.svg'); - background-repeat: no-repeat; - background-position: center; opacity: .65; } .file_upload_target { display:none; } @@ -33,7 +30,7 @@ height: 44px; margin: -5px -3px; padding: 0; - font-size: 1em; + font-size: 16px; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index: 20; cursor: pointer; @@ -119,11 +116,6 @@ .oc-dialog .fileexists .conflict input[type='checkbox'] { float: left; } -.oc-dialog .fileexists .toggle { - background-image: url('%webroot%/core/img/actions/triangle-e.png'); - width: 16px; - height: 16px; -} .oc-dialog .fileexists #allfileslabel { float:right; } diff --git a/apps/files/download.php b/apps/files/download.php index e3fe24e45d7..6b055e99a53 100644 --- a/apps/files/download.php +++ b/apps/files/download.php @@ -37,12 +37,7 @@ if(!\OC\Files\Filesystem::file_exists($filename)) { $ftype=\OC\Files\Filesystem::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::setContentDispositionHeader(basename($filename), 'attachment'); OCP\Response::disableCaching(); header('Content-Length: '.\OC\Files\Filesystem::filesize($filename)); diff --git a/apps/files/index.php b/apps/files/index.php index 8f6838aa0d9..2ce8fdb065f 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -63,7 +63,6 @@ $files = array(); $user = OC_User::getUser(); if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache $needUpgrade = true; - $freeSpace = 0; } else { if ($isIE8){ // after the redirect above, the URL will have a format @@ -77,7 +76,6 @@ if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we ne else{ $files = \OCA\Files\Helper::getFiles($dir); } - $freeSpace = \OC\Files\Filesystem::free_space($dir); $needUpgrade = false; } diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index e9663353f74..486273a910c 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -222,6 +222,14 @@ $(document).ready(function() { //examine file var file = data.files[0]; + try { + // FIXME: not so elegant... need to refactor that method to return a value + Files.isFileNameValid(file.name); + } + catch (errorMessage) { + data.textStatus = 'invalidcharacters'; + data.errorThrown = errorMessage; + } if (file.type === '' && file.size === 4096) { data.textStatus = 'dirorzero'; @@ -307,6 +315,13 @@ $(document).ready(function() { } else { // HTTP connection problem OC.Notification.show(data.errorThrown); + if (data.result) { + var result = JSON.parse(data.result); + if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') { + // abort upload of next files if any + OC.Upload.cancelUploads(); + } + } } //hide notification after 10 sec setTimeout(function() { @@ -605,7 +620,7 @@ $(document).ready(function() { if (result.status === 'success') { var date=new Date(); FileList.addDir(name, 0, date, hidden); - var tr=$('tr[data-file="'+name+'"]'); + var tr = FileList.findFileEl(name); tr.attr('data-id', result.data.id); } else { OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); @@ -647,7 +662,7 @@ $(document).ready(function() { $('#uploadprogressbar').fadeOut(); var date = new Date(); FileList.addFile(localName, size, date, false, hidden); - var tr = $('tr[data-file="'+localName+'"]'); + var tr = FileList.findFileEl(localName); tr.data('mime', mime).data('id', id); tr.attr('data-id', id); var path = $('#dir').val()+'/'+localName; @@ -658,7 +673,12 @@ $(document).ready(function() { }); eventSource.listen('error',function(error) { $('#uploadprogressbar').fadeOut(); - alert(error); + var message = (error && error.message) || t('core', 'Error fetching URL'); + OC.Notification.show(message); + //hide notification after 10 sec + setTimeout(function() { + OC.Notification.hide(); + }, 10000); }); break; } diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 03e23189a97..d0ef2491bdf 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -71,7 +71,7 @@ var FileActions = { FileActions.currentFile = parent; var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var file = FileActions.getCurrentFile(); - if ($('tr[data-file="'+file+'"]').data('renaming')) { + if (FileList.findFileEl(file).data('renaming')) { return; } @@ -103,9 +103,9 @@ var FileActions = { } var html = ''; if (img) { - html += ' '; + html += ''; } - html += t('files', name) + ''; + html += ' ' + t('files', name) + ''; var element = $(html); element.data('action', name); @@ -173,7 +173,10 @@ $(document).ready(function () { 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()); + var url = FileList.getDownloadUrl(filename); + if (url) { + OC.redirect(url); + } }); } $('#fileList tr').each(function () { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 473bcf25f2d..63fd0f4ce05 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -6,6 +6,13 @@ var FileList={ $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); }); }, + /** + * Returns the tr element for a given file name + */ + findFileEl: function(fileName){ + // use filterAttr to avoid escaping issues + return $('#fileList tr').filterAttr('data-file', fileName); + }, update:function(fileListHtml) { var $fileList = $('#fileList'); $fileList.empty().html(fileListHtml); @@ -20,6 +27,8 @@ var FileList={ Files.setupDragAndDrop(); } FileList.updateFileSummary(); + procesSelection(); + $fileList.trigger(jQuery.Event("updated")); }, createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions) { @@ -292,8 +301,12 @@ var FileList={ $('#filestable').toggleClass('hidden', show); }, remove:function(name){ - $('tr').filterAttr('data-file',name).find('td.filename').draggable('destroy'); - $('tr').filterAttr('data-file',name).remove(); + var fileEl = FileList.findFileEl(name); + if (fileEl.data('permissions') & OC.PERMISSION_DELETE) { + // file is only draggable when delete permissions are set + fileEl.find('td.filename').draggable('destroy'); + } + fileEl.remove(); FileList.updateFileSummary(); if ( ! $('tr[data-file]').exists() ) { $('#emptycontent').removeClass('hidden'); @@ -334,7 +347,7 @@ var FileList={ FileList.updateFileSummary(); }, loadingDone:function(name, id) { - var mime, tr = $('tr[data-file="'+name+'"]'); + var mime, tr = FileList.findFileEl(name); tr.data('loading', false); mime = tr.data('mime'); tr.attr('data-mime', mime); @@ -347,12 +360,12 @@ var FileList={ }, null, null, tr.attr('data-etag')); tr.find('td.filename').draggable(dragOptions); }, - isLoading:function(name) { - return $('tr[data-file="'+name+'"]').data('loading'); + isLoading:function(file) { + return FileList.findFileEl(file).data('loading'); }, rename:function(oldname) { var tr, td, input, form; - tr = $('tr[data-file="'+oldname+'"]'); + tr = FileList.findFileEl(oldname); tr.data('renaming',true); td = tr.children('td.filename'); input = $('').val(oldname); @@ -500,14 +513,16 @@ var FileList={ form.trigger('submit'); }); }, - inList:function(filename) { - return $('#fileList tr[data-file="'+filename+'"]').length; + inList:function(file) { + return FileList.findFileEl(file).length; }, replace:function(oldName, newName, isNewFile) { // Finish any existing actions - $('tr[data-file="'+oldName+'"]').hide(); - $('tr[data-file="'+newName+'"]').hide(); - var tr = $('tr[data-file="'+oldName+'"]').clone(); + var oldFileEl = FileList.findFileEl(oldName); + var newFileEl = FileList.findFileEl(newName); + oldFileEl.hide(); + newFileEl.hide(); + var tr = oldFileEl.clone(); tr.attr('data-replace', 'true'); tr.attr('data-file', newName); var td = tr.children('td.filename'); @@ -559,7 +574,7 @@ var FileList={ files=[files]; } for (var i=0; i span').attr('data-oldName'); + FileList.findFileEl(file).show(); OC.Notification.hide(); }); $('#notification:first-child').on('click', '.cancel', function() { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index fdaa3aa3342..d794a1584de 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -282,7 +282,7 @@ $(document).ready(function() { procesSelection(); } else { var filename=$(this).parent().parent().attr('data-file'); - var tr=$('tr[data-file="'+filename+'"]'); + var tr = FileList.findFileEl(filename); var renaming=tr.data('renaming'); if (!renaming && !FileList.isLoading(filename)) { FileActions.currentFile = $(this).parent(); @@ -493,7 +493,7 @@ var createDragShadow = function(event) { var dir=$('#dir').val(); $(selectedFiles).each(function(i,elem) { - var newtr = $('').attr('data-dir', dir).attr('data-filename', elem.name); + var newtr = $('').attr('data-dir', dir).attr('data-filename', elem.name).attr('data-origin', elem.origin); newtr.append($('').addClass('filename').text(elem.name)); newtr.append($('').addClass('size').text(humanFileSize(elem.size))); tbody.append(newtr); @@ -511,13 +511,30 @@ var createDragShadow = function(event) { }; //options for file drag/drop +//start&stop handlers needs some cleaning up var dragOptions={ revert: 'invalid', revertDuration: 300, opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: 24, top: 18 }, helper: createDragShadow, cursor: 'move', - stop: function(event, ui) { - $('#fileList tr td.filename').addClass('ui-draggable'); - } + start: function(event, ui){ + var $selectedFiles = $('td.filename input:checkbox:checked'); + if($selectedFiles.length > 1){ + $selectedFiles.parents('tr').fadeTo(250, 0.2); + } + else{ + $(this).fadeTo(250, 0.2); + } + }, + stop: function(event, ui) { + var $selectedFiles = $('td.filename input:checkbox:checked'); + if($selectedFiles.length > 1){ + $selectedFiles.parents('tr').fadeTo(250, 1); + } + else{ + $(this).fadeTo(250, 1); + } + $('#fileList tr td.filename').addClass('ui-draggable'); + } }; // sane browsers support using the distance option if ( $('html.ie').length === 0) { @@ -525,6 +542,7 @@ if ( $('html.ie').length === 0) { } var folderDropOptions={ + hoverClass: "canDrop", drop: function( event, ui ) { //don't allow moving a file into a selected folder if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { @@ -537,14 +555,21 @@ var folderDropOptions={ $(files).each(function(i,row) { var dir = $(row).data('dir'); var file = $(row).data('filename'); + //slapdash selector, tracking down our original element that the clone budded off of. + var origin = $('tr[data-id=' + $(row).data('origin') + ']'); + var td = origin.children('td.filename'); + var oldBackgroundImage = td.css('background-image'); + td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); $.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[data-file="'+target+'"]').data('size'); - var newSize = oldSize + $('#fileList tr[data-file="'+file+'"]').data('size'); - $('#fileList tr[data-file="'+target+'"]').data('size', newSize); - $('#fileList tr[data-file="'+target+'"]').find('td.filesize').text(humanFileSize(newSize)); + var oldFile = FileList.findFileEl(target); + var newFile = FileList.findFileEl(file); + var oldSize = oldFile.data('size'); + var newSize = oldSize + newFile.data('size'); + oldFile.data('size', newSize); + oldFile.find('td.filesize').text(humanFileSize(newSize)); FileList.remove(file); procesSelection(); @@ -557,6 +582,7 @@ var folderDropOptions={ } else { OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } + td.css('background-image', oldBackgroundImage); }); }); }, @@ -581,6 +607,11 @@ var crumbDropOptions={ $(files).each(function(i,row) { var dir = $(row).data('dir'); var file = $(row).data('filename'); + //slapdash selector, tracking down our original element that the clone budded off of. + var origin = $('tr[data-id=' + $(row).data('origin') + ']'); + var td = origin.children('td.filename'); + var oldBackgroundImage = td.css('background-image'); + td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { if (result) { if (result.status === 'success') { @@ -595,6 +626,7 @@ var crumbDropOptions={ } else { OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } + td.css('background-image', oldBackgroundImage); }); }); }, @@ -610,11 +642,12 @@ function procesSelection() { return el.type==='dir'; }); if (selectedFiles.length === 0 && selectedFolders.length === 0) { - $('#headerName>span.name').text(t('files','Name')); + $('#headerName span.name').text(t('files','Name')); $('#headerSize').text(t('files','Size')); $('#modified').text(t('files','Modified')); $('table').removeClass('multiselect'); $('.selectedActions').hide(); + $('#select_all').removeAttr('checked'); } else { $('.selectedActions').show(); @@ -660,7 +693,8 @@ function getSelectedFilesTrash(property) { mime:$(element).data('mime'), type:$(element).data('type'), size:$(element).data('size'), - etag:$(element).data('etag') + etag:$(element).data('etag'), + origin: $(element).data('id') }; if (property) { files.push(file[property]); @@ -717,7 +751,7 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { console.warn('Files.lazyLoadPreview(): missing etag argument'); } - if ( $('#publicUploadButtonMock').length ) { + if ( $('#isPublic').length ) { urlSpec.t = $('#dirToken').val(); previewURL = OC.Router.generate('core_ajax_public_preview', urlSpec); } else { @@ -738,7 +772,7 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { } function getUniqueName(name) { - if ($('tr[data-file="'+name+'"]').exists()) { + if (FileList.findFileEl(name).exists()) { var parts=name.split('.'); var extension = ""; if (parts.length > 1) { @@ -780,3 +814,4 @@ function onClickBreadcrumb(e) { FileList.changeDirectory(decodeURIComponent($targetDir)); } } + diff --git a/apps/files/l10n/ak.php b/apps/files/l10n/ak.php new file mode 100644 index 00000000000..f229792722d --- /dev/null +++ b/apps/files/l10n/ak.php @@ -0,0 +1,7 @@ + array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=n > 1;"; diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index 23f8030de12..0148f5ca3c1 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -65,7 +65,6 @@ $TRANSLATIONS = array( "Cancel upload" => "إلغاء رفع الملفات", "Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرفع بعض الملفات!", "Download" => "تحميل", -"Unshare" => "إلغاء مشاركة", "Delete" => "إلغاء", "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/az.php b/apps/files/l10n/az.php new file mode 100644 index 00000000000..70ab6572ba4 --- /dev/null +++ b/apps/files/l10n/az.php @@ -0,0 +1,7 @@ + array(""), +"_%n file_::_%n files_" => array(""), +"_Uploading %n file_::_Uploading %n files_" => array("") +); +$PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files/l10n/be.php b/apps/files/l10n/be.php index 17262d2184d..830400b93fb 100644 --- a/apps/files/l10n/be.php +++ b/apps/files/l10n/be.php @@ -2,6 +2,7 @@ $TRANSLATIONS = array( "_%n folder_::_%n folders_" => array("","","",""), "_%n file_::_%n files_" => array("","","",""), -"_Uploading %n file_::_Uploading %n files_" => array("","","","") +"_Uploading %n file_::_Uploading %n files_" => array("","","",""), +"Error" => "Памылка" ); $PLURAL_FORMS = "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index b24aa979e2e..f7266517e9e 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -47,7 +47,6 @@ $TRANSLATIONS = array( "Cancel upload" => "আপলোড বাতিল কর", "Nothing in here. Upload something!" => "এখানে কিছুই নেই। কিছু আপলোড করুন !", "Download" => "ডাউনলোড", -"Unshare" => "ভাগাভাগি বাতিল ", "Delete" => "মুছে", "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/ca.php b/apps/files/l10n/ca.php index 26eb4276246..261713bc581 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -83,7 +83,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "No teniu permisos per a pujar o crear els fitxers aquí", "Nothing in here. Upload something!" => "Res per aquí. Pugeu alguna cosa!", "Download" => "Baixa", -"Unshare" => "Deixa de compartir", "Delete" => "Esborra", "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", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index e1a1ed53b32..44e0fdae1f1 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Název souboru nesmí obsahovat \"/\". Vyberte prosím jiné jméno.", "The name %s is already used in the folder %s. Please choose a different name." => "Název %s ve složce %s již existuje. Vyberte prosím jiné jméno.", "Not a valid source" => "Neplatný zdroj", +"Server is not allowed to open URLs, please check the server configuration" => "Server není oprávněn otevírat adresy URL. Ověřte, prosím, konfiguraci serveru.", "Error while downloading %s to %s" => "Chyba při stahování %s do %s", "Error when creating the file" => "Chyba při vytváření souboru", "Folder name cannot be empty." => "Název složky nemůže být prázdný.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} již existuje", "Could not create file" => "Nepodařilo se vytvořit soubor", "Could not create folder" => "Nepodařilo se vytvořit složku", +"Error fetching URL" => "Chyba při načítání URL", "Share" => "Sdílet", "Delete permanently" => "Trvale odstranit", "Rename" => "Přejmenovat", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Nemáte oprávnění zde nahrávat či vytvářet soubory", "Nothing in here. Upload something!" => "Žádný obsah. Nahrajte něco.", "Download" => "Stáhnout", -"Unshare" => "Zrušit sdílení", "Delete" => "Smazat", "Upload too large" => "Odesílaný 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.", diff --git a/apps/files/l10n/cy_GB.php b/apps/files/l10n/cy_GB.php index b08c02d8d07..2a007761c52 100644 --- a/apps/files/l10n/cy_GB.php +++ b/apps/files/l10n/cy_GB.php @@ -53,7 +53,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Diddymu llwytho i fyny", "Nothing in here. Upload something!" => "Does dim byd fan hyn. Llwythwch rhywbeth i fyny!", "Download" => "Llwytho i lawr", -"Unshare" => "Dad-rannu", "Delete" => "Dileu", "Upload too large" => "Maint llwytho i fyny'n rhy fawr", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Mae'r ffeiliau rydych yn ceisio llwytho i fyny'n fwy na maint mwyaf llwytho ffeiliau i fyny ar y gweinydd hwn.", diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index f5bb13e870f..9b7722444a8 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -3,6 +3,15 @@ $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", "File name cannot be empty." => "Filnavnet kan ikke stå tomt.", +"File name must not contain \"/\". Please choose a different name." => "Filnavnet må ikke indeholde \"/\". Vælg venligst et andet navn.", +"The name %s is already used in the folder %s. Please choose a different name." => "Navnet %s er allerede i brug i mappen %s. Vælg venligst et andet navn.", +"Not a valid source" => "Ikke en gyldig kilde", +"Server is not allowed to open URLs, please check the server configuration" => "Server har ikke tilladelse til at åbne URL'er. Kontroller venligst serverens indstillinger", +"Error while downloading %s to %s" => "Fejl ved hentning af %s til %s", +"Error when creating the file" => "Fejl ved oprettelse af fil", +"Folder name cannot be empty." => "Mappenavnet kan ikke være tomt.", +"Folder name must not contain \"/\". Please choose a different name." => "Mappenavnet må ikke indeholde \"/\". Vælg venligst et andet navn.", +"Error when creating the folder" => "Fejl ved oprettelse af mappen", "Unable to set upload directory." => "Ude af stand til at vælge upload mappe.", "Invalid Token" => "Ugyldig Token ", "No file was uploaded. Unknown error" => "Ingen fil blev uploadet. Ukendt fejl.", @@ -23,13 +32,20 @@ $TRANSLATIONS = array( "Upload cancelled." => "Upload afbrudt.", "Could not get result from server." => "Kunne ikke hente resultat fra server.", "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.", +"URL cannot be empty" => "URL kan ikke være tom", +"In the home folder 'Shared' is a reserved filename" => "Navnet 'Shared' er reserveret i hjemmemappen.", "{new_name} already exists" => "{new_name} eksisterer allerede", +"Could not create file" => "Kunne ikke oprette fil", +"Could not create folder" => "Kunne ikke oprette mappe", +"Error fetching URL" => "Fejl ved URL", "Share" => "Del", "Delete permanently" => "Slet permanent", "Rename" => "Omdøb", "Pending" => "Afventer", +"Could not rename file" => "Kunne ikke omdøbe filen", "replaced {new_name} with {old_name}" => "erstattede {new_name} med {old_name}", "undo" => "fortryd", +"Error deleting file." => "Fejl ved sletnign af fil.", "_%n folder_::_%n folders_" => array("%n mappe","%n mapper"), "_%n file_::_%n files_" => array("%n fil","%n filer"), "{dirs} and {files}" => "{dirs} og {files}", @@ -38,6 +54,8 @@ $TRANSLATIONS = array( "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}%)", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Krypteringsprogrammet er aktiveret, men din nøgle er ikke igangsat. Log venligst ud og ind igen.", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Ugyldig privat nøgle for krypteringsprogrammet. Opdater venligst dit kodeord for den private nøgle i dine personlige indstillinger. Det kræves for at få adgang til dine krypterede filer.", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Krypteringen blev deaktiveret, men dine filer er stadig krypteret. Gå venligst til dine personlige indstillinger for at dekryptere dine filer. ", "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.", "Error moving file" => "Fejl ved flytning af fil", @@ -45,6 +63,7 @@ $TRANSLATIONS = array( "Name" => "Navn", "Size" => "Størrelse", "Modified" => "Ændret", +"Invalid folder name. Usage of 'Shared' is reserved." => "Ugyldig mappenavn. 'Shared' er reserveret.", "%s could not be renamed" => "%s kunne ikke omdøbes", "Upload" => "Upload", "File handling" => "Filhåndtering", @@ -56,15 +75,16 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "Maksimal størrelse på ZIP filer", "Save" => "Gem", "New" => "Ny", +"New text file" => "Ny tekstfil", "Text file" => "Tekstfil", "New folder" => "Ny Mappe", "Folder" => "Mappe", "From link" => "Fra link", "Deleted files" => "Slettede filer", "Cancel upload" => "Fortryd upload", +"You don’t have permission to upload or create files here" => "Du har ikke tilladelse til at uploade eller oprette filer her", "Nothing in here. Upload something!" => "Her er tomt. Upload noget!", "Download" => "Download", -"Unshare" => "Fjern deling", "Delete" => "Slet", "Upload too large" => "Upload er 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.", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 4301e2d1afb..b209fee88ae 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Der Dateiname darf kein \"/\" enthalten. Bitte wähle einen anderen Namen.", "The name %s is already used in the folder %s. Please choose a different name." => "Der Name %s wird bereits im Ordner %s benutzt. Bitte wähle einen anderen Namen.", "Not a valid source" => "Keine gültige Quelle", +"Server is not allowed to open URLs, please check the server configuration" => "Dem Server ist das Öffnen von URLs nicht erlaubt, bitte die Serverkonfiguration prüfen", "Error while downloading %s to %s" => "Fehler beim Herunterladen von %s nach %s", "Error when creating the file" => "Fehler beim Erstellen der Datei", "Folder name cannot be empty." => "Der Ordner-Name darf nicht leer sein.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} existiert bereits", "Could not create file" => "Die Datei konnte nicht erstellt werden", "Could not create folder" => "Der Ordner konnte nicht erstellt werden", +"Error fetching URL" => "Fehler beim Abrufen der URL", "Share" => "Teilen", "Delete permanently" => "Endgültig löschen", "Rename" => "Umbenennen", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Du besitzt hier keine Berechtigung, um Dateien hochzuladen oder zu erstellen", "Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!", "Download" => "Herunterladen", -"Unshare" => "Freigabe aufheben", "Delete" => "Löschen", "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.", diff --git a/apps/files/l10n/de_CH.php b/apps/files/l10n/de_CH.php index 144df3ee7d7..b9504293467 100644 --- a/apps/files/l10n/de_CH.php +++ b/apps/files/l10n/de_CH.php @@ -58,7 +58,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Upload abbrechen", "Nothing in here. Upload something!" => "Alles leer. Laden Sie etwas hoch!", "Download" => "Herunterladen", -"Unshare" => "Freigabe aufheben", "Delete" => "Löschen", "Upload too large" => "Der Upload ist zu gross", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgrösse für Uploads auf diesem Server.", diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 6a2e3885251..76cdce00e4c 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Der Dateiname darf kein \"/\" enthalten. Bitte wählen Sie einen anderen Namen.", "The name %s is already used in the folder %s. Please choose a different name." => "Der Name %s wird bereits im Ordner %s benutzt. Bitte wählen Sie einen anderen Namen.", "Not a valid source" => "Keine gültige Quelle", +"Server is not allowed to open URLs, please check the server configuration" => "Dem Server ist das Öffnen von URLs nicht erlaubt, bitte die Serverkonfiguration prüfen", "Error while downloading %s to %s" => "Fehler beim Herunterladen von %s nach %s", "Error when creating the file" => "Fehler beim Erstellen der Datei", "Folder name cannot be empty." => "Der Ordner-Name darf nicht leer sein.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} existiert bereits", "Could not create file" => "Die Datei konnte nicht erstellt werden", "Could not create folder" => "Der Ordner konnte nicht erstellt werden", +"Error fetching URL" => "Fehler beim Abrufen der URL", "Share" => "Teilen", "Delete permanently" => "Endgültig löschen", "Rename" => "Umbenennen", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Sie besitzen hier keine Berechtigung Dateien hochzuladen oder zu erstellen", "Nothing in here. Upload something!" => "Alles leer. Laden Sie etwas hoch!", "Download" => "Herunterladen", -"Unshare" => "Freigabe aufheben", "Delete" => "Löschen", "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.", diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 0c0b59ea881..9efe1af7dd3 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -3,6 +3,15 @@ $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Αδυναμία μετακίνησης του %s - υπάρχει ήδη αρχείο με αυτό το όνομα", "Could not move %s" => "Αδυναμία μετακίνησης του %s", "File name cannot be empty." => "Το όνομα αρχείου δεν μπορεί να είναι κενό.", +"File name must not contain \"/\". Please choose a different name." => "Το όνομα αρχείου δεν μπορεί να περιέχει \"/\". Παρακαλώ επιλέξτε ένα διαφορετικό όνομα. ", +"The name %s is already used in the folder %s. Please choose a different name." => "Το όνομα %s χρησιμοποιείτε ήδη στον φάκελο %s. Παρακαλώ επιλέξτε ένα άλλο όνομα.", +"Not a valid source" => "Μη έγκυρη πηγή", +"Server is not allowed to open URLs, please check the server configuration" => "Ο διακομιστής δεν επιτρέπεται να ανοίγει URL, παρακαλώ ελέγξτε τις ρυθμίσεις του διακομιστή", +"Error while downloading %s to %s" => "Σφάλμα κατά τη λήψη του %s στο %s", +"Error when creating the file" => "Σφάλμα κατά τη δημιουργία του αρχείου", +"Folder name cannot be empty." => "Το όνομα φακέλου δεν μπορεί να είναι κενό.", +"Folder name must not contain \"/\". Please choose a different name." => "Το όνομα φακέλου δεν μπορεί να περιέχει \"/\". Παρακαλώ επιλέξτε ένα διαφορετικό όνομα. ", +"Error when creating the folder" => "Σφάλμα δημιουργίας φακέλου", "Unable to set upload directory." => "Αδυναμία ορισμού καταλόγου αποστολής.", "Invalid Token" => "Μη έγκυρο Token", "No file was uploaded. Unknown error" => "Δεν ανέβηκε κάποιο αρχείο. Άγνωστο σφάλμα", @@ -14,25 +23,39 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Λείπει ο προσωρινός φάκελος", "Failed to write to disk" => "Αποτυχία εγγραφής στο δίσκο", "Not enough storage available" => "Μη επαρκής διαθέσιμος αποθηκευτικός χώρος", +"Upload failed. Could not get file info." => "Η φόρτωση απέτυχε. Αδυναμία λήψης πληροφοριών αρχείων.", +"Upload failed. Could not find uploaded file" => "Η φόρτωση απέτυχε. Αδυναμία εύρεσης αρχείου προς φόρτωση.", "Invalid directory." => "Μη έγκυρος φάκελος.", "Files" => "Αρχεία", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Αδυναμία φόρτωσης {filename} καθώς είναι κατάλογος αρχείων ή έχει 0 bytes", "Not enough space available" => "Δεν υπάρχει αρκετός διαθέσιμος χώρος", "Upload cancelled." => "Η αποστολή ακυρώθηκε.", +"Could not get result from server." => "Αδυναμία λήψης αποτελέσματος από το διακομιστή.", "File upload is in progress. Leaving the page now will cancel the upload." => "Η αποστολή του αρχείου βρίσκεται σε εξέλιξη. Το κλείσιμο της σελίδας θα ακυρώσει την αποστολή.", +"URL cannot be empty" => "Η URL δεν πρέπει να είναι κενή", +"In the home folder 'Shared' is a reserved filename" => "Στον αρχικό φάκελο το όνομα 'Shared' διατηρείται από το σύστημα", "{new_name} already exists" => "{new_name} υπάρχει ήδη", +"Could not create file" => "Αδυναμία δημιουργίας αρχείου", +"Could not create folder" => "Αδυναμία δημιουργίας φακέλου", +"Error fetching URL" => "Σφάλμα φόρτωσης URL", "Share" => "Διαμοιρασμός", "Delete permanently" => "Μόνιμη διαγραφή", "Rename" => "Μετονομασία", "Pending" => "Εκκρεμεί", +"Could not rename file" => "Αδυναμία μετονομασίας αρχείου", "replaced {new_name} with {old_name}" => "αντικαταστάθηκε το {new_name} με {old_name}", "undo" => "αναίρεση", +"Error deleting file." => "Σφάλμα διαγραφής αρχείου.", "_%n folder_::_%n folders_" => array("%n φάκελος","%n φάκελοι"), "_%n file_::_%n files_" => array("%n αρχείο","%n αρχεία"), +"{dirs} and {files}" => "{Κατάλογοι αρχείων} και {αρχεία}", "_Uploading %n file_::_Uploading %n files_" => array("Ανέβασμα %n αρχείου","Ανέβασμα %n αρχείων"), "'.' is an invalid file name." => "'.' είναι μη έγκυρο όνομα αρχείου.", "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}%)", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Η εφαρμογή κρυπτογράφησης είναι ενεργοποιημένη αλλά τα κλειδιά σας δεν έχουν καταγραφεί, παρακαλώ αποσυνδεθείτε και επανασυνδεθείτε.", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Άκυρο προσωπικό κλειδί για την εφαρμογή κρυπτογράφησης. Παρακαλώ ενημερώστε τον κωδικό του προσωπικού κλειδίου σας στις προσωπικές ρυθμίσεις για να επανακτήσετε πρόσβαση στα κρυπτογραφημένα σας αρχεία.", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Η κρυπτογράφηση απενεργοποιήθηκε, αλλά τα αρχεία σας είναι ακόμα κρυπτογραφημένα. Παρακαλούμε απενεργοποιήσετε την κρυπτογράφηση αρχείων από τις προσωπικές σας ρυθμίσεις", "Your download is being prepared. This might take some time if the files are big." => "Η λήψη προετοιμάζεται. Αυτό μπορεί να πάρει ώρα εάν τα αρχεία έχουν μεγάλο μέγεθος.", "Error moving file" => "Σφάλμα κατά τη μετακίνηση του αρχείου", @@ -40,6 +63,7 @@ $TRANSLATIONS = array( "Name" => "Όνομα", "Size" => "Μέγεθος", "Modified" => "Τροποποιήθηκε", +"Invalid folder name. Usage of 'Shared' is reserved." => "Άκυρο όνομα φακέλου. Η χρήση του 'Shared' διατηρείται από το σύστημα.", "%s could not be renamed" => "Αδυναμία μετονομασίας του %s", "Upload" => "Μεταφόρτωση", "File handling" => "Διαχείριση αρχείων", @@ -51,15 +75,16 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "Μέγιστο μέγεθος για αρχεία ZIP", "Save" => "Αποθήκευση", "New" => "Νέο", +"New text file" => "Νέο αρχείο κειμένου", "Text file" => "Αρχείο κειμένου", "New folder" => "Νέος κατάλογος", "Folder" => "Φάκελος", "From link" => "Από σύνδεσμο", "Deleted files" => "Διαγραμμένα αρχεία", "Cancel upload" => "Ακύρωση αποστολής", +"You don’t have permission to upload or create files here" => "Δεν έχετε δικαιώματα φόρτωσης ή δημιουργίας αρχείων εδώ", "Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανεβάστε κάτι!", "Download" => "Λήψη", -"Unshare" => "Σταμάτημα διαμοιρασμού", "Delete" => "Διαγραφή", "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/en_GB.php b/apps/files/l10n/en_GB.php index 40de4f714af..ac93aa68abb 100644 --- a/apps/files/l10n/en_GB.php +++ b/apps/files/l10n/en_GB.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "File name must not contain \"/\". Please choose a different name.", "The name %s is already used in the folder %s. Please choose a different name." => "The name %s is already used in the folder %s. Please choose a different name.", "Not a valid source" => "Not a valid source", +"Server is not allowed to open URLs, please check the server configuration" => "Server is not allowed to open URLs, please check the server configuration", "Error while downloading %s to %s" => "Error whilst downloading %s to %s", "Error when creating the file" => "Error when creating the file", "Folder name cannot be empty." => "Folder name cannot be empty.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} already exists", "Could not create file" => "Could not create file", "Could not create folder" => "Could not create folder", +"Error fetching URL" => "Error fetching URL", "Share" => "Share", "Delete permanently" => "Delete permanently", "Rename" => "Rename", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "You don’t have permission to upload or create files here", "Nothing in here. Upload something!" => "Nothing in here. Upload something!", "Download" => "Download", -"Unshare" => "Unshare", "Delete" => "Delete", "Upload too large" => "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.", diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index 8f7d4de5702..81cfa03fd6d 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -75,7 +75,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Vi ne havas permeson alŝuti aŭ krei dosierojn ĉi tie", "Nothing in here. Upload something!" => "Nenio estas ĉi tie. Alŝutu ion!", "Download" => "Elŝuti", -"Unshare" => "Malkunhavigi", "Delete" => "Forigi", "Upload too large" => "Alŝ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.", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index a711e97a42d..bfbb80e8962 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "El nombre del archivo, NO puede contener el simbolo\"/\", por favor elija un nombre diferente.", "The name %s is already used in the folder %s. Please choose a different name." => "El nombre %s ya está en uso por la carpeta %s. Por favor elija uno diferente.", "Not a valid source" => "No es un origen válido", +"Server is not allowed to open URLs, please check the server configuration" => "El servidor no puede acceder URLs; revise la configuración del servidor.", "Error while downloading %s to %s" => "Error mientras se descargaba %s a %s", "Error when creating the file" => "Error al crear el archivo", "Folder name cannot be empty." => "El nombre de la carpeta no puede estar vacío.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} ya existe", "Could not create file" => "No se pudo crear el archivo", "Could not create folder" => "No se pudo crear la carpeta", +"Error fetching URL" => "Error al descargar URL.", "Share" => "Compartir", "Delete permanently" => "Eliminar permanentemente", "Rename" => "Renombrar", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "No tienes permisos para subir o crear archivos aquí.", "Nothing in here. Upload something!" => "No hay nada aquí. ¡Suba algo!", "Download" => "Descargar", -"Unshare" => "Dejar de compartir", "Delete" => "Eliminar", "Upload too large" => "Subida demasido 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 en este servidor.", diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index 9f54a65af33..78d6388cd9b 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -59,7 +59,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Cancelar subida", "Nothing in here. Upload something!" => "No hay nada. ¡Subí contenido!", "Download" => "Descargar", -"Unshare" => "Dejar de compartir", "Delete" => "Borrar", "Upload too large" => "El tamaño del archivo que querés subir 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 ", diff --git a/apps/files/l10n/es_CL.php b/apps/files/l10n/es_CL.php new file mode 100644 index 00000000000..6f97758878f --- /dev/null +++ b/apps/files/l10n/es_CL.php @@ -0,0 +1,9 @@ + "Archivos", +"_%n folder_::_%n folders_" => array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("",""), +"Upload" => "Subir" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/es_MX.php b/apps/files/l10n/es_MX.php index 0157af093e9..0b7571defc7 100644 --- a/apps/files/l10n/es_MX.php +++ b/apps/files/l10n/es_MX.php @@ -1,7 +1,95 @@ array("",""), -"_%n file_::_%n files_" => array("",""), -"_Uploading %n file_::_Uploading %n files_" => array("","") +"Could not move %s - File with this name already exists" => "No se pudo mover %s - Ya existe un archivo con ese nombre.", +"Could not move %s" => "No se pudo mover %s", +"File name cannot be empty." => "El nombre de archivo no puede estar vacío.", +"File name must not contain \"/\". Please choose a different name." => "El nombre del archivo, NO puede contener el simbolo\"/\", por favor elija un nombre diferente.", +"The name %s is already used in the folder %s. Please choose a different name." => "El nombre %s ya está en uso por la carpeta %s. Por favor elija uno diferente.", +"Not a valid source" => "No es un origen válido", +"Server is not allowed to open URLs, please check the server configuration" => "El servidor no puede acceder URLs; revise la configuración del servidor.", +"Error while downloading %s to %s" => "Error mientras se descargaba %s a %s", +"Error when creating the file" => "Error al crear el archivo", +"Folder name cannot be empty." => "El nombre de la carpeta no puede estar vacío.", +"Folder name must not contain \"/\". Please choose a different name." => "El nombre de la carpeta, NO puede contener el simbolo\"/\", por favor elija un nombre diferente.", +"Error when creating the folder" => "Error al crear la carpeta.", +"Unable to set upload directory." => "Incapaz de crear directorio de subida.", +"Invalid Token" => "Token Inválido", +"No file was uploaded. Unknown error" => "No se subió ningún archivo. Error desconocido", +"There is no error, the file uploaded with success" => "No hubo ningún problema, el archivo se subió con éxito", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo subido sobrepasa la directiva 'upload_max_filesize' en php.ini:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo subido sobrepasa la directiva 'MAX_FILE_SIZE' especificada en el formulario HTML", +"The uploaded file was only partially uploaded" => "El archivo subido fue sólo subido parcialmente", +"No file was uploaded" => "No se subió ningún archivo", +"Missing a temporary folder" => "Falta la carpeta temporal", +"Failed to write to disk" => "Falló al escribir al disco", +"Not enough storage available" => "No hay suficiente espacio disponible", +"Upload failed. Could not get file info." => "Actualización fallida. No se pudo obtener información del archivo.", +"Upload failed. Could not find uploaded file" => "Actualización fallida. No se pudo encontrar el archivo subido", +"Invalid directory." => "Directorio inválido.", +"Files" => "Archivos", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "No ha sido posible subir {filename} porque es un directorio o tiene 0 bytes", +"Not enough space available" => "No hay suficiente espacio disponible", +"Upload cancelled." => "Subida cancelada.", +"Could not get result from server." => "No se pudo obtener respuesta del servidor.", +"File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si sale de la página ahora, la subida será cancelada.", +"URL cannot be empty" => "La dirección URL no puede estar vacía", +"In the home folder 'Shared' is a reserved filename" => "En la carpeta de inicio, 'Shared' es un nombre reservado", +"{new_name} already exists" => "{new_name} ya existe", +"Could not create file" => "No se pudo crear el archivo", +"Could not create folder" => "No se pudo crear la carpeta", +"Error fetching URL" => "Error al descargar URL.", +"Share" => "Compartir", +"Delete permanently" => "Eliminar permanentemente", +"Rename" => "Renombrar", +"Pending" => "Pendiente", +"Could not rename file" => "No se pudo renombrar el archivo", +"replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}", +"undo" => "deshacer", +"Error deleting file." => "Error borrando el archivo.", +"_%n folder_::_%n folders_" => array("%n carpeta","%n carpetas"), +"_%n file_::_%n files_" => array("%n archivo","%n archivos"), +"{dirs} and {files}" => "{dirs} y {files}", +"_Uploading %n file_::_Uploading %n files_" => array("Subiendo %n archivo","Subiendo %n archivos"), +"'.' is an invalid file name." => "'.' no es un nombre de archivo válido.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre inválido, los caracteres \"\\\", \"/\", \"<\", \">\", \":\", \"\", \"|\" \"?\" y \"*\" no están permitidos ", +"Your storage is full, files can not be updated or synced anymore!" => "Su almacenamiento está lleno, ¡los archivos no se actualizarán ni sincronizarán más!", +"Your storage is almost full ({usedSpacePercent}%)" => "Su almacenamiento está casi lleno ({usedSpacePercent}%)", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "La aplicación de crifrado está habilitada pero tus claves no han sido inicializadas, por favor, cierra la sesión y vuelva a iniciarla de nuevo.", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "La clave privada no es válida para la aplicación de cifrado. Por favor, actualiza la contraseña de tu clave privada en tus ajustes personales para recuperar el acceso a tus archivos cifrados.", +"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "El cifrado ha sido deshabilitado pero tus archivos permanecen cifrados. Por favor, ve a tus ajustes personales para descifrar tus archivos.", +"Your download is being prepared. This might take some time if the files are big." => "Su descarga está siendo preparada. Esto podría tardar algo de tiempo si los archivos son grandes.", +"Error moving file" => "Error moviendo archivo", +"Error" => "Error", +"Name" => "Nombre", +"Size" => "Tamaño", +"Modified" => "Modificado", +"Invalid folder name. Usage of 'Shared' is reserved." => "Nombre de carpeta inválido. El uso de \"Shared\" esta reservado.", +"%s could not be renamed" => "%s no pudo ser renombrado", +"Upload" => "Subir", +"File handling" => "Administración de archivos", +"Maximum upload size" => "Tamaño máximo de subida", +"max. possible: " => "máx. posible:", +"Needed for multi-file and folder downloads." => "Necesario para multi-archivo y descarga de carpetas", +"Enable ZIP-download" => "Habilitar descarga en ZIP", +"0 is unlimited" => "0 significa ilimitado", +"Maximum input size for ZIP files" => "Tamaño máximo para archivos ZIP de entrada", +"Save" => "Guardar", +"New" => "Nuevo", +"New text file" => "Nuevo archivo de texto", +"Text file" => "Archivo de texto", +"New folder" => "Nueva carpeta", +"Folder" => "Carpeta", +"From link" => "Desde enlace", +"Deleted files" => "Archivos eliminados", +"Cancel upload" => "Cancelar subida", +"You don’t have permission to upload or create files here" => "No tienes permisos para subir o crear archivos aquí.", +"Nothing in here. Upload something!" => "No hay nada aquí. ¡Suba algo!", +"Download" => "Descargar", +"Delete" => "Eliminar", +"Upload too large" => "Subida demasido 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 en este servidor.", +"Files are being scanned, please wait." => "Los archivos están siendo escaneados, por favor espere.", +"Current scanning" => "Escaneo actual", +"Upgrading filesystem cache..." => "Actualizando caché del sistema de archivos..." ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 4830835ead3..fd031527738 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Faili nimi ei tohi sisaldada \"/\". Palun vali mõni teine nimi.", "The name %s is already used in the folder %s. Please choose a different name." => "Nimi %s on juba kasutusel kataloogis %s. Palun vali mõni teine nimi.", "Not a valid source" => "Pole korrektne lähteallikas", +"Server is not allowed to open URLs, please check the server configuration" => "Server ei võimalda URL-ide avamist, palun kontrolli serveri seadistust", "Error while downloading %s to %s" => "Viga %s allalaadimisel %s", "Error when creating the file" => "Viga faili loomisel", "Folder name cannot be empty." => "Kataloogi nimi ei saa olla tühi.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} on juba olemas", "Could not create file" => "Ei suuda luua faili", "Could not create folder" => "Ei suuda luua kataloogi", +"Error fetching URL" => "Viga URL-i haaramisel", "Share" => "Jaga", "Delete permanently" => "Kustuta jäädavalt", "Rename" => "Nimeta ümber", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Sul puuduvad õigused siia failide üleslaadimiseks või tekitamiseks", "Nothing in here. Upload something!" => "Siin pole midagi. Lae midagi üles!", "Download" => "Lae alla", -"Unshare" => "Lõpeta jagamine", "Delete" => "Kustuta", "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.", diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index e68cb5acd2e..5df480c2bc3 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -3,6 +3,14 @@ $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", "File name cannot be empty." => "Fitxategi izena ezin da hutsa izan.", +"File name must not contain \"/\". Please choose a different name." => "Fitxategi izenak ezin du \"/\" izan. Mesedez hautatu beste izen bat.", +"The name %s is already used in the folder %s. Please choose a different name." => "%s izena dagoeneko erabilita dago %s karpetan. Mesdez hautatu izen ezberdina.", +"Not a valid source" => "Ez da jatorri baliogarria", +"Error while downloading %s to %s" => "Errorea %s %sra deskargatzerakoan", +"Error when creating the file" => "Errorea fitxategia sortzerakoan", +"Folder name cannot be empty." => "Karpeta izena ezin da hutsa izan.", +"Folder name must not contain \"/\". Please choose a different name." => "Karpeta izenak ezin du \"/\" izan. Mesedez hautatu beste izen bat.", +"Error when creating the folder" => "Errorea karpeta sortzerakoan", "Unable to set upload directory." => "Ezin da igoera direktorioa ezarri.", "Invalid Token" => "Lekuko baliogabea", "No file was uploaded. Unknown error" => "Ez da fitxategirik igo. Errore ezezaguna", @@ -23,13 +31,19 @@ $TRANSLATIONS = array( "Upload cancelled." => "Igoera ezeztatuta", "Could not get result from server." => "Ezin da zerbitzaritik emaitzik lortu", "File upload is in progress. Leaving the page now will cancel the upload." => "Fitxategien igoera martxan da. Orria orain uzteak igoera ezeztatutko du.", +"URL cannot be empty" => "URLa ezin da hutsik egon", +"In the home folder 'Shared' is a reserved filename" => "Etxeko (home) karpetan 'Shared' erreserbatutako fitxategi izena da", "{new_name} already exists" => "{new_name} dagoeneko existitzen da", +"Could not create file" => "Ezin izan da fitxategia sortu", +"Could not create folder" => "Ezin izan da karpeta sortu", "Share" => "Elkarbanatu", "Delete permanently" => "Ezabatu betirako", "Rename" => "Berrizendatu", "Pending" => "Zain", +"Could not rename file" => "Ezin izan da fitxategia berrizendatu", "replaced {new_name} with {old_name}" => " {new_name}-k {old_name} ordezkatu du", "undo" => "desegin", +"Error deleting file." => "Errorea fitxategia ezabatzerakoan.", "_%n folder_::_%n folders_" => array("karpeta %n","%n karpeta"), "_%n file_::_%n files_" => array("fitxategi %n","%n fitxategi"), "{dirs} and {files}" => "{dirs} eta {files}", @@ -47,6 +61,7 @@ $TRANSLATIONS = array( "Name" => "Izena", "Size" => "Tamaina", "Modified" => "Aldatuta", +"Invalid folder name. Usage of 'Shared' is reserved." => "Baliogabeako karpeta izena. 'Shared' izena erreserbatuta dago.", "%s could not be renamed" => "%s ezin da berrizendatu", "Upload" => "Igo", "File handling" => "Fitxategien kudeaketa", @@ -58,15 +73,16 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "ZIP fitxategien gehienezko tamaina", "Save" => "Gorde", "New" => "Berria", +"New text file" => "Testu fitxategi berria", "Text file" => "Testu fitxategia", "New folder" => "Karpeta berria", "Folder" => "Karpeta", "From link" => "Estekatik", "Deleted files" => "Ezabatutako fitxategiak", "Cancel upload" => "Ezeztatu igoera", +"You don’t have permission to upload or create files here" => "Ez duzu fitxategiak hona igotzeko edo hemen sortzeko baimenik", "Nothing in here. Upload something!" => "Ez dago ezer. Igo zerbait!", "Download" => "Deskargatu", -"Unshare" => "Ez elkarbanatu", "Delete" => "Ezabatu", "Upload too large" => "Igoera 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.", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 78311c03990..4f3257bc075 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -57,7 +57,6 @@ $TRANSLATIONS = array( "Cancel upload" => "متوقف کردن بار گذاری", "Nothing in here. Upload something!" => "اینجا هیچ چیز نیست.", "Download" => "دانلود", -"Unshare" => "لغو اشتراک", "Delete" => "حذف", "Upload too large" => "سایز فایل برای آپلود زیاد است(م.تنظیمات در php.ini)", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "فایلها بیش از حد تعیین شده در این سرور هستند\nمترجم:با تغییر فایل php,ini میتوان این محدودیت را برطرف کرد", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index e9099d0b491..d1241b77da0 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Tiedoston nimessä ei saa olla merkkiä \"/\". Valitse toinen nimi.", "The name %s is already used in the folder %s. Please choose a different name." => "Nimi %s on jo käytössä kansiossa %s. Valitse toinen nimi.", "Not a valid source" => "Virheellinen lähde", +"Server is not allowed to open URLs, please check the server configuration" => "Palvelimen ei ole lupa avata verkko-osoitteita. Tarkista palvelimen asetukset", "Error while downloading %s to %s" => "Virhe ladatessa kohdetta %s sijaintiin %s", "Error when creating the file" => "Virhe tiedostoa luotaessa", "Folder name cannot be empty." => "Kansion nimi ei voi olla tyhjä.", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} on jo olemassa", "Could not create file" => "Tiedoston luominen epäonnistui", "Could not create folder" => "Kansion luominen epäonnistui", +"Error fetching URL" => "Virhe noutaessa verkko-osoitetta", "Share" => "Jaa", "Delete permanently" => "Poista pysyvästi", "Rename" => "Nimeä uudelleen", @@ -76,7 +78,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Käyttöoikeutesi eivät riitä tiedostojen lähettämiseen tai kansioiden luomiseen tähän sijaintiin", "Nothing in here. Upload something!" => "Täällä ei ole mitään. Lähetä tänne jotakin!", "Download" => "Lataa", -"Unshare" => "Peru jakaminen", "Delete" => "Poista", "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.", diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index 2ada24187bc..73b89434778 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Le nom de fichier ne doit pas contenir \"/\". Merci de choisir un nom différent.", "The name %s is already used in the folder %s. Please choose a different name." => "Le nom %s est déjà utilisé dans le dossier %s. Merci de choisir un nom différent.", "Not a valid source" => "La source n'est pas valide", +"Server is not allowed to open URLs, please check the server configuration" => "Le serveur n'est pas autorisé à ouvrir des URL, veuillez vérifier la configuration du serveur", "Error while downloading %s to %s" => "Erreur pendant le téléchargement de %s à %s", "Error when creating the file" => "Erreur pendant la création du fichier", "Folder name cannot be empty." => "Le nom de dossier ne peux pas être vide.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} existe déjà", "Could not create file" => "Impossible de créer le fichier", "Could not create folder" => "Impossible de créer le dossier", +"Error fetching URL" => "Erreur d'accès à l'URL", "Share" => "Partager", "Delete permanently" => "Supprimer de façon définitive", "Rename" => "Renommer", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Vous n'avez pas la permission de téléverser ou de créer des fichiers ici", "Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)", "Download" => "Télécharger", -"Unshare" => "Ne plus partager", "Delete" => "Supprimer", "Upload too large" => "Téléversement 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.", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index fba3db66da9..1d22691d93c 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "O nome do ficheiro non pode conter «/». Escolla outro nome.", "The name %s is already used in the folder %s. Please choose a different name." => "Xa existe o nome %s no cartafol %s. Escolla outro nome.", "Not a valid source" => "Esta orixe non é correcta", +"Server is not allowed to open URLs, please check the server configuration" => "O servidor non ten permisos para abrir os enderezos URL, comprobe a configuración do servidor", "Error while downloading %s to %s" => "Produciuse un erro ao descargar %s en %s", "Error when creating the file" => "Produciuse un erro ao crear o ficheiro", "Folder name cannot be empty." => "O nome de cartafol non pode estar baleiro.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "Xa existe un {new_name}", "Could not create file" => "Non foi posíbel crear o ficheiro", "Could not create folder" => "Non foi posíbel crear o cartafol", +"Error fetching URL" => "Produciuse un erro ao obter o URL", "Share" => "Compartir", "Delete permanently" => "Eliminar permanentemente", "Rename" => "Renomear", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Non ten permisos para enviar ou crear ficheiros aquí.", "Nothing in here. Upload something!" => "Aquí non hai nada. Envíe algo.", "Download" => "Descargar", -"Unshare" => "Deixar de compartir", "Delete" => "Eliminar", "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 tenta enviar exceden do tamaño máximo permitido neste servidor", diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index 1d33c42446a..0cdb178254e 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -51,7 +51,6 @@ $TRANSLATIONS = array( "Cancel upload" => "ביטול ההעלאה", "Nothing in here. Upload something!" => "אין כאן שום דבר. אולי ברצונך להעלות משהו?", "Download" => "הורדה", -"Unshare" => "הסר שיתוף", "Delete" => "מחיקה", "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/hr.php b/apps/files/l10n/hr.php index 157b73b9f5c..5048a15c8bb 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Prekini upload", "Nothing in here. Upload something!" => "Nema ničega u ovoj mapi. Pošalji nešto!", "Download" => "Preuzimanje", -"Unshare" => "Makni djeljenje", "Delete" => "Obriši", "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.", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index cb253a7e5a1..22c3926ed1c 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Az állomány neve nem tartalmazhatja a \"/\" karaktert. Kérem válasszon másik nevet!", "The name %s is already used in the folder %s. Please choose a different name." => "A %s név már létezik a %s mappában. Kérem válasszon másik nevet!", "Not a valid source" => "A kiinduló állomány érvénytelen", +"Server is not allowed to open URLs, please check the server configuration" => "A kiszolgálón nincs engedélyezve URL-ek megnyitása, kérem ellenőrizze a beállításokat", "Error while downloading %s to %s" => "Hiba történt miközben %s-t letöltöttük %s-be", "Error when creating the file" => "Hiba történt az állomány létrehozásakor", "Folder name cannot be empty." => "A mappa neve nem maradhat kitöltetlenül", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} már létezik", "Could not create file" => "Az állomány nem hozható létre", "Could not create folder" => "A mappa nem hozható létre", +"Error fetching URL" => "A megadott URL-ről nem sikerül adatokat kapni", "Share" => "Megosztás", "Delete permanently" => "Végleges törlés", "Rename" => "Átnevezés", @@ -43,6 +45,7 @@ $TRANSLATIONS = array( "Could not rename file" => "Az állomány nem nevezhető át", "replaced {new_name} with {old_name}" => "{new_name} fájlt kicseréltük ezzel: {old_name}", "undo" => "visszavonás", +"Error deleting file." => "Hiba a file törlése közben.", "_%n folder_::_%n folders_" => array("%n mappa","%n mappa"), "_%n file_::_%n files_" => array("%n állomány","%n állomány"), "{dirs} and {files}" => "{dirs} és {files}", @@ -72,6 +75,7 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "ZIP-fájlok maximális kiindulási mérete", "Save" => "Mentés", "New" => "Új", +"New text file" => "Új szöveges file", "Text file" => "Szövegfájl", "New folder" => "Új mappa", "Folder" => "Mappa", @@ -81,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Önnek nincs jogosultsága ahhoz, hogy ide állományokat töltsön föl, vagy itt újakat hozzon létre", "Nothing in here. Upload something!" => "Itt nincs semmi. Töltsön fel valamit!", "Download" => "Letöltés", -"Unshare" => "A megosztás visszavonása", "Delete" => "Törlés", "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.", diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index e1e0b97e607..4e254ff6f60 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -3,6 +3,16 @@ $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Tidak dapat memindahkan %s - Berkas dengan nama ini sudah ada", "Could not move %s" => "Tidak dapat memindahkan %s", "File name cannot be empty." => "Nama berkas tidak boleh kosong.", +"File name must not contain \"/\". Please choose a different name." => "Nama berkas tidak boleh mengandung \"/\". Silakan pilih nama yang berbeda.", +"The name %s is already used in the folder %s. Please choose a different name." => "Nama %s sudah digunakan dalam folder %s. Silakan pilih nama yang berbeda.", +"Not a valid source" => "Sumber tidak sah", +"Error while downloading %s to %s" => "Galat saat mengunduh %s ke %s", +"Error when creating the file" => "Galat saat membuat berkas", +"Folder name cannot be empty." => "Nama folder tidak bolh kosong.", +"Folder name must not contain \"/\". Please choose a different name." => "Nama folder tidak boleh mengandung \"/\". Silakan pilih nama yang berbeda.", +"Error when creating the folder" => "Galat saat membuat folder", +"Unable to set upload directory." => "Tidak dapat mengatur folder unggah", +"Invalid Token" => "Token tidak sah", "No file was uploaded. Unknown error" => "Tidak ada berkas yang diunggah. Galat tidak dikenal.", "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: " => "Berkas yang diunggah melampaui direktif upload_max_filesize pada php.ini", @@ -12,30 +22,47 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Folder sementara tidak ada", "Failed to write to disk" => "Gagal menulis ke disk", "Not enough storage available" => "Ruang penyimpanan tidak mencukupi", +"Upload failed. Could not get file info." => "Unggah gagal. Tidak mendapatkan informasi berkas.", +"Upload failed. Could not find uploaded file" => "Unggah gagal. Tidak menemukan berkas yang akan diunggah", "Invalid directory." => "Direktori tidak valid.", "Files" => "Berkas", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Tidak dapat mengunggah {filename} karena ini sebuah direktori atau memiliki ukuran 0 byte", "Not enough space available" => "Ruang penyimpanan tidak mencukupi", "Upload cancelled." => "Pengunggahan dibatalkan.", +"Could not get result from server." => "Tidak mendapatkan hasil dari server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Berkas sedang diunggah. Meninggalkan halaman ini akan membatalkan proses.", +"URL cannot be empty" => "URL tidak boleh kosong", +"In the home folder 'Shared' is a reserved filename" => "Pada folder home, 'Shared' adalah nama berkas yang sudah digunakan", "{new_name} already exists" => "{new_name} sudah ada", +"Could not create file" => "Tidak dapat membuat berkas", +"Could not create folder" => "Tidak dapat membuat folder", "Share" => "Bagikan", "Delete permanently" => "Hapus secara permanen", "Rename" => "Ubah nama", "Pending" => "Menunggu", +"Could not rename file" => "Tidak dapat mengubah nama berkas", "replaced {new_name} with {old_name}" => "mengganti {new_name} dengan {old_name}", "undo" => "urungkan", -"_%n folder_::_%n folders_" => array(""), -"_%n file_::_%n files_" => array(""), -"_Uploading %n file_::_Uploading %n files_" => array(""), +"Error deleting file." => "Galat saat menghapus berkas.", +"_%n folder_::_%n folders_" => array("%n folder"), +"_%n file_::_%n files_" => array("%n berkas"), +"{dirs} and {files}" => "{dirs} dan {files}", +"_Uploading %n file_::_Uploading %n files_" => array("Mengunggah %n berkas"), "'.' is an invalid file name." => "'.' bukan nama berkas yang valid.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nama tidak valid, karakter '\\', '/', '<', '>', ':', '\"', '|', '?' dan '*' tidak diizinkan.", "Your storage is full, files can not be updated or synced anymore!" => "Ruang penyimpanan Anda penuh, berkas tidak dapat diperbarui atau disinkronkan lagi!", "Your storage is almost full ({usedSpacePercent}%)" => "Ruang penyimpanan hampir penuh ({usedSpacePercent}%)", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Aplikasi Enskripsi telah diaktifkan tetapi kunci tidak diinisialisasi, silakan log-out dan log-in lagi", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Kunci privat tidak sah untuk Aplikasi Enskripsi. Silakan perbarui sandi kunci privat anda pada pengaturan pribadi untuk memulihkan akses ke berkas anda yang dienskripsi.", +"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Enskripi telah dinonaktifkan tetapi berkas anda tetap dienskripsi. Silakan menuju ke pengaturan pribadi untuk deskrip berkas anda.", "Your download is being prepared. This might take some time if the files are big." => "Unduhan Anda sedang disiapkan. Prosesnya dapat berlangsung agak lama jika ukuran berkasnya besar.", +"Error moving file" => "Galat saat memindahkan berkas", "Error" => "Galat", "Name" => "Nama", "Size" => "Ukuran", "Modified" => "Dimodifikasi", +"Invalid folder name. Usage of 'Shared' is reserved." => "Nama folder tidak sah. Menggunakan 'Shared' sudah digunakan.", +"%s could not be renamed" => "%s tidak dapat diubah nama", "Upload" => "Unggah", "File handling" => "Penanganan berkas", "Maximum upload size" => "Ukuran pengunggahan maksimum", @@ -46,15 +73,16 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "Ukuran masukan maksimum untuk berkas ZIP", "Save" => "Simpan", "New" => "Baru", +"New text file" => "Berkas teks baru", "Text file" => "Berkas teks", "New folder" => "Map baru", "Folder" => "Folder", "From link" => "Dari tautan", "Deleted files" => "Berkas yang dihapus", "Cancel upload" => "Batal pengunggahan", +"You don’t have permission to upload or create files here" => "Anda tidak memiliki akses untuk mengunggah atau membuat berkas disini", "Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!", "Download" => "Unduh", -"Unshare" => "Batalkan berbagi", "Delete" => "Hapus", "Upload too large" => "Yang diunggah terlalu besar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang dicoba untuk diunggah melebihi ukuran maksimum pengunggahan berkas di server ini.", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index 9aae5390db9..5c5cc7d5d73 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -47,7 +47,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Hætta við innsendingu", "Nothing in here. Upload something!" => "Ekkert hér. Settu eitthvað inn!", "Download" => "Niðurhal", -"Unshare" => "Hætta deilingu", "Delete" => "Eyða", "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.", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 9e5d82296bf..2a10e9977f4 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Il nome del file non può contenere il carattere \"/\". Scegli un nome diverso.", "The name %s is already used in the folder %s. Please choose a different name." => "Il nome %s è attualmente in uso nella cartella %s. Scegli un nome diverso.", "Not a valid source" => "Non è una sorgente valida", +"Server is not allowed to open URLs, please check the server configuration" => "Al server non è permesso aprire URL, controlla la configurazione del server", "Error while downloading %s to %s" => "Errore durante lo scaricamento di %s su %s", "Error when creating the file" => "Errore durante la creazione del file", "Folder name cannot be empty." => "Il nome della cartella non può essere vuoto.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} esiste già", "Could not create file" => "Impossibile creare il file", "Could not create folder" => "Impossibile creare la cartella", +"Error fetching URL" => "Errore durante il recupero dello URL", "Share" => "Condividi", "Delete permanently" => "Elimina definitivamente", "Rename" => "Rinomina", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Qui non hai i permessi di caricare o creare file", "Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!", "Download" => "Scarica", -"Unshare" => "Rimuovi condivisione", "Delete" => "Elimina", "Upload too large" => "Caricamento 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.", diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index 495af244250..8019b825d3c 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "ファイル名には \"/\" を含めることはできません。別の名前を選択してください。", "The name %s is already used in the folder %s. Please choose a different name." => "%s はフォルダ %s ないですでに使われています。別の名前を選択してください。", "Not a valid source" => "有効なソースではありません", +"Server is not allowed to open URLs, please check the server configuration" => "サーバーは、URLを開くことは許されません。サーバーの設定をチェックしてください。", "Error while downloading %s to %s" => "%s から %s へのダウンロードエラー", "Error when creating the file" => "ファイルの生成エラー", "Folder name cannot be empty." => "フォルダ名は空にできません", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} はすでに存在しています", "Could not create file" => "ファイルを作成できませんでした", "Could not create folder" => "フォルダを作成できませんでした", +"Error fetching URL" => "URL取得エラー", "Share" => "共有", "Delete permanently" => "完全に削除する", "Rename" => "名前の変更", @@ -73,6 +75,7 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "ZIPファイルへの最大入力サイズ", "Save" => "保存", "New" => "新規作成", +"New text file" => "新規のテキストファイル作成", "Text file" => "テキストファイル", "New folder" => "新しいフォルダ", "Folder" => "フォルダ", @@ -82,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "ここにファイルをアップロードもしくは作成する権限がありません", "Nothing in here. Upload something!" => "ここには何もありません。何かアップロードしてください。", "Download" => "ダウンロード", -"Unshare" => "共有解除", "Delete" => "削除", "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/ka_GE.php b/apps/files/l10n/ka_GE.php index b2e656ca190..eafa0c083f7 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -54,7 +54,6 @@ $TRANSLATIONS = array( "Cancel upload" => "ატვირთვის გაუქმება", "Nothing in here. Upload something!" => "აქ არაფერი არ არის. ატვირთე რამე!", "Download" => "ჩამოტვირთვა", -"Unshare" => "გაუზიარებადი", "Delete" => "წაშლა", "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/ko.php b/apps/files/l10n/ko.php index d8da49acbca..35a9f2fb85c 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -1,11 +1,20 @@ "%s 항목을 이동시키지 못하였음 - 파일 이름이 이미 존재함", -"Could not move %s" => "%s 항목을 이딩시키지 못하였음", +"Could not move %s - File with this name already exists" => "항목 %s을(를) 이동시킬 수 없음 - 같은 이름의 파일이 이미 존재함", +"Could not move %s" => "항목 %s을(를) 이동시킬 수 없음", "File name cannot be empty." => "파일 이름이 비어 있을 수 없습니다.", -"Unable to set upload directory." => "업로드 디렉터리를 정할수 없습니다", +"File name must not contain \"/\". Please choose a different name." => "파일 이름에는 \"/\"가 들어갈 수 없습니다. 다른 이름을 사용하십시오.", +"The name %s is already used in the folder %s. Please choose a different name." => "이름 %s이(가) 폴더 %s에서 이미 사용 중입니다. 다른 이름을 사용하십시오.", +"Not a valid source" => "올바르지 않은 원본", +"Server is not allowed to open URLs, please check the server configuration" => "서버에서 URL을 열 수 없습니다. 서버 설정을 확인하십시오", +"Error while downloading %s to %s" => "%s을(를) %s(으)로 다운로드하는 중 오류 발생", +"Error when creating the file" => "파일 생성 중 오류 발생", +"Folder name cannot be empty." => "폴더 이름이 비어있을 수 없습니다.", +"Folder name must not contain \"/\". Please choose a different name." => "폴더 이름에는 \"/\"가 들어갈 수 없습니다. 다른 이름을 사용하십시오.", +"Error when creating the folder" => "폴더 생성 중 오류 발생", +"Unable to set upload directory." => "업로드 디렉터리를 설정할 수 없습니다.", "Invalid Token" => "잘못된 토큰", -"No file was uploaded. Unknown error" => "파일이 업로드되지 않았습니다. 알 수 없는 오류입니다", +"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보다 큼", @@ -14,38 +23,48 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "임시 폴더가 없음", "Failed to write to disk" => "디스크에 쓰지 못했습니다", "Not enough storage available" => "저장소가 용량이 충분하지 않습니다.", -"Upload failed. Could not get file info." => "업로드에 실패했습니다. 파일 정보를 가져올수 없습니다.", -"Upload failed. Could not find uploaded file" => "업로드에 실패했습니다. 업로드할 파일을 찾을수 없습니다", +"Upload failed. Could not get file info." => "업로드에 실패했습니다. 파일 정보를 가져올 수 없습니다.", +"Upload failed. Could not find uploaded file" => "업로드에 실패했습니다. 업로드할 파일을 찾을 수 없습니다", "Invalid directory." => "올바르지 않은 디렉터리입니다.", "Files" => "파일", -"Unable to upload {filename} as it is a directory or has 0 bytes" => "{filename}을 업로드 할수 없습니다. 폴더이거나 0 바이트 파일입니다.", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "{filename}을(를) 업로드할 수 없습니다. 폴더이거나 0 바이트 파일입니다.", "Not enough space available" => "여유 공간이 부족합니다", "Upload cancelled." => "업로드가 취소되었습니다.", -"Could not get result from server." => "서버에서 결과를 가져올수 없습니다.", +"Could not get result from server." => "서버에서 결과를 가져올 수 없습니다.", "File upload is in progress. Leaving the page now will cancel the upload." => "파일 업로드가 진행 중입니다. 이 페이지를 벗어나면 업로드가 취소됩니다.", +"URL cannot be empty" => "URL이 비어있을 수 없음", +"In the home folder 'Shared' is a reserved filename" => "'공유됨'은 홈 폴더의 예약된 파일 이름임", "{new_name} already exists" => "{new_name}이(가) 이미 존재함", +"Could not create file" => "파일을 만들 수 없음", +"Could not create folder" => "폴더를 만들 수 없음", +"Error fetching URL" => "URL을 가져올 수 없음", "Share" => "공유", -"Delete permanently" => "영원히 삭제", +"Delete permanently" => "영구히 삭제", "Rename" => "이름 바꾸기", "Pending" => "대기 중", +"Could not rename file" => "이름을 변경할 수 없음", "replaced {new_name} with {old_name}" => "{old_name}이(가) {new_name}(으)로 대체됨", -"undo" => "되돌리기", -"_%n folder_::_%n folders_" => array("폴더 %n"), -"_%n file_::_%n files_" => array("파일 %n 개"), +"undo" => "실행 취소", +"Error deleting file." => "파일 삭제 오류.", +"_%n folder_::_%n folders_" => array("폴더 %n개"), +"_%n file_::_%n files_" => array("파일 %n개"), "{dirs} and {files}" => "{dirs} 그리고 {files}", -"_Uploading %n file_::_Uploading %n files_" => array("%n 개의 파일을 업로드중"), -"'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름 입니다.", +"_Uploading %n file_::_Uploading %n files_" => array("파일 %n개 업로드 중"), +"'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름입니다.", "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}%)", -"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "암호화는 해제되어 있지만, 파일은 아직 암호화 되어 있습니다. 개인 설저에 가셔서 암호를 해제하십시오", -"Your download is being prepared. This might take some time if the files are big." => "다운로드가 준비 중입니다. 파일 크기가 크다면 시간이 오래 걸릴 수도 있습니다.", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "암호화 앱이 활성화되어 있지만 키가 초기화되지 않았습니다. 로그아웃한 후 다시 로그인하십시오", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "암호화 앱의 개인 키가 잘못되었습니다. 암호화된 파일에 다시 접근하려면 개인 설정에서 개인 키 암호를 업데이트해야 합니다.", +"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "암호화는 해제되어 있지만, 파일은 아직 암호화되어 있습니다. 개인 설정에서 파일을 복호화하십시오.", +"Your download is being prepared. This might take some time if the files are big." => "다운로드 준비 중입니다. 파일 크기가 크면 시간이 오래 걸릴 수도 있습니다.", "Error moving file" => "파일 이동 오류", "Error" => "오류", "Name" => "이름", "Size" => "크기", "Modified" => "수정됨", -"%s could not be renamed" => "%s 의 이름을 변경할수 없습니다", +"Invalid folder name. Usage of 'Shared' is reserved." => "폴더 이름이 잘못되었습니다. '공유됨'은 예약된 폴더 이름입니다.", +"%s could not be renamed" => "%s의 이름을 변경할 수 없습니다", "Upload" => "업로드", "File handling" => "파일 처리", "Maximum upload size" => "최대 업로드 크기", @@ -56,15 +75,16 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "ZIP 파일 최대 크기", "Save" => "저장", "New" => "새로 만들기", +"New text file" => "새 텍스트 파일", "Text file" => "텍스트 파일", "New folder" => "새 폴더", "Folder" => "폴더", "From link" => "링크에서", -"Deleted files" => "파일 삭제됨", +"Deleted files" => "삭제된 파일", "Cancel upload" => "업로드 취소", +"You don’t have permission to upload or create files here" => "여기에 파일을 업로드하거나 만들 권한이 없습니다", "Nothing in here. Upload something!" => "내용이 없습니다. 업로드할 수 있습니다!", "Download" => "다운로드", -"Unshare" => "공유 해제", "Delete" => "삭제", "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/lb.php b/apps/files/l10n/lb.php index bd95197746e..822ca7e2d02 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -34,7 +34,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Upload ofbriechen", "Nothing in here. Upload something!" => "Hei ass näischt. Lued eppes rop!", "Download" => "Download", -"Unshare" => "Net méi deelen", "Delete" => "Läschen", "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.", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index 2f039e1bb92..d9023658631 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -80,7 +80,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Jūs neturite leidimo čia įkelti arba kurti failus", "Nothing in here. Upload something!" => "Čia tuščia. Įkelkite ką nors!", "Download" => "Atsisiųsti", -"Unshare" => "Nebesidalinti", "Delete" => "Ištrinti", "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ų, kuris leidžiamas šiame serveryje", diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index 11edd2bd8d2..7f8975b2940 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -58,7 +58,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Atcelt augšupielādi", "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", "Delete" => "Dzēst", "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", diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index b0c1ae330c7..fadf5efc07b 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -71,7 +71,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Откажи прикачување", "Nothing in here. Upload something!" => "Тука нема ништо. Снимете нешто!", "Download" => "Преземи", -"Unshare" => "Не споделувај", "Delete" => "Избриши", "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/nb_NO.php b/apps/files/l10n/nb_NO.php index 46f4b40594d..ebd7ef38998 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -57,7 +57,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Avbryt opplasting", "Nothing in here. Upload something!" => "Ingenting her. Last opp noe!", "Download" => "Last ned", -"Unshare" => "Avslutt deling", "Delete" => "Slett", "Upload too large" => "Filen er 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.", diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index b212fe596ad..a391e25b952 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "De bestandsnaam mag geen \"/\" bevatten. Kies een andere naam.", "The name %s is already used in the folder %s. Please choose a different name." => "De naam %s bestaat al in map %s. Kies een andere naam.", "Not a valid source" => "Geen geldige bron", +"Server is not allowed to open URLs, please check the server configuration" => "Server mag geen URS's openen, controleer de server configuratie", "Error while downloading %s to %s" => "Fout bij downloaden %s naar %s", "Error when creating the file" => "Fout bij creëren bestand", "Folder name cannot be empty." => "Mapnaam mag niet leeg zijn.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} bestaat al", "Could not create file" => "Kon bestand niet creëren", "Could not create folder" => "Kon niet creëren map", +"Error fetching URL" => "Fout bij ophalen URL", "Share" => "Delen", "Delete permanently" => "Verwijder definitief", "Rename" => "Hernoem", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "U hebt geen toestemming om hier te uploaden of bestanden te maken", "Nothing in here. Upload something!" => "Er bevindt zich hier niets. Upload een bestand!", "Download" => "Downloaden", -"Unshare" => "Stop met delen", "Delete" => "Verwijder", "Upload too large" => "Upload is 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.", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 58a39cd4ff5..a5e6e737eae 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -63,7 +63,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Avbryt opplasting", "Nothing in here. Upload something!" => "Ingenting her. Last noko opp!", "Download" => "Last ned", -"Unshare" => "Udel", "Delete" => "Slett", "Upload too large" => "For stor opplasting", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å lasta opp er større enn maksgrensa til denne tenaren.", diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php index a0c3cc7ca6b..eb1a9038b08 100644 --- a/apps/files/l10n/oc.php +++ b/apps/files/l10n/oc.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Cancel upload" => " Anulla l'amontcargar", "Nothing in here. Upload something!" => "Pas res dedins. Amontcarga qualquaren", "Download" => "Avalcarga", -"Unshare" => "Pas partejador", "Delete" => "Escafa", "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.", diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index e7cac28ce70..031bd508700 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Nazwa pliku nie może zawierać \"/\". Proszę wybrać inną nazwę.", "The name %s is already used in the folder %s. Please choose a different name." => "Nazwa %s jest już używana w folderze %s. Proszę wybrać inną nazwę.", "Not a valid source" => "Niepoprawne źródło", +"Server is not allowed to open URLs, please check the server configuration" => "Serwer nie mógł otworzyć adresów URL, należy sprawdzić konfigurację serwera", "Error while downloading %s to %s" => "Błąd podczas pobierania %s do %S", "Error when creating the file" => "Błąd przy tworzeniu pliku", "Folder name cannot be empty." => "Nazwa folderu nie może być pusta.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} już istnieje", "Could not create file" => "Nie można utworzyć pliku", "Could not create folder" => "Nie można utworzyć folderu", +"Error fetching URL" => "Błąd przy pobieraniu adresu URL", "Share" => "Udostępnij", "Delete permanently" => "Trwale usuń", "Rename" => "Zmień nazwę", @@ -43,9 +45,10 @@ $TRANSLATIONS = array( "Could not rename file" => "Nie można zmienić nazwy pliku", "replaced {new_name} with {old_name}" => "zastąpiono {new_name} przez {old_name}", "undo" => "cofnij", +"Error deleting file." => "Błąd podczas usuwania pliku", "_%n folder_::_%n folders_" => array("%n katalog","%n katalogi","%n katalogów"), "_%n file_::_%n files_" => array("%n plik","%n pliki","%n plików"), -"{dirs} and {files}" => "{katalogi} and {pliki}", +"{dirs} and {files}" => "{dirs} and {files}", "_Uploading %n file_::_Uploading %n files_" => array("Wysyłanie %n pliku","Wysyłanie %n plików","Wysyłanie %n plików"), "'.' is an invalid file name." => "„.” jest nieprawidłową nazwą pliku.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nieprawidłowa nazwa. Znaki '\\', '/', '<', '>', ':', '\"', '|', '?' oraz '*' są niedozwolone.", @@ -72,6 +75,7 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "Maksymalna wielkość pliku wejściowego ZIP ", "Save" => "Zapisz", "New" => "Nowy", +"New text file" => "Nowy plik tekstowy", "Text file" => "Plik tekstowy", "New folder" => "Nowy folder", "Folder" => "Folder", @@ -81,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Nie masz uprawnień do wczytywania lub tworzenia plików w tym miejscu", "Nothing in here. Upload something!" => "Pusto. Wyślij coś!", "Download" => "Pobierz", -"Unshare" => "Zatrzymaj współdzielenie", "Delete" => "Usuń", "Upload too large" => "Ładowany plik jest za duży", "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ść.", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index 2f41760f0d6..f84dcfcd488 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "O nome do arquivo não deve conter \"/\". Por favor, escolha um nome diferente.", "The name %s is already used in the folder %s. Please choose a different name." => "O nome %s já é usado na pasta %s. Por favor, escolha um nome diferente.", "Not a valid source" => "Não é uma fonte válida", +"Server is not allowed to open URLs, please check the server configuration" => "Não é permitido ao servidor abrir URLs, por favor verificar a configuração do servidor.", "Error while downloading %s to %s" => "Erro ao baixar %s para %s", "Error when creating the file" => "Erro ao criar o arquivo", "Folder name cannot be empty." => "O nome da pasta não pode estar vazio.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} já existe", "Could not create file" => "Não foi possível criar o arquivo", "Could not create folder" => "Não foi possível criar a pasta", +"Error fetching URL" => "Erro ao buscar URL", "Share" => "Compartilhar", "Delete permanently" => "Excluir permanentemente", "Rename" => "Renomear", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Você não tem permissão para carregar ou criar arquivos aqui", "Nothing in here. Upload something!" => "Nada aqui.Carrege alguma coisa!", "Download" => "Baixar", -"Unshare" => "Descompartilhar", "Delete" => "Excluir", "Upload too large" => "Upload 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.", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index 4cb7266cf42..4b9fade2e22 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -62,7 +62,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Cancelar envio", "Nothing in here. Upload something!" => "Vazio. Envie alguma coisa!", "Download" => "Transferir", -"Unshare" => "Deixar de partilhar", "Delete" => "Eliminar", "Upload too large" => "Upload muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiro que está a tentar enviar excedem o tamanho máximo de envio neste servidor.", diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index 322eb55e3ef..933625d8d1e 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -63,7 +63,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Anulează încărcarea", "Nothing in here. Upload something!" => "Nimic aici. Încarcă ceva!", "Download" => "Descarcă", -"Unshare" => "Anulare", "Delete" => "Șterge", "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.", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 328ece9a813..968da63aaca 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Имя файла не должно содержать символ \"/\". Пожалуйста, выберите другое имя.", "The name %s is already used in the folder %s. Please choose a different name." => "Имя %s уже используется в папке %s. Пожалуйста выберите другое имя.", "Not a valid source" => "Неправильный источник", +"Server is not allowed to open URLs, please check the server configuration" => "Сервер не позволяет открывать URL-адреса, пожалуйста, проверьте настройки сервера", "Error while downloading %s to %s" => "Ошибка при загрузке %s в %s", "Error when creating the file" => "Ошибка при создании файла", "Folder name cannot be empty." => "Имя папки не может быть пустым.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} уже существует", "Could not create file" => "Не удалось создать файл", "Could not create folder" => "Не удалось создать папку", +"Error fetching URL" => "Ошибка получения URL", "Share" => "Открыть доступ", "Delete permanently" => "Удалено навсегда", "Rename" => "Переименовать", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "У вас недостаточно прав для загрузки или создания файлов отсюда.", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Скачать", -"Unshare" => "Закрыть общий доступ", "Delete" => "Удалить", "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/ru_RU.php b/apps/files/l10n/ru_RU.php deleted file mode 100644 index 0df5b9c8c82..00000000000 --- a/apps/files/l10n/ru_RU.php +++ /dev/null @@ -1,20 +0,0 @@ - "Имя файла не может быть пустым.", -"Files" => "Файлы", -"Share" => "Сделать общим", -"Rename" => "Переименовать", -"_%n folder_::_%n folders_" => array("","",""), -"_%n file_::_%n files_" => array("","",""), -"_Uploading %n file_::_Uploading %n files_" => array("","",""), -"'.' is an invalid file name." => "'.' является неверным именем файла.", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Некорректное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не допустимы.", -"Error" => "Ошибка", -"Size" => "Размер", -"Upload" => "Загрузка", -"Save" => "Сохранить", -"Cancel upload" => "Отмена загрузки", -"Download" => "Загрузка", -"Delete" => "Удалить" -); -$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php index c30703c0b9d..44decfef1b0 100644 --- a/apps/files/l10n/si_LK.php +++ b/apps/files/l10n/si_LK.php @@ -36,7 +36,6 @@ $TRANSLATIONS = array( "Cancel upload" => "උඩුගත කිරීම අත් හරින්න", "Nothing in here. Upload something!" => "මෙහි කිසිවක් නොමැත. යමක් උඩුගත කරන්න", "Download" => "බාන්න", -"Unshare" => "නොබෙදු", "Delete" => "මකා දමන්න", "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/sk.php b/apps/files/l10n/sk.php index a3178a95c47..53daf549eaa 100644 --- a/apps/files/l10n/sk.php +++ b/apps/files/l10n/sk.php @@ -1,7 +1,11 @@ "Zdieľať", "_%n folder_::_%n folders_" => array("","",""), "_%n file_::_%n files_" => array("","",""), -"_Uploading %n file_::_Uploading %n files_" => array("","","") +"_Uploading %n file_::_Uploading %n files_" => array("","",""), +"Save" => "Uložiť", +"Download" => "Stiahnuť", +"Delete" => "Odstrániť" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index c513ab80539..2d0eb25f622 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -83,7 +83,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Nemáte oprávnenie sem nahrávať alebo vytvoriť súbory", "Nothing in here. Upload something!" => "Žiadny súbor. Nahrajte niečo!", "Download" => "Sťahovanie", -"Unshare" => "Zrušiť zdieľanie", "Delete" => "Zmazať", "Upload too large" => "Nahrávanie 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.", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index 32ac1ff1e78..037e5f6b6b0 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Ime datoteke ne sme vsebovati znaka \"/\". Določiti je treba drugo ime.", "The name %s is already used in the folder %s. Please choose a different name." => "Ime %s je že v mapi %s že v uporabi. Izbrati je treba drugo ime.", "Not a valid source" => "Vir ni veljaven", +"Server is not allowed to open URLs, please check the server configuration" => "Odpiranje naslovov URL preko strežnika ni dovoljeno. Preverite nastavitve strežnika.", "Error while downloading %s to %s" => "Napaka med prejemanjem %s v mapo %s", "Error when creating the file" => "Napaka med ustvarjanjem datoteke", "Folder name cannot be empty." => "Ime mape ne more biti prazna vrednost.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} že obstaja", "Could not create file" => "Ni mogoče ustvariti datoteke", "Could not create folder" => "Ni mogoče ustvariti mape", +"Error fetching URL" => "Napaka pridobivanja naslova URL", "Share" => "Souporaba", "Delete permanently" => "Izbriši dokončno", "Rename" => "Preimenuj", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Ni ustreznih dovoljenj za pošiljanje ali ustvarjanje datotek na tem mestu.", "Nothing in here. Upload something!" => "Tukaj še ni ničesar. Najprej je treba kakšno datoteko poslati v oblak!", "Download" => "Prejmi", -"Unshare" => "Prekliči souporabo", "Delete" => "Izbriši", "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.", diff --git a/apps/files/l10n/sq.php b/apps/files/l10n/sq.php index 66ca9d08992..a06b6821afe 100644 --- a/apps/files/l10n/sq.php +++ b/apps/files/l10n/sq.php @@ -58,7 +58,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Anullo ngarkimin", "Nothing in here. Upload something!" => "Këtu nuk ka asgje. Ngarko dicka", "Download" => "Shkarko", -"Unshare" => "Hiq ndarjen", "Delete" => "Fshi", "Upload too large" => "Ngarkimi shumë i madh", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Skedarët që po mundoheni të ngarkoni e tejkalojnë madhësinë maksimale të lejuar nga serveri.", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index 38dc6988da7..e383b66e078 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -53,7 +53,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Прекини отпремање", "Nothing in here. Upload something!" => "Овде нема ничег. Отпремите нешто!", "Download" => "Преузми", -"Unshare" => "Укини дељење", "Delete" => "Обриши", "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/sr@latin.php b/apps/files/l10n/sr@latin.php index 998ca7fe0c8..13ce5ec8b3e 100644 --- a/apps/files/l10n/sr@latin.php +++ b/apps/files/l10n/sr@latin.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Save" => "Snimi", "Nothing in here. Upload something!" => "Ovde nema ničeg. Pošaljite nešto!", "Download" => "Preuzmi", -"Unshare" => "Ukljoni deljenje", "Delete" => "Obriši", "Upload too large" => "Pošiljka je prevelika", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fajlovi koje želite da pošaljete prevazilaze ograničenje maksimalne veličine pošiljke na ovom serveru." diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index b4a23ad3b58..9e1b8110d48 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -82,7 +82,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Du har ej tillåtelse att ladda upp eller skapa filer här", "Nothing in here. Upload something!" => "Ingenting här. Ladda upp något!", "Download" => "Ladda ner", -"Unshare" => "Sluta dela", "Delete" => "Radera", "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.", diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php index 3d3bc6302a6..f91a74ee9bd 100644 --- a/apps/files/l10n/ta_LK.php +++ b/apps/files/l10n/ta_LK.php @@ -40,7 +40,6 @@ $TRANSLATIONS = array( "Cancel upload" => "பதிவேற்றலை இரத்து செய்க", "Nothing in here. Upload something!" => "இங்கு ஒன்றும் இல்லை. ஏதாவது பதிவேற்றுக!", "Download" => "பதிவிறக்குக", -"Unshare" => "பகிரப்படாதது", "Delete" => "நீக்குக", "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/th_TH.php b/apps/files/l10n/th_TH.php index 86c25c6de7c..38a67c50679 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -51,7 +51,6 @@ $TRANSLATIONS = array( "Cancel upload" => "ยกเลิกการอัพโหลด", "Nothing in here. Upload something!" => "ยังไม่มีไฟล์ใดๆอยู่ที่นี่ กรุณาอัพโหลดไฟล์!", "Download" => "ดาวน์โหลด", -"Unshare" => "ยกเลิกการแชร์", "Delete" => "ลบ", "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/tr.php b/apps/files/l10n/tr.php index 0f9ea6d84ba..90b16922a76 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -6,6 +6,7 @@ $TRANSLATIONS = array( "File name must not contain \"/\". Please choose a different name." => "Dosya adı \"/\" içermemelidir. Lütfen farklı bir isim seçin.", "The name %s is already used in the folder %s. Please choose a different name." => "%s ismi zaten %s klasöründe kullanılıyor. Lütfen farklı bir isim seçin.", "Not a valid source" => "Geçerli bir kaynak değil", +"Server is not allowed to open URLs, please check the server configuration" => "Sunucunun adresleri açma izi yok, lütfen sunucu yapılandırmasını denetleyin", "Error while downloading %s to %s" => "%s, %s içine indirilirken hata", "Error when creating the file" => "Dosya oluşturulurken hata", "Folder name cannot be empty." => "Klasör adı boş olamaz.", @@ -36,6 +37,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} zaten mevcut", "Could not create file" => "Dosya oluşturulamadı", "Could not create folder" => "Klasör oluşturulamadı", +"Error fetching URL" => "Adres getirilirken hata", "Share" => "Paylaş", "Delete permanently" => "Kalıcı olarak sil", "Rename" => "İsim değiştir.", @@ -50,11 +52,11 @@ $TRANSLATIONS = array( "_Uploading %n file_::_Uploading %n files_" => array("%n dosya yükleniyor","%n dosya yükleniyor"), "'.' is an invalid file name." => "'.' geçersiz bir dosya adı.", "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 full, files can not be updated or synced anymore!" => "Depolama alanınız dolu, artık dosyalar güncellenmeyecek veya eşitlenmeyecek.", "Your storage is almost full ({usedSpacePercent}%)" => "Depolama alanınız neredeyse dolu ({usedSpacePercent}%)", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Şifreleme Uygulaması etkin ancak anahtarlarınız başlatılmamış. Lütfen oturumu kapatıp yeniden açın", "Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Şifreleme Uygulaması için geçersiz özel anahtar. Lütfen şifreli dosyalarınıza erişimi tekrar kazanabilmek için kişisel ayarlarınızdan özel anahtar parolanızı güncelleyin.", -"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Şifreleme işlemi durduruldu ancak dosyalarınız şifreli. Dosyalarınızın şifresini kaldırmak için lütfen kişisel ayarlar kısmına geçiniz.", +"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Şifreleme işlemi durduruldu ancak dosyalarınız şifreli. Dosyalarınızın şifresini kaldırmak için lütfen kişisel ayarlar kısmına geçin.", "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.", "Error moving file" => "Dosya taşıma hatası", "Error" => "Hata", @@ -64,13 +66,13 @@ $TRANSLATIONS = array( "Invalid folder name. Usage of 'Shared' is reserved." => "Geçersiz dizin adı. 'Shared' ismi ayrılmıştır.", "%s could not be renamed" => "%s yeniden adlandırılamadı", "Upload" => "Yükle", -"File handling" => "Dosya taşıma", +"File handling" => "Dosya işlemleri", "Maximum upload size" => "Maksimum yükleme boyutu", "max. possible: " => "mümkün olan en fazla: ", "Needed for multi-file and folder downloads." => "Çoklu dosya ve dizin indirmesi için gerekli.", -"Enable ZIP-download" => "ZIP indirmeyi aktif et", +"Enable ZIP-download" => "ZIP indirmeyi etkinleştir", "0 is unlimited" => "0 limitsiz demektir", -"Maximum input size for ZIP files" => "ZIP dosyaları için en fazla girdi sayısı", +"Maximum input size for ZIP files" => "ZIP dosyaları için en fazla girdi boyutu", "Save" => "Kaydet", "New" => "Yeni", "New text file" => "Yeni metin dosyası", @@ -83,7 +85,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "Buraya dosya yükleme veya oluşturma izniniz yok", "Nothing in here. Upload something!" => "Burada hiçbir şey yok. Bir şeyler yükleyin!", "Download" => "İndir", -"Unshare" => "Paylaşılmayan", "Delete" => "Sil", "Upload too large" => "Yükleme ç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.", diff --git a/apps/files/l10n/ug.php b/apps/files/l10n/ug.php index 51113446c2c..45458c51af0 100644 --- a/apps/files/l10n/ug.php +++ b/apps/files/l10n/ug.php @@ -33,7 +33,6 @@ $TRANSLATIONS = array( "Cancel upload" => "يۈكلەشتىن ۋاز كەچ", "Nothing in here. Upload something!" => "بۇ جايدا ھېچنېمە يوق. Upload something!", "Download" => "چۈشۈر", -"Unshare" => "ھەمبەھىرلىمە", "Delete" => "ئۆچۈر", "Upload too large" => "يۈكلەندىغىنى بەك چوڭ", "Upgrading filesystem cache..." => "ھۆججەت سىستېما غەملىكىنى يۈكسەلدۈرۈۋاتىدۇ…" diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 06e9851c45d..1e37d3d77d4 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -62,7 +62,6 @@ $TRANSLATIONS = array( "Cancel upload" => "Перервати завантаження", "Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!", "Download" => "Завантажити", -"Unshare" => "Закрити доступ", "Delete" => "Видалити", "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/ur.php b/apps/files/l10n/ur.php new file mode 100644 index 00000000000..0157af093e9 --- /dev/null +++ b/apps/files/l10n/ur.php @@ -0,0 +1,7 @@ + array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/ur_PK.php b/apps/files/l10n/ur_PK.php index b423291095b..3e13a7f516d 100644 --- a/apps/files/l10n/ur_PK.php +++ b/apps/files/l10n/ur_PK.php @@ -3,7 +3,6 @@ $TRANSLATIONS = array( "_%n folder_::_%n folders_" => array("",""), "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), -"Error" => "ایرر", -"Unshare" => "شئیرنگ ختم کریں" +"Error" => "ایرر" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 6c8a016d21f..8236d5e7efe 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -54,7 +54,6 @@ $TRANSLATIONS = array( "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ì đó !", "Download" => "Tải về", -"Unshare" => "Bỏ chia sẻ", "Delete" => "Xóa", "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ủ .", diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index a7cac62a5f9..effcb0225ca 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -57,7 +57,6 @@ $TRANSLATIONS = array( "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里还什么都没有。上传些东西吧!", "Download" => "下载", -"Unshare" => "取消共享", "Delete" => "删除", "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/zh_HK.php b/apps/files/l10n/zh_HK.php index faf6c619385..4885500ce43 100644 --- a/apps/files/l10n/zh_HK.php +++ b/apps/files/l10n/zh_HK.php @@ -10,7 +10,6 @@ $TRANSLATIONS = array( "Upload" => "上傳", "Save" => "儲存", "Download" => "下載", -"Unshare" => "取消分享", "Delete" => "刪除" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index f4458c3daff..dd9cbfdf1a9 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -80,7 +80,6 @@ $TRANSLATIONS = array( "You don’t have permission to upload or create files here" => "您沒有權限在這裡上傳或建立檔案", "Nothing in here. Upload something!" => "這裡還沒有東西,上傳一些吧!", "Download" => "下載", -"Unshare" => "取消分享", "Delete" => "刪除", "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/lib/app.php b/apps/files/lib/app.php index e04ac173d55..fea88faa92a 100644 --- a/apps/files/lib/app.php +++ b/apps/files/lib/app.php @@ -59,6 +59,13 @@ class App { $result['data'] = array( 'message' => $this->l10n->t("Invalid folder name. Usage of 'Shared' is reserved.") ); + // rename to non-existing folder is denied + } else if (!$this->view->file_exists($dir)) { + $result['data'] = array('message' => (string)$this->l10n->t( + 'The target folder has been moved or deleted.', + array($dir)), + 'code' => 'targetnotfound' + ); // rename to existing file is denied } else if ($this->view->file_exists($dir . '/' . $newname)) { @@ -83,14 +90,17 @@ class App { else { $meta['type'] = 'file'; } + // these need to be set for determineIcon() + $meta['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($meta['mimetype']); + $meta['directory'] = $dir; $fileinfo = array( 'id' => $meta['fileid'], 'mime' => $meta['mimetype'], 'size' => $meta['size'], 'etag' => $meta['etag'], - 'directory' => $dir, + 'directory' => $meta['directory'], 'name' => $newname, - 'isPreviewAvailable' => \OC::$server->getPreviewManager()->isMimeSupported($meta['mimetype']), + 'isPreviewAvailable' => $meta['isPreviewAvailable'], 'icon' => \OCA\Files\Helper::determineIcon($meta) ); $result['success'] = true; diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php index 697fc52526a..a5afd55fbc3 100644 --- a/apps/files/templates/admin.php +++ b/apps/files/templates/admin.php @@ -5,7 +5,7 @@

t('File handling')); ?>

- '/> + '/> (t('max. possible: ')); p($_['maxPossibleUploadSize']) ?>) diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 99d66ed3f9c..ff78f0ca551 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -1,6 +1,7 @@
+
t('New'));?>
    @@ -12,21 +13,26 @@ data-type='web'>

    t('From link'));?>

+
= 0):?> + + + + - +
- > + />
@@ -44,7 +50,7 @@
class="hidden">t('Nothing in here. Upload something!'))?>
- + @@ -69,20 +75,11 @@ diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 9db27eb9b29..2a0df622767 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,10 +1,12 @@ - - diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 2f630e1f014..f4fb96a7a7c 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -18,7 +18,7 @@ $totalsize = 0; ?> data-size="" data-etag="" data-permissions=""> - +
class="hidden" id="headerDate"> t( 'Modified' )); ?> - - - - t('Unshare'))?> - <?php p($l->t('Unshare'))?>" /> - - - - t('Delete'))?> - <?php p($l->t('Delete'))?>" /> - - + + t('Delete'))?> + <?php p($l->t('Delete'))?>" /> +
expects($this->any()) ->method('t') ->will($this->returnArgument(0)); - $viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath', 'getFileInfo'), array(), '', false); + $viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath', 'getFileInfo', 'file_exists'), array(), '', false); $viewMock->expects($this->any()) ->method('normalizePath') ->will($this->returnArgument(0)); @@ -63,6 +63,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'Shared'; $newname = 'new_name'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/') + ->will($this->returnValue(true)); + $result = $this->files->rename($dir, $oldname, $newname); $expected = array( 'success' => false, @@ -80,6 +85,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'Shared'; $newname = 'new_name'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/test') + ->will($this->returnValue(true)); + $this->viewMock->expects($this->any()) ->method('getFileInfo') ->will($this->returnValue(array( @@ -129,6 +139,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'oldname'; $newname = 'newname'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/') + ->will($this->returnValue(true)); + $this->viewMock->expects($this->any()) ->method('getFileInfo') ->will($this->returnValue(array( @@ -141,7 +156,6 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { 'name' => 'new_name', ))); - $result = $this->files->rename($dir, $oldname, $newname); $this->assertTrue($result['success']); @@ -154,4 +168,35 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $this->assertEquals(\OC_Helper::mimetypeIcon('dir'), $result['data']['icon']); $this->assertFalse($result['data']['isPreviewAvailable']); } + + /** + * Test rename inside a folder that doesn't exist any more + */ + function testRenameInNonExistingFolder() { + $dir = '/unexist'; + $oldname = 'oldname'; + $newname = 'newname'; + + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/unexist') + ->will($this->returnValue(false)); + + $this->viewMock->expects($this->any()) + ->method('getFileInfo') + ->will($this->returnValue(array( + 'fileid' => 123, + 'type' => 'dir', + 'mimetype' => 'httpd/unix-directory', + 'size' => 18, + 'etag' => 'abcdef', + 'directory' => '/unexist', + 'name' => 'new_name', + ))); + + $result = $this->files->rename($dir, $oldname, $newname); + + $this->assertFalse($result['success']); + $this->assertEquals('targetnotfound', $result['data']['code']); + } } diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js new file mode 100644 index 00000000000..23f7b58dcd9 --- /dev/null +++ b/apps/files/tests/js/fileactionsSpec.js @@ -0,0 +1,61 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* 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 . +* +*/ +describe('FileActions tests', function() { + beforeEach(function() { + // init horrible parameters + var $body = $('body'); + $body.append(''); + $body.append(''); + // dummy files table + $filesTable = $body.append('
'); + }); + afterEach(function() { + $('#dir, #permissions, #filestable').remove(); + }); + it('calling display() sets file actions', function() { + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + + // no actions before call + expect($tr.find('.action[data-action=Download]').length).toEqual(0); + expect($tr.find('.action[data-action=Rename]').length).toEqual(0); + expect($tr.find('.action.delete').length).toEqual(0); + + FileActions.display($tr.find('td.filename'), true); + + // actions defined after cal + expect($tr.find('.action[data-action=Download]').length).toEqual(1); + expect($tr.find('.action[data-action=Rename]').length).toEqual(1); + expect($tr.find('.action.delete').length).toEqual(1); + }); + it('redirects to download URL when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('test download File.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + FileActions.display($tr.find('td.filename'), true); + + $tr.find('.action[data-action=Download]').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=test%20download%20File.txt&dir=%2Fsubdir&download'); + redirectStub.restore(); + }); +}); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js new file mode 100644 index 00000000000..61e026c0725 --- /dev/null +++ b/apps/files/tests/js/filelistSpec.js @@ -0,0 +1,63 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* 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 . +* +*/ +describe('FileList tests', function() { + beforeEach(function() { + // init horrible parameters + var $body = $('body'); + $body.append(''); + $body.append(''); + // dummy files table + $body.append('
'); + }); + afterEach(function() { + $('#dir, #permissions, #filestable').remove(); + }); + it('generates file element with correct attributes when calling addFile', function() { + var lastMod = new Date(10000); + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('testName.txt', 1234, lastMod, false, false, {download_url: 'test/download/url'}); + + expect($tr).toBeDefined(); + expect($tr[0].tagName.toLowerCase()).toEqual('tr'); + expect($tr.find('a:first').attr('href')).toEqual('test/download/url'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('testName.txt'); + expect($tr.attr('data-size')).toEqual('1234'); + expect($tr.attr('data-permissions')).toEqual('31'); + //expect($tr.attr('data-mime')).toEqual('plain/text'); + }); + it('generates dir element with correct attributes when calling addDir', function() { + var lastMod = new Date(10000); + var $tr = FileList.addDir('testFolder', 1234, lastMod, false); + + expect($tr).toBeDefined(); + expect($tr[0].tagName.toLowerCase()).toEqual('tr'); + expect($tr.attr('data-type')).toEqual('dir'); + expect($tr.attr('data-file')).toEqual('testFolder'); + expect($tr.attr('data-size')).toEqual('1234'); + expect($tr.attr('data-permissions')).toEqual('31'); + //expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); + }); + it('returns correct download URL', function() { + expect(FileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fsubdir&download'); + expect(FileList.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fanotherpath%2Fabc&download'); + }); +}); diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js new file mode 100644 index 00000000000..9d0a2e4f9d7 --- /dev/null +++ b/apps/files/tests/js/filesSpec.js @@ -0,0 +1,81 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* 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 . +* +*/ +describe('Files tests', function() { + describe('File name validation', function() { + it('Validates correct file names', function() { + var fileNames = [ + 'boringname', + 'something.with.extension', + 'now with spaces', + '.a', + '..a', + '.dotfile', + 'single\'quote', + ' spaces before', + 'spaces after ', + 'allowed chars including the crazy ones $%&_-^@!,()[]{}=;#', + '汉字也能用', + 'und Ümläüte sind auch willkommen' + ]; + for ( var i = 0; i < fileNames.length; i++ ) { + try { + expect(Files.isFileNameValid(fileNames[i])).toEqual(true); + } + catch (e) { + fail(); + } + } + }); + it('Detects invalid file names', function() { + var fileNames = [ + '', + ' ', + '.', + '..', + 'back\\slash', + 'sl/ash', + 'ltgt', + 'col:on', + 'double"quote', + 'pi|pe', + 'dont?ask?questions?', + 'super*star', + 'new\nline', + ' ..', + '.. ', + '. ', + ' .' + ]; + for ( var i = 0; i < fileNames.length; i++ ) { + var threwException = false; + try { + Files.isFileNameValid(fileNames[i]); + fail(); + } + catch (e) { + threwException = true; + } + expect(threwException).toEqual(true); + } + }); + }); +}); diff --git a/apps/files/triggerupdate.php b/apps/files/triggerupdate.php index 0e29edbba35..a37b9823add 100644 --- a/apps/files/triggerupdate.php +++ b/apps/files/triggerupdate.php @@ -6,6 +6,7 @@ if (OC::$CLI) { if (count($argv) === 2) { $file = $argv[1]; list(, $user) = explode('/', $file); + OCP\JSON::checkUserExists($owner); OC_Util::setupFS($user); $view = new \OC\Files\View(''); /** diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml index 9d495916d26..b6d9d6bb0a3 100644 --- a/apps/files_encryption/appinfo/info.xml +++ b/apps/files_encryption/appinfo/info.xml @@ -2,11 +2,15 @@ files_encryption Encryption - The new ownCloud 5 files encryption system. After the app was enabled you need to re-login to initialize your encryption keys. + The ownCloud files encryption system provides server side-encryption. After the app was enabled you need to re-login to initialize your encryption keys. AGPL Sam Tuke, Bjoern Schiessle, Florin Peter 4 true + + http://doc.owncloud.org/server/6.0/user_manual/files/encryption.html + http://doc.owncloud.org/server/6.0/admin_manual/configuration/configuration_encryption.html + false diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index f142f525cfa..09d5687e226 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -30,6 +30,9 @@ use OC\Files\Filesystem; */ class Hooks { + // file for which we want to rename the keys after the rename operation was successful + private static $renamedFiles = array(); + /** * @brief Startup encryption backend upon user login * @note This method should never be called for users using client side encryption @@ -179,9 +182,9 @@ class Hooks { // the necessary keys) if (Crypt::mode() === 'server') { - if ($params['uid'] === \OCP\User::getUser()) { + $view = new \OC_FilesystemView('/'); - $view = new \OC_FilesystemView('/'); + if ($params['uid'] === \OCP\User::getUser()) { $session = new \OCA\Encryption\Session($view); @@ -202,36 +205,41 @@ class Hooks { } else { // admin changed the password for a different user, create new keys and reencrypt file keys $user = $params['uid']; - $recoveryPassword = $params['recoveryPassword']; - $newUserPassword = $params['password']; + $util = new Util($view, $user); + $recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null; - $view = new \OC_FilesystemView('/'); + if (($util->recoveryEnabledForUser() && $recoveryPassword) + || !$util->userKeysExists()) { - // make sure that the users home is mounted - \OC\Files\Filesystem::initMountPoints($user); + $recoveryPassword = $params['recoveryPassword']; + $newUserPassword = $params['password']; - $keypair = Crypt::createKeypair(); + // make sure that the users home is mounted + \OC\Files\Filesystem::initMountPoints($user); - // Disable encryption proxy to prevent recursive calls - $proxyStatus = \OC_FileProxy::$enabled; - \OC_FileProxy::$enabled = false; + $keypair = Crypt::createKeypair(); - // Save public key - $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']); + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; - // Encrypt private key empty passphrase - $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword); + // Save public key + $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']); - // Save private key - $view->file_put_contents( - '/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey); + // Encrypt private key empty passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword); - if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files - $util = new Util($view, $user); - $util->recoverUsersFiles($recoveryPassword); + // Save private key + $view->file_put_contents( + '/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey); + + if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files + $util = new Util($view, $user); + $util->recoverUsersFiles($recoveryPassword); + } + + \OC_FileProxy::$enabled = $proxyStatus; } - - \OC_FileProxy::$enabled = $proxyStatus; } } } @@ -474,6 +482,18 @@ class Hooks { } } + /** + * @brief mark file as renamed so that we know the original source after the file was renamed + * @param array $params with the old path and the new path + */ + public static function preRename($params) { + $util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); + list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']); + self::$renamedFiles[$params['oldpath']] = array( + 'uid' => $ownerOld, + 'path' => $pathOld); + } + /** * @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing * @param array with oldpath and newpath @@ -496,19 +516,32 @@ class Hooks { $userId = \OCP\User::getUser(); $util = new Util($view, $userId); - // Format paths to be relative to user files dir - if ($util->isSystemWideMountPoint($params['oldpath'])) { - $baseDir = 'files_encryption/'; - $oldKeyfilePath = $baseDir . 'keyfiles/' . $params['oldpath']; + if (isset(self::$renamedFiles[$params['oldpath']]['uid']) && + isset(self::$renamedFiles[$params['oldpath']]['path'])) { + $ownerOld = self::$renamedFiles[$params['oldpath']]['uid']; + $pathOld = self::$renamedFiles[$params['oldpath']]['path']; } else { - $baseDir = $userId . '/' . 'files_encryption/'; - $oldKeyfilePath = $baseDir . 'keyfiles/' . $params['oldpath']; + \OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::ERROR); + return false; } - if ($util->isSystemWideMountPoint($params['newpath'])) { - $newKeyfilePath = $baseDir . 'keyfiles/' . $params['newpath']; + list($ownerNew, $pathNew) = $util->getUidAndFilename($params['newpath']); + + // Format paths to be relative to user files dir + if ($util->isSystemWideMountPoint($pathOld)) { + $oldKeyfilePath = 'files_encryption/keyfiles/' . $pathOld; + $oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld; } else { - $newKeyfilePath = $baseDir . 'keyfiles/' . $params['newpath']; + $oldKeyfilePath = $ownerOld . '/' . 'files_encryption/keyfiles/' . $pathOld; + $oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld; + } + + if ($util->isSystemWideMountPoint($pathNew)) { + $newKeyfilePath = 'files_encryption/keyfiles/' . $pathNew; + $newShareKeyPath = 'files_encryption/share-keys/' . $pathNew; + } else { + $newKeyfilePath = $ownerNew . '/files_encryption/keyfiles/' . $pathNew; + $newShareKeyPath = $ownerNew . '/files_encryption/share-keys/' . $pathNew; } // add key ext if this is not an folder @@ -517,11 +550,11 @@ class Hooks { $newKeyfilePath .= '.key'; // handle share-keys - $localKeyPath = $view->getLocalFile($baseDir . 'share-keys/' . $params['oldpath']); + $localKeyPath = $view->getLocalFile($oldShareKeyPath); $escapedPath = Helper::escapeGlobPattern($localKeyPath); $matches = glob($escapedPath . '*.shareKey'); foreach ($matches as $src) { - $dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src)); + $dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src)); // create destination folder if not exists if (!file_exists(dirname($dst))) { @@ -533,15 +566,13 @@ class Hooks { } else { // handle share-keys folders - $oldShareKeyfilePath = $baseDir . 'share-keys/' . $params['oldpath']; - $newShareKeyfilePath = $baseDir . 'share-keys/' . $params['newpath']; // create destination folder if not exists - if (!$view->file_exists(dirname($newShareKeyfilePath))) { - $view->mkdir(dirname($newShareKeyfilePath), 0750, true); + if (!$view->file_exists(dirname($newShareKeyPath))) { + $view->mkdir(dirname($newShareKeyPath), 0750, true); } - $view->rename($oldShareKeyfilePath, $newShareKeyfilePath); + $view->rename($oldShareKeyPath, $newShareKeyPath); } // Rename keyfile so it isn't orphaned @@ -556,18 +587,17 @@ class Hooks { } // build the path to the file - $newPath = '/' . $userId . '/files' . $params['newpath']; - $newPathRelative = $params['newpath']; + $newPath = '/' . $ownerNew . '/files' . $pathNew; if ($util->fixFileSize($newPath)) { // get sharing app state $sharingEnabled = \OCP\Share::isEnabled(); // get users - $usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative); + $usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew); // update sharing-keys - $util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative); + $util->setSharedFileKeyfiles($session, $usersSharing, $pathNew); } \OC_FileProxy::$enabled = $proxyStatus; diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php index 9d307f1064d..9e4290534c0 100644 --- a/apps/files_encryption/l10n/da.php +++ b/apps/files_encryption/l10n/da.php @@ -8,19 +8,27 @@ $TRANSLATIONS = array( "Could not change the password. Maybe the old password was not correct." => "Kunne ikke ændre kodeordet. Måske var det gamle kodeord ikke korrekt.", "Private key password successfully updated." => "Privat nøgle kodeord succesfuldt opdateret.", "Could not update the private key password. Maybe the old password was not correct." => "Kunne ikke opdatere det private nøgle kodeord-. Måske var det gamle kodeord forkert.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "Krypteringsprogrammet er ikke igangsat. Det kan skyldes at krypteringsprogrammet er blevet genaktiveret under din session. Prøv at logge ud og ind igen for at aktivere krypteringsprogrammet. ", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Din private nøgle er ikke gyldig. Sandsynligvis er dit kodeord blevet ændret uden for %s (f.eks dit firmas adressebog). Du kan opdatere din private nøglekode i dine personlige indstillinger for at genskabe adgang til dine krypterede filer.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Kan ikke kryptere denne fil, sandsynligvis fordi felen er delt. Bed venligst filens ejer om at dele den med dig på ny.", +"Unknown error please check your system settings or contact your administrator" => "Ukendt fejl. Kontroller venligst dit system eller kontakt din administrator", "Missing requirements." => "Manglende betingelser.", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Sørg for at PHP 5.3.3 eller nyere er installeret og at OpenSSL sammen med PHP-udvidelsen er aktiveret og korrekt konfigureret. Indtil videre er krypteringsprogrammet deaktiveret.", "Following users are not set up for encryption:" => "Følgende brugere er ikke sat op til kryptering:", +"Initial encryption started... This can take some time. Please wait." => "Førstegangskryptering er påbegyndt... Dette kan tage nogen tid. Vent venligst.", "Saving..." => "Gemmer...", +"Go directly to your " => "Gå direkte til din ", "personal settings" => "Personlige indstillinger", "Encryption" => "Kryptering", "Enable recovery key (allow to recover users files in case of password loss):" => "Aktiver gendannelsesnøgle (Tillad gendannelse af brugerfiler i tilfælde af tab af kodeord):", "Recovery key password" => "Gendannelsesnøgle kodeord", +"Repeat Recovery key password" => "Gentag gendannelse af nøglekoden", "Enabled" => "Aktiveret", "Disabled" => "Deaktiveret", "Change recovery key password:" => "Skift gendannelsesnøgle kodeord:", "Old Recovery key password" => "Gammel Gendannelsesnøgle kodeord", "New Recovery key password" => "Ny Gendannelsesnøgle kodeord", +"Repeat New Recovery key password" => "Gentag dannelse af ny gendannaleses nøglekode", "Change Password" => "Skift Kodeord", "Your private key password no longer match your log-in password:" => "Dit private nøgle kodeord stemmer ikke længere overens med dit login kodeord:", "Set your old private key password to your current log-in password." => "Sæt dit gamle private nøgle kodeord til at være dit nuværende login kodeord. ", diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php index 08b655d2f32..3ba50f074b9 100644 --- a/apps/files_encryption/l10n/de.php +++ b/apps/files_encryption/l10n/de.php @@ -1,16 +1,16 @@ "Wiederherstellungsschlüssel wurde erfolgreich aktiviert", -"Could not enable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht aktiviert werden. Überprüfen Sie Ihr Wiederherstellungspasswort!", +"Could not enable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht aktiviert werden. Überprüfe Dein Wiederherstellungspasswort!", "Recovery key successfully disabled" => "Wiederherstellungsschlüssel deaktiviert.", -"Could not disable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht deaktiviert werden. Überprüfen Sie Ihr Wiederherstellungspasswort!", +"Could not disable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht deaktiviert werden. Überprüfe Dein Wiederherstellungspasswort!", "Password successfully changed." => "Dein Passwort wurde geändert.", "Could not change the password. Maybe the old password was not correct." => "Das Passwort konnte nicht geändert werden. Vielleicht war das alte Passwort falsch.", "Private key password successfully updated." => "Passwort des privaten Schlüssels erfolgreich aktualisiert", "Could not update the private key password. Maybe the old password was not correct." => "Das Passwort des privaten Schlüssels konnte nicht aktualisiert werden. Eventuell war das alte Passwort falsch.", "Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "Verschlüsselung-App ist nicht initialisiert! Vielleicht wurde die Verschlüsselung-App in der aktuellen Sitzung reaktiviert. Bitte versuche Dich ab- und wieder anzumelden, um die Verschlüsselung-App zu initialisieren.", -"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Dein privater Schlüssel ist ungültig. Möglicher Weise wurde außerhalb von%s Dein Passwort geändert (z.B. in deinem gemeinsamen Verzeichnis). Du kannst das Passwort deines privaten Schlüssels in den persönlichen Einstellungen aktualisieren, um wieder an deine Dateien zu gelangen.", -"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Die Datei kann nicht entschlüsselt werden, da die Datei möglicherweise eine geteilte Datei ist. Bitte frage den Datei-Besitzer, dass er die Datei nochmals mit Dir teilt.", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Dein privater Schlüssel ist ungültig. Möglicher Weise wurde außerhalb von %s Dein Passwort geändert (z.B. in Deinem gemeinsamen Verzeichnis). Du kannst das Passwort Deines privaten Schlüssels in den persönlichen Einstellungen aktualisieren, um wieder an Deine Dateien zu gelangen.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Die Datei kann nicht entschlüsselt werden, da die Datei möglicherweise eine geteilte Datei ist. Bitte frage den Dateibesitzer, ob er die Datei nochmals mit Dir teilt.", "Unknown error please check your system settings or contact your administrator" => "Unbekannter Fehler, bitte prüfe Deine Systemeinstellungen oder kontaktiere Deinen Administrator", "Missing requirements." => "Fehlende Vorraussetzungen", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Bitte stelle sicher, dass PHP 5.3.3 oder neuer installiert und das OpenSSL zusammen mit der PHP-Erweiterung aktiviert und richtig konfiguriert ist. Zur Zeit ist die Verschlüsselungs-App deaktiviert.", @@ -30,14 +30,14 @@ $TRANSLATIONS = array( "New Recovery key password" => "Neues Wiederherstellungsschlüssel-Passwort", "Repeat New Recovery key password" => "Neues Schlüssel-Passwort zur Wiederherstellung wiederholen", "Change Password" => "Passwort ändern", -"Your private key password no longer match your log-in password:" => "Ihr Passwort für ihren privaten Schlüssel stimmt nicht mehr mit ihrem Loginpasswort überein.", -"Set your old private key password to your current log-in password." => "Setzen Sie ihr altes Passwort für ihren privaten Schlüssel auf ihr aktuelles Login-Passwort", -" If you don't remember your old password you can ask your administrator to recover your files." => "Wenn Sie Ihr altes Passwort vergessen haben, können Sie den Administrator bitten, Ihre Daten wiederherzustellen.", -"Old log-in password" => "Altes login Passwort", +"Your private key password no longer match your log-in password:" => "Das Passwort für Deinen privaten Schlüssel stimmt nicht mehr mit Deinem Loginpasswort überein.", +"Set your old private key password to your current log-in password." => "Setze Dein altes Passwort für Deinen privaten Schlüssel auf Dein aktuelles Login-Passwort", +" If you don't remember your old password you can ask your administrator to recover your files." => "Wenn Du Dein altes Passwort vergessen hast, könntest Du Deinen Administrator bitten, Deine Daten wiederherzustellen.", +"Old log-in password" => "Altes Login Passwort", "Current log-in password" => "Aktuelles Passwort", "Update Private Key Password" => "Passwort für den privaten Schlüssel aktualisieren", -"Enable password recovery:" => "Passwortwiederherstellung aktivvieren:", -"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "Wenn Sie diese Option aktivieren, können Sie Ihre verschlüsselten Dateien wiederherstellen, falls Sie Ihr Passwort vergessen", +"Enable password recovery:" => "Passwortwiederherstellung aktivieren:", +"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "Wenn Du diese Option aktivierst, kannst Du Deine verschlüsselten Dateien wiederherstellen, falls Du Dein Passwort vergisst", "File recovery settings updated" => "Einstellungen zur Wiederherstellung von Dateien wurden aktualisiert", "Could not update file recovery" => "Dateiwiederherstellung konnte nicht aktualisiert werden" ); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php index 6b21122027a..22c1095e284 100644 --- a/apps/files_encryption/l10n/el.php +++ b/apps/files_encryption/l10n/el.php @@ -8,20 +8,30 @@ $TRANSLATIONS = array( "Could not change the password. Maybe the old password was not correct." => "Αποτυχία αλλαγής κωδικού ίσως ο παλιός κωδικός να μην ήταν σωστός.", "Private key password successfully updated." => "Το Προσωπικό κλειδί πρόσβασης ενημερώθηκε επιτυχώς", "Could not update the private key password. Maybe the old password was not correct." => "Αποτυχία ενημέρωσης του κωδικού για το προσωπικό κλειδί. Ενδεχομένως ο παλιός κωδικός δεν ήταν σωστός.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "Η εφαρμογή κρυπτογράφησης δεν έχει εκκινήσει! Ίσως η εφαρμογή κρυπτογράφησης επανενεργοποιήθηκε κατά τη διάρκεια της τρέχουσας σύνδεσής σας. Παρακαλώ προσπαθήστε να αποσυνδεθείτε και να ξανασυνδεθείτε για να εκκινήσετε την εφαρμογή κρυπτογράφησης.", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Το προσωπικό σας κλειδί δεν είναι έγκυρο! Πιθανόν ο κωδικός σας να άλλαξε έξω από το %s (π.χ. τη λίστα διευθύνσεων της εταιρείας σας). Μπορείτε να ενημερώσετε το προσωπικό σας κλειδί επαναφοράς κωδικού στις προσωπικές σας ρυθμίσεις για να επανακτήσετε πρόσβαση στα κρυπτογραφημένα σας αρχεία.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Δεν ήταν δυνατό να αποκρυπτογραφηθεί αυτό το αρχείο, πιθανόν πρόκειται για κοινόχρηστο αρχείο. Παρακαλώ ζητήστε από τον ιδιοκτήτη του αρχείου να το ξαναμοιραστεί μαζί σας.", "Unknown error please check your system settings or contact your administrator" => "Άγνωστο σφάλμα, παρακαλώ ελέγξτε τις ρυθμίσεις συστήματος ή επικοινωνήστε με τον διαχειριστή σας ", "Missing requirements." => "Προαπαιτούμενα που απουσιάζουν.", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Παρακαλώ επιβεβαιώστε ότι η PHP 5.3.3 ή νεότερη είναι εγκατεστημένη και ότι το OpenSSL μαζί με το PHP extension είναι ενεργοποιήμένο και έχει ρυθμιστεί σωστά. Προς το παρόν, η εφαρμογή κρυπτογράφησης είναι απενεργοποιημένη.", "Following users are not set up for encryption:" => "Οι κάτωθι χρήστες δεν έχουν ρυθμιστεί για κρυπογράφηση:", +"Initial encryption started... This can take some time. Please wait." => "Η αρχική κρυπτογράφηση άρχισε... Αυτό μπορεί να πάρει κάποια ώρα. Παρακαλώ περιμένετε.", "Saving..." => "Γίνεται αποθήκευση...", "Go directly to your " => "Πηγαίνε απευθείας στο ", "personal settings" => "προσωπικές ρυθμίσεις", "Encryption" => "Κρυπτογράφηση", +"Enable recovery key (allow to recover users files in case of password loss):" => "Ενεργοποίηση κλειδιού ανάκτησης (επιτρέψτε την ανάκτηση αρχείων χρηστών σε περίπτωση απώλειας κωδικού):", "Recovery key password" => "Επαναφορά κωδικού κλειδιού", +"Repeat Recovery key password" => "Επαναλάβετε το κλειδί επαναφοράς κωδικού", "Enabled" => "Ενεργοποιημένο", "Disabled" => "Απενεργοποιημένο", +"Change recovery key password:" => "Αλλαγή κλειδιού επαναφοράς κωδικού:", +"Old Recovery key password" => "Παλιό κλειδί επαναφοράς κωδικού", "New Recovery key password" => "Νέο κλειδί επαναφοράς κωδικού", +"Repeat New Recovery key password" => "Επαναλάβετε νέο κλειδί επαναφοράς κωδικού", "Change Password" => "Αλλαγή Κωδικού Πρόσβασης", "Your private key password no longer match your log-in password:" => "Ο κωδικός του προσωπικού κλειδιού δεν ταιριάζει πλέον με τον κωδικό σύνδεσής σας:", +"Set your old private key password to your current log-in password." => "Ορίστε το παλιό σας προσωπικό κλειδί ως τον τρέχων κωδικό πρόσβασης.", " If you don't remember your old password you can ask your administrator to recover your files." => "Εάν δεν θυμάστε τον παλιό σας κωδικό μπορείτε να ζητήσετε από τον διαχειριστή σας να επανακτήσει τα αρχεία σας.", "Old log-in password" => "Παλαιό συνθηματικό εισόδου", "Current log-in password" => "Τρέχον συνθηματικό πρόσβασης", diff --git a/apps/files_encryption/l10n/es_MX.php b/apps/files_encryption/l10n/es_MX.php new file mode 100644 index 00000000000..3906e3cacbe --- /dev/null +++ b/apps/files_encryption/l10n/es_MX.php @@ -0,0 +1,44 @@ + "Se ha habilitado la recuperación de archivos", +"Could not enable recovery key. Please check your recovery key password!" => "No se pudo habilitar la clave de recuperación. Por favor compruebe su contraseña.", +"Recovery key successfully disabled" => "Clave de recuperación deshabilitada", +"Could not disable recovery key. Please check your recovery key password!" => "No se pudo deshabilitar la clave de recuperación. Por favor compruebe su contraseña!", +"Password successfully changed." => "Su contraseña ha sido cambiada", +"Could not change the password. Maybe the old password was not correct." => "No se pudo cambiar la contraseña. Compruebe que la contraseña actual sea correcta.", +"Private key password successfully updated." => "Contraseña de clave privada actualizada con éxito.", +"Could not update the private key password. Maybe the old password was not correct." => "No se pudo cambiar la contraseña. Puede que la contraseña antigua no sea correcta.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "¡La aplicación de cifrado no ha sido inicializada! Quizá fue restablecida durante tu sesión. Por favor intenta cerrar la sesión y volver a iniciarla para inicializar la aplicación de cifrado.", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "¡Su clave privada no es válida! Tal vez su contraseña ha sido cambiada desde fuera. de %s (Ej:Su directorio corporativo). Puede actualizar la contraseña de su clave privada en sus opciones personales para recuperar el acceso a sus archivos.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "No fue posible descifrar este archivo, probablemente se trate de un archivo compartido. Solicite al propietario del mismo que vuelva a compartirlo con usted.", +"Unknown error please check your system settings or contact your administrator" => "Error desconocido. Verifique la configuración de su sistema o póngase en contacto con su administrador", +"Missing requirements." => "Requisitos incompletos.", +"Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Por favor, asegúrese de que PHP 5.3.3 o posterior está instalado y que la extensión OpenSSL de PHP está habilitada y configurada correctamente. Por el momento, la aplicación de cifrado ha sido deshabilitada.", +"Following users are not set up for encryption:" => "Los siguientes usuarios no han sido configurados para el cifrado:", +"Initial encryption started... This can take some time. Please wait." => "Encriptación iniciada... Esto puede tomar un tiempo. Por favor espere.", +"Saving..." => "Guardando...", +"Go directly to your " => "Ir directamente a su", +"personal settings" => "opciones personales", +"Encryption" => "Cifrado", +"Enable recovery key (allow to recover users files in case of password loss):" => "Habilitar la clave de recuperación (permite recuperar los archivos del usuario en caso de pérdida de la contraseña);", +"Recovery key password" => "Contraseña de clave de recuperación", +"Repeat Recovery key password" => "Repite la contraseña de clave de recuperación", +"Enabled" => "Habilitar", +"Disabled" => "Deshabilitado", +"Change recovery key password:" => "Cambiar la contraseña de la clave de recuperación", +"Old Recovery key password" => "Antigua clave de recuperación", +"New Recovery key password" => "Nueva clave de recuperación", +"Repeat New Recovery key password" => "Repetir la nueva clave de recuperación", +"Change Password" => "Cambiar contraseña", +"Your private key password no longer match your log-in password:" => "Su contraseña de clave privada ya no coincide con su contraseña de acceso:", +"Set your old private key password to your current log-in password." => "Establecer la contraseña de su antigua clave privada a su contraseña actual de acceso.", +" If you don't remember your old password you can ask your administrator to recover your files." => "Si no recuerda su antigua contraseña puede pedir a su administrador que le recupere sus archivos.", +"Old log-in password" => "Contraseña de acceso antigua", +"Current log-in password" => "Contraseña de acceso actual", +"Update Private Key Password" => "Actualizar Contraseña de Clave Privada", +"Enable password recovery:" => "Habilitar la recuperación de contraseña:", +"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "Habilitar esta opción le permitirá volver a tener acceso a sus archivos cifrados en caso de pérdida de contraseña", +"File recovery settings updated" => "Opciones de recuperación de archivos actualizada", +"Could not update file recovery" => "No se pudo actualizar la recuperación de archivos" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php index caf5f6799e0..6b1bafdda1a 100644 --- a/apps/files_encryption/l10n/eu.php +++ b/apps/files_encryption/l10n/eu.php @@ -8,19 +8,27 @@ $TRANSLATIONS = array( "Could not change the password. Maybe the old password was not correct." => "Ezin izan da pasahitza aldatu. Agian pasahitz zaharra okerrekoa da.", "Private key password successfully updated." => "Gako pasahitz pribatu behar bezala eguneratu da.", "Could not update the private key password. Maybe the old password was not correct." => "Ezin izan da gako pribatu pasahitza eguneratu. Agian pasahitz zaharra okerrekoa da.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "Enkriptazio aplikazioa ez dago hasieratuta! Agian aplikazioa birgaitu egin da zure saioa bitartean. Mesdez atear eta sartu berriz enkriptazio aplikazioa hasierarazteko.", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Zure gako pribatua ez da egokia! Seguruaski zure pasahitza %s-tik kanpo aldatu da (adb. zure direktorio korporatiboa). Zure gako pribatuaren pasahitza eguneratu dezakezu zure ezarpen pertsonaletan zure enkriptatutako fitxategiak berreskuratzeko.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Ezin izan da fitxategi hau deszifratu, ziurrenik elkarbanatutako fitxategi bat da. Mesdez, eskatu fitxategiaren jabeari fitxategia zurekin berriz elkarbana dezan.", +"Unknown error please check your system settings or contact your administrator" => "Errore ezezaguna mesedez egiaztatu zure sistemaren ezarpenak edo harremanetan jarri zure administradorearekin", "Missing requirements." => "Eskakizun batzuk ez dira betetzen.", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Mesedez ziurtatu PHP 5.3.3 edo berriago bat instalatuta dagoela eta OpenSSL PHP hedapenarekin gaitua eta ongi konfiguratuta dagoela. Oraingoz, enkriptazio aplikazioa desgaituta dago.", "Following users are not set up for encryption:" => "Hurrengo erabiltzaileak ez daude enktriptatzeko konfiguratutak:", +"Initial encryption started... This can take some time. Please wait." => "Hasierako enkriptazioa hasi da... Honek denbora har dezake. Mesedez itxaron.", "Saving..." => "Gordetzen...", +"Go directly to your " => "Joan zuzenean zure", "personal settings" => "ezarpen pertsonalak", "Encryption" => "Enkriptazioa", "Enable recovery key (allow to recover users files in case of password loss):" => "Gaitu berreskurapen gakoa (erabiltzaileen fitxategiak berreskuratzea ahalbidetzen du pasahitza galtzen badute ere):", "Recovery key password" => "Berreskuratze gako pasahitza", +"Repeat Recovery key password" => "Errepikatu berreskuratze gakoaren pasahitza", "Enabled" => "Gaitua", "Disabled" => "Ez-gaitua", "Change recovery key password:" => "Aldatu berreskuratze gako pasahitza:", "Old Recovery key password" => "Berreskuratze gako pasahitz zaharra", "New Recovery key password" => "Berreskuratze gako pasahitz berria", +"Repeat New Recovery key password" => "Errepikatu berreskuratze gako berriaren pasahitza", "Change Password" => "Aldatu Pasahitza", "Your private key password no longer match your log-in password:" => "Zure gako pribatuaren pasahitza ez da dagoeneko zure sarrera pasahitza:", "Set your old private key password to your current log-in password." => "Ezarri zure gako pribatu zaharraren pasahitza zure oraingo sarrerako psahitzara.", diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php index 323291bbfbe..163011ff80b 100644 --- a/apps/files_encryption/l10n/hu_HU.php +++ b/apps/files_encryption/l10n/hu_HU.php @@ -1,18 +1,44 @@ "Visszaállítási kulcs sikeresen kikapcsolva", -"Password successfully changed." => "Jelszó sikeresen megváltoztatva.", +"Recovery key successfully enabled" => "A helyreállítási kulcs sikeresen bekapcsolva", +"Could not enable recovery key. Please check your recovery key password!" => "A helyreállítási kulcsot nem lehetett engedélyezni. Ellenőrizze a helyreállítási kulcsa jelszavát!", +"Recovery key successfully disabled" => "A helyreállítási kulcs sikeresen kikapcsolva", +"Could not disable recovery key. Please check your recovery key password!" => "A helyreállítási kulcsot nem lehetett kikapcsolni. Ellenőrizze a helyreállítási kulcsa jelszavát!", +"Password successfully changed." => "A jelszót sikeresen megváltoztattuk.", "Could not change the password. Maybe the old password was not correct." => "A jelszót nem lehet megváltoztatni! Lehet, hogy hibás volt a régi jelszó.", -"Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Kérlek győződj meg arról, hogy PHP 5.3.3 vagy annál frissebb van telepítve, valamint a PHP-hez tartozó OpenSSL bővítmény be van-e kapcsolva és az helyesen van-e konfigurálva! Ki lett kapcsolva ideiglenesen a titkosító alkalmazás.", +"Private key password successfully updated." => "A személyes kulcsának jelszava frissítésre került.", +"Could not update the private key password. Maybe the old password was not correct." => "A személyes kulcsa jelszavát nem lehetett frissíteni. Lehet, hogy hibás volt a régi jelszó.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "A titkosítási modul nincs elindítva! Talán a munkafolyamat közben került engedélyezésre. Kérjük jelentkezzen ki majd ismét jelentkezzen be, hogy a titkosítási modul megfelelően elinduljon!", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "Az állományok titkosításához használt titkos kulcsa érvénytelen. Valószínűleg a %s rendszeren kívül változtatta meg a jelszavát (pl. a munkahelyi címtárban). A személyes beállításoknál frissítheti a titkos kulcsát, hogy ismét elérhesse a titkosított állományait.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Az állományt nem sikerült dekódolni, valószínűleg ez egy megosztott fájl. Kérje meg az állomány tulajdonosát, hogy újra ossza meg Önnel ezt az állományt!", +"Unknown error please check your system settings or contact your administrator" => "Ismeretlen hiba. Ellenőrizze a rendszer beállításait vagy forduljon a rendszergazdához!", +"Missing requirements." => "Hiányzó követelmények.", +"Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Kérem gondoskodjon arról, hogy PHP 5.3.3 vagy annál frissebb legyen telepítve, továbbá az OpenSSL a megfelelő PHP-bővítménnyel együtt rendelkezésre álljon és helyesen legyen konfigurálva! A titkosító modul egyelőre kikapcsolásra került.", +"Following users are not set up for encryption:" => "A következő felhasználók nem állították be a titkosítást:", +"Initial encryption started... This can take some time. Please wait." => "A titkosítási folyamat megkezdődött... Ez hosszabb ideig is eltarthat. Kérem várjon.", "Saving..." => "Mentés...", +"Go directly to your " => "Ugrás ide:", "personal settings" => "személyes beállítások", "Encryption" => "Titkosítás", +"Enable recovery key (allow to recover users files in case of password loss):" => "A helyreállítási kulcs beállítása (lehetővé teszi a felhasználók állományainak visszaállítását, ha elfelejtik a jelszavukat):", +"Recovery key password" => "A helyreállítási kulcs jelszava", +"Repeat Recovery key password" => "Ismételje meg a helyreállítási kulcs jelszavát", "Enabled" => "Bekapcsolva", "Disabled" => "Kikapcsolva", +"Change recovery key password:" => "A helyreállítási kulcs jelszavának módosítása:", +"Old Recovery key password" => "Régi Helyreállítási Kulcs Jelszava", +"New Recovery key password" => "Új Helyreállítási kulcs jelszava", +"Repeat New Recovery key password" => "Ismételje meg az új helyreállítási kulcs jelszavát", "Change Password" => "Jelszó megváltoztatása", +"Your private key password no longer match your log-in password:" => "A személyes kulcs jelszava mostantól nem azonos a belépési jelszavával:", +"Set your old private key password to your current log-in password." => "Állítsuk be a személyes kulcs jelszavát a jelenlegi bejelentkezési jelszavunkra.", +" If you don't remember your old password you can ask your administrator to recover your files." => "Ha nem emlékszik a régi jelszavára akkor megkérheti a rendszergazdát, hogy állítsa vissza az állományait.", "Old log-in password" => "Régi bejelentkezési jelszó", "Current log-in password" => "Jelenlegi bejelentkezési jelszó", -"Update Private Key Password" => "Privát kulcs jelszó frissítése", -"Enable password recovery:" => "Jelszó-visszaállítás bekapcsolása" +"Update Private Key Password" => "A személyest kulcs jelszó frissítése", +"Enable password recovery:" => "Jelszó-visszaállítás bekapcsolása", +"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "Ez az opció lehetővé teszi, hogy a titkosított állományok tartalmát visszanyerjük abban az esetben, ha elfelejti a jelszavát", +"File recovery settings updated" => "A fájlhelyreállítási beállítások frissültek", +"Could not update file recovery" => "A fájlhelyreállítás nem frissíthető" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_encryption/l10n/id.php b/apps/files_encryption/l10n/id.php index 32c348bd8ba..a719d445820 100644 --- a/apps/files_encryption/l10n/id.php +++ b/apps/files_encryption/l10n/id.php @@ -1,6 +1,41 @@ "Kunci pemulihan berhasil diaktifkan", +"Could not enable recovery key. Please check your recovery key password!" => "Tidak dapat mengaktifkan kunci pemulihan. Silakan periksa sandi kunci pemulihan Anda!", +"Recovery key successfully disabled" => "Kunci pemulihan berhasil dinonaktifkan", +"Could not disable recovery key. Please check your recovery key password!" => "Tidak dapat menonaktifkan kunci pemulihan. Silakan periksa sandi kunci pemulihan Anda!", +"Password successfully changed." => "Sandi berhasil diubah", +"Could not change the password. Maybe the old password was not correct." => "Tidak dapat mengubah sandi. Kemungkinan sandi lama yang dimasukkan salah.", +"Private key password successfully updated." => "Sandi kunci privat berhasil diperbarui.", +"Could not update the private key password. Maybe the old password was not correct." => "Tidak dapat memperbarui sandi kunci privat. Kemungkinan sandi lama yang Anda masukkan salah.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Tidak dapat mendekripsi berkas ini, mungkin ini adalah berkas bersama. Silakan meminta pemilik berkas ini untuk membagikan kembali dengan Anda.", +"Unknown error please check your system settings or contact your administrator" => "Kesalahan tak dikenal, silakan periksa pengaturan sistem Anda atau hubungi admin.", +"Missing requirements." => "Persyaratan yang hilang.", +"Following users are not set up for encryption:" => "Pengguna berikut belum diatur untuk enkripsi:", +"Initial encryption started... This can take some time. Please wait." => "Inisial enskripsi dijalankan... Ini dapat memakan waktu. Silakan tunggu.", "Saving..." => "Menyimpan...", -"Encryption" => "Enkripsi" +"Go directly to your " => "Langsung ke anda", +"personal settings" => "pengaturan pribadi", +"Encryption" => "Enkripsi", +"Enable recovery key (allow to recover users files in case of password loss):" => "Aktifkan kunci pemulihan (memungkinkan pengguna untuk memulihkan berkas dalam kasus kehilangan sandi):", +"Recovery key password" => "Sandi kunci pemulihan", +"Repeat Recovery key password" => "Ulangi sandi kunci Pemulihan", +"Enabled" => "Diaktifkan", +"Disabled" => "Dinonaktifkan", +"Change recovery key password:" => "Ubah sandi kunci pemulihan:", +"Old Recovery key password" => "Sandi kunci Pemulihan Lama", +"New Recovery key password" => "Sandi kunci Pemulihan Baru", +"Repeat New Recovery key password" => "Ulangi sandi kunci Pemulihan baru", +"Change Password" => "Ubah sandi", +"Your private key password no longer match your log-in password:" => "Sandi kunci privat Anda tidak lagi cocok dengan sandi masuk:", +"Set your old private key password to your current log-in password." => "Atur sandi kunci privat lama Anda sebagai sandi masuk Anda saat ini.", +" If you don't remember your old password you can ask your administrator to recover your files." => "Jika Anda tidak ingat sandi lama, Anda dapat meminta administrator Anda untuk memulihkan berkas.", +"Old log-in password" => "Sandi masuk yang lama", +"Current log-in password" => "Sandi masuk saat ini", +"Update Private Key Password" => "Perbarui Sandi Kunci Privat", +"Enable password recovery:" => "Aktifkan sandi pemulihan:", +"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "Mengaktifkan opsi ini memungkinkan Anda untuk mendapatkan kembali akses ke berkas terenkripsi Anda dalam kasus kehilangan sandi", +"File recovery settings updated" => "Pengaturan pemulihan berkas diperbarui", +"Could not update file recovery" => "Tidak dapat memperbarui pemulihan berkas" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_encryption/l10n/ko.php b/apps/files_encryption/l10n/ko.php index cf06136c9b6..d91e861ca5d 100644 --- a/apps/files_encryption/l10n/ko.php +++ b/apps/files_encryption/l10n/ko.php @@ -1,26 +1,44 @@ "복구키가 성공적으로 활성화 되었습니다", -"Could not enable recovery key. Please check your recovery key password!" => "복구키를 활성화 할수 없습니다. 복구키의 비밀번호를 확인해주세요!", -"Recovery key successfully disabled" => "복구키가 성공적으로 비활성화 되었습니다", -"Could not disable recovery key. Please check your recovery key password!" => "복구키를 비활성화 할수 없습니다. 복구키의 비밀번호를 확인해주세요!", +"Recovery key successfully enabled" => "복구 키가 성공적으로 활성화되었습니다", +"Could not enable recovery key. Please check your recovery key password!" => "복구 키를 활성화 할 수 없습니다. 복구 키의 암호를 확인해 주세요!", +"Recovery key successfully disabled" => "복구 키가 성공적으로 비활성화 되었습니다", +"Could not disable recovery key. Please check your recovery key password!" => "복구 키를 비활성화 할 수 없습니다. 복구 키의 암호를 확인해주세요!", "Password successfully changed." => "암호가 성공적으로 변경되었습니다", -"Could not change the password. Maybe the old password was not correct." => "암호를 변경할수 없습니다. 아마도 예전 암호가 정확하지 않은것 같습니다.", -"Private key password successfully updated." => "개인키 암호가 성공적으로 업데이트 됨.", +"Could not change the password. Maybe the old password was not correct." => "암호를 변경할 수 없습니다. 예전 암호가 정확하지 않은 것 같습니다.", +"Private key password successfully updated." => "개인 키 암호가 성공적으로 업데이트 됨.", +"Could not update the private key password. Maybe the old password was not correct." => "개인 키 암호를 업데이트할 수 없습니다. 이전 암호가 올바르지 않은 것 같습니다.", +"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "암호화 앱이 초기화되지 않았습니다! 암호화 앱이 다시 활성화된 것 같습니다. 암호화 앱을 초기화하려면 로그아웃했다 다시 로그인하십시오.", +"Your private key is not valid! Likely your password was changed outside of %s (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files." => "개인 키가 올바르지 않습니다! 암호가 %s(예: 회사 디렉터리) 외부에서 변경된 것 같습니다. 암호화된 파일에 다시 접근하려면 개인 설정에서 개인 키 암호를 수정하십시오.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "이 파일을 복호화할 수 없습니다. 공유된 파일일 수도 있습니다. 파일 소유자에게 공유를 다시 요청하십시오.", +"Unknown error please check your system settings or contact your administrator" => "알 수 없는 오류. 시스템 설정을 확인하거나 관리자에게 문의하십시오.", +"Missing requirements." => "요구 사항이 부족합니다.", +"Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "PHP 5.3.3 이상 설치 여부, PHP의 OpenSSL 확장 기능 활성화 및 설정 여부를 확인하십시오. 암호화 앱이 비활성화 되었습니다.", +"Following users are not set up for encryption:" => "다음 사용자는 암호화를 사용할 수 없습니다:", +"Initial encryption started... This can take some time. Please wait." => "초기 암호화가 시작되었습니다... 시간이 걸릴 수도 있으니 기다려 주십시오.", "Saving..." => "저장 중...", +"Go directly to your " => "다음으로 바로 가기: ", "personal settings" => "개인 설정", "Encryption" => "암호화", -"Recovery key password" => "키 비밀번호 복구", +"Enable recovery key (allow to recover users files in case of password loss):" => "복구 키 사용 (암호를 잊었을 때 파일을 복구할 수 있도록 함):", +"Recovery key password" => "복구 키 암호", +"Repeat Recovery key password" => "복구 키 암호 재입력", "Enabled" => "활성화", "Disabled" => "비활성화", -"Change recovery key password:" => "복구 키 비밀번호 변경", -"Old Recovery key password" => "예전 복구 키 비밀번호", -"New Recovery key password" => "새 복구 키 비밀번호", +"Change recovery key password:" => "복구 키 암호 변경:", +"Old Recovery key password" => "이전 복구 키 암호", +"New Recovery key password" => "새 복구 키 암호", +"Repeat New Recovery key password" => "새 복구 키 암호 재입력", "Change Password" => "암호 변경", -"Old log-in password" => "예전 로그인 암호", +"Your private key password no longer match your log-in password:" => "개인 키 암호와 로그인 암호가 일치하지 않습니다:", +"Set your old private key password to your current log-in password." => "이전 개인 키 암호를 현재 로그인 암호로 설정하십시오.", +" If you don't remember your old password you can ask your administrator to recover your files." => " 이전 암호가 기억나지 않으면 시스템 관리자에게 파일 복구를 요청하십시오.", +"Old log-in password" => "이전 로그인 암호", "Current log-in password" => "현재 로그인 암호", "Update Private Key Password" => "개인 키 암호 업데이트", +"Enable password recovery:" => "암호 복구 사용:", +"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" => "이 옵션을 사용하면 암호를 잊었을 때 암호화된 파일에 다시 접근할 수 있습니다", "File recovery settings updated" => "파일 복구 설정 업데이트됨", -"Could not update file recovery" => "파일 복구를 업데이트 할수 없습니다" +"Could not update file recovery" => "파일 복구를 업데이트할 수 없습니다" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php index 5d1a48d1246..b768bd46f8c 100644 --- a/apps/files_encryption/l10n/pl.php +++ b/apps/files_encryption/l10n/pl.php @@ -15,6 +15,7 @@ $TRANSLATIONS = array( "Missing requirements." => "Brak wymagań.", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Proszę upewnić się, że PHP 5.3.3 lub nowszy jest zainstalowany i że OpenSSL oraz rozszerzenie PHP jest włączone i poprawnie skonfigurowane. Obecnie szyfrowanie aplikacji zostało wyłączone.", "Following users are not set up for encryption:" => "Następujący użytkownicy nie mają skonfigurowanego szyfrowania:", +"Initial encryption started... This can take some time. Please wait." => "Rozpoczęto szyfrowanie... To może chwilę potrwać. Proszę czekać.", "Saving..." => "Zapisywanie...", "Go directly to your " => "Przejdź bezpośrednio do", "personal settings" => "Ustawienia osobiste", diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 17bcac5c585..5dcb05fa196 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -29,6 +29,8 @@ namespace OCA\Encryption; */ class Helper { + private static $tmpFileMapping; // Map tmp files to files in data/user/files + /** * @brief register share related hooks * @@ -59,6 +61,7 @@ class Helper { */ public static function registerFilesystemHooks() { + \OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename'); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename'); } @@ -274,7 +277,7 @@ class Helper { $split = explode('/', $trimmed); // it is not a file relative to data/user/files - if (count($split) < 2 || $split[1] !== 'files') { + if (count($split) < 2 || ($split[1] !== 'files' && $split[1] !== 'cache')) { return false; } @@ -423,5 +426,27 @@ class Helper { public static function escapeGlobPattern($path) { return preg_replace('/(\*|\?|\[)/', '[$1]', $path); } + + /** + * @brief remember from which file the tmp file (getLocalFile() call) was created + * @param string $tmpFile path of tmp file + * @param string $originalFile path of the original file relative to data/ + */ + public static function addTmpFileToMapper($tmpFile, $originalFile) { + self::$tmpFileMapping[$tmpFile] = $originalFile; + } + + /** + * @brief get the path of the original file + * @param string $tmpFile path of the tmp file + * @return mixed path of the original file or false + */ + public static function getPathFromTmpFile($tmpFile) { + if (isset(self::$tmpFileMapping[$tmpFile])) { + return self::$tmpFileMapping[$tmpFile]; + } + + return false; + } } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 5ba3bfa784f..4e71ab1dd5d 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -37,6 +37,7 @@ namespace OCA\Encryption; class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption + private static $unencryptedSizes = array(); // remember unencrypted size /** * Check if a file requires encryption @@ -114,6 +115,13 @@ class Proxy extends \OC_FileProxy { // get encrypted content $data = $view->file_get_contents($tmpPath); + // store new unenecrypted size so that it can be updated + // in the post proxy + $tmpFileInfo = $view->getFileInfo($tmpPath); + if ( isset($tmpFileInfo['size']) ) { + self::$unencryptedSizes[\OC_Filesystem::normalizePath($path)] = $tmpFileInfo['size']; + } + // remove our temp file $view->deleteAll('/' . \OCP\User::getUser() . '/cache/' . $cacheFolder); @@ -127,6 +135,24 @@ class Proxy extends \OC_FileProxy { } + /** + * @brief update file cache with the new unencrypted size after file was written + * @param string $path + * @param mixed $result + * @return mixed + */ + public function postFile_put_contents($path, $result) { + $normalizedPath = \OC_Filesystem::normalizePath($path); + if ( isset(self::$unencryptedSizes[$normalizedPath]) ) { + $view = new \OC_FilesystemView('/'); + $view->putFileInfo($normalizedPath, + array('encrypted' => true, 'encrypted_size' => self::$unencryptedSizes[$normalizedPath])); + unset(self::$unencryptedSizes[$normalizedPath]); + } + + return $result; + } + /** * @param string $path Path of file from which has been read * @param string $data Data that has been read from file @@ -182,8 +208,11 @@ class Proxy extends \OC_FileProxy { */ public function preUnlink($path) { - // let the trashbin handle this - if (\OCP\App::isEnabled('files_trashbin')) { + $relPath = Helper::stripUserFilesPath($path); + + // skip this method if the trash bin is enabled or if we delete a file + // outside of /data/user/files + if (\OCP\App::isEnabled('files_trashbin') || $relPath === false) { return true; } @@ -197,10 +226,7 @@ class Proxy extends \OC_FileProxy { $util = new Util($view, $userId); - // get relative path - $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); - - list($owner, $ownerPath) = $util->getUidAndFilename($relativePath); + list($owner, $ownerPath) = $util->getUidAndFilename($relPath); // Delete keyfile & shareKey so it isn't orphaned if (!Keymanager::deleteFileKey($view, $ownerPath)) { @@ -246,8 +272,8 @@ class Proxy extends \OC_FileProxy { // split the path parts $pathParts = explode('/', $path); - // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted - if (isset($pathParts[2]) && $pathParts[2] === 'cache') { + // don't try to encrypt/decrypt cache chunks or files in the trash bin + if (isset($pathParts[2]) && ($pathParts[2] === 'cache' || $pathParts[2] === 'files_trashbin')) { return $result; } diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 7a37d2200a4..b3bf34ddb82 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -64,6 +64,9 @@ class Stream { private $publicKey; private $encKeyfile; private $newFile; // helper var, we only need to write the keyfile for new files + private $isLocalTmpFile = false; // do we operate on a local tmp file + private $localTmpFile; // path of local tmp file + /** * @var \OC\Files\View */ @@ -91,13 +94,18 @@ class Stream { $this->rootView = new \OC_FilesystemView('/'); } - $this->session = new \OCA\Encryption\Session($this->rootView); $this->privateKey = $this->session->getPrivateKey(); - // rawPath is relative to the data directory - $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + $normalizedPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + if ($originalFile = Helper::getPathFromTmpFile($normalizedPath)) { + $this->rawPath = $originalFile; + $this->isLocalTmpFile = true; + $this->localTmpFile = $normalizedPath; + } else { + $this->rawPath = $normalizedPath; + } $this->userId = Helper::getUser($this->rawPath); @@ -141,10 +149,14 @@ class Stream { \OCA\Encryption\Helper::redirectToErrorPage($this->session); } - $this->size = $this->rootView->filesize($this->rawPath, $mode); + $this->size = $this->rootView->filesize($this->rawPath); } - $this->handle = $this->rootView->fopen($this->rawPath, $mode); + if ($this->isLocalTmpFile) { + $this->handle = fopen($this->localTmpFile, $mode); + } else { + $this->handle = $this->rootView->fopen($this->rawPath, $mode); + } \OC_FileProxy::$enabled = $proxyStatus; @@ -163,15 +175,26 @@ class Stream { } + /** + * @brief Returns the current position of the file pointer + * @return int position of the file pointer + */ + public function stream_tell() { + return ftell($this->handle); + } + /** * @param $offset * @param int $whence + * @return bool true if fseek was successful, otherwise false */ public function stream_seek($offset, $whence = SEEK_SET) { $this->flush(); - fseek($this->handle, $offset, $whence); + // this wrapper needs to return "true" for success. + // the fseek call itself returns 0 on succeess + return !fseek($this->handle, $offset, $whence); } @@ -477,7 +500,7 @@ class Stream { if ($this->privateKey === false) { // cleanup - if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb') { + if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && !$this->isLocalTmpFile) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; @@ -498,6 +521,7 @@ class Stream { if ( $this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && + $this->isLocalTmpFile === false && $this->size > 0 && $this->unencryptedSize > 0 ) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 434ed225644..8816d4d649a 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -2,9 +2,10 @@ /** * ownCloud * - * @author Sam Tuke, Frank Karlitschek + * @author Sam Tuke, Frank Karlitschek, Bjoern Schiessle * @copyright 2012 Sam Tuke , - * Frank Karlitschek + * Frank Karlitschek , + * Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -101,15 +102,24 @@ class Util { or !$this->view->file_exists($this->publicKeyPath) or !$this->view->file_exists($this->privateKeyPath) ) { - return false; - } else { - return true; - } + } + /** + * @brief check if the users private & public key exists + * @return boolean + */ + public function userKeysExists() { + if ( + $this->view->file_exists($this->privateKeyPath) && + $this->view->file_exists($this->publicKeyPath)) { + return true; + } else { + return false; + } } /** @@ -232,11 +242,9 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - if (isset($row['recovery_enabled'])) { - $recoveryEnabled[] = $row['recovery_enabled']; - } + $row = $result->fetchRow(); + if ($row && isset($row['recovery_enabled'])) { + $recoveryEnabled[] = $row['recovery_enabled']; } } @@ -280,7 +288,7 @@ class Util { $sql = 'UPDATE `*PREFIX*encryption` SET `recovery_enabled` = ? WHERE `uid` = ?'; $args = array( - $enabled, + $enabled ? '1' : '0', $this->userId ); @@ -405,49 +413,6 @@ class Util { } - /** - * @brief Fetch the last lines of a file efficiently - * @note Safe to use on large files; does not read entire file to memory - * @note Derivative of http://tekkie.flashbit.net/php/tail-functionality-in-php - */ - public function tail($filename, $numLines) { - - \OC_FileProxy::$enabled = false; - - $text = ''; - $pos = -1; - $handle = $this->view->fopen($filename, 'r'); - - while ($numLines > 0) { - - --$pos; - - if (fseek($handle, $pos, SEEK_END) !== 0) { - - rewind($handle); - $numLines = 0; - - } elseif (fgetc($handle) === "\n") { - - --$numLines; - - } - - $block_size = (-$pos) % 8192; - if ($block_size === 0 || $numLines === 0) { - - $text = fread($handle, ($block_size === 0 ? 8192 : $block_size)) . $text; - - } - } - - fclose($handle); - - \OC_FileProxy::$enabled = true; - - return $text; - } - /** * @brief Check if a given path identifies an encrypted file * @param string $path @@ -455,20 +420,36 @@ class Util { */ public function isEncryptedPath($path) { - $relPath = Helper::getPathToRealFile($path); + // Disable encryption proxy so data retrieved is in its + // original form + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; - if ($relPath === false) { - $relPath = Helper::stripUserFilesPath($path); + // we only need 24 byte from the last chunk + $data = ''; + $handle = $this->view->fopen($path, 'r'); + if (is_resource($handle)) { + // suppress fseek warining, we handle the case that fseek doesn't + // work in the else branch + if (@fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } else { + // if fseek failed on the storage we create a local copy from the file + // and read this one + fclose($handle); + $localFile = $this->view->getLocalFile($path); + $handle = fopen($localFile, 'r'); + if (is_resource($handle) && fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } + } + fclose($handle); } - $fileKey = Keymanager::getFileKey($this->view, $this, $relPath); - - if ($fileKey === false) { - return false; - } - - return true; + // re-enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + return Crypt::isCatfileContent($data); } /** @@ -514,7 +495,20 @@ class Util { $lastChunckPos = ($lastChunkNr * 8192); // seek to end - fseek($stream, $lastChunckPos); + if (@fseek($stream, $lastChunckPos) === -1) { + // storage doesn't support fseek, we need a local copy + fclose($stream); + $localFile = $this->view->getLocalFile($path); + Helper::addTmpFileToMapper($localFile, $path); + $stream = fopen('crypt://' . $localFile, "r"); + if (fseek($stream, $lastChunckPos) === -1) { + // if fseek also fails on the local storage, than + // there is nothing we can do + fclose($stream); + \OCP\Util::writeLog('Encryption library', 'couldn\'t determine size of "' . $path, \OCP\Util::ERROR); + return $result; + } + } // get the content of the last chunk $lastChunkContent = fread($stream, $lastChunkSize); @@ -761,7 +755,7 @@ class Util { \OC\Files\Filesystem::putFileInfo($relPath, array( 'encrypted' => false, 'size' => $size, - 'unencrypted_size' => $size, + 'unencrypted_size' => 0, 'etag' => $fileInfo['etag'] )); @@ -831,32 +825,35 @@ class Util { // Open enc file handle for binary writing, with same filename as original plain file $encHandle = fopen('crypt://' . $rawPath . '.part', 'wb'); - // Move plain file to a temporary location - $size = stream_copy_to_stream($plainHandle, $encHandle); + if (is_resource($encHandle)) { + // Move plain file to a temporary location + $size = stream_copy_to_stream($plainHandle, $encHandle); - fclose($encHandle); - fclose($plainHandle); + fclose($encHandle); + fclose($plainHandle); - $fakeRoot = $this->view->getRoot(); - $this->view->chroot('/' . $this->userId . '/files'); + $fakeRoot = $this->view->getRoot(); + $this->view->chroot('/' . $this->userId . '/files'); - $this->view->rename($relPath . '.part', $relPath); + $this->view->rename($relPath . '.part', $relPath); - // set timestamp - $this->view->touch($relPath, $timestamp); + // set timestamp + $this->view->touch($relPath, $timestamp); - $this->view->chroot($fakeRoot); + $encSize = $this->view->filesize($relPath); - // Add the file to the cache - \OC\Files\Filesystem::putFileInfo($relPath, array( - 'encrypted' => true, - 'size' => $size, - 'unencrypted_size' => $size, - 'etag' => $fileInfo['etag'] - )); + $this->view->chroot($fakeRoot); - $encryptedFiles[] = $relPath; + // Add the file to the cache + \OC\Files\Filesystem::putFileInfo($relPath, array( + 'encrypted' => true, + 'size' => $encSize, + 'unencrypted_size' => $size, + 'etag' => $fileInfo['etag'] + )); + $encryptedFiles[] = $relPath; + } } // Encrypt legacy encrypted files @@ -973,8 +970,8 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); + $row = $result->fetchRow(); + if ($row) { $path = substr($row['path'], strlen('files')); } } @@ -1254,11 +1251,9 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - if (isset($row['migration_status'])) { - $migrationStatus[] = $row['migration_status']; - } + $row = $result->fetchRow(); + if ($row && isset($row['migration_status'])) { + $migrationStatus[] = $row['migration_status']; } } @@ -1366,59 +1361,32 @@ class Util { } } - /** * @brief go recursively through a dir and collect all files and sub files. * @param string $dir relative to the users files folder * @return array with list of files relative to the users files folder */ public function getAllFiles($dir) { - $result = array(); + $dirList = array($dir); - $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( - $this->userFilesDir . '/' . $dir)); - - // handling for re shared folders - $pathSplit = explode('/', $dir); - - foreach ($content as $c) { - - $sharedPart = $pathSplit[sizeof($pathSplit) - 1]; - $targetPathSplit = array_reverse(explode('/', $c['path'])); - - $path = ''; - - // rebuild path - foreach ($targetPathSplit as $pathPart) { - - if ($pathPart !== $sharedPart) { - - $path = '/' . $pathPart . $path; + while ($dirList) { + $dir = array_pop($dirList); + $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( + $this->userFilesDir . '/' . $dir)); + foreach ($content as $c) { + $usersPath = isset($c['usersPath']) ? $c['usersPath'] : $c['path']; + if ($c['type'] === 'dir') { + $dirList[] = substr($usersPath, strlen("files")); } else { - - break; - + $result[] = substr($usersPath, strlen("files")); } - } - $path = $dir . $path; - - if ($c['type'] === 'dir') { - - $result = array_merge($result, $this->getAllFiles($path)); - - } else { - - $result[] = $path; - - } } return $result; - } /** @@ -1438,9 +1406,7 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - } + $row = $result->fetchRow(); } return $row; @@ -1464,9 +1430,7 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - } + $row = $result->fetchRow(); } return $row; @@ -1485,18 +1449,16 @@ class Util { $result = $query->execute(array($id)); - $source = array(); + $source = null; if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $source = $result->fetchRow(); - } + $source = $result->fetchRow(); } $fileOwner = false; - if (isset($source['parent'])) { + if ($source && isset($source['parent'])) { $parent = $source['parent']; @@ -1506,16 +1468,14 @@ class Util { $result = $query->execute(array($parent)); - $item = array(); + $item = null; if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $item = $result->fetchRow(); - } + $item = $result->fetchRow(); } - if (isset($item['parent'])) { + if ($item && isset($item['parent'])) { $parent = $item['parent']; diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index a4ed89b8a49..1b4239d82cd 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -2,7 +2,7 @@

t( 'Encryption' ) ); ?>

- +

' . $scrollback . '
'; } -} +} \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php index 766c6e7ebf4..9f481fc3ccb 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php @@ -37,17 +37,9 @@ * @author Jim Wigginton * @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 * @@ -249,6 +241,22 @@ class File_ASN1 { FILE_ASN1_TYPE_VISIBLE_STRING => 1, ); + /** + * Default Constructor. + * + * @access public + */ + function File_ASN1() + { + static $static_init = null; + if (!$static_init) { + $static_init = true; + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + } + } + /** * Parse BER-encoding * @@ -304,12 +312,12 @@ class File_ASN1 { } while ( $loop ); } - // Length, as discussed in 8.1.3 of X.690-0207.pdf#page=13 + // Length, as discussed in paragraph 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 + // immediately available." -- paragraph 8.1.3.2.c //if ( !$constructed ) { // return false; //} @@ -319,11 +327,15 @@ class File_ASN1 { // support it up to four. $length&= 0x7F; $temp = $this->_string_shift($encoded, $length); + // tags of indefinite length don't really have a header length; this length includes the tag + $current+= array('headerlength' => $length + 2); $start+= $length; extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } else { + $current+= array('headerlength' => 2); } - // 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 + // End-of-content, see paragraphs 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; } @@ -349,6 +361,7 @@ class File_ASN1 { 'content' => $constructed ? $this->_decode_ber($content, $start) : $content, 'length' => $length + $start - $current['start'] ) + $current; + $start+= $length; continue 2; } @@ -357,7 +370,7 @@ class File_ASN1 { // decode UNIVERSAL tags switch ($tag) { case FILE_ASN1_TYPE_BOOLEAN: - // "The contents octets shall consist of a single octet." -- 8.2.1 + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 //if (strlen($content) != 1) { // return false; //} @@ -410,7 +423,7 @@ class File_ASN1 { } break; case FILE_ASN1_TYPE_NULL: - // "The contents octets shall not contain any octets." -- 8.8.2 + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 //if (strlen($content)) { // return false; //} @@ -441,7 +454,7 @@ class File_ASN1 { /* 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) + -- X.690-0207.pdf#page=23 (paragraph 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 */ @@ -487,12 +500,15 @@ class File_ASN1 { * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * + * "Special" mappings may be applied on a per tag-name basis via $special. + * * @param Array $decoded * @param Array $mapping + * @param Array $special * @return Array * @access public */ - function asn1map($decoded, $mapping) + function asn1map($decoded, $mapping, $special = array()) { if (isset($mapping['explicit'])) { $decoded = $decoded['content'][0]; @@ -506,7 +522,7 @@ class File_ASN1 { } $inmap = $this->ANYmap[$intype]; if (is_string($inmap)) { - return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping)); + return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); } break; case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: @@ -514,15 +530,18 @@ class File_ASN1 { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: - $value = $this->asn1map($decoded, $option); + $value = $this->asn1map($decoded, $option, $special); break; case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: - $v = $this->asn1map($decoded, $option); + $v = $this->asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; } } if (isset($value)) { + if (isset($special[$key])) { + $value = call_user_func($special[$key], $value); + } return array($key => $value); } } @@ -547,7 +566,7 @@ class File_ASN1 { if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { - if (($map[] = $this->asn1map($content, $child)) === NULL) { + if (($map[] = $this->asn1map($content, $child, $special)) === NULL) { return NULL; } } @@ -591,12 +610,15 @@ class File_ASN1 { if ($maymatch) { // Attempt submapping. - $candidate = $this->asn1map($temp, $child); + $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== NULL; } if ($maymatch) { // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } $map[$key] = $candidate; $i++; } elseif (isset($child['default'])) { @@ -617,7 +639,7 @@ class File_ASN1 { if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { - if (($map[] = $this->asn1map($content, $child)) === NULL) { + if (($map[] = $this->asn1map($content, $child, $special)) === NULL) { return NULL; } } @@ -660,7 +682,7 @@ class File_ASN1 { if ($maymatch) { // Attempt submapping. - $candidate = $this->asn1map($temp, $child); + $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== NULL; } @@ -669,6 +691,9 @@ class File_ASN1 { } // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } $map[$key] = $candidate; break; } @@ -761,18 +786,30 @@ class File_ASN1 { * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function * an ASN.1 compiler. * + * "Special" mappings can be applied via $special. + * * @param String $source * @param String $mapping * @param Integer $idx * @return String * @access public */ - function encodeDER($source, $mapping) + function encodeDER($source, $mapping, $special = array()) { $this->location = array(); - return $this->_encode_der($source, $mapping); + return $this->_encode_der($source, $mapping, NULL, $special); } + /** + * ASN.1 Encode (Helper function) + * + * @param String $source + * @param Array $mapping + * @param Integer $idx + * @param Array $special + * @return String + * @access private + */ /** * ASN.1 Encode (Helper function) * @@ -782,7 +819,7 @@ class File_ASN1 { * @return String * @access private */ - function _encode_der($source, $mapping, $idx = NULL) + function _encode_der($source, $mapping, $idx = NULL, $special = array()) { if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { return $source->element; @@ -794,6 +831,9 @@ class File_ASN1 { } if (isset($idx)) { + if (isset($special[$idx])) { + $source = call_user_func($special[$idx], $source); + } $this->location[] = $idx; } @@ -810,7 +850,7 @@ class File_ASN1 { $child = $mapping['children']; foreach ($source as $content) { - $temp = $this->_encode_der($content, $child); + $temp = $this->_encode_der($content, $child, NULL, $special); if ($temp === false) { return false; } @@ -827,7 +867,7 @@ class File_ASN1 { continue; } - $temp = $this->_encode_der($source[$key], $child, $key); + $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } @@ -868,7 +908,7 @@ class File_ASN1 { continue; } - $temp = $this->_encode_der($source[$key], $child, $key); + $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } @@ -914,6 +954,9 @@ class File_ASN1 { $value = new Math_BigInteger($value); $value = $value->toBytes(true); } + if (!strlen($value)) { + $value = chr(0); + } break; case FILE_ASN1_TYPE_UTC_TIME: case FILE_ASN1_TYPE_GENERALIZED_TIME: @@ -987,19 +1030,19 @@ class File_ASN1 { switch (true) { case !isset($source): - return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping); + return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, NULL, $special); 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); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, NULL, $special); case is_float($source): - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, NULL, $special); case is_bool($source): - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, NULL, $special); 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); + return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, NULL, $special); } } @@ -1015,7 +1058,7 @@ class File_ASN1 { user_error('No filters defined for ' . implode('/', $loc)); return false; } - return $this->_encode_der($source, $filters + $mapping); + return $this->_encode_der($source, $filters + $mapping, NULL, $special); case FILE_ASN1_TYPE_NULL: $value = ''; break; @@ -1055,7 +1098,7 @@ class File_ASN1 { * 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. + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private * @param Integer $length diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php index 278da62e262..0b4e291361e 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php @@ -40,22 +40,22 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ - * @link htp://phpseclib.sourceforge.net + * @link http://phpseclib.sourceforge.net */ /** * Include File_ASN1 */ if (!class_exists('File_ASN1')) { - require_once('File/ASN1.php'); + require_once('ASN1.php'); } /** * Flag to only accept signatures signed by certificate authorities * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * * @access public - * @see File_X509::validateSignature() */ define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); @@ -306,6 +306,10 @@ class File_X509 { */ function File_X509() { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + // Explicitly Tagged Module, 1988 Syntax // http://tools.ietf.org/html/rfc5280#appendix-A.1 @@ -1436,18 +1440,7 @@ class File_X509 { $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; - } + $cert = $this->_extractBER($cert); if ($cert === false) { $this->currentCert = false; @@ -1569,7 +1562,7 @@ class File_X509 { corresponding to the extension type identified by extnID */ $map = $this->_getMapping($id); if (!is_bool($map)) { - $mapped = $asn1->asn1map($decoded[0], $map); + $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP'))); $value = $mapped === false ? $decoded[0] : $mapped; if ($id == 'id-ce-certificatePolicies') { @@ -1651,7 +1644,7 @@ class File_X509 { unset($extensions[$i]); } } else { - $temp = $asn1->encodeDER($value, $map); + $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); $value = base64_encode($temp); } } @@ -2001,16 +1994,19 @@ class File_X509 { * 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 * + * By default returns false for self-signed certs. Call validateSignature(false) to make this support + * self-signed. + * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * - * @param Integer $options optional + * @param Boolean $caonly optional * @access public * @return Mixed */ - function validateSignature($options = 0) + function validateSignature($caonly = true) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { - return 0; + return NULL; } /* TODO: @@ -2048,10 +2044,10 @@ class File_X509 { } } } - if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + if (count($this->CAs) == $i && $caonly) { return false; } - } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + } elseif (!isset($signingCert) || $caonly) { return false; } return $this->_validateSignature( @@ -2182,6 +2178,36 @@ class File_X509 { } } + /** + * Decodes an IP address + * + * Takes in a base64 encoded "blob" and returns a human readable IP address + * + * @param String $ip + * @access private + * @return String + */ + function _decodeIP($ip) + { + $ip = base64_decode($ip); + list(, $ip) = unpack('N', $ip); + return long2ip($ip); + } + + /** + * Encodes an IP address + * + * Takes a human readable IP address into a base64-encoded "blob" + * + * @param String $ip + * @access private + * @return String + */ + function _encodeIP($ip) + { + return base64_encode(pack('N', ip2long($ip))); + } + /** * "Normalizes" a Distinguished Name property * @@ -2207,7 +2233,7 @@ class File_X509 { case 'commonname': case 'cn': return 'id-at-commonName'; - case 'id-at-stateorprovinceName': + case 'id-at-stateorprovincename': case 'stateorprovincename': case 'state': case 'province': @@ -2630,9 +2656,9 @@ class File_X509 { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): - return $this->getDNProp($propname, $this->currentCert['tbsCertificate']['issuer'], $withType); + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); case isset($this->currentCert['tbsCertList']): - return $this->getDNProp($propname, $this->currentCert['tbsCertList']['issuer'], $withType); + return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); } return false; @@ -2656,7 +2682,7 @@ class File_X509 { 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 $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); } return false; @@ -2718,6 +2744,7 @@ class File_X509 { */ function setPublicKey($key) { + $key->setPublicKey(); $this->publicKey = $key; } @@ -2804,11 +2831,7 @@ class File_X509 { $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; - } + $csr = $this->_extractBER($csr); $orig = $csr; if ($csr === false) { @@ -2917,51 +2940,51 @@ class File_X509 { * @access public * @return Mixed */ - function loadSPKAC($csr) + function loadSPKAC($spkac) { - if (is_array($csr) && isset($csr['publicKeyAndChallenge'])) { + if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); - $this->currentCert = $csr; - return $csr; + $this->currentCert = $spkac; + return $spkac; } // 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_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $spkac); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; if ($temp != false) { - $csr = $temp; + $spkac = $temp; } - $orig = $csr; + $orig = $spkac; - if ($csr === false) { + if ($spkac === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); - $decoded = $asn1->decodeBER($csr); + $decoded = $asn1->decodeBER($spkac); if (empty($decoded)) { $this->currentCert = false; return false; } - $csr = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); + $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); - if (!isset($csr) || $csr === false) { + if (!isset($spkac) || $spkac === 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']; + $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; + $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']; $key = $this->_reformatKey($algorithm, $key); switch ($algorithm) { @@ -2978,9 +3001,9 @@ class File_X509 { } $this->currentKeyIdentifier = NULL; - $this->currentCert = $csr; + $this->currentCert = $spkac; - return $csr; + return $spkac; } /** @@ -3000,11 +3023,7 @@ class File_X509 { $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; - } + $crl = $this->_extractBER($crl); $orig = $crl; if ($crl === false) { @@ -3209,9 +3228,29 @@ class File_X509 { $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); } + $altName = array(); + if (isset($subject->domains) && count($subject->domains) > 1) { - $this->setExtension('id-ce-subjectAltName', - array_map(array('File_X509', '_dnsName'), $subject->domains)); + $altName = array_map(array('File_X509', '_dnsName'), $subject->domains); + } + + if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { + // should an IP address appear as the CN if no domain name is specified? idk + //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1); + $ipAddresses = array(); + foreach ($subject->ipAddresses as $ipAddress) { + $encoded = $subject->_ipAddress($ipAddress); + if ($encoded !== false) { + $ipAddresses[] = $encoded; + } + } + if (count($ipAddresses)) { + $altName = array_merge($altName, $ipAddresses); + } + } + + if (!empty($altName)) { + $this->setExtension('id-ce-subjectAltName', $altName); } if ($this->caFlag) { @@ -4012,12 +4051,29 @@ class File_X509 { case !is_object($key): return false; case strtolower(get_class($key)) == 'file_asn1_element': + // Assume the element is a bitstring-packed key. $asn1 = new File_ASN1(); - $decoded = $asn1->decodeBER($cert); + $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } - $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + $raw = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + if (empty($raw)) { + return false; + } + $raw = base64_decode($raw); + // If the key is private, compute identifier from its corresponding public key. + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $key = new Crypt_RSA(); + if (!$key->loadKey($raw)) { + return false; // Not an unencrypted RSA key. + } + if ($key->getPrivateKey() !== false) { // If private. + return $this->computeKeyIdentifier($key, $method); + } + $key = $raw; // Is a public key. break; case strtolower(get_class($key)) == 'file_x509': if (isset($key->publicKey)) { @@ -4036,9 +4092,7 @@ class File_X509 { } // If in PEM format, convert to binary. - if (preg_match('#^-----BEGIN #', $key)) { - $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key)); - } + $key = $this->_extractBER($key); // Now we have the key string: compute its sha-1 sum. if (!class_exists('Crypt_Hash')) { @@ -4094,6 +4148,23 @@ class File_X509 { $this->setDNProp('id-at-commonName', $this->domains[0]); } + /** + * Set the IP Addresses's which the cert is to be valid for + * + * @access public + * @param String $ipAddress optional + */ + function setIPAddress() + { + $this->ipAddresses = func_get_args(); + /* + if (!isset($this->domains)) { + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->ipAddresses[0]); + } + */ + } + /** * Helper function to build domain array * @@ -4106,6 +4177,20 @@ class File_X509 { return array('dNSName' => $domain); } + /** + * Helper function to build IP Address array + * + * (IPv6 is not currently supported) + * + * @access private + * @param String $address + * @return Array + */ + function _iPAddress($address) + { + return array('iPAddress' => $address); + } + /** * Get the index of a revoked certificate. * @@ -4320,4 +4405,31 @@ class File_X509 { return false; } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param String $str + * @return String + */ + function _extractBER($str) + { + /* + 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 preceding the -----BEGIN CERTIFICATE----- line: + + Bag Attributes + localKeyID: 01 00 00 00 + subject=/O=organization/OU=org unit/CN=common name + issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } } diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php index d048cb032c5..e40433de5bd 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php @@ -70,7 +70,6 @@ * @author Jim Wigginton * @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 */ @@ -162,16 +161,6 @@ define('MATH_BIGINTEGER_MODE_BCMATH', 2); 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 * @@ -232,7 +221,7 @@ class Math_BigInteger { var $bitmask = false; /** - * Mode independant value used for serialization. + * Mode independent 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, @@ -246,20 +235,20 @@ class Math_BigInteger { var $hex; /** - * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers. + * Converts base-2, base-10, base-16, and binary strings (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: * - * toString(); // outputs 50 - * ?> + * ?> * * * @param optional $x base-10 number or base-$base number if $base set. @@ -283,7 +272,64 @@ class Math_BigInteger { } if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + ob_start(); + phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + $versions = array(); + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + break; + default: + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + } + + if (!defined('PHP_INT_SIZE')) { + define('PHP_INT_SIZE', 4); + } + + if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) { + switch (PHP_INT_SIZE) { + case 8: // use 64-bit integers if int size is 8 bytes + define('MATH_BIGINTEGER_BASE', 31); + define('MATH_BIGINTEGER_BASE_FULL', 0x80000000); + define('MATH_BIGINTEGER_MAX_DIGIT', 0x7FFFFFFF); + define('MATH_BIGINTEGER_MSB', 0x40000000); + // 10**9 is the closest we can get to 2**31 without passing it + define('MATH_BIGINTEGER_MAX10', 1000000000); + define('MATH_BIGINTEGER_MAX10_LEN', 9); + // the largest digit that may be used in addition / subtraction + define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62)); + break; + //case 4: // use 64-bit floats if int size is 4 bytes + default: + define('MATH_BIGINTEGER_BASE', 26); + define('MATH_BIGINTEGER_BASE_FULL', 0x4000000); + define('MATH_BIGINTEGER_MAX_DIGIT', 0x3FFFFFF); + define('MATH_BIGINTEGER_MSB', 0x2000000); + // 10**7 is the closest to 2**26 without passing it + define('MATH_BIGINTEGER_MAX10', 10000000); + define('MATH_BIGINTEGER_MAX10_LEN', 7); + // 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. + define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52)); + } } switch ( MATH_BIGINTEGER_MODE ) { @@ -338,7 +384,7 @@ class Math_BigInteger { // 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)); + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE)); } } @@ -390,7 +436,10 @@ class Math_BigInteger { break; case 10: case -10: - $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x); + // (?value = (string) $x; + $this->value = $x === '-' ? '0' : (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); + $multiplier->value = array(MATH_BIGINTEGER_MAX10); 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); - + $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 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); + $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256)); + $x = substr($x, MATH_BIGINTEGER_MAX10_LEN); } $this->value = $temp->value; @@ -543,7 +590,7 @@ class Math_BigInteger { $temp = $this->copy(); for ($i = count($temp->value) - 2; $i >= 0; --$i) { - $temp->_base256_lshift($result, 26); + $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE); $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } @@ -659,11 +706,11 @@ class Math_BigInteger { $temp->is_negative = false; $divisor = new Math_BigInteger(); - $divisor->value = array(10000000); // eg. 10**7 + $divisor->value = array(MATH_BIGINTEGER_MAX10); $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 = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result; } $result = ltrim($result, '0'); if (empty($result)) { @@ -874,25 +921,25 @@ class Math_BigInteger { $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; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry; + $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = (int) ($sum / 0x4000000); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $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; + $carry = $sum >= MATH_BIGINTEGER_BASE_FULL; + $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; // ie. let $i = $j since we've just done $value[$i] } if ($carry) { - for (; $value[$i] == 0x3FFFFFF; ++$i) { + for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) { $value[$i] = 0; } ++$value[$i]; @@ -1010,26 +1057,26 @@ class Math_BigInteger { $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; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $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; + $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = (int) ($sum / 0x4000000); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $x_value[$i] = (int) ($sum - 0x4000000 * $temp); + $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $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; + $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { - $x_value[$i] = 0x3FFFFFF; + $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT; } --$x_value[$i]; } @@ -1162,8 +1209,8 @@ class Math_BigInteger { 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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$j] = $carry; @@ -1175,8 +1222,8 @@ class Math_BigInteger { 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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$k] = $carry; @@ -1263,14 +1310,14 @@ class Math_BigInteger { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; - $carry = (int) ($temp / 0x4000000); - $square_value[$i2] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } // the following line can yield values larger 2**15. at this point, PHP should switch @@ -1418,7 +1465,7 @@ class Math_BigInteger { // 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) { + for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) { $msb <<= 1; } $x->_lshift($shift); @@ -1465,10 +1512,10 @@ class Math_BigInteger { $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { - $quotient_value[$q_index] = 0x3FFFFFF; + $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT; } else { $quotient_value[$q_index] = (int) ( - ($x_window[0] * 0x4000000 + $x_window[1]) + ($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1]) / $y_window[0] ); @@ -1536,7 +1583,7 @@ class Math_BigInteger { $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { - $temp = 0x4000000 * $carry + $dividend[$i]; + $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i]; $result[$i] = (int) ($temp / $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } @@ -1754,7 +1801,7 @@ class Math_BigInteger { $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_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT); } $e_length = strlen($e_bits); @@ -2089,7 +2136,7 @@ class Math_BigInteger { 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 = $this->_add($result, false, $corrector_value, false); $result = $result[MATH_BIGINTEGER_VALUE]; } @@ -2112,6 +2159,7 @@ class Math_BigInteger { * @param Boolean $x_negative * @param Array $y_value * @param Boolean $y_negative + * @param Integer $stop * @return Array * @access private */ @@ -2148,8 +2196,8 @@ class Math_BigInteger { 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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($j < $stop) { @@ -2164,8 +2212,8 @@ class Math_BigInteger { 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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($k < $stop) { @@ -2213,7 +2261,7 @@ class Math_BigInteger { 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 = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $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); @@ -2265,9 +2313,9 @@ class Math_BigInteger { $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 = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; - $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $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); @@ -2325,15 +2373,15 @@ class Math_BigInteger { * @param Array $x * @return Integer */ - function _modInverse67108864($x) // 2**26 == 67108864 + function _modInverse67108864($x) // 2**26 == 67,108,864 { $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; + $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26 + return $result & MATH_BIGINTEGER_MAX_DIGIT; } /** @@ -2402,12 +2450,12 @@ class Math_BigInteger { } /** - * Calculates the greatest common divisor and Bzout's identity. + * Calculates the greatest common divisor and Bezout'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 + * Say you have 693 and 609. The GCD is 21. Bezout'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. + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * Here's an example: * @@ -2604,8 +2652,8 @@ class Math_BigInteger { * * 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. + * @param Math_BigInteger $y + * @return Integer < 0 if $this is less than $y; > 0 if $this is greater than $y, 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. @@ -2684,9 +2732,8 @@ class Math_BigInteger { * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * - * @param Math_BigInteger $x + * @param Integer $bits * @access public - * @return Math_BigInteger */ function setPrecision($bits) { @@ -2736,7 +2783,7 @@ class Math_BigInteger { $result->value = array_slice($result->value, 0, $length); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $result->value[$i] & $x->value[$i]; + $result->value[$i]&= $x->value[$i]; } return $this->_normalize($result); @@ -2772,11 +2819,11 @@ class Math_BigInteger { $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); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $this->value[$i] | $x->value[$i]; + $result->value[$i]|= $x->value[$i]; } return $this->_normalize($result); @@ -2812,11 +2859,11 @@ class Math_BigInteger { $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); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $this->value[$i] ^ $x->value[$i]; + $result->value[$i]^= $x->value[$i]; } return $this->_normalize($result); @@ -3004,6 +3051,37 @@ class Math_BigInteger { { } + /** + * Generates a random BigInteger + * + * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not. + * + * @param Integer $length + * @return Math_BigInteger + * @access private + */ + function _random_number_helper($size) + { + $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)); + } + } + + return new Math_BigInteger($random, 256); + } + /** * Generate a random number * @@ -3033,48 +3111,45 @@ class Math_BigInteger { $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)); - } + static $one; + if (!isset($one)) { + $one = new Math_BigInteger(1); } - $fragment = new Math_BigInteger($random, 256); - $leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ? - ord($max[0]) - 1 : ord($max[0]); + $max = $max->subtract($min->subtract($one)); + $size = strlen(ltrim($max->toBytes(), chr(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); + /* + doing $random % $max doesn't work because some numbers will be more likely to occur than others. + eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 + would produce 5 whereas the only value of random that could produce 139 would be 139. ie. + not all numbers would be equally likely. some would be more likely than others. + + creating a whole new random number until you find one that is within the range doesn't work + because, for sufficiently small ranges, the likelihood that you'd get a number within that range + would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability + would be pretty high that $random would be greater than $max. + + phpseclib works around this using the technique described here: + + http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string + */ + $random_max = new Math_BigInteger(chr(1) . str_repeat("\0", $size), 256); + $random = $this->_random_number_helper($size); + + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + + while ($random->compare($max_multiple) >= 0) { + $random = $random->subtract($max_multiple); + $random_max = $random_max->subtract($max_multiple); + $random = $random->bitwise_leftShift(8); + $random = $random->add($this->_random_number_helper(1)); + $random_max = $random_max->bitwise_leftShift(8); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); } - - $random = new Math_BigInteger($msb . $random, 256); + list(, $random) = $random->divide($max); return $this->_normalize($random->add($min)); } @@ -3094,10 +3169,18 @@ class Math_BigInteger { */ function randomPrime($min = false, $max = false, $timeout = false) { + if ($min === false) { + $min = new Math_BigInteger(0); + } + + if ($max === false) { + $max = new Math_BigInteger(0x7FFFFFFF); + } + $compare = $max->compare($min); if (!$compare) { - return $min; + return $min->isPrime() ? $min : false; } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; @@ -3105,36 +3188,6 @@ class Math_BigInteger { $min = $temp; } - // gmp_nextprime() requires PHP 5 >= 5.2.0 per . - 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); @@ -3144,6 +3197,23 @@ class Math_BigInteger { $start = time(); $x = $this->random($min, $max); + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per . + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { + $p = new Math_BigInteger(); + $p->value = gmp_nextprime($x->value); + + if ($p->compare($max) <= 0) { + return $p; + } + + if (!$min->equals($x)) { + $x = $x->subtract($one); + } + + return $x->randomPrime($min, $x); + } + if ($x->equals($two)) { return $x; } @@ -3375,16 +3445,16 @@ class Math_BigInteger { return; } - $num_digits = (int) ($shift / 26); - $shift %= 26; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; $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); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL); } if ( $carry ) { @@ -3410,9 +3480,9 @@ class Math_BigInteger { return; } - $num_digits = (int) ($shift / 26); - $shift %= 26; - $carry_shift = 26 - $shift; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; + $carry_shift = MATH_BIGINTEGER_BASE - $shift; $carry_mask = (1 << $shift) - 1; if ( $num_digits ) { @@ -3485,6 +3555,7 @@ class Math_BigInteger { * * Removes leading zeros * + * @param Array $value * @return Math_BigInteger * @access private */ @@ -3630,4 +3701,4 @@ class Math_BigInteger { $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/SCP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SCP.php new file mode 100644 index 00000000000..88180cac67a --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SCP.php @@ -0,0 +1,362 @@ + + * login('username', 'password')) { + * exit('bad login'); + * } + + * $scp = new Net_SCP($ssh); + * $scp->put('abcd', str_repeat('x', 1024*1024)); + * ?> + * + * + * 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_SCP + * @author Jim Wigginton + * @copyright MMX Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access public + * @see Net_SCP::put() + */ +/** + * Reads data from a local file. + */ +define('NET_SCP_LOCAL_FILE', 1); +/** + * Reads data from a string. + */ +define('NET_SCP_STRING', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see Net_SCP::_send() + * @see Net_SCP::_receive() + */ +/** + * SSH1 is being used. + */ +define('NET_SCP_SSH1', 1); +/** + * SSH2 is being used. + */ +define('NET_SCP_SSH2', 2); +/**#@-*/ + +/** + * Pure-PHP implementations of SCP. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Net_SCP + */ +class Net_SCP { + /** + * SSH Object + * + * @var Object + * @access private + */ + var $ssh; + + /** + * Packet Size + * + * @var Integer + * @access private + */ + var $packet_size; + + /** + * Mode + * + * @var Integer + * @access private + */ + var $mode; + + /** + * Default Constructor. + * + * Connects to an SSH server + * + * @param String $host + * @param optional Integer $port + * @param optional Integer $timeout + * @return Net_SCP + * @access public + */ + function Net_SCP($ssh) + { + if (!is_object($ssh)) { + return; + } + + switch (strtolower(get_class($ssh))) { + case'net_ssh2': + $this->mode = NET_SCP_SSH2; + break; + case 'net_ssh1': + $this->packet_size = 50000; + $this->mode = NET_SCP_SSH1; + break; + default: + return; + } + + $this->ssh = $ssh; + } + + /** + * Uploads a file to the SCP server. + * + * By default, Net_SCP::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_SCP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to NET_SCP_LOCAL_FILE will change the above behavior. With NET_SCP_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 + * @param optional Callable $callback + * @return Boolean + * @access public + */ + function put($remote_file, $data, $mode = NET_SCP_STRING, $callback = null) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -t ' . $remote_file, false)) { // -t = to + return false; + } + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + if ($this->mode == NET_SCP_SSH2) { + $this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC]; + } + + $remote_file = basename($remote_file); + + if ($mode == NET_SCP_STRING) { + $size = strlen($data); + } else { + if (!is_file($data)) { + user_error("$data is not a valid file", E_USER_NOTICE); + return false; + } + + $fp = @fopen($data, 'rb'); + if (!$fp) { + fclose($fp); + return false; + } + $size = filesize($data); + } + + $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + $sent = 0; + while ($sent < $size) { + $temp = $mode & NET_SCP_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); + $this->_send($temp); + $sent+= strlen($temp); + + if (is_callable($callback)) { + $callback($sent); + } + } + $this->_close(); + + if ($mode != NET_SCP_STRING) { + fclose($fp); + } + + return true; + } + + /** + * Downloads a file from the SCP 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) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -f ' . $remote_file, false)) { // -f = from + return false; + } + + $this->_send("\0"); + + if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { + return false; + } + + $this->_send("\0"); + + $size = 0; + + if ($local_file !== false) { + $fp = @fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } + + $content = ''; + while ($size < $info['size']) { + $data = $this->_receive(); + // SCP usually seems to split stuff out into 16k chunks + $size+= strlen($data); + + if ($local_file === false) { + $content.= $data; + } else { + fputs($fp, $data); + } + } + + $this->_close(); + + if ($local_file !== false) { + fclose($fp); + return true; + } + + return $content; + } + + /** + * Sends a packet to an SSH server + * + * @param String $data + * @access private + */ + function _send($data) + { + switch ($this->mode) { + case NET_SCP_SSH2: + $this->ssh->_send_channel_packet(NET_SSH2_CHANNEL_EXEC, $data); + break; + case NET_SCP_SSH1: + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); + $this->ssh->_send_binary_packet($data); + } + } + + /** + * Receives a packet from an SSH server + * + * @return String + * @access private + */ + function _receive() + { + switch ($this->mode) { + case NET_SCP_SSH2: + return $this->ssh->_get_channel_packet(NET_SSH2_CHANNEL_EXEC, true); + case NET_SCP_SSH1: + if (!$this->ssh->bitmap) { + return false; + } + while (true) { + $response = $this->ssh->_get_binary_packet(); + switch ($response[NET_SSH1_RESPONSE_TYPE]) { + case NET_SSH1_SMSG_STDOUT_DATA: + extract(unpack('Nlength', $response[NET_SSH1_RESPONSE_DATA])); + return $this->ssh->_string_shift($response[NET_SSH1_RESPONSE_DATA], $length); + case NET_SSH1_SMSG_STDERR_DATA: + break; + case NET_SSH1_SMSG_EXITSTATUS: + $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); + fclose($this->ssh->fsock); + $this->ssh->bitmap = 0; + return false; + default: + user_error('Unknown packet received', E_USER_NOTICE); + return false; + } + } + } + } + + /** + * Closes the connection to an SSH server + * + * @access private + */ + function _close() + { + switch ($this->mode) { + case NET_SCP_SSH2: + $this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC); + break; + case NET_SCP_SSH1: + $this->ssh->disconnect(); + } + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php index 8db087d3d99..5356ffbf77a 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php @@ -58,7 +58,7 @@ * Include Net_SSH2 */ if (!class_exists('Net_SSH2')) { - require_once('Net/SSH2.php'); + require_once('SSH2.php'); } /**#@+ @@ -88,7 +88,7 @@ define('NET_SFTP_LOG_REALTIME', 3); * @see Net_SSH2::_get_channel_packet() * @access private */ -define('NET_SFTP_CHANNEL', 2); +define('NET_SFTP_CHANNEL', 0x100); /**#@+ * @access public @@ -97,16 +97,20 @@ define('NET_SFTP_CHANNEL', 2); /** * Reads data from a local file. */ -define('NET_SFTP_LOCAL_FILE', 1); +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); +define('NET_SFTP_STRING', 2); /** * Resumes an upload */ -define('NET_SFTP_RESUME', 4); +define('NET_SFTP_RESUME', 4); +/** + * Append a local file to an already existing remote file + */ +define('NET_SFTP_RESUME_START', 8); /**#@-*/ /** @@ -225,15 +229,6 @@ class Net_SFTP extends Net_SSH2 { */ var $sftp_errors = array(); - /** - * File Type - * - * @see Net_SFTP::_parseLongname() - * @var Integer - * @access private - */ - var $fileType = 0; - /** * Directory Cache * @@ -248,6 +243,16 @@ class Net_SFTP extends Net_SSH2 { */ var $dirs = array(); + /** + * Max SFTP Packet Size + * + * @see Net_SFTP::Net_SFTP() + * @see Net_SFTP::get() + * @var Array + * @access private + */ + var $max_sftp_packet; + /** * Default Constructor. * @@ -262,6 +267,9 @@ class Net_SFTP extends Net_SSH2 { function Net_SFTP($host, $port = 22, $timeout = 10) { parent::Net_SSH2($host, $port, $timeout); + + $this->max_sftp_packet = 1 << 15; + $this->packet_types = array( 1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', @@ -306,7 +314,30 @@ class Net_SFTP extends Net_SSH2 { 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', - 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED' + 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', + 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', + 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', + 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', + 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', + 13 => 'NET_SFTP_STATUS_NO_MEDIA', + 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', + 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', + 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', + 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', + 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', + 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', + 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', + 21 => 'NET_SFTP_STATUS_LINK_LOOP', + 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', + 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', + 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', + 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', + 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', + 27 => 'NET_SFTP_STATUS_DELETE_PENDING', + 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', + 29 => 'NET_SFTP_STATUS_OWNER_INVALID', + 30 => 'NET_SFTP_STATUS_GROUP_INVALID', + 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' ); // 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 @@ -329,7 +360,8 @@ class Net_SFTP extends Net_SSH2 { 0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000004 => 'NET_SFTP_OPEN_APPEND', 0x00000008 => 'NET_SFTP_OPEN_CREATE', - 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE' + 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', + 0x00000020 => 'NET_SFTP_OPEN_EXCL' ); // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 // see Net_SFTP::_parseLongname() for an explanation @@ -337,7 +369,14 @@ class Net_SFTP extends Net_SSH2 { 1 => 'NET_SFTP_TYPE_REGULAR', 2 => 'NET_SFTP_TYPE_DIRECTORY', 3 => 'NET_SFTP_TYPE_SYMLINK', - 4 => 'NET_SFTP_TYPE_SPECIAL' + 4 => 'NET_SFTP_TYPE_SPECIAL', + 5 => 'NET_SFTP_TYPE_UNKNOWN', + // the followin types were first defined for use in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + 6 => 'NET_SFTP_TYPE_SOCKET', + 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', + 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', + 9 => 'NET_SFTP_TYPE_FIFO' ); $this->_define_array( $this->packet_types, @@ -346,6 +385,10 @@ class Net_SFTP extends Net_SSH2 { $this->open_flags, $this->file_types ); + + if (!defined('NET_SFTP_QUEUE_SIZE')) { + define('NET_SFTP_QUEUE_SIZE', 50); + } } /** @@ -356,13 +399,14 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access public */ - function login($username, $password = '') + function login($username) { - if (!parent::login($username, $password)) { + $args = func_get_args(); + if (!call_user_func_array(array('Net_SSH2', 'login'), $args)) { return false; } - $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size; + $this->window_size_server_to_client[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); @@ -388,7 +432,24 @@ class Net_SFTP extends Net_SSH2 { $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); if ($response === false) { - return false; + // from PuTTY's psftp.exe + $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . + "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . + "exec sftp-server"; + // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does + // is redundant + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('exec'), 'exec', 1, strlen($command), $command); + 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; @@ -460,7 +521,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - $this->pwd = $this->_realpath('.', false); + $this->pwd = $this->_realpath('.'); $this->_save_dir($this->pwd); @@ -485,7 +546,8 @@ class Net_SFTP extends Net_SSH2 { * @param optional Integer $status * @access public */ - function _logError($response, $status = -1) { + function _logError($response, $status = -1) + { if ($status == -1) { extract(unpack('Nstatus', $this->_string_shift($response, 4))); } @@ -507,98 +569,57 @@ class Net_SFTP extends Net_SSH2 { * the absolute (canonicalized) path. * * @see Net_SFTP::chdir() - * @param String $dir + * @param String $path * @return Mixed * @access private */ - function _realpath($dir, $check_dir = true) + function _realpath($path) { - 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'); + if ($this->pwd === false) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { 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))); + return $this->_string_shift($response, $length); + 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; + if ($path[0] != '/') { + $path = $this->pwd . '/' . $path; } - return $realpath; + $path = explode('/', $path); + $new = array(); + foreach ($path as $dir) { + if (!strlen($dir)) { + continue; + } + switch ($dir) { + case '..': + array_pop($new); + case '.': + break; + default: + $new[] = $dir; + } + } + + return '/' . implode('/', $new); } /** @@ -618,18 +639,18 @@ class Net_SFTP extends Net_SSH2 { $dir.= '/'; } + $dir = $this->_realpath($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; - } + // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us + // the currently logged in user has the appropriate permissions or not. maybe you could see if + // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy + // way to get those with SFTP if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { return false; @@ -649,19 +670,7 @@ class Net_SFTP extends Net_SSH2 { 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); + if (!$this->_close_handle($handle)) { return false; } @@ -760,18 +769,21 @@ class Net_SFTP extends Net_SSH2 { $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 + $attributes = $this->_parseAttributes($response); + if (!isset($attributes['type'])) { + $fileType = $this->_parseLongname($longname); + if ($fileType) { + $attributes['type'] = $fileType; + } + } 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; - } + } + + if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->_save_dir($dir . '/' . $shortname); } // 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. @@ -790,21 +802,7 @@ class Net_SFTP extends Net_SSH2 { } } - 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); + if (!$this->_close_handle($handle)) { return false; } @@ -880,6 +878,9 @@ class Net_SFTP extends Net_SSH2 { /** * Checks cache for directory * + * Mainly used by chdir, which is, in turn, also used for determining whether or not an individual + * file is a directory or not by stat() and lstat() + * * @param String $dir * @access private */ @@ -894,6 +895,7 @@ class Net_SFTP extends Net_SSH2 { } $temp = &$temp[$dir]; } + return true; } /** @@ -920,6 +922,9 @@ class Net_SFTP extends Net_SSH2 { if ($stat === false) { return false; } + if (isset($stat['type'])) { + return $stat; + } $pwd = $this->pwd; $stat['type'] = $this->chdir($filename) ? @@ -951,10 +956,14 @@ class Net_SFTP extends Net_SSH2 { } $lstat = $this->_stat($filename, NET_SFTP_LSTAT); - $stat = $this->_stat($filename, NET_SFTP_STAT); - if ($stat === false) { + if ($lstat === false) { return false; } + if (isset($lstat['type'])) { + return $lstat; + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); if ($lstat != $stat) { return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); @@ -991,11 +1000,7 @@ class Net_SFTP extends Net_SSH2 { $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; + return $this->_parseAttributes($response); case NET_SFTP_STATUS: $this->_logError($response); return false; @@ -1005,33 +1010,6 @@ class Net_SFTP extends Net_SSH2 { 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 * @@ -1043,7 +1021,7 @@ class Net_SFTP extends Net_SSH2 { */ function _size($filename) { - $result = $this->_stat($filename, NET_SFTP_LSTAT); + $result = $this->_stat($filename, NET_SFTP_STAT); if ($result === false) { return false; } @@ -1051,16 +1029,32 @@ class Net_SFTP extends Net_SSH2 { } /** - * Set permissions on a file. + * Truncates a file to a given length * - * Returns the new file permissions on success or FALSE on error. - * - * @param Integer $mode * @param String $filename - * @return Mixed + * @param Integer $new_size + * @return Boolean * @access public */ - function chmod($mode, $filename, $recursive = false) + function truncate($filename, $new_size) + { + $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 0x100000000, $new_size); + + return $this->_setstat($filename, $attr, false); + } + + /** + * Sets access and modification time of file. + * + * If the file does not exist, it will be created. + * + * @param String $filename + * @param optional Integer $time + * @param optional Integer $atime + * @return Boolean + * @access public + */ + function touch($filename, $time = NULL, $atime = NULL) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; @@ -1071,36 +1065,99 @@ class Net_SFTP extends Net_SSH2 { return false; } - if ($recursive) { - $i = 0; - $result = $this->_chmod_recursive($mode, $filename, $i); - $this->_read_put_responses($i); - return $result; + if (!isset($time)) { + $time = time(); + } + if (!isset($atime)) { + $atime = $time; } - // 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))) { + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); + $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { 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; + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return $this->_close_handle(substr($response, 4)); + case NET_SFTP_STATUS: + $this->_logError($response); + break; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; } - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + return $this->_setstat($filename, $attr, false); + } + + /** + * Changes file or directory owner + * + * Returns TRUE on success or FALSE on error. + * + * @param String $filename + * @param Integer $uid + * @param optional Boolean $recursive + * @return Boolean + * @access public + */ + function chown($filename, $uid, $recursive = false) + { + // quoting from , + // "if the owner or group is specified as -1, then that ID is not changed" + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Changes file or directory group + * + * Returns TRUE on success or FALSE on error. + * + * @param String $filename + * @param Integer $gid + * @param optional Boolean $recursive + * @return Boolean + * @access public + */ + function chgrp($filename, $gid, $recursive = false) + { + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or FALSE on error. + * If $recursive is true than this just returns TRUE or FALSE. + * + * @param Integer $mode + * @param String $filename + * @param optional Boolean $recursive + * @return Mixed + * @access public + */ + function chmod($mode, $filename, $recursive = false) + { + if (is_string($mode) && is_int($filename)) { + $temp = $mode; + $mode = $filename; + $filename = $temp; + } + + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_setstat($filename, $attr, $recursive)) { + return false; + } + if ($recursive) { + return true; } // rather than return what the permissions *should* be, we'll return what they actually are. this will also @@ -1126,16 +1183,72 @@ class Net_SFTP extends Net_SSH2 { } /** - * Recursively chmods directories on the SFTP server + * Sets information about a file * - * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. - * - * @param Integer $mode * @param String $filename + * @param String $attr + * @param Boolean $recursive * @return Boolean * @access private */ - function _chmod_recursive($mode, $path, &$i) + function _setstat($filename, $attr, $recursive) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($recursive) { + $i = 0; + $result = $this->_setstat_recursive($filename, $attr, $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. + 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); + return false; + } + + return true; + } + + /** + * Recursively sets information on directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param String $path + * @param String $attr + * @param Integer $i + * @return Boolean + * @access private + */ + function _setstat_recursive($path, $attr, &$i) { if (!$this->_read_put_responses($i)) { return false; @@ -1144,7 +1257,7 @@ class Net_SFTP extends Net_SSH2 { $entries = $this->_list($path, true, false); if ($entries === false) { - return $this->chmod($mode, $path); + return $this->_setstat($path, $attr, false); } // normally $entries would have at least . and .. but it might not if the directories @@ -1164,18 +1277,17 @@ class Net_SFTP extends Net_SSH2 { $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { - if (!$this->_chmod_recursive($mode, $temp, $i)) { + if (!$this->_setstat_recursive($temp, $attr, $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 ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1184,14 +1296,13 @@ class Net_SFTP extends Net_SSH2 { } } - $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 ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1208,33 +1319,32 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access public */ - function mkdir($dir) + function mkdir($dir, $mode = -1, $recursive = false) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; } - if ($dir[0] != '/') { - $dir = $this->_realpath(rtrim($dir, '/')); - if ($dir === false) { - return false; + $dir = $this->_realpath($dir); + // by not providing any permissions, hopefully the server will use the logged in users umask - their + // default permissions. + $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + + if ($recursive) { + $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); + if (empty($dirs[0])) { + array_shift($dirs); + $dirs[0] = '/' . $dirs[0]; } - 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; + for ($i = 0; $i < count($dirs); $i++) { + $temp = array_slice($dirs, 0, $i + 1); + $temp = implode('/', $temp); + $result = $this->_mkdir_helper($temp, $attr); } + return $result; } - return true; + return $this->_mkdir_helper($dir, $attr); } /** @@ -1244,11 +1354,9 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access private */ - function _mkdir_helper($dir) + function _mkdir_helper($dir, $attr) { - // 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))) { + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { return false; } @@ -1323,14 +1431,33 @@ class Net_SFTP extends Net_SSH2 { * 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. * + * $mode can take an additional two parameters - NET_SFTP_RESUME and NET_SFTP_RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * NET_SFTP_LOCAL_FILE | NET_SFTP_RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * NET_SFTP_RESUME with NET_SFTP_RESUME_START. + * + * If $mode & (NET_SFTP_RESUME | NET_SFTP_RESUME_START) then NET_SFTP_RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over NET_SFTP_RESUME + * when they're non-negative. ie. $start could let you write at the end of a file (like NET_SFTP_RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like NET_SFTP_RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | NET_SFTP_RESUME_START doesn't do anything unless $mode | NET_SFTP_LOCAL_FILE. + * * @param String $remote_file * @param String $data * @param optional Integer $mode + * @param optional Integer $start + * @param optional Integer $local_start * @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) + function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; @@ -1346,15 +1473,16 @@ class Net_SFTP extends Net_SSH2 { // 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) { + if ($start >= 0) { + $offset = $start; + } elseif ($mode & NET_SFTP_RESUME) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called $size = $this->_size($remote_file); $offset = $size !== false ? $size : 0; } else { + $offset = 0; $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)) { @@ -1387,6 +1515,14 @@ class Net_SFTP extends Net_SSH2 { return false; } $size = filesize($data); + + if ($local_start >= 0) { + fseek($fp, $local_start); + } elseif ($mode & NET_SFTP_RESUME_START) { + // do nothing + } else { + fseek($fp, $offset); + } } else { $size = strlen($data); } @@ -1395,10 +1531,13 @@ class Net_SFTP extends Net_SSH2 { $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $sftp_packet_size = 4096; // PuTTY uses 4096 + // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size-= strlen($handle) + 25; $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); + $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); + $subtemp = $offset + $sent; + $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 0x100000000, $subtemp, strlen($temp), $temp); if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { fclose($fp); return false; @@ -1407,7 +1546,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i == 50) { + if ($i == NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { $i = 0; break; @@ -1417,6 +1556,10 @@ class Net_SFTP extends Net_SSH2 { } if (!$this->_read_put_responses($i)) { + if ($mode & NET_SFTP_LOCAL_FILE) { + fclose($fp); + } + $this->_close_handle($handle); return false; } @@ -1424,23 +1567,7 @@ class Net_SFTP extends Net_SSH2 { 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; + return $this->_close_handle($handle); } /** @@ -1472,15 +1599,49 @@ class Net_SFTP extends Net_SSH2 { return $i < 0; } + /** + * Close handle + * + * @param String $handle + * @return Boolean + * @access private + */ + function _close_handle($handle) + { + 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 true; + } + /** * 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 + * operation. + * + * $offset and $length can be used to download files in chunks. * * @param String $remote_file * @param optional String $local_file + * @param optional Integer $offset + * @param optional Integer $length * @return Mixed * @access public */ @@ -1522,10 +1683,9 @@ class Net_SFTP extends Net_SSH2 { $content = ''; } - $size = (1 << 20) < $length || $length < 0 ? 1 << 20 : $length; - $start = $offset; + $size = $this->max_sftp_packet < $length || $length < 0 ? $this->max_sftp_packet : $length; while (true) { - $packet = pack('Na*N3', strlen($handle), $handle, 0, $offset, $size); + $packet = pack('Na*N3', strlen($handle), $handle, $offset / 0x100000000, $offset, $size); if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { if ($local_file !== false) { fclose($fp); @@ -1545,6 +1705,7 @@ class Net_SFTP extends Net_SSH2 { } break; case NET_SFTP_STATUS: + // could, in theory, return false if !strlen($content) but we'll hold off for the time being $this->_logError($response); break 2; default: @@ -1556,40 +1717,28 @@ class Net_SFTP extends Net_SSH2 { } if ($length > 0 && $length <= $offset - $size) { - if ($local_file === false) { - $content = substr($content, 0, $length); - } else { - ftruncate($fp, $length); - } break; } } + if ($length > 0 && $length <= $offset - $size) { + if ($local_file === false) { + $content = substr($content, 0, $length); + } else { + ftruncate($fp, $length); + } + } + if ($local_file !== false) { fclose($fp); } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + if (!$this->_close_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; + // if $content isn't set that means a file was written to + return isset($content) ? $content : true; } /** @@ -1683,7 +1832,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1699,7 +1848,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1774,17 +1923,21 @@ class Net_SFTP extends Net_SSH2 { // (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; - } + $attr['size'] = $upper ? 0x100000000 * $upper : 0; + $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)); + // mode == permissions; permissions was the original array key and is retained for bc purposes. + // mode was added because that's the more industry standard terminology + $attr+= array('mode' => $attr['permissions']); + $fileType = $this->_parseMode($attr['permissions']); + if ($fileType !== false) { + $attr+= array('type' => $fileType); + } break; case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); @@ -1802,6 +1955,47 @@ class Net_SFTP extends Net_SSH2 { return $attr; } + /** + * Attempt to identify the file type + * + * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway + * + * @param Integer $mode + * @return Integer + * @access private + */ + function _parseMode($mode) + { + // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 + // see, also, http://linux.die.net/man/2/stat + switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 + case 0000000: // no file type specified - figure out the file type using alternative means + return false; + case 0040000: + return NET_SFTP_TYPE_DIRECTORY; + case 0100000: + return NET_SFTP_TYPE_REGULAR; + case 0120000: + return NET_SFTP_TYPE_SYMLINK; + // new types introduced in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + case 0010000: // named pipe (fifo) + return NET_SFTP_TYPE_FIFO; + case 0020000: // character special + return NET_SFTP_TYPE_CHAR_DEVICE; + case 0060000: // block special + return NET_SFTP_BLOCK_DEVICE; + case 0140000: // socket + return NET_SFTP_TYPE_SOCKET; + case 0160000: // whiteout + // "SPECIAL should be used for files that are of + // a known type which cannot be expressed in the protocol" + return NET_SFTP_TYPE_SPECIAL; + default: + return NET_SFTP_TYPE_UNKNOWN; + } + } + /** * Parse Longname * @@ -2026,4 +2220,4 @@ class Net_SFTP extends Net_SSH2 { $this->pwd = false; parent::_disconnect($reason); } -} \ No newline at end of file +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php new file mode 100644 index 00000000000..0572c5c4025 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -0,0 +1,771 @@ + + * @copyright MMXIII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/** + * SFTP Stream Wrapper + * + * @author Jim Wigginton + * @version 0.3.2 + * @access public + * @package Net_SFTP_Stream + */ +class Net_SFTP_Stream { + /** + * SFTP instances + * + * Rather than re-create the connection we re-use instances if possible + * + * @var Array + * @access static + */ + static $instances; + + /** + * SFTP instance + * + * @var Object + * @access private + */ + var $sftp; + + /** + * Path + * + * @var String + * @access private + */ + var $path; + + /** + * Mode + * + * @var String + * @access private + */ + var $mode; + + /** + * Position + * + * @var Integer + * @access private + */ + var $pos; + + /** + * Size + * + * @var Integer + * @access private + */ + var $size; + + /** + * Directory entries + * + * @var Array + * @access private + */ + var $entries; + + /** + * EOF flag + * + * @var Boolean + * @access private + */ + var $eof; + + /** + * Context resource + * + * Technically this needs to be publically accessible so PHP can set it directly + * + * @var Resource + * @access public + */ + var $context; + + /** + * Notification callback function + * + * @var Callable + * @access public + */ + var $notification; + + /** + * The Constructor + * + * @access public + */ + function Net_SFTP_Stream() + { + if (!class_exists('Net_SFTP')) { + require_once('Net/SFTP.php'); + } + } + + /** + * Path Parser + * + * Extract a path from a URI and actually connect to an SSH server if appropriate + * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * + * @param String $path + * @return String + * @access private + */ + function _parse_path($path) + { + extract(parse_url($path) + array('port' => 22)); + + if (!isset($host)) { + return false; + } + + if (isset($this->context)) { + $context = stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + } + + if ($host[0] == '$') { + $host = substr($host, 1); + global $$host; + if (!is_object($$host) || get_class($$host) != 'Net_SFTP') { + return false; + } + $this->sftp = $$host; + } else { + if (isset($this->context)) { + $context = stream_context_get_options($this->context); + } + if (isset($context['sftp']['session'])) { + $sftp = $context['sftp']['session']; + } + if (isset($context['sftp']['sftp'])) { + $sftp = $context['sftp']['sftp']; + } + if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') { + $this->sftp = $sftp; + return $path; + } + if (isset($context['sftp']['username'])) { + $user = $context['sftp']['username']; + } + if (isset($context['sftp']['password'])) { + $pass = $context['sftp']['password']; + } + if (isset($context['sftp']['privkey']) && is_object($context['sftp']['privkey']) && get_Class($context['sftp']['privkey']) == 'Crypt_RSA') { + $pass = $context['sftp']['privkey']; + } + + if (!isset($user) || !isset($pass)) { + return false; + } + + // casting $pass to a string is necessary in the event that it's a Crypt_RSA object + if (isset(self::$instances[$host][$port][$user][(string) $pass])) { + $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; + } else { + $this->sftp = new Net_SFTP($host, $port); + if (isset($this->notification) && is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return false; + } + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return false; + } + } + self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; + } + } + + return $path; + } + + /** + * Opens file or URL + * + * @param String $path + * @param String $mode + * @param Integer $options + * @param String $opened_path + * @return Boolean + * @access public + */ + function _stream_open($path, $mode, $options, &$opened_path) + { + $path = $this->_parse_path($path); + + if ($path === false) { + return false; + } + $this->path = $path; + + $this->size = $this->sftp->size($path); + $this->mode = preg_replace('#[bt]$#', '', $mode); + $this->eof = false; + + if ($this->size === false) { + if ($this->mode[0] == 'r') { + return false; + } + } else { + switch ($this->mode[0]) { + case 'x': + return false; + case 'w': + case 'c': + $this->sftp->truncate($path, 0); + } + } + + $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; + + return true; + } + + /** + * Read from stream + * + * @param Integer $count + * @return Mixed + * @access public + */ + function _stream_read($count) + { + switch ($this->mode) { + case 'w': + case 'a': + case 'x': + case 'c': + return false; + } + + // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite + //if ($this->pos >= $this->size) { + // $this->eof = true; + // return false; + //} + + $result = $this->sftp->get($this->path, false, $this->pos, $count); + if (isset($this->notification) && is_callable($this->notification)) { + if ($result === false) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); + } + + if (empty($result)) { // ie. false or empty string + $this->eof = true; + return false; + } + $this->pos+= strlen($result); + + return $result; + } + + /** + * Write to stream + * + * @param String $data + * @return Mixed + * @access public + */ + function _stream_write($data) + { + switch ($this->mode) { + case 'r': + return false; + } + + $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos); + if (isset($this->notification) && is_callable($this->notification)) { + if (!$result) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); + } + + if ($result === false) { + return false; + } + $this->pos+= strlen($data); + if ($this->pos > $this->size) { + $this->size = $this->pos; + } + $this->eof = false; + return strlen($data); + } + + /** + * Retrieve the current position of a stream + * + * @return Integer + * @access public + */ + function _stream_tell() + { + return $this->pos; + } + + /** + * Tests for end-of-file on a file pointer + * + * In my testing there are four classes functions that normally effect the pointer: + * fseek, fputs / fwrite, fgets / fread and ftruncate. + * + * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. + * + * @return Boolean + * @access public + */ + function _stream_eof() + { + return $this->eof; + } + + /** + * Seeks to specific location in a stream + * + * @param Integer $offset + * @param Integer $whence + * @return Boolean + * @access public + */ + function _stream_seek($offset, $whence) + { + switch ($whence) { + case SEEK_SET: + if ($offset >= $this->size || $offset < 0) { + return false; + } + break; + case SEEK_CUR: + $offset+= $this->pos; + break; + case SEEK_END: + $offset+= $this->size; + } + + $this->pos = $offset; + $this->eof = false; + return true; + } + + /** + * Change stream options + * + * @param String $path + * @param Integer $option + * @param Mixed $var + * @return Boolean + * @access public + */ + function _stream_metadata($path, $option, $var) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined + // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 + // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 + switch ($option) { + case 1: // PHP_STREAM_META_TOUCH + return $this->sftp->touch($path, $var[0], $var[1]); + case 2: // PHP_STREAM_OWNER_NAME + case 3: // PHP_STREAM_GROUP_NAME + return false; + case 4: // PHP_STREAM_META_OWNER + return $this->sftp->chown($path, $var); + case 5: // PHP_STREAM_META_GROUP + return $this->sftp->chgrp($path, $var); + case 6: // PHP_STREAM_META_ACCESS + return $this->sftp->chmod($path, $var) !== false; + } + } + + /** + * Retrieve the underlaying resource + * + * @param Integer $cast_as + * @return Resource + * @access public + */ + function _stream_cast($cast_as) + { + return $this->sftp->fsock; + } + + /** + * Advisory file locking + * + * @param Integer $operation + * @return Boolean + * @access public + */ + function _stream_lock($operation) + { + return false; + } + + /** + * Renames a file or directory + * + * Attempts to rename oldname to newname, moving it between directories if necessary. + * If newname exists, it will be overwritten. This is a departure from what Net_SFTP + * does. + * + * @param String $path_from + * @param String $path_to + * @return Boolean + * @access public + */ + function _rename($path_from, $path_to) + { + $path1 = parse_url($path_from); + $path2 = parse_url($path_to); + unset($path1['path'], $path2['path']); + if ($path1 != $path2) { + return false; + } + + $path_from = $this->_parse_path($path_from); + $path_to = parse_url($path_to); + if ($path_from == false) { + return false; + } + + $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 + // "It is an error if there already exists a file with the name specified by newpath." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 + if (!$this->sftp->rename($path_from, $path_to)) { + if ($this->sftp->stat($path_to)) { + return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); + } + return false; + } + + return true; + } + + /** + * Open directory handle + * + * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and + * removed in 5.4 I'm just going to ignore it + * + * @param String $path + * @param Integer $options + * @return Boolean + * @access public + */ + function _dir_opendir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + $this->pos = 0; + $this->entries = $this->sftp->nlist($path); + return $this->entries !== false; + } + + /** + * Read entry from directory handle + * + * @return Mixed + * @access public + */ + function _dir_readdir() + { + if (isset($this->entries[$this->pos])) { + return $this->entries[$this->pos++]; + } + return false; + } + + /** + * Rewind directory handle + * + * @return Boolean + * @access public + */ + function _dir_rewinddir() + { + $this->pos = 0; + return true; + } + + /** + * Close directory handle + * + * @return Boolean + * @access public + */ + function _dir_closedir() + { + return true; + } + + /** + * Create a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE + * + * @param String $path + * @param Integer $mode + * @param Integer $options + * @return Boolean + * @access public + */ + function _mkdir($path, $mode, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + /** + * Removes a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, + * does not have a $recursive parameter as mkdir() does so I don't know how + * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as + * $options. What does 8 correspond to? + * + * @param String $path + * @param Integer $mode + * @param Integer $options + * @return Boolean + * @access public + */ + function _rmdir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->rmdir($path); + } + + /** + * Flushes the output + * + * See . Always returns true because Net_SFTP doesn't cache stuff before writing + * + * @return Boolean + * @access public + */ + function _stream_flush() + { + return true; + } + + /** + * Retrieve information about a file resource + * + * @return Mixed + * @access public + */ + function _stream_stat() + { + $results = $this->sftp->stat($this->path); + if ($results === false) { + return false; + } + return $results; + } + + /** + * Delete a file + * + * @param String $path + * @return Boolean + * @access public + */ + function _unlink($path) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->delete($path, false); + } + + /** + * Retrieve information about a file + * + * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of Net_SFTP_Stream is quiet by default + * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll + * cross that bridge when and if it's reached + * + * @param String $path + * @param Integer $flags + * @return Mixed + * @access public + */ + function _url_stat($path, $flags) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); + if ($results === false) { + return false; + } + + return $results; + } + + /** + * Truncate stream + * + * @param Integer $new_size + * @return Boolean + * @access public + */ + function _stream_truncate($new_size) + { + if (!$this->sftp->truncate($this->path, $new_size)) { + return false; + } + + $this->eof = false; + $this->size = $new_size; + + return true; + } + + /** + * Change stream options + * + * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. + * The other two aren't supported because of limitations in Net_SFTP. + * + * @param Integer $option + * @param Integer $arg1 + * @param Integer $arg2 + * @return Boolean + * @access public + */ + function _stream_set_option($option, $arg1, $arg2) + { + return false; + } + + /** + * Close an resource + * + * @access public + */ + function _stream_close() + { + } + + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param String + * @param Array + * @return Mixed + * @access public + */ + function __call($name, $arguments) + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = count($arguments) - 1; + foreach ($arguments as $i => $argument) { + var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!method_exists($this, $name)) { + return false; + } + return call_user_func_array(array($this, $name), $arguments); + } +} + +if (function_exists('stream_wrapper_register')) { + stream_wrapper_register('sftp', 'Net_SFTP_Stream'); +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php index 8f5c79938e4..83d5980d00a 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php @@ -62,56 +62,9 @@ * @author Jim Wigginton * @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 * @@ -495,6 +448,19 @@ class Net_SSH1 { */ function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES) { + 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'); + } + $this->protocol_flags = array( 1 => 'NET_SSH1_MSG_DISCONNECT', 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', @@ -636,18 +602,27 @@ class Net_SSH1 { // $this->crypto = new Crypt_Null(); // break; case NET_SSH1_CIPHER_DES: + if (!class_exists('Crypt_DES')) { + require_once('Crypt/DES.php'); + } $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: + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $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: + // if (!class_exists('Crypt_RC4')) { + // require_once('Crypt/RC4.php'); + // } // $this->crypto = new Crypt_RC4(); // $this->crypto->enableContinuousBuffer(); // $this->crypto->setKey(substr($session_key, 0, 16)); @@ -935,7 +910,7 @@ class Net_SSH1 { * 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 + * "^[[00m", 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. @@ -1574,4 +1549,4 @@ class Net_SSH1 { 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 index 43bfbca2dbe..dad03697233 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php @@ -64,67 +64,20 @@ * @author Jim Wigginton * @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); +define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); +define('NET_SSH2_MASK_LOGIN_REQ', 0x00000002); +define('NET_SSH2_MASK_LOGIN', 0x00000004); +define('NET_SSH2_MASK_SHELL', 0x00000008); +define('NET_SSH2_MASK_WINDOW_ADJUST', 0X00000010); /**#@-*/ /**#@+ @@ -143,8 +96,9 @@ define('NET_SSH2_MASK_SHELL', 0x00000004); * @see Net_SSH2::_get_channel_packet() * @access private */ -define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 -define('NET_SSH2_CHANNEL_SHELL',1); +define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 +define('NET_SSH2_CHANNEL_SHELL', 1); +define('NET_SSH2_CHANNEL_SUBSYSTEM', 2); /**#@-*/ /**#@+ @@ -580,7 +534,7 @@ class Net_SSH2 { /** * The Window Size * - * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB) + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) * * @var Integer * @see Net_SSH2::_send_channel_packet() @@ -590,7 +544,7 @@ class Net_SSH2 { var $window_size = 0x7FFFFFFF; /** - * Window size + * Window size, server to client * * Window size indexed by channel * @@ -598,6 +552,17 @@ class Net_SSH2 { * @var Array * @access private */ + var $window_size_server_to_client = array(); + + /** + * Window size, client to server + * + * Window size indexed by channel + * + * @see Net_SSH2::_get_channel_packet() + * @var Array + * @access private + */ var $window_size_client_to_server = array(); /** @@ -717,6 +682,71 @@ class Net_SSH2 { */ var $exit_status; + /** + * Flag to request a PTY when using exec() + * + * @see Net_SSH2::enablePTY() + * @access private + */ + var $request_pty = false; + + /** + * Flag set while exec() is running when using enablePTY() + * + * @access private + */ + var $in_request_pty_exec = false; + + /** + * Flag set after startSubsystem() is called + * + * @access private + */ + var $in_subsystem; + + /** + * Contents of stdError + * + * @access private + */ + var $stdErrorLog; + + /** + * The Last Interactive Response + * + * @see Net_SSH2::_keyboard_interactive_process() + * @access private + */ + var $last_interactive_response = ''; + + /** + * Keyboard Interactive Request / Responses + * + * @see Net_SSH2::_keyboard_interactive_process() + * @access private + */ + var $keyboard_requests_responses = array(); + + /** + * Banner Message + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @see Net_SSH2::_filter() + * @see Net_SSH2::getBannerMessage() + * @access private + */ + var $banner_message = ''; + + /** + * Did read() timeout or return normally? + * + * @see Net_SSH2::isTimeout + * @access private + */ + var $is_timeout = false; + /** * Default Constructor. * @@ -730,6 +760,20 @@ class Net_SSH2 { */ function Net_SSH2($host, $port = 22, $timeout = 10) { + // 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'); + } + + if (!function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5 $this->message_numbers = array( 1 => 'NET_SSH2_MSG_DISCONNECT', @@ -918,26 +962,76 @@ class Net_SSH2 { 'ssh-dss' // REQUIRED sign Raw DSS Key ); - static $encryption_algorithms = array( - // from : - 'arcfour256', - 'arcfour128', + static $encryption_algorithms = false; + if ($encryption_algorithms === false) { + $encryption_algorithms = array( + // from : + 'arcfour256', + 'arcfour128', - 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + '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 + // CTR modes from : + '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 - // from : - '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 + 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key - '3des-cbc', // REQUIRED three-key 3DES in CBC mode - 'none' // OPTIONAL no encryption; NOT RECOMMENDED - ); + '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 + + 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + + 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode + + 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode + + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + 'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + if (!$this->_is_includable('Crypt/RC4.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('arcfour256', 'arcfour128', 'arcfour') + ); + } + if (!$this->_is_includable('Crypt/Rijndael.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc') + ); + } + if (!$this->_is_includable('Crypt/Twofish.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc') + ); + } + if (!$this->_is_includable('Crypt/Blowfish.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('blowfish-ctr', 'blowfish-cbc') + ); + } + if (!$this->_is_includable('Crypt/TripleDES.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('3des-ctr', '3des-cbc') + ); + } + $encryption_algorithms = array_values($encryption_algorithms); + } static $mac_algorithms = array( 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) @@ -1045,14 +1139,23 @@ class Net_SSH2 { break; case 'aes256-cbc': case 'aes256-ctr': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': $decryptKeyLength = 32; // eg. 256 / 8 break; case 'aes192-cbc': case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': $decryptKeyLength = 24; // eg. 192 / 8 break; case 'aes128-cbc': case 'aes128-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + case 'blowfish-cbc': + case 'blowfish-ctr': $decryptKeyLength = 16; // eg. 128 / 8 break; case 'arcfour': @@ -1080,14 +1183,23 @@ class Net_SSH2 { break; case 'aes256-cbc': case 'aes256-ctr': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': $encryptKeyLength = 32; break; case 'aes192-cbc': case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': $encryptKeyLength = 24; break; case 'aes128-cbc': case 'aes128-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + case 'blowfish-cbc': + case 'blowfish-ctr': $encryptKeyLength = 16; break; case 'arcfour': @@ -1114,28 +1226,29 @@ class Net_SSH2 { // 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'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; 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'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; } - $p = new Math_BigInteger($p, 256); + // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 + // the generator field element is 2 (decimal) and the hash function is sha1. + $g = new Math_BigInteger(2); + $prime = new Math_BigInteger($prime, 16); + $kexHash = new Crypt_Hash('sha1'); //$q = $p->bitwise_rightShift(1); /* To increase the speed of the key exchange, both client and server may @@ -1145,14 +1258,12 @@ class Net_SSH2 { [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)); + $one = new Math_BigInteger(1); + $keyLength = min($keyLength, $kexHash->getLength()); + $max = $one->bitwise_leftShift(16 * $keyLength)->subtract($one); // 2 * 8 * $keyLength - $g = new Math_BigInteger(2); - $x = new Math_BigInteger(); - $x = $x->random(new Math_BigInteger(1), $q); - $e = $g->modPow($x, $p); + $x = $one->random($one, $max); + $e = $g->modPow($x, $prime); $eBytes = $e->toBytes(true); $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes); @@ -1190,7 +1301,7 @@ class Net_SSH2 { $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); - $key = $f->modPow($x, $p); + $key = $f->modPow($x, $prime); $keyBytes = $key->toBytes(true); $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*', @@ -1200,7 +1311,7 @@ class Net_SSH2 { $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes ); - $this->exchange_hash = pack('H*', $hash($this->exchange_hash)); + $this->exchange_hash = $kexHash->hash($this->exchange_hash); if ($this->session_id === false) { $this->session_id = $this->exchange_hash; @@ -1213,7 +1324,7 @@ class Net_SSH2 { } if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) { - user_error('Sever Host Key Algorithm Mismatch'); + user_error('Server Host Key Algorithm Mismatch'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } @@ -1241,28 +1352,76 @@ class Net_SSH2 { switch ($encrypt) { case '3des-cbc': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->encrypt = new Crypt_TripleDES(); // $this->encrypt_block_size = 64 / 8 == the default break; case '3des-ctr': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $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(); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->encrypt = new Crypt_Rijndael(); $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); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR); $this->encrypt_block_size = 16; // eg. 128 / 8 break; + case 'blowfish-cbc': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->encrypt = new Crypt_Blowfish(); + $this->encrypt_block_size = 8; + break; + case 'blowfish-ctr': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR); + $this->encrypt_block_size = 8; + break; + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->encrypt = new Crypt_Twofish(); + $this->encrypt_block_size = 16; + break; + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR); + $this->encrypt_block_size = 16; + break; case 'arcfour': case 'arcfour128': case 'arcfour256': + if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); + } $this->encrypt = new Crypt_RC4(); break; case 'none'; @@ -1271,26 +1430,74 @@ class Net_SSH2 { switch ($decrypt) { case '3des-cbc': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->decrypt = new Crypt_TripleDES(); break; case '3des-ctr': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); break; case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': - $this->decrypt = new Crypt_AES(); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->decrypt = new Crypt_Rijndael(); $this->decrypt_block_size = 16; break; case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': - $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR); + $this->decrypt_block_size = 16; + break; + case 'blowfish-cbc': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->decrypt = new Crypt_Blowfish(); + $this->decrypt_block_size = 8; + break; + case 'blowfish-ctr': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR); + $this->decrypt_block_size = 8; + break; + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->decrypt = new Crypt_Twofish(); + $this->decrypt_block_size = 16; + break; + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR); $this->decrypt_block_size = 16; break; case 'arcfour': case 'arcfour128': case 'arcfour256': + if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); + } $this->decrypt = new Crypt_RC4(); break; case 'none'; @@ -1303,15 +1510,15 @@ class Net_SSH2 { $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id)); + $iv = $kexHash->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)); + $iv.= $kexHash->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)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); } @@ -1320,15 +1527,15 @@ class Net_SSH2 { $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id)); + $iv = $kexHash->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)); + $iv.= $kexHash->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)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); } @@ -1402,15 +1609,15 @@ class Net_SSH2 { $this->hmac_size = 12; } - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->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)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); while ($checkKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); @@ -1434,40 +1641,72 @@ class Net_SSH2 { /** * Login * - * The $password parameter can be a plaintext password or a Crypt_RSA object. + * The $password parameter can be a plaintext password, a Crypt_RSA object or an array + * + * @param String $username + * @param Mixed $password + * @param Mixed $... + * @return Boolean + * @see _login_helper + * @access public + */ + function login($username) + { + $args = array_slice(func_get_args(), 1); + if (empty($args)) { + return $this->_login_helper($username); + } + + foreach ($args as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } + } + return false; + } + + /** + * Login Helper * * @param String $username * @param optional String $password * @return Boolean - * @access public + * @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 login($username, $password = null) + function _login_helper($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->bitmap & NET_SSH2_MASK_LOGIN_REQ)) { + $packet = pack('CNa*', + NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' + ); - if (!$this->_send_binary_packet($packet)) { - return false; + 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; + } + $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ; } - $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; + if (strlen($this->last_interactive_response)) { + return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); } // although PHP5's get_class() preserves the case, PHP4's does not @@ -1475,6 +1714,14 @@ class Net_SSH2 { return $this->_privatekey_login($username, $password); } + if (is_array($password)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + return false; + } + if (!isset($password)) { $packet = pack('CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', @@ -1508,17 +1755,18 @@ class Net_SSH2 { 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*', + // remove the username and password from the logged packet + if (!defined('NET_SSH2_LOGGING')) { + $logged = NULL; + } else { + $logged = 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; + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; } $response = $this->_get_binary_packet(); @@ -1542,7 +1790,10 @@ class Net_SSH2 { // 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)) { + extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); + $partial_success = $partial_success != 0; + + if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { if ($this->_keyboard_interactive_login($username, $password)) { $this->bitmap |= NET_SSH2_MASK_LOGIN; return true; @@ -1593,25 +1844,20 @@ class Net_SSH2 { { $responses = func_get_args(); - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; + if (strlen($this->last_interactive_response)) { + $response = $this->last_interactive_response; + } else { + $orig = $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))); @@ -1619,14 +1865,48 @@ class Net_SSH2 { 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); + + for ($i = 0; $i < count($responses); $i++) { + if (is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = array_values($responses); + + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $prompt = $this->_string_shift($response, $length); + //$echo = $this->_string_shift($response) != chr(0); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (substr($prompt, 0, strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } + } + + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (strlen($this->last_interactive_response)) { + $this->last_interactive_response = ''; + } else 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] + ); + } + + if (!count($responses) && $num_prompts) { + $this->last_interactive_response = $orig; + $this->bitmap |= NET_SSH_MASK_LOGIN_INTERACTIVE; + return false; } - */ /* After obtaining the requested information from the user, the client @@ -1639,17 +1919,16 @@ class Net_SSH2 { $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); } - if (!$this->_send_binary_packet($packet)) { + if (!$this->_send_binary_packet($packet, $logged)) { return false; } - if (defined('NET_SSH2_LOGGING')) { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { $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; } /* @@ -1718,11 +1997,11 @@ class Net_SSH2 { 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); + return false; 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')) { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { $this->message_number_log[count($this->message_number_log) - 1] = str_replace( 'UNKNOWN', 'NET_SSH2_MSG_USERAUTH_PK_OK', @@ -1768,12 +2047,23 @@ class Net_SSH2 { * Setting $timeout to false or 0 will mean there is no timeout. * * @param Mixed $timeout + * @access public */ function setTimeout($timeout) { $this->timeout = $this->curTimeout = $timeout; } + /** + * Get the output from stdError + * + * @access public + */ + function getStdError() + { + return $this->stdErrorLog; + } + /** * Execute Command * @@ -1785,25 +2075,27 @@ class Net_SSH2 { * @return String * @access public */ - function exec($command, $block = true) + function exec($command, $callback = NULL) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; + $this->stdErrorLog = ''; 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. + // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transfering more than 2GB, 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; + $this->window_size_server_to_client[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); + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC], $packet_size); if (!$this->_send_binary_packet($packet)) { return false; @@ -1816,6 +2108,34 @@ class Net_SSH2 { return false; } + if ($this->request_pty === true) { + $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_EXEC], 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); + } + $this->in_request_pty_exec = true; + } + // 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 @@ -1840,7 +2160,7 @@ class Net_SSH2 { $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; - if (!$block) { + if ($callback === false || $this->in_request_pty_exec) { return true; } @@ -1849,11 +2169,15 @@ class Net_SSH2 { $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); switch (true) { case $temp === true: - return $output; + return is_callable($callback) ? true : $output; case $temp === false: return false; default: - $output.= $temp; + if (is_callable($callback)) { + $callback($temp); + } else { + $output.= $temp; + } } } } @@ -1868,11 +2192,15 @@ class Net_SSH2 { */ function _initShell() { - $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF; + if ($this->in_request_pty_exec === true) { + return true; + } + + $this->window_size_server_to_client[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); + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL], $packet_size); if (!$this->_send_binary_packet($packet)) { return false; @@ -1904,8 +2232,9 @@ class Net_SSH2 { switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: - break; + // if a pty can't be opened maybe commands can still be executed case NET_SSH2_MSG_CHANNEL_FAILURE: + break; default: user_error('Unable to request pseudo-terminal'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); @@ -1931,13 +2260,33 @@ class Net_SSH2 { return true; } + /** + * Return the channel to be used with read() / write() + * + * @see Net_SSH2::read() + * @see Net_SSH2::write() + * @return Integer + * @access public + */ + function _get_interactive_channel() + { + switch (true) { + case $this->in_subsystem: + return NET_SSH2_CHANNEL_SUBSYSTEM; + case $this->in_request_pty_exec: + return NET_SSH2_CHANNEL_EXEC; + default: + return NET_SSH2_CHANNEL_SHELL; + } + } + /** * 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() + * @see Net_SSH2::write() * @param String $expect * @param Integer $mode * @return String @@ -1946,6 +2295,7 @@ class Net_SSH2 { function read($expect = '', $mode = NET_SSH2_READ_SIMPLE) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); @@ -1957,6 +2307,8 @@ class Net_SSH2 { return false; } + $channel = $this->_get_interactive_channel(); + $match = $expect; while (true) { if ($mode == NET_SSH2_READ_REGEX) { @@ -1967,8 +2319,9 @@ class Net_SSH2 { if ($pos !== false) { return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); } - $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + $response = $this->_get_channel_packet($channel); if (is_bool($response)) { + $this->in_request_pty_exec = false; return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; } @@ -1979,7 +2332,7 @@ class Net_SSH2 { /** * Inputs a command into an interactive shell. * - * @see Net_SSH1::interactiveWrite() + * @see Net_SSH2::read() * @param String $cmd * @return Boolean * @access public @@ -1996,7 +2349,101 @@ class Net_SSH2 { return false; } - return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd); + $channel = $this->in_request_pty_exec ? NET_SSH2_CHANNEL_EXEC : NET_SSH2_CHANNEL_SHELL; + return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); + } + + /** + * Start a subsystem. + * + * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept + * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. + * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and + * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented + * if there's sufficient demand for such a feature. + * + * @see Net_SSH2::stopSubsystem() + * @param String $subsystem + * @return Boolean + * @access public + */ + function startSubsystem($subsystem) + { + $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size; + + $packet = pack('CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SUBSYSTEM, $this->window_size, 0x4000); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM); + if ($response === false) { + return false; + } + + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM], strlen('subsystem'), 'subsystem', 1, strlen($subsystem), $subsystem); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM); + + if ($response === false) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= NET_SSH2_MASK_SHELL; + $this->in_subsystem = true; + + return true; + } + + /** + * Stops a subsystem. + * + * @see Net_SSH2::startSubsystem() + * @return Boolean + * @access public + */ + function stopSubsystem() + { + $this->in_subsystem = false; + $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM); + return true; + } + + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * @access public + */ + function reset() + { + $channel = $this->in_request_pty_exec ? NET_SSH2_CHANNEL_EXEC : NET_SSH2_CHANNEL_SHELL; + $this->_close_channel($channel); + } + + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + * @access public + */ + function isTimeout() + { + return $this->is_timeout; } /** @@ -2025,6 +2472,16 @@ class Net_SSH2 { $this->disconnect(); } + /** + * Is the connection still active? + * + * @access public + */ + function isConnected() + { + return $this->bitmap & NET_SSH2_MASK_LOGIN; + } + /** * Gets Binary Packets * @@ -2038,7 +2495,7 @@ class Net_SSH2 { { if (!is_resource($this->fsock) || feof($this->fsock)) { user_error('Connection closed prematurely'); - $this->bitmask = 0; + $this->bitmap = 0; return false; } @@ -2125,7 +2582,7 @@ class Net_SSH2 { $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; + $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: $payload = $this->_get_binary_packet(); @@ -2141,7 +2598,7 @@ class Net_SSH2 { case NET_SSH2_MSG_KEXINIT: if ($this->session_id !== false) { if (!$this->_key_exchange($payload)) { - $this->bitmask = 0; + $this->bitmap = 0; return false; } $payload = $this->_get_binary_packet(); @@ -2152,7 +2609,7 @@ class Net_SSH2 { 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)); + $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); $payload = $this->_get_binary_packet(); } @@ -2172,7 +2629,7 @@ class Net_SSH2 { 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))); + extract(unpack('Nlength', $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 @@ -2188,7 +2645,12 @@ class Net_SSH2 { $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: - $payload = $this->_get_binary_packet(); + $this->_string_shift($payload, 1); + extract(unpack('Nchannel', $this->_string_shift($payload, 4))); + extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); + $this->window_size_client_to_server[$channel]+= $window_size; + + $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); } } @@ -2219,6 +2681,26 @@ class Net_SSH2 { $this->quiet_mode = false; } + /** + * Enable request-pty when using exec() + * + * @access public + */ + function enablePTY() + { + $this->request_pty = true; + } + + /** + * Disable request-pty when using exec() + * + * @access public + */ + function disablePTY() + { + $this->request_pty = false; + } + /** * Gets channel data * @@ -2236,6 +2718,11 @@ class Net_SSH2 { while (true) { if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + $read = array($this->fsock); $write = $except = NULL; @@ -2244,7 +2731,7 @@ class Net_SSH2 { $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); + $this->is_timeout = true; return true; } $elapsed = strtok(microtime(), ' ') + strtok('') - $start; @@ -2256,20 +2743,34 @@ class Net_SSH2 { user_error('Connection closed by server'); return false; } - + if ($client_channel == -1 && $response === true) { + return true; + } if (!strlen($response)) { return ''; } extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); + $this->window_size_server_to_client[$channel]-= strlen($response) + 4; + + // resize the window, if appropriate + if ($this->window_size_server_to_client[$channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_server_to_client[$channel]+= $this->window_size; + } + 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 + extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); + $this->window_size_client_to_server[$channel] = $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); @@ -2283,24 +2784,27 @@ class Net_SSH2 { switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: return true; - //case NET_SSH2_MSG_CHANNEL_FAILURE: + case NET_SSH2_MSG_CHANNEL_FAILURE: + return false; default: - user_error('Unable to request pseudo-terminal'); + user_error('Unable to fulfill channel request'); 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); } + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: /* - if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + if ($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)); + $this->_send_channel_packet($channel, chr(0)); } */ extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2308,15 +2812,12 @@ class Net_SSH2 { if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$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)); @@ -2325,13 +2826,17 @@ class Net_SSH2 { // 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); + $this->stdErrorLog .= $data; + if ($skip_extended || $this->quiet_mode) { + break; + } if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$channel][] = $data; break; case NET_SSH2_MSG_CHANNEL_REQUEST: extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2346,6 +2851,13 @@ class Net_SSH2 { if ($length) { $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); } + + $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; + + break; case 'exit-status': extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); $this->exit_status = $exit_status; @@ -2355,6 +2867,8 @@ class Net_SSH2 { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; default: // "Some systems may not implement signals, in which case they SHOULD ignore this message." // -- http://tools.ietf.org/html/rfc4254#section-6.9 @@ -2388,15 +2902,16 @@ class Net_SSH2 { * See '6. Binary Packet Protocol' of rfc4253 for more info. * * @param String $data + * @param optional String $logged * @see Net_SSH2::_get_binary_packet() * @return Boolean * @access private */ - function _send_binary_packet($data) + function _send_binary_packet($data, $logged = NULL) { if (!is_resource($this->fsock) || feof($this->fsock)) { user_error('Connection closed prematurely'); - $this->bitmask = 0; + $this->bitmap = 0; return false; } @@ -2435,7 +2950,7 @@ class Net_SSH2 { $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->_append_log($message_number, isset($logged) ? $logged : $data); $this->last_packet = $current; } @@ -2452,6 +2967,11 @@ class Net_SSH2 { */ function _append_log($message_number, $message) { + // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) + if (strlen($message_number) > 2) { + $this->_string_shift($message); + } + switch (NET_SSH2_LOGGING) { // useful for benchmarks case NET_SSH2_LOG_SIMPLE: @@ -2460,7 +2980,6 @@ class Net_SSH2 { // 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) { @@ -2472,7 +2991,15 @@ class Net_SSH2 { // passwords won't be filtered out and select other packets may not be correctly // identified case NET_SSH2_LOG_REALTIME: - echo "
\r\n" . $this->_format_log(array($message), array($message_number)) . "\r\n
\r\n"; + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                            $stop = '
'; + } + echo $start . $this->_format_log(array($message), array($message_number)) . $stop; @flush(); @ob_flush(); break; @@ -2483,7 +3010,7 @@ class Net_SSH2 { 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; + $filename = NET_SSH2_LOG_REALTIME_FILENAME; $fp = fopen($filename, 'w'); $this->realtime_log_file = $fp; } @@ -2518,39 +3045,49 @@ class Net_SSH2 { */ 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; + /* The maximum amount of data allowed is determined by the maximum + packet size for the channel, and the current window size, whichever + is smaller. + + -- http://tools.ietf.org/html/rfc4254#section-5.2 */ + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ) - 4; + while (strlen($data) > $max_size) { + if (!$this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + // using an invalid channel will let the buffers be built up for the valid channels + $output = $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ) - 4; } $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]) + $max_size, + $this->_string_shift($data, $max_size) ); + $this->window_size_client_to_server[$client_channel]-= $max_size + 4; + 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; + if (strlen($data) >= $this->window_size_client_to_server[$client_channel] - 4) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; } + $this->window_size_client_to_server[$client_channel]-= strlen($data) + 4; + return $this->_send_binary_packet(pack('CN2a*', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], @@ -2858,6 +3395,20 @@ class Net_SSH2 { return $this->languages_client_to_server; } + /** + * Returns the banner message. + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @return String + * @access public + */ + function getBannerMessage() + { + return $this->banner_message; + } + /** * Returns the server public host key. * @@ -2885,6 +3436,8 @@ class Net_SSH2 { switch ($this->signature_format) { case 'ssh-dss': + $zero = new Math_BigInteger(); + $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); @@ -2909,9 +3462,13 @@ class Net_SSH2 { $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); + switch (true) { + case $r->equals($zero): + case $r->compare($q) >= 0: + case $s->equals($zero): + case $s->compare($q) >= 0: + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $w = $s->modInverse($q); @@ -3006,4 +3563,24 @@ class Net_SSH2 { } return $this->exit_status; } + + /** + * Is a path includable? + * + * @return Boolean + * @access private + */ + function _is_includable($suffix) + { + foreach (explode(PATH_SEPARATOR, get_include_path()) as $prefix) { + $ds = substr($prefix, -1) == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR; + $file = $prefix . $ds . $suffix; + + if (file_exists($file)) { + return true; + } + } + + return false; + } } diff --git a/apps/files_external/3rdparty/smb4php/smb.php b/apps/files_external/3rdparty/smb4php/smb.php index 87638271f0e..622942b052a 100644 --- a/apps/files_external/3rdparty/smb4php/smb.php +++ b/apps/files_external/3rdparty/smb4php/smb.php @@ -19,6 +19,10 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # +# Addition 17/12/2012 Frank Karlitschek (frank@owncloud.org) +# On the official website http://www.phpclasses.org/smb4php the +# license is listed as LGPL so we assume that this is +# dual-licensed GPL/LGPL ################################################################### define ('SMB4PHP_VERSION', '0.8'); @@ -126,7 +130,7 @@ class smb { // this put env is necessary to read the output of smbclient correctly $old_locale = getenv('LC_ALL'); putenv('LC_ALL=en_US.UTF-8'); - $output = popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r'); + $output = popen ('TZ=UTC '.SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r'); $gotInfo = false; $info = array (); $info['info']= array (); @@ -160,7 +164,7 @@ class smb { $i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master); break; case 'files': - list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/", trim ($regs[1]), $regs2) + list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|N|S|R]+)$/", trim ($regs[1]), $regs2) ? array (trim ($regs2[2]), trim ($regs2[1])) : array ('', trim ($regs[1])); list ($his, $im) = array ( diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index f78f3abf0fa..5b1cd86a170 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -9,6 +9,7 @@ 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\OwnCloud'] = 'files_external/lib/owncloud.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'; diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css index 9858c10ea35..11aeb10184b 100644 --- a/apps/files_external/css/settings.css +++ b/apps/files_external/css/settings.css @@ -12,8 +12,8 @@ span.error { background: #ce3702; } -td.mountPoint, td.backend { width:10em; } -td.remove>img { visibility:hidden; padding-top:0.8em; } +td.mountPoint, td.backend { width:160px; } +td.remove>img { visibility:hidden; padding-top:13px; } tr:hover>td.remove>img { visibility:visible; cursor:pointer; } #addMountPoint>td { border:none; } #addMountPoint>td.applicable { visibility:hidden; } diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js index 957daeb4d1f..6baaabe11b6 100644 --- a/apps/files_external/js/dropbox.js +++ b/apps/files_external/js/dropbox.js @@ -23,9 +23,12 @@ $(document).ready(function() { $(token).val(result.access_token); $(token_secret).val(result.access_token_secret); $(configured).val('true'); - OC.MountConfig.saveStorage(tr); - $(tr).find('.configuration input').attr('disabled', 'disabled'); - $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); + OC.MountConfig.saveStorage(tr, function(status) { + if (status) { + $(tr).find('.configuration input').attr('disabled', 'disabled'); + $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); + } + }); } else { OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); } @@ -77,7 +80,6 @@ $(document).ready(function() { 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"]'); @@ -88,10 +90,9 @@ $(document).ready(function() { $(configured).val('false'); $(token).val(result.data.request_token); $(token_secret).val(result.data.request_token_secret); - OC.MountConfig.saveStorage(tr); - statusSpan.removeClass(); - statusSpan.addClass('waiting'); - window.location = result.data.url; + OC.MountConfig.saveStorage(tr, function() { + window.location = result.data.url; + }); } else { OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); } diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js index b4be1c1dc41..068c2c13c66 100644 --- a/apps/files_external/js/google.js +++ b/apps/files_external/js/google.js @@ -32,11 +32,14 @@ $(document).ready(function() { if (result && result.status == 'success') { $(token).val(result.data.token); $(configured).val('true'); - OC.MountConfig.saveStorage(tr); - $(tr).find('.configuration input').attr('disabled', 'disabled'); - $(tr).find('.configuration').append($('') - .attr('id', 'access') - .text(t('files_external', 'Access granted'))); + OC.MountConfig.saveStorage(tr, function(status) { + if (status) { + $(tr).find('.configuration input').attr('disabled', 'disabled'); + $(tr).find('.configuration').append($('') + .attr('id', 'access') + .text(t('files_external', 'Access granted'))); + } + }); } else { OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Google Drive storage') @@ -99,7 +102,6 @@ $(document).ready(function() { var configured = $(this).parent().find('[data-parameter="configured"]'); var client_id = $(this).parent().find('[data-parameter="client_id"]').val(); var client_secret = $(this).parent().find('[data-parameter="client_secret"]').val(); - var statusSpan = $(tr).find('.status span'); if (client_id != '' && client_secret != '') { var token = $(this).parent().find('[data-parameter="token"]'); $.post(OC.filePath('files_external', 'ajax', 'google.php'), @@ -112,10 +114,9 @@ $(document).ready(function() { if (result && result.status == 'success') { $(configured).val('false'); $(token).val('false'); - OC.MountConfig.saveStorage(tr); - statusSpan.removeClass(); - statusSpan.addClass('waiting'); - window.location = result.data.url; + OC.MountConfig.saveStorage(tr, function(status) { + window.location = result.data.url; + }); } else { 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 886c324e338..895f97bd2c3 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -12,7 +12,7 @@ function updateStatus(statusEl, result){ } OC.MountConfig={ - saveStorage:function(tr) { + saveStorage:function(tr, callback) { var mountPoint = $(tr).find('.mountPoint input').val(); if (mountPoint == '') { return false; @@ -84,9 +84,15 @@ OC.MountConfig={ }, success: function(result) { status = updateStatus(statusSpan, result); + if (callback) { + callback(status); + } }, error: function(result){ status = updateStatus(statusSpan, result); + if (callback) { + callback(status); + } } }); }); @@ -137,9 +143,15 @@ OC.MountConfig={ }, success: function(result) { status = updateStatus(statusSpan, result); + if (callback) { + callback(status); + } }, error: function(result){ status = updateStatus(statusSpan, result); + if (callback) { + callback(status); + } } }); } diff --git a/apps/files_external/l10n/es_MX.php b/apps/files_external/l10n/es_MX.php new file mode 100644 index 00000000000..b508df8476a --- /dev/null +++ b/apps/files_external/l10n/es_MX.php @@ -0,0 +1,28 @@ + "Acceso concedido", +"Error configuring Dropbox storage" => "Error configurando el almacenamiento de Dropbox", +"Grant access" => "Conceder acceso", +"Please provide a valid Dropbox app key and secret." => "Por favor, proporcione un una clave válida de la app Dropbox y una clave secreta.", +"Error configuring Google Drive storage" => "Error configurando el almacenamiento de Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Advertencia: El cliente \"smbclient\" no se encuentra instalado. El montado de carpetas CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.", +"Warning: 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." => "Advertencia: El soporte de FTP en PHP no se encuentra instalado. El montado de carpetas FTP no es posible. Por favor pida al administrador de su sistema que lo instale.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Advertencia: El soporte de Curl en PHP no está activado ni instalado. El montado de ownCloud, WebDAV o GoogleDrive no es posible. Pida al administrador de su sistema que lo instale.", +"External Storage" => "Almacenamiento externo", +"Folder name" => "Nombre de la carpeta", +"External storage" => "Almacenamiento externo", +"Configuration" => "Configuración", +"Options" => "Opciones", +"Applicable" => "Aplicable", +"Add storage" => "Añadir almacenamiento", +"None set" => "No se ha configurado", +"All Users" => "Todos los usuarios", +"Groups" => "Grupos", +"Users" => "Usuarios", +"Delete" => "Eliminar", +"Enable User External Storage" => "Habilitar almacenamiento externo de usuario", +"Allow users to mount their own external storage" => "Permitir a los usuarios montar su propio almacenamiento externo", +"SSL root certificates" => "Certificados raíz SSL", +"Import Root Certificate" => "Importar certificado raíz" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_external/l10n/ru_RU.php b/apps/files_external/l10n/ru_RU.php deleted file mode 100644 index d517597f59c..00000000000 --- a/apps/files_external/l10n/ru_RU.php +++ /dev/null @@ -1,6 +0,0 @@ - "Опции", -"Delete" => "Удалить" -); -$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_external/l10n/sk.php b/apps/files_external/l10n/sk.php new file mode 100644 index 00000000000..3129cf5c411 --- /dev/null +++ b/apps/files_external/l10n/sk.php @@ -0,0 +1,5 @@ + "Odstrániť" +); +$PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 12836c7b88c..01d588b3721 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -114,14 +114,24 @@ class OC_Mount_Config { } } - if(OC_Mount_Config::checkcurl()) $backends['\OC\Files\Storage\DAV']=array( - 'backend' => 'ownCloud / WebDAV', - 'configuration' => array( - 'host' => 'URL', - 'user' => 'Username', - 'password' => '*Password', - 'root' => '&Root', - 'secure' => '!Secure https://')); + if(OC_Mount_Config::checkcurl()){ + $backends['\OC\Files\Storage\DAV']=array( + 'backend' => 'WebDAV', + 'configuration' => array( + 'host' => 'URL', + 'user' => 'Username', + 'password' => '*Password', + 'root' => '&Root', + 'secure' => '!Secure https://')); + $backends['\OC\Files\Storage\OwnCloud']=array( + 'backend' => 'ownCloud', + 'configuration' => array( + 'host' => 'URL', + 'user' => 'Username', + 'password' => '*Password', + 'root' => '&Remote subfolder', + 'secure' => '!Secure https://')); + } $backends['\OC\Files\Storage\SFTP']=array( 'backend' => 'SFTP', @@ -382,8 +392,7 @@ class OC_Mount_Config { * @return array */ public static function getCertificates() { - $view = \OCP\Files::getStorage('files_external'); - $path=\OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/'; + $path=OC_User::getHome(OC_User::getUser()) . '/files_external/uploads/'; \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) @@ -405,8 +414,7 @@ class OC_Mount_Config { * creates certificate bundle */ public static function createCertificateBundle() { - $view = \OCP\Files::getStorage("files_external"); - $path = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath(""); + $path=OC_User::getHome(OC_User::getUser()) . '/files_external'; $certs = OC_Mount_Config::getCertificates(); $fh_certs = fopen($path."/rootcerts.crt", 'w'); @@ -431,7 +439,7 @@ class OC_Mount_Config { */ public static function checksmbclient() { if(function_exists('shell_exec')) { - $output=shell_exec('which smbclient'); + $output=shell_exec('which smbclient 2> /dev/null'); return !empty($output); }else{ return false; diff --git a/apps/files_external/lib/owncloud.php b/apps/files_external/lib/owncloud.php new file mode 100644 index 00000000000..98314102a64 --- /dev/null +++ b/apps/files_external/lib/owncloud.php @@ -0,0 +1,51 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; + +/** + * ownCloud backend for external storage based on DAV backend. + * + * The ownCloud URL consists of three parts: + * http://%host/%context/remote.php/webdav/%root + * + */ +class OwnCloud extends \OC\Files\Storage\DAV{ + const OC_URL_SUFFIX = 'remote.php/webdav'; + + public function __construct($params) { + // extract context path from host if specified + // (owncloud install path on host) + $host = $params['host']; + $contextPath = ''; + $hostSlashPos = strpos($host, '/'); + if ($hostSlashPos !== false){ + $contextPath = substr($host, $hostSlashPos); + $host = substr($host, 0, $hostSlashPos); + } + + if (substr($contextPath , 1) !== '/'){ + $contextPath .= '/'; + } + + if (isset($params['root'])){ + $root = $params['root']; + if (substr($root, 1) !== '/'){ + $root = '/' . $root; + } + } + else{ + $root = '/'; + } + + $params['host'] = $host; + $params['root'] = $contextPath . self::OC_URL_SUFFIX . $root; + + parent::__construct($params); + } +} diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index 95e0cefa398..cd8a0e78864 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -10,6 +10,7 @@ namespace OC\Files\Storage; set_include_path(get_include_path() . PATH_SEPARATOR . \OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib'); require 'Net/SFTP.php'; +require 'Net/SFTP/Stream.php'; class SFTP extends \OC\Files\Storage\Common { private $host; @@ -205,16 +206,6 @@ class SFTP extends \OC\Files\Storage\Common { 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($absPath, $tmp); - return fopen($tmp, $mode); - case 'w': case 'wb': case 'a': @@ -227,38 +218,14 @@ class SFTP extends \OC\Files\Storage\Common { 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($absPath, $tmpFile); - } - - self::$tempFiles[$tmpFile]=$absPath; - return fopen('close://'.$tmpFile, $mode); + // FIXME: make client login lazy to prevent it when using fopen() + return fopen($this->constructUrl($path), $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)) { @@ -309,4 +276,9 @@ class SFTP extends \OC\Files\Storage\Common { return false; } } + + public function constructUrl($path) { + $url = 'sftp://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path; + return $url; + } } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 02f6cb5fc4f..f6f4cb16e87 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -14,6 +14,7 @@ class DAV extends \OC\Files\Storage\Common{ private $host; private $secure; private $root; + private $certPath; private $ready; /** * @var \Sabre_DAV_Client @@ -40,6 +41,12 @@ class DAV extends \OC\Files\Storage\Common{ } else { $this->secure = false; } + if ($this->secure === true) { + $certPath=\OC_User::getHome(\OC_User::getUser()) . '/files_external/rootcerts.crt'; + if (file_exists($certPath)) { + $this->certPath=$certPath; + } + } $this->root=isset($params['root'])?$params['root']:'/'; if ( ! $this->root || $this->root[0]!='/') { $this->root='/'.$this->root; @@ -58,20 +65,16 @@ class DAV extends \OC\Files\Storage\Common{ } $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 \Sabre_DAV_Client($settings); - $caview = \OCP\Files::getStorage('files_external'); - if ($caview) { - $certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt'; - if (file_exists($certPath)) { - $this->client->addTrustedCertificates($certPath); - } + if ($this->secure === true && $this->certPath) { + $this->client->addTrustedCertificates($this->certPath); } } @@ -79,7 +82,7 @@ class DAV extends \OC\Files\Storage\Common{ return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root; } - private function createBaseUri() { + protected function createBaseUri() { $baseUri='http'; if ($this->secure) { $baseUri.='s'; @@ -166,7 +169,14 @@ class DAV extends \OC\Files\Storage\Common{ curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().str_replace(' ', '%20', $path)); curl_setopt($curl, CURLOPT_FILE, $fp); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - + if ($this->secure === true) { + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); + if($this->certPath){ + curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); + } + } + curl_exec ($curl); curl_close ($curl); rewind($fp); @@ -214,7 +224,7 @@ class DAV extends \OC\Files\Storage\Common{ if (isset($response['{DAV:}quota-available-bytes'])) { return (int)$response['{DAV:}quota-available-bytes']; } else { - return 0; + return \OC\Files\SPACE_UNKNOWN; } } catch(\Exception $e) { return \OC\Files\SPACE_UNKNOWN; @@ -254,6 +264,13 @@ class DAV extends \OC\Files\Storage\Common{ curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path)); curl_setopt($curl, CURLOPT_PUT, true); + if ($this->secure === true) { + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); + if($this->certPath){ + curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); + } + } curl_exec ($curl); curl_close ($curl); } @@ -331,3 +348,4 @@ class DAV extends \OC\Files\Storage\Common{ } } } + diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index a523809e2f9..e296bfcb5b2 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -23,6 +23,13 @@ return array( 'password'=>'test', 'root'=>'/owncloud/files/webdav.php', ), + 'owncloud'=>array( + 'run'=>true, + 'host'=>'localhost/owncloud', + 'user'=>'test', + 'password'=>'test', + 'root'=>'', + ), 'google'=>array( 'run'=> false, 'configured' => 'true', diff --git a/apps/files_external/tests/owncloud.php b/apps/files_external/tests/owncloud.php new file mode 100644 index 00000000000..408a55864f2 --- /dev/null +++ b/apps/files_external/tests/owncloud.php @@ -0,0 +1,31 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Storage; + +class OwnCloud 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['owncloud']) or ! $this->config['owncloud']['run']) { + $this->markTestSkipped('ownCloud backend not configured'); + } + $this->config['owncloud']['root'] .= '/' . $id; //make sure we have an new empty folder to work in + $this->instance = new \OC\Files\Storage\OwnCloud($this->config['owncloud']); + $this->instance->mkdir('/'); + } + + public function tearDown() { + if ($this->instance) { + $this->instance->rmdir('/'); + } + } +} diff --git a/apps/files_sharing/ajax/publicpreview.php b/apps/files_sharing/ajax/publicpreview.php index 8c3085f1589..a52f522afac 100644 --- a/apps/files_sharing/ajax/publicpreview.php +++ b/apps/files_sharing/ajax/publicpreview.php @@ -36,7 +36,10 @@ if(!isset($linkedItem['uid_owner']) || !isset($linkedItem['file_source'])) { exit; } -$userId = $linkedItem['uid_owner']; +$rootLinkItem = OCP\Share::resolveReShare($linkedItem); +$userId = $rootLinkItem['uid_owner']; + +OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); \OC_Util::setupFS($userId); \OC\Files\Filesystem::initMountPoints($userId); $view = new \OC\Files\View('/' . $userId . '/files'); @@ -86,4 +89,4 @@ try{ } catch (\Exception $e) { \OC_Response::setStatus(500); \OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG); -} \ No newline at end of file +} diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index 0d827da28ea..4b716e764f4 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -44,6 +44,7 @@ if (version_compare($installedVersion, '0.3', '<')) { $shareType = OCP\Share::SHARE_TYPE_USER; $shareWith = $row['uid_shared_with']; } + OCP\JSON::checkUserExists($row['uid_owner']); OC_User::setUserId($row['uid_owner']); //we need to setup the filesystem for the user, otherwise OC_FileSystem::getRoot will fail and break OC_Util::setupFS($row['uid_owner']); diff --git a/apps/files_sharing/css/authenticate.css b/apps/files_sharing/css/authenticate.css index cebe906dd59..ef963ba7c65 100644 --- a/apps/files_sharing/css/authenticate.css +++ b/apps/files_sharing/css/authenticate.css @@ -11,16 +11,13 @@ margin: 6px; } -input[type="submit"]{ +input[type='submit'] { width: 45px; height: 45px; margin: 6px; - background-image: url('%webroot%/core/img/actions/confirm.svg'); - background-repeat: no-repeat; - background-position: center; } -#body-login input[type="submit"] { +#body-login input[type='submit'] { position: absolute; top: 0px; } diff --git a/apps/files_sharing/css/mobile.css b/apps/files_sharing/css/mobile.css new file mode 100644 index 00000000000..7d2116d190d --- /dev/null +++ b/apps/files_sharing/css/mobile.css @@ -0,0 +1,49 @@ +@media only screen and (max-width: 600px) { + +/* make header scroll up for single shares, more view of content on small screens */ +#header.share-file { + position: absolute !important; +} + +/* hide size and date columns */ +table th#headerSize, +table td.filesize, +table th#headerDate, +table td.date { + display: none; +} + +/* restrict length of displayed filename to prevent overflow */ +table td.filename .nametext { + max-width: 75% !important; +} + +/* on mobile, show single shared image at full width without margin */ +#imgframe { + width: 100%; + padding: 0; + margin-bottom: 35px; +} +/* some margin for the file type icon */ +#imgframe .publicpreview { + margin-top: 32px; +} + +/* always show actions on mobile */ +#fileList a.action { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)" !important; + filter: alpha(opacity=20) !important; + opacity: .2 !important; + display: inline !important; +} +/* some padding for better clickability */ +#fileList a.action img { + padding: 0 6px 0 12px; +} +/* hide text of the actions on mobile */ +#fileList a.action span { + display: none; +} + + +} diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index 3ccb35e327a..21f0c82b829 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -3,7 +3,7 @@ body { } #header { - background: #1d2d44 url('%webroot%/core/img/noise.png') repeat; + background-color: #1d2d44; height:32px; left:0; line-height:32px; @@ -14,39 +14,17 @@ body { padding:7px; } -#details { - color:#fff; - float: left; -} - -#public_upload, -#download { - font-weight:700; - margin: 0 0 0 .4em; - padding: 0 5px; - height: 32px; - float: left; - -} - -.header-right #details { - margin-right: 28px; -} - .header-right { padding: 0; height: 32px; } -#public_upload { - margin-left: 0.3em; -} - -#public_upload img, -#download img { - padding-left:.1em; - padding-right:.3em; - vertical-align:text-bottom; +#details { + color:#fff; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; + padding-right: 5px; } #controls { @@ -61,19 +39,18 @@ body { #noPreview { display:none; - padding-top:5em; + padding-top:80px; } footer { - margin-top: 45px; + margin-top: 65px; } p.info { color: #777; text-align: center; - width: 22em; margin: 0 auto; - padding: 20px; + padding: 20px 0; } p.info a { @@ -83,8 +60,8 @@ p.info a { #imgframe { height:75%; - padding-bottom:2em; - padding-top:2em; + padding-bottom:32px; + padding-top:32px; width:80%; margin:0 auto; } @@ -94,9 +71,13 @@ p.info a { max-width:100%; } -thead{ - background-color: white; - padding-left:0 !important; /* fixes multiselect bar offset on shared page */ +/* some margin for the file type icon */ +#imgframe .publicpreview { + margin-top: 10%; +} + +thead { + padding-left: 0 !important; /* fixes multiselect bar offset on shared page */ } #data-upload-form { @@ -110,41 +91,20 @@ thead{ margin: 0; } -#file_upload_start { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - filter: alpha(opacity=0); - opacity: 0; - z-index: 20; - position: absolute !important; - top: 0; - left: 0; - width: 100% !important; -} - -#publicUploadButtonMock { - position:relative; - display:block; - width:100%; - height:32px; - cursor:pointer; - z-index:10; - background-image:url('%webroot%/core/img/actions/upload.svg'); - background-repeat:no-repeat; - background-position:7px 8px; -} - -#publicUploadButtonMock span { - margin: 0 5px 0 28px; - color: #555; -} - +.directDownload, .directLink { margin-bottom: 20px; } - .directLink label { - font-weight: normal; - } - .directLink input { - margin-left: 10px; - width: 300px; - } +.directDownload .button img { + vertical-align: text-bottom; +} +.directLink label { + font-weight: normal; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; +} +.directLink input { + margin-left: 5px; + width: 300px; +} diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index eacd4096ed8..c1b7eee3fb7 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -9,43 +9,32 @@ function fileDownloadPath(dir, file) { $(document).ready(function() { - $('#data-upload-form').tipsy({gravity:'ne', fade:true}); - if (typeof FileActions !== 'undefined') { var mimetype = $('#mimetype').val(); // Show file preview if previewer is available, images are already handled by the template if (mimetype.substr(0, mimetype.indexOf('/')) != 'image' && $('.publicpreview').length === 0) { // Trigger default action if not download TODO var action = FileActions.getDefault(mimetype, 'file', OC.PERMISSION_READ); - if (typeof action === 'undefined') { - $('#noPreview').show(); - if (mimetype != 'httpd/unix-directory') { - // NOTE: Remove when a better file previewer solution exists - $('#content').remove(); - $('table').remove(); - } - } else { + if (typeof action !== 'undefined') { action($('#filename').val()); } } FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename) { - var tr = $('tr').filterAttr('data-file', filename); + var tr = FileList.findFileEl(filename); if (tr.length > 0) { window.location = $(tr).find('a.name').attr('href'); } }); - FileActions.register('file', 'Download', OC.PERMISSION_READ, '', function(filename) { - var tr = $('tr').filterAttr('data-file', filename); + + // override since the format is different + FileList.getDownloadUrl = function(filename, dir) { + // we use this because we need the service and token attributes + var tr = FileList.findFileEl(filename); if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href'); + return $(tr).find('a.name').attr('href') + '&download'; } - }); - FileActions.register('dir', 'Download', OC.PERMISSION_READ, '', function(filename) { - var tr = $('tr').filterAttr('data-file', filename); - if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href')+'&download'; - } - }); + return null; + }; } var file_upload_start = $('#file_upload_start'); @@ -58,15 +47,9 @@ $(document).ready(function() { }; }); - // Add Uploadprogress Wrapper to controls bar - $('#controls').append($('#additional_controls div#uploadprogresswrapper')); - - // Cancel upload trigger - $('#cancel_upload_button').click(function() { - OC.Upload.cancelUploads(); - procesSelection(); + $(document).on('click', '#directLink', function() { + $(this).focus(); + $(this).select(); }); - $('#directLink').focus(); - }); diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 340e0939445..36de452a55e 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -22,7 +22,7 @@ $(document).ready(function() { } else { var item = $('#dir').val() + '/' + filename; } - var tr = $('tr').filterAttr('data-file', filename); + var tr = FileList.findFileEl(filename); if ($(tr).data('type') == 'dir') { var itemType = 'folder'; } else { diff --git a/apps/files_sharing/l10n/da.php b/apps/files_sharing/l10n/da.php index aef3ad98811..849b0e28d30 100644 --- a/apps/files_sharing/l10n/da.php +++ b/apps/files_sharing/l10n/da.php @@ -1,5 +1,6 @@ "Delingen er beskyttet af kodeord", "The password is wrong. Try again." => "Kodeordet er forkert. Prøv igen.", "Password" => "Kodeord", "Sorry, this link doesn’t seem to work anymore." => "Desværre, dette link ser ikke ud til at fungerer længere.", @@ -13,6 +14,7 @@ $TRANSLATIONS = array( "Download" => "Download", "Upload" => "Upload", "Cancel upload" => "Fortryd upload", -"No preview available for" => "Forhåndsvisning ikke tilgængelig for" +"No preview available for" => "Forhåndsvisning ikke tilgængelig for", +"Direct link" => "Direkte link" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/l10n/de.php b/apps/files_sharing/l10n/de.php index 47dfbd525a9..d7879833ca9 100644 --- a/apps/files_sharing/l10n/de.php +++ b/apps/files_sharing/l10n/de.php @@ -1,20 +1,20 @@ "Diese Freigabe ist durch ein Passwort geschützt", -"The password is wrong. Try again." => "Bitte überprüfen sie Ihr Passwort und versuchen Sie es erneut.", +"The password is wrong. Try again." => "Bitte überprüfe Dein Passwort und versuche es erneut.", "Password" => "Passwort", "Sorry, this link doesn’t seem to work anymore." => "Entschuldigung, dieser Link scheint nicht mehr zu funktionieren.", "Reasons might be:" => "Gründe könnten sein:", -"the item was removed" => "Die Elemente wurden entfernt", +"the item was removed" => "Das Element wurde entfernt", "the link expired" => "Der Link ist abgelaufen", "sharing is disabled" => "Teilen ist deaktiviert", -"For more info, please ask the person who sent this link." => "Für mehr Informationen, frage bitte die Person, die dir diesen Link geschickt hat.", +"For more info, please ask the person who sent this link." => "Für mehr Informationen, frage bitte die Person, die Dir diesen Link geschickt hat.", "%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", -"Upload" => "Hochladen", +"Upload" => "Upload", "Cancel upload" => "Upload abbrechen", "No preview available for" => "Es ist keine Vorschau verfügbar für", -"Direct link" => "Direkte Verlinkung" +"Direct link" => "Direkter Link" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/l10n/el.php b/apps/files_sharing/l10n/el.php index 79387a91472..3ea666504b1 100644 --- a/apps/files_sharing/l10n/el.php +++ b/apps/files_sharing/l10n/el.php @@ -1,19 +1,19 @@ "Αυτός ο κοινόχρηστος φάκελος προστατεύεται με κωδικό", -"The password is wrong. Try again." => "Εσφαλμένο συνθηματικό. Προσπαθήστε ξανά.", -"Password" => "Συνθηματικό", +"The password is wrong. Try again." => "Εσφαλμένος κωδικός πρόσβασης. Προσπαθήστε ξανά.", +"Password" => "Κωδικός πρόσβασης", "Sorry, this link doesn’t seem to work anymore." => "Συγγνώμη, αυτός ο σύνδεσμος μοιάζει να μην ισχύει πια.", "Reasons might be:" => "Οι λόγοι μπορεί να είναι:", "the item was removed" => "το αντικείμενο απομακρύνθηκε", "the link expired" => "ο σύνδεσμος έληξε", "sharing is disabled" => "ο διαμοιρασμός απενεργοποιήθηκε", "For more info, please ask the person who sent this link." => "Για περισσότερες πληροφορίες, παρακαλώ ρωτήστε το άτομο που σας έστειλε αυτόν τον σύνδεσμο.", -"%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" => "Λήψη", "Upload" => "Μεταφόρτωση", -"Cancel upload" => "Ακύρωση αποστολής", +"Cancel upload" => "Ακύρωση μεταφόρτωσης", "No preview available for" => "Δεν υπάρχει διαθέσιμη προεπισκόπηση για", "Direct link" => "Άμεσος σύνδεσμος" ); diff --git a/apps/files_sharing/l10n/es_CL.php b/apps/files_sharing/l10n/es_CL.php new file mode 100644 index 00000000000..31dc045870c --- /dev/null +++ b/apps/files_sharing/l10n/es_CL.php @@ -0,0 +1,6 @@ + "Clave", +"Upload" => "Subir" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/l10n/es_MX.php b/apps/files_sharing/l10n/es_MX.php new file mode 100644 index 00000000000..9100ef8b35f --- /dev/null +++ b/apps/files_sharing/l10n/es_MX.php @@ -0,0 +1,20 @@ + "Este elemento compartido esta protegido por contraseña", +"The password is wrong. Try again." => "La contraseña introducida es errónea. Inténtelo de nuevo.", +"Password" => "Contraseña", +"Sorry, this link doesn’t seem to work anymore." => "Lo siento, este enlace al parecer ya no funciona.", +"Reasons might be:" => "Las causas podrían ser:", +"the item was removed" => "el elemento fue eliminado", +"the link expired" => "el enlace expiró", +"sharing is disabled" => "compartir está desactivado", +"For more info, please ask the person who sent this link." => "Para mayor información, contacte a la persona que le envió el enlace.", +"%s shared the folder %s with you" => "%s compartió la carpeta %s contigo", +"%s shared the file %s with you" => "%s compartió el archivo %s contigo", +"Download" => "Descargar", +"Upload" => "Subir", +"Cancel upload" => "Cancelar subida", +"No preview available for" => "No hay vista previa disponible para", +"Direct link" => "Enlace directo" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/l10n/id.php b/apps/files_sharing/l10n/id.php index e91ef94bf38..865a951c2d2 100644 --- a/apps/files_sharing/l10n/id.php +++ b/apps/files_sharing/l10n/id.php @@ -1,11 +1,20 @@ "Berbagi ini dilindungi sandi", +"The password is wrong. Try again." => "Sandi salah. Coba lagi", "Password" => "Sandi", +"Sorry, this link doesn’t seem to work anymore." => "Maaf, tautan ini tampaknya tidak berfungsi lagi.", +"Reasons might be:" => "Alasan mungkin:", +"the item was removed" => "item telah dihapus", +"the link expired" => "tautan telah kadaluarsa", +"sharing is disabled" => "berbagi dinonaktifkan", +"For more info, please ask the person who sent this link." => "Untuk info lebih lanjut, silakan tanyakan orang yang mengirim tautan ini.", "%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", +"%s shared the file %s with you" => "%s membagikan berkas %s dengan Anda", "Download" => "Unduh", "Upload" => "Unggah", -"Cancel upload" => "Batal pengunggahan", -"No preview available for" => "Tidak ada pratinjau tersedia untuk" +"Cancel upload" => "Batal unggah", +"No preview available for" => "Tidak ada pratinjau yang tersedia untuk", +"Direct link" => "Tautan langsung" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_sharing/l10n/ko.php b/apps/files_sharing/l10n/ko.php index 90f59ed1673..03c4c1aea94 100644 --- a/apps/files_sharing/l10n/ko.php +++ b/apps/files_sharing/l10n/ko.php @@ -1,18 +1,20 @@ "비밀번호가 틀립니다. 다시 입력해주세요.", +"This share is password-protected" => "이 공유는 암호로 보호되어 있습니다", +"The password is wrong. Try again." => "암호가 잘못되었습니다. 다시 입력해 주십시오.", "Password" => "암호", -"Sorry, this link doesn’t seem to work anymore." => "죄송합니다만 이 링크는 더이상 작동되지 않습니다.", +"Sorry, this link doesn’t seem to work anymore." => "죄송합니다. 이 링크는 더 이상 작동하지 않습니다.", "Reasons might be:" => "이유는 다음과 같을 수 있습니다:", -"the item was removed" => "이 항목은 삭제되었습니다", -"the link expired" => "링크가 만료되었습니다", -"sharing is disabled" => "공유가 비활성되었습니다", -"For more info, please ask the person who sent this link." => "더 자세한 설명은 링크를 보내신 분에게 여쭤보십시오", +"the item was removed" => "항목이 삭제됨", +"the link expired" => "링크가 만료됨", +"sharing is disabled" => "공유가 비활성화됨", +"For more info, please ask the person who sent this link." => "자세한 정보는 링크를 보낸 사람에게 문의하십시오.", "%s shared the folder %s with you" => "%s 님이 폴더 %s을(를) 공유하였습니다", "%s shared the file %s with you" => "%s 님이 파일 %s을(를) 공유하였습니다", "Download" => "다운로드", "Upload" => "업로드", "Cancel upload" => "업로드 취소", -"No preview available for" => "다음 항목을 미리 볼 수 없음:" +"No preview available for" => "다음 항목을 미리 볼 수 없음:", +"Direct link" => "직접 링크" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_sharing/l10n/ru_RU.php b/apps/files_sharing/l10n/ru_RU.php deleted file mode 100644 index 2686b1e852d..00000000000 --- a/apps/files_sharing/l10n/ru_RU.php +++ /dev/null @@ -1,8 +0,0 @@ - "Пароль", -"Download" => "Загрузка", -"Upload" => "Загрузка", -"Cancel upload" => "Отмена загрузки" -); -$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_sharing/l10n/sk.php b/apps/files_sharing/l10n/sk.php new file mode 100644 index 00000000000..72c9039571e --- /dev/null +++ b/apps/files_sharing/l10n/sk.php @@ -0,0 +1,5 @@ + "Stiahnuť" +); +$PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 84e90c71681..f828a4e840d 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -162,7 +162,7 @@ class Api { $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); if(!$view->is_dir($path)) { - return new \OC_OCS_Result(null, 404, "not a directory"); + return new \OC_OCS_Result(null, 400, "not a directory"); } $content = $view->getDirectoryContent($path); @@ -220,10 +220,8 @@ class Api { $shareWith = isset($_POST['password']) ? $_POST['password'] : null; //check public link share $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - if(isset($_POST['publicUpload']) && - ($encryptionEnabled || $publicUploadEnabled !== 'yes')) { - return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator"); + if(isset($_POST['publicUpload']) && $publicUploadEnabled !== 'yes') { + return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator"); } $publicUpload = isset($_POST['publicUpload']) ? $_POST['publicUpload'] : 'false'; // read, create, update (7) if public upload is enabled or @@ -231,7 +229,7 @@ class Api { $permissions = $publicUpload === 'true' ? 7 : 1; break; default: - return new \OC_OCS_Result(null, 404, "unknown share type"); + return new \OC_OCS_Result(null, 400, "unknown share type"); } try { @@ -243,7 +241,7 @@ class Api { $permissions ); } catch (\Exception $e) { - return new \OC_OCS_Result(null, 404, $e->getMessage()); + return new \OC_OCS_Result(null, 403, $e->getMessage()); } if ($token) { @@ -321,11 +319,8 @@ class Api { $permissions = isset($params['_put']['permissions']) ? (int)$params['_put']['permissions'] : null; $publicUploadStatus = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - $publicUploadEnabled = false; - if(!$encryptionEnabled && $publicUploadStatus === 'yes') { - $publicUploadEnabled = true; - } + $publicUploadEnabled = ($publicUploadStatus === 'yes') ? true : false; + // only change permissions for public shares if public upload is enabled // and we want to set permissions to 1 (read only) or 7 (allow upload) @@ -363,9 +358,8 @@ class Api { private static function updatePublicUpload($share, $params) { $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - if($encryptionEnabled || $publicUploadEnabled !== 'yes') { - return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator"); + if($publicUploadEnabled !== 'yes') { + return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator"); } if ($share['item_type'] !== 'folder' || diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 90440d08f4e..425d51113b1 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -127,7 +127,18 @@ class Shared_Cache extends Cache { return $files; } else { if ($cache = $this->getSourceCache($folder)) { - return $cache->getFolderContents($this->files[$folder]); + $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); + foreach ($sourceFolderContent as $key => $c) { + $ownerPathParts = explode('/', \OC_Filesystem::normalizePath($c['path'])); + $userPathParts = explode('/', \OC_Filesystem::normalizePath($folder)); + $usersPath = 'files/Shared/'.$userPathParts[1]; + foreach (array_slice($ownerPathParts, 3) as $part) { + $usersPath .= '/'.$part; + } + $sourceFolderContent[$key]['usersPath'] = $usersPath; + } + + return $sourceFolderContent; } } return false; @@ -260,7 +271,7 @@ class Shared_Cache extends Cache { return $this->searchWithWhere($where, $value); } - + /** * The maximum number of placeholders that can be used in an SQL query. * Value MUST be <= 1000 for oracle: @@ -268,7 +279,7 @@ class Shared_Cache extends Cache { * FIXME we should get this from doctrine as other DBs allow a lot more placeholders */ const MAX_SQL_CHUNK_SIZE = 1000; - + /** * search for files with a custom where clause and value * the $wherevalue will be array_merge()d with the file id chunks @@ -282,16 +293,16 @@ class Shared_Cache extends Cache { $ids = $this->getAll(); $files = array(); - + // divide into chunks $chunks = array_chunk($ids, $chunksize); - + foreach ($chunks as $chunk) { $placeholders = join(',', array_fill(0, count($chunk), '?')); $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` WHERE ' . $sqlwhere . ' `fileid` IN (' . $placeholders . ')'; - + $stmt = \OC_DB::prepare($sql); $result = $stmt->execute(array_merge(array($wherevalue), $chunk)); diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index e2978e12bfb..1dc53428a7f 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -42,6 +42,19 @@ class Shared_Permissions extends Permissions { } } + private function getFile($fileId, $user) { + if ($fileId == -1) { + return \OCP\PERMISSION_READ; + } + $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, + null, false); + if ($source) { + return $source['permissions']; + } else { + return -1; + } + } + /** * set the permissions of a file * @@ -82,7 +95,7 @@ class Shared_Permissions extends Permissions { if ($parentId === -1) { return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_PERMISSIONS); } - $permissions = $this->get($parentId, $user); + $permissions = $this->getFile($parentId, $user); $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `parent` = ?'); $result = $query->execute(array($parentId)); $filePermissions = array(); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 07e7a4ca0c5..c956c55a1df 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -172,7 +172,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $source['fileOwner'] = $fileOwner; return $source; } - \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG); return false; } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 3116cd717fb..afe5dffdebd 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -279,43 +279,26 @@ class Shared extends \OC\Files\Storage\Common { if ($this->isDeletable($path)) { 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)) { - $itemType = 'folder'; - } else { - $itemType = 'file'; - } - return \OCP\Share::unshareFromSelf($itemType, $path); } } return false; } public function rename($path1, $path2) { - // Check for partial files - if (pathinfo($path1, PATHINFO_EXTENSION) === 'part') { - if ($oldSource = $this->getSourcePath($path1)) { + // 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); + // Within the same folder, we only need UPDATE permissions + if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) { list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - $newInternalPath = substr($oldInternalPath, 0, -5); + list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); 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); - // Within the same folder, we only need UPDATE permissions - if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); - return $storage->rename($oldInternalPath, $newInternalPath); - // otherwise DELETE and CREATE permissions required - } elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { - $rootView = new \OC\Files\View(''); - return $rootView->rename($oldSource, $newSource); - } + // otherwise DELETE and CREATE permissions required + } elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { + $rootView = new \OC\Files\View(''); + return $rootView->rename($oldSource, $newSource); } } return false; diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index 44ebb5cd3cd..23ebc9fb811 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -112,8 +112,12 @@ class Shared_Updater { */ 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 (isset($params['uidOwner'])) { + $uidOwner = $params['uidOwner']; + } else { + $uidOwner = \OCP\User::getUser(); + } + $users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true, false); if (!empty($users)) { while (!empty($users)) { $reshareUsers = array(); diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php index c40cf6911b8..285b1a58c6e 100644 --- a/apps/files_sharing/lib/watcher.php +++ b/apps/files_sharing/lib/watcher.php @@ -32,7 +32,7 @@ class Shared_Watcher extends Watcher { * @param string $path */ public function checkUpdate($path) { - if ($path != '' && parent::checkUpdate($path)) { + if ($path != '' && parent::checkUpdate($path) === true) { // since checkUpdate() has already updated the size of the subdirs, // only apply the update to the owner's parent dirs diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index f4042f65248..0ca923fff08 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -35,7 +35,7 @@ function determineIcon($file, $sharingRoot, $sharingToken) { if (isset($_GET['t'])) { $token = $_GET['t']; - $linkItem = OCP\Share::getShareByToken($token); + $linkItem = OCP\Share::getShareByToken($token, false); if (is_array($linkItem) && isset($linkItem['uid_owner'])) { // seems to be a valid share $type = $linkItem['item_type']; @@ -43,10 +43,10 @@ if (isset($_GET['t'])) { $shareOwner = $linkItem['uid_owner']; $path = null; $rootLinkItem = OCP\Share::resolveReShare($linkItem); - $fileOwner = $rootLinkItem['uid_owner']; - if (isset($fileOwner)) { + if (isset($rootLinkItem['uid_owner'])) { + OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); OC_Util::tearDownFS(); - OC_Util::setupFS($fileOwner); + OC_Util::setupFS($rootLinkItem['uid_owner']); $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); } } @@ -111,6 +111,7 @@ if (isset($path)) { } } $basePath = $path; + $rootName = basename($path); if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($basePath . $_GET['path'])) { $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']); $path .= $getPath; @@ -136,6 +137,7 @@ if (isset($path)) { } else { OCP\Util::addScript('files', 'file-upload'); OCP\Util::addStyle('files_sharing', 'public'); + OCP\Util::addStyle('files_sharing', 'mobile'); OCP\Util::addScript('files_sharing', 'public'); OCP\Util::addScript('files', 'fileactions'); OCP\Util::addScript('files', 'jquery.iframe-transport'); @@ -158,7 +160,6 @@ if (isset($path)) { if ($linkItem['item_type'] !== 'folder') { $allowPublicUploadEnabled = false; } - $tmpl->assign('allowPublicUploadEnabled', $allowPublicUploadEnabled); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); @@ -188,8 +189,8 @@ if (isset($path)) { } else { $i['extension'] = ''; } - $i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']); } + $i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']); $i['directory'] = $getPath; $i['permissions'] = OCP\PERMISSION_READ; $i['icon'] = determineIcon($i, $basePath, $token); @@ -216,6 +217,7 @@ if (isset($path)) { $list->assign('sharingroot', $basePath); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb); + $breadcrumbNav->assign('rootBreadCrumb', $rootName); $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); $fileHeader = (!isset($files) or count($files) > 0); @@ -224,7 +226,8 @@ if (isset($path)) { $folder->assign('fileList', $list->fetchPage()); $folder->assign('breadcrumb', $breadcrumbNav->fetchPage()); $folder->assign('dir', $getPath); - $folder->assign('isCreatable', false); + $folder->assign('isCreatable', $allowPublicUploadEnabled); + $folder->assign('dirToken', $linkItem['token']); $folder->assign('permissions', OCP\PERMISSION_READ); $folder->assign('isPublic',true); $folder->assign('publicUploadEnabled', 'no'); diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 6b98e6c9f34..928be93fc96 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -9,7 +9,7 @@

- +

diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 1d527dca8eb..3ddaf4446df 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -9,64 +9,14 @@ -
@@ -82,25 +32,28 @@
- -
- -
- +
+ + +
- + +
-
-

- getLongFooter()); ?> -

-
+ + +
+

+ getLongFooter()); ?> +

+
diff --git a/apps/files_sharing/tests/base.php b/apps/files_sharing/tests/base.php index 689c80cb9e6..3e283271f5d 100644 --- a/apps/files_sharing/tests/base.php +++ b/apps/files_sharing/tests/base.php @@ -132,8 +132,8 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase { $share = Null; - if ($result && $result->numRows() > 0) { - $share = $result->fetchRow(); + if ($result) { + $share = $result->fetchRow(); } return $share; diff --git a/apps/files_sharing/tests/permissions.php b/apps/files_sharing/tests/permissions.php new file mode 100644 index 00000000000..e301d384a49 --- /dev/null +++ b/apps/files_sharing/tests/permissions.php @@ -0,0 +1,110 @@ + + * + * 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 . + * + */ +require_once __DIR__ . '/base.php'; + +class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base { + + function setUp() { + parent::setUp(); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + // prepare user1's dir structure + $textData = "dummy file data\n"; + $this->view->mkdir('container'); + $this->view->mkdir('container/shareddir'); + $this->view->mkdir('container/shareddir/subdir'); + $this->view->mkdir('container/shareddirrestricted'); + $this->view->mkdir('container/shareddirrestricted/subdir'); + $this->view->file_put_contents('container/shareddir/textfile.txt', $textData); + $this->view->file_put_contents('container/shareddirrestricted/textfile1.txt', $textData); + + list($this->ownerStorage, $internalPath) = $this->view->resolvePath(''); + $this->ownerCache = $this->ownerStorage->getCache(); + $this->ownerStorage->getScanner()->scan(''); + + // share "shareddir" with user2 + $fileinfo = $this->view->getFileInfo('container/shareddir'); + \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2, 31); + $fileinfo2 = $this->view->getFileInfo('container/shareddirrestricted'); + \OCP\Share::shareItem('folder', $fileinfo2['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2, 7); + + // login as user2 + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // retrieve the shared storage + $this->secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); + list($this->sharedStorage, $internalPath) = $this->secondView->resolvePath('files/Shared/shareddir'); + $this->sharedCache = $this->sharedStorage->getCache(); + } + + function tearDown() { + $this->sharedCache->clear(); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + $fileinfo = $this->view->getFileInfo('container/shareddir'); + \OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2); + $fileinfo2 = $this->view->getFileInfo('container/shareddirrestricted'); + \OCP\Share::unshare('folder', $fileinfo2['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2); + + $this->view->deleteAll('container'); + + $this->ownerCache->clear(); + + parent::tearDown(); + } + + /** + * Test that the permissions of shared directory are returned correctly + */ + function testGetPermissions() { + $sharedDirPerms = $this->sharedStorage->getPermissions('shareddir'); + $this->assertEquals(31, $sharedDirPerms); + $sharedDirPerms = $this->sharedStorage->getPermissions('shareddir/textfile.txt'); + $this->assertEquals(31, $sharedDirPerms); + $sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted'); + $this->assertEquals(7, $sharedDirRestrictedPerms); + $sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted/textfile.txt'); + $this->assertEquals(7, $sharedDirRestrictedPerms); + } + + /** + * Test that the permissions of shared directory are returned correctly + */ + function testGetDirectoryPermissions() { + $contents = $this->secondView->getDirectoryContent('files/Shared/shareddir'); + $this->assertEquals('subdir', $contents[0]['name']); + $this->assertEquals(31, $contents[0]['permissions']); + $this->assertEquals('textfile.txt', $contents[1]['name']); + $this->assertEquals(31, $contents[1]['permissions']); + $contents = $this->secondView->getDirectoryContent('files/Shared/shareddirrestricted'); + $this->assertEquals('subdir', $contents[0]['name']); + $this->assertEquals(7, $contents[0]['permissions']); + $this->assertEquals('textfile1.txt', $contents[1]['name']); + $this->assertEquals(7, $contents[1]['permissions']); + } +} diff --git a/apps/files_trashbin/appinfo/database.xml b/apps/files_trashbin/appinfo/database.xml index aae334b1489..d08c3469b02 100644 --- a/apps/files_trashbin/appinfo/database.xml +++ b/apps/files_trashbin/appinfo/database.xml @@ -44,7 +44,7 @@ true 512 - + type text @@ -52,15 +52,15 @@ true 4 - + mime text true - 30 + 255 - + id_index @@ -68,7 +68,7 @@ ascending - + timestamp_index @@ -76,7 +76,7 @@ ascending - + user_index @@ -84,7 +84,7 @@ ascending - +
@@ -110,7 +110,7 @@ true 50 - + diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version index bd73f47072b..2eb3c4fe4ee 100644 --- a/apps/files_trashbin/appinfo/version +++ b/apps/files_trashbin/appinfo/version @@ -1 +1 @@ -0.4 +0.5 diff --git a/apps/files_trashbin/download.php b/apps/files_trashbin/download.php deleted file mode 100644 index 60328e1dddb..00000000000 --- a/apps/files_trashbin/download.php +++ /dev/null @@ -1,51 +0,0 @@ -. -* -*/ - -// 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/js/trash.js b/apps/files_trashbin/js/trash.js index 48e9629f7de..46d8b56308c 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -3,8 +3,8 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) { - var tr = $('tr').filterAttr('data-file', filename); - var deleteAction = $('tr').filterAttr('data-file', filename).children("td.date").children(".action.delete"); + var tr = FileList.findFileEl(filename); + var deleteAction = tr.children("td.date").children(".action.delete"); deleteAction.removeClass('delete-icon').addClass('progress-icon'); disableActions(); $.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), @@ -30,8 +30,8 @@ $(document).ready(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 tr = FileList.findFileEl(filename); + var deleteAction = tr.children("td.date").children(".action.delete"); deleteAction.removeClass('delete-icon').addClass('progress-icon'); disableActions(); $.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), @@ -73,7 +73,7 @@ $(document).ready(function() { var dirlisting = getSelectedFiles('dirlisting')[0]; disableActions(); for (var i = 0; i < files.length; i++) { - var deleteAction = $('tr').filterAttr('data-file', files[i]).children("td.date").children(".action.delete"); + var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); deleteAction.removeClass('delete-icon').addClass('progress-icon'); } @@ -119,7 +119,7 @@ $(document).ready(function() { } else { for (var i = 0; i < files.length; i++) { - var deleteAction = $('tr').filterAttr('data-file', files[i]).children("td.date").children(".action.delete"); + var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); deleteAction.removeClass('delete-icon').addClass('progress-icon'); } } @@ -152,6 +152,14 @@ $(document).ready(function() { $('#fileList').on('click', 'td.filename input', function() { var checkbox = $(this).parent().children('input:checkbox'); $(checkbox).parent().parent().toggleClass('selected'); + if ($(checkbox).is(':checked')) { + var selectedCount = $('td.filename input:checkbox:checked').length; + if (selectedCount === $('td.filename input:checkbox').length) { + $('#select_all').prop('checked', true); + } + } else { + $('#select_all').prop('checked',false); + } procesSelection(); }); @@ -161,7 +169,7 @@ $(document).ready(function() { event.preventDefault(); } var filename = $(this).parent().parent().attr('data-file'); - var tr = $('tr').filterAttr('data-file',filename); + var tr = FileList.findFileEl(filename); var renaming = tr.data('renaming'); if(!renaming && !FileList.isLoading(filename)){ if(mime.substr(0, 5) === 'text/'){ //no texteditor for now diff --git a/apps/files_trashbin/l10n/be.php b/apps/files_trashbin/l10n/be.php index 50df7ff5a97..6a34f1fe246 100644 --- a/apps/files_trashbin/l10n/be.php +++ b/apps/files_trashbin/l10n/be.php @@ -1,6 +1,5 @@ array("","","",""), -"_%n file_::_%n files_" => array("","","","") +"Error" => "Памылка" ); $PLURAL_FORMS = "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/el.php b/apps/files_trashbin/l10n/el.php index ffeafb7e9d5..b4ee30c578d 100644 --- a/apps/files_trashbin/l10n/el.php +++ b/apps/files_trashbin/l10n/el.php @@ -3,11 +3,11 @@ $TRANSLATIONS = array( "Couldn't delete %s permanently" => "Αδύνατη η μόνιμη διαγραφή του %s", "Couldn't restore %s" => "Αδυναμία επαναφοράς %s", "Error" => "Σφάλμα", -"restored" => "έγινε επαναφορά", +"restored" => "επαναφέρθηκαν", "Nothing in here. Your trash bin is empty!" => "Δεν υπάρχει τίποτα εδώ. Ο κάδος σας είναι άδειος!", "Name" => "Όνομα", "Restore" => "Επαναφορά", -"Deleted" => "Διαγράφηκε", +"Deleted" => "Διαγραμμένα", "Delete" => "Διαγραφή", "Deleted Files" => "Διαγραμμένα Αρχεία" ); diff --git a/apps/files_trashbin/l10n/es_MX.php b/apps/files_trashbin/l10n/es_MX.php index 0acad00e8b5..db7a617729b 100644 --- a/apps/files_trashbin/l10n/es_MX.php +++ b/apps/files_trashbin/l10n/es_MX.php @@ -1,6 +1,14 @@ array("",""), -"_%n file_::_%n files_" => array("","") +"Couldn't delete %s permanently" => "No se puede eliminar %s permanentemente", +"Couldn't restore %s" => "No se puede restaurar %s", +"Error" => "Error", +"restored" => "recuperado", +"Nothing in here. Your trash bin is empty!" => "No hay nada aquí. ¡Tu papelera esta vacía!", +"Name" => "Nombre", +"Restore" => "Recuperar", +"Deleted" => "Eliminado", +"Delete" => "Eliminar", +"Deleted Files" => "Archivos Eliminados" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php index 809ecfea15d..9c3cd2de15f 100644 --- a/apps/files_trashbin/l10n/ko.php +++ b/apps/files_trashbin/l10n/ko.php @@ -1,14 +1,14 @@ "%s를 영구적으로 삭제할수 없습니다", -"Couldn't restore %s" => "%s를 복원할수 없습니다", +"Couldn't delete %s permanently" => "%s을(를_ 영구적으로 삭제할 수 없습니다", +"Couldn't restore %s" => "%s을(를) 복원할 수 없습니다", "Error" => "오류", "restored" => "복원됨", -"Nothing in here. Your trash bin is empty!" => "현재 휴지통은 비어있습니다!", +"Nothing in here. Your trash bin is empty!" => "휴지통이 비어 있습니다!", "Name" => "이름", "Restore" => "복원", "Deleted" => "삭제됨", "Delete" => "삭제", -"Deleted Files" => "삭제된 파일들" +"Deleted Files" => "삭제된 파일" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/ru.php b/apps/files_trashbin/l10n/ru.php index b6de1948f77..06a4f864c67 100644 --- a/apps/files_trashbin/l10n/ru.php +++ b/apps/files_trashbin/l10n/ru.php @@ -8,6 +8,7 @@ $TRANSLATIONS = array( "Name" => "Имя", "Restore" => "Восстановить", "Deleted" => "Удалён", +"Delete" => "Удалить", "Deleted Files" => "Удаленные файлы" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php deleted file mode 100644 index dfc99f594fd..00000000000 --- a/apps/files_trashbin/l10n/ru_RU.php +++ /dev/null @@ -1,6 +0,0 @@ - "Ошибка", -"Delete" => "Удалить" -); -$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/sk.php b/apps/files_trashbin/l10n/sk.php index 94aaf9b3a94..3129cf5c411 100644 --- a/apps/files_trashbin/l10n/sk.php +++ b/apps/files_trashbin/l10n/sk.php @@ -1,6 +1,5 @@ array("","",""), -"_%n file_::_%n files_" => array("","","") +"Delete" => "Odstrániť" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 567f88b91b1..7544980e071 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -189,7 +189,7 @@ class Trashbin { if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) { $size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath)); if ($owner !== $user) { - $rootView->copy($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp); + self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp, $rootView); } $rootView->rename($owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp); } else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) { @@ -247,7 +247,7 @@ class Trashbin { if ($rootView->is_dir($keyfile)) { $size += self::calculateSize(new \OC\Files\View($keyfile)); if ($owner !== $user) { - $rootView->copy($keyfile, $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.d' . $timestamp); + self::copy_recursive($keyfile, $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.d' . $timestamp, $rootView); } $rootView->rename($keyfile, $user . '/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp); } else { @@ -265,7 +265,7 @@ class Trashbin { if ($rootView->is_dir($sharekeys)) { $size += self::calculateSize(new \OC\Files\View($sharekeys)); if ($owner !== $user) { - $rootView->copy($sharekeys, $owner . '/files_trashbin/share-keys/' . basename($ownerPath) . '.d' . $timestamp); + self::copy_recursive($sharekeys, $owner . '/files_trashbin/share-keys/' . basename($ownerPath) . '.d' . $timestamp, $rootView); } $rootView->rename($sharekeys, $user . '/files_trashbin/share-keys/' . $filename . '.d' . $timestamp); } else { @@ -734,7 +734,7 @@ class Trashbin { // calculate available space for trash bin // subtract size of files and current trash bin size from quota if ($softQuota) { - $rootInfo = $view->getFileInfo('/files/'); + $rootInfo = $view->getFileInfo('/files/', false); $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 diff --git a/apps/files_trashbin/templates/part.breadcrumb.php b/apps/files_trashbin/templates/part.breadcrumb.php index 4acc298adbe..fdf78c190d0 100644 --- a/apps/files_trashbin/templates/part.breadcrumb.php +++ b/apps/files_trashbin/templates/part.breadcrumb.php @@ -3,11 +3,11 @@
- - + setMimetype($mimetype); diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css index c53935711c7..16755f35a76 100644 --- a/apps/files_versions/css/versions.css +++ b/apps/files_versions/css/versions.css @@ -1,5 +1,5 @@ #dropdown.drop-versions { - width:24em; + width:320px; } #found_versions li { diff --git a/apps/files_versions/download.php b/apps/files_versions/download.php index 040a662e61b..2fe56d2e638 100644 --- a/apps/files_versions/download.php +++ b/apps/files_versions/download.php @@ -36,12 +36,7 @@ $view = new OC\Files\View('/'); $ftype = $view->getMimeType('/'.$uid.'/files/'.$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::setContentDispositionHeader(basename($filename), 'attachment'); OCP\Response::disableCaching(); header('Content-Length: '.$view->filesize($versionName)); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index 738a7ece6f2..4adf14745de 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -77,6 +77,7 @@ function goToVersionPage(url){ function createVersionsDropdown(filename, files) { var start = 0; + var fileEl; var html = '