diff --git a/.htaccess b/.htaccess index 048a56d6389dd0b05cc0b4bd36490f125631bdaa..616118b2ce8ae57bd914f595f1d6f069e52478c7 100755 --- a/.htaccess +++ b/.htaccess @@ -1,4 +1,5 @@ +php_value cgi.fix_pathinfo 1 SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1 @@ -32,4 +33,5 @@ RewriteRule ^remote/(.*) remote.php [QSA,L] AddType image/svg+xml svg svgz AddEncoding gzip svgz +AddDefaultCharset utf-8 Options -Indexes diff --git a/README b/README index 9b113c4f6742e981d1310f0af731120323009295..5f5d190cb0152dc5d85541614b71de81468ea298 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ A personal cloud which runs on your own server. http://ownCloud.org -Installation instructions: http://owncloud.org/support +Installation instructions: http://doc.owncloud.org/server/5.0/developer_manual/app/gettingstarted.html Contribution Guidelines: http://owncloud.org/dev/contribute/ Source code: https://github.com/owncloud diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 07977f5ddf1ddb04972bd027b903429889dc04db..5b697777e47cd701f09c4d61d1d7c95dfcb9c8d1 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -26,8 +26,7 @@ foreach ($_FILES['files']['error'] as $error) { UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'), UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ') . ini_get('upload_max_filesize'), - UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified' - . ' in the HTML form'), + UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'), UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'), UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'), UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'), @@ -48,7 +47,7 @@ $totalSize = 0; foreach ($files['size'] as $size) { $totalSize += $size; } -if ($totalSize > \OC\Files\Filesystem::free_space($dir)) { +if ($totalSize > $maxUploadFilesize) { OCP\JSON::error(array('data' => array('message' => $l->t('Not enough storage available'), 'uploadMaxFilesize' => $maxUploadFilesize, 'maxHumanFilesize' => $maxHumanFilesize))); diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index 7c82c839dabd9f609ace7b8527b883fb41001518..34800378537ead29daf6456563038a43dc3bbbe4 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -5,7 +5,7 @@ File Management AGPL Robin Appelman - 4.91 + 4.93 true diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 67bd569ceef3e7577bfaec11155656658b341482..fd06c61ce2ca9ae7f41091ba94a462a892588d3e 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -25,7 +25,7 @@ #trash { height:17px; margin: 0 1em; z-index:1010; float: right; } -#upload { +#upload { height:27px; padding:0; margin-left:0.2em; overflow:hidden; } #upload a { @@ -55,7 +55,7 @@ font-size:1.5em; font-weight:bold; color:#888; text-shadow:#fff 0 1px 0; } -table { position:relative; width:100%; } +#filestable { position: relative; top:37px; width:100%; } tbody tr { background-color:#fff; height:2.5em; } tbody tr:hover, tbody tr:active, tbody tr.selected { background-color:#f8f8f8; } tbody tr.selected { background-color:#eee; } @@ -74,9 +74,9 @@ table th#headerDate, table td.date { min-width:11em; padding:0 .1em 0 1em; text- /* Multiselect bar */ table.multiselect { top:63px; } -table.multiselect thead { position:fixed; top:82px; z-index:1; } +table.multiselect thead { position:fixed; top:82px; z-index:1; -moz-box-sizing: border-box; box-sizing: border-box; left: 0; padding-left: 64px; width:100%; } table.multiselect thead th { background:rgba(230,230,230,.8); color:#000; font-weight:bold; border-bottom:0; } - +table.multiselect #headerName { width: 100%; } table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; } table td.filename a.name { display:block; height:1.5em; vertical-align:middle; margin-left:3em; } table tr[data-type="dir"] td.filename a.name span.nametext {font-weight:bold; } @@ -87,9 +87,14 @@ table td.filename .nametext, .uploadtext, .modified { float:left; padding:.3em 0 table td.filename .nametext { overflow:hidden; text-overflow:ellipsis; } table td.filename .uploadtext { font-weight:normal; margin-left:.5em; } table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } + +/* File checkboxes */ #fileList tr td.filename>input[type="checkbox"]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 200ms; -moz-transition:opacity 200ms; -o-transition:opacity 200ms; transition:opacity 200ms; } #fileList tr td.filename>input[type="checkbox"]:hover:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } +/* Always show checkbox when selected */ #fileList tr td.filename>input[type="checkbox"]:checked:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } +#fileList tr.selected td.filename>input[type="checkbox"]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } + #fileList tr td.filename { position:relative; width:100%; -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; @@ -118,6 +123,24 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } .selectedActions a { display:inline; margin:-.5em 0; padding:.5em !important; } .selectedActions a img { position:relative; top:.3em; } +#fileList a.action { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + opacity: 0; + display:none; +} +#fileList tr:hover a.action, #fileList a.action.permanent { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=.5)"; + filter: alpha(opacity=.5); + opacity: .5; + display:inline; +} +#fileList tr:hover a.action:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=1)"; + filter: alpha(opacity=1); + opacity: 1; + display:inline; +} #scanning-message{ top:40%; left:40%; position:absolute; display:none; } diff --git a/apps/files/index.php b/apps/files/index.php index 434e98c6ea847a8b2f2316c570685be84b1446d4..20fbf7f93be9a1232f271188c10c5f28da047976 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -90,13 +90,13 @@ foreach (explode('/', $dir) as $i) { // make breadcrumb und filelist markup $list = new OCP\Template('files', 'part.list', ''); -$list->assign('files', $files, false); -$list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=', false); -$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/')), false); +$list->assign('files', $files); +$list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir='); +$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); $list->assign('disableSharing', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); -$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); -$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=', false); +$breadcrumbNav->assign('breadcrumb', $breadcrumb); +$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir='); $permissions = OCP\PERMISSION_READ; if (\OC\Files\Filesystem::isCreatable($dir . '/')) { @@ -125,8 +125,8 @@ if ($needUpgrade) { OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'keyboardshortcuts'); $tmpl = new OCP\Template('files', 'index', 'user'); - $tmpl->assign('fileList', $list->fetchPage(), false); - $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $tmpl->assign('fileList', $list->fetchPage()); + $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage()); $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir)); $tmpl->assign('isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/')); $tmpl->assign('permissions', $permissions); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index e1d8b60d31530f3ef3869dc5d30bda2ccbe0d6b1..38f5bab6f73a4ba0bf52c516d8eadf1cf24bc18e 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -81,7 +81,7 @@ var FileActions = { event.data.actionFunc(file); }; - $.each(actions, function (name, action) { + var addAction = function (name, action) { // NOTE: Temporary fix to prevent rename action in root of Shared directory if (name === 'Rename' && $('#dir').val() === '/Shared') { return true; @@ -92,7 +92,7 @@ var FileActions = { if (img.call) { img = img(file); } - var html = ''; + var html = ''; if (img) { html += ' '; } @@ -101,18 +101,27 @@ var FileActions = { var element = $(html); element.data('action', name); //alert(element); - element.on('click',{a:null, elem:parent, actionFunc:actions[name]},actionHandler); + element.on('click', {a: null, elem: parent, actionFunc: actions[name]}, actionHandler); parent.find('a.name>span.fileactions').append(element); } + }; + + $.each(actions, function (name, action) { + if (name !== 'Share') { + addAction(name, action); + } }); + if(actions.Share){ + addAction('Share', actions.Share); + } if (actions['Delete']) { var img = FileActions.icons['Delete']; if (img.call) { img = img(file); } - if (typeof trashBinApp !== 'undefined' && trashBinApp) { + if (typeof trashBinApp !== 'undefined' && trashBinApp) { var html = ''; } else { var html = ''; @@ -122,7 +131,7 @@ var FileActions = { element.append($('')); } element.data('action', actions['Delete']); - element.on('click',{a:null, elem:parent, actionFunc:actions['Delete']},actionHandler); + element.on('click', {a: null, elem: parent, actionFunc: actions['Delete']}, actionHandler); parent.parent().children().last().append(element); } }, @@ -146,7 +155,7 @@ $(document).ready(function () { } else { var downloadScope = 'file'; } - + if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) { FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); @@ -154,11 +163,11 @@ $(document).ready(function () { window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); }); } - - $('#fileList tr').each(function(){ + + $('#fileList tr').each(function () { FileActions.display($(this).children('td.filename')); }); - + }); FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4a66b33694d37148c516622ad3c4ba5375f48333..1db54f45bb82839d86cc41ec6f68a16178de636f 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -3,35 +3,92 @@ var FileList={ update:function(fileListHtml) { $('#fileList').empty().html(fileListHtml); }, - addFile:function(name,size,lastModified,loading,hidden){ - var basename, extension, simpleSize, sizeColor, lastModifiedTime, modifiedColor, - img=(loading)?OC.imagePath('core', 'loading.gif'):OC.imagePath('core', 'filetypes/file.png'), - html=''; - if(name.indexOf('.')!=-1){ + createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions){ + var td, simpleSize, basename, extension; + //containing tr + var tr = $('').attr({ + "data-type": type, + "data-size": size, + "data-file": name, + "data-permissions": permissions + }); + // filename td + td = $('').attr({ + "class": "filename", + "style": 'background-image:url('+iconurl+')' + }); + td.append(''); + var link_elem = $('').attr({ + "class": "name", + "href": linktarget + }); + //split extension from filename for non dirs + if (type != 'dir' && name.indexOf('.')!=-1) { basename=name.substr(0,name.lastIndexOf('.')); extension=name.substr(name.lastIndexOf('.')); - }else{ + } else { basename=name; extension=false; } - html+=''; - html+=''+escapeHTML(basename); + var name_span=$('').addClass('nametext').text(basename); + link_elem.append(name_span); if(extension){ - html+=''+escapeHTML(extension)+''; + name_span.append($('').addClass('extension').text(extension)); } - html+=''; - if(size!='Pending'){ + //dirs can show the number of uploaded files + if (type == 'dir') { + link_elem.append($('').attr({ + 'class': 'uploadtext', + 'currentUploads': 0 + })); + } + td.append(link_elem); + tr.append(td); + + //size column + if(size!=t('files', 'Pending')){ simpleSize=simpleFileSize(size); }else{ - simpleSize='Pending'; + simpleSize=t('files', 'Pending'); + } + var sizeColor = Math.round(200-Math.pow((size/(1024*1024)),2)); + var lastModifiedTime = Math.round(lastModified.getTime() / 1000); + td = $('').attr({ + "class": "filesize", + "title": humanFileSize(size), + "style": 'color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')' + }).text(simpleSize); + tr.append(td); + + // date column + var modifiedColor = Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*5); + td = $('').attr({ "class": "date" }); + td.append($('').attr({ + "class": "modified", + "title": formatDate(lastModified), + "style": 'color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')' + }).text( relative_modified_date(lastModified.getTime() / 1000) )); + tr.append(td); + return tr; + }, + addFile:function(name,size,lastModified,loading,hidden){ + var imgurl; + if (loading) { + imgurl = OC.imagePath('core', 'loading.gif'); + } else { + imgurl = OC.imagePath('core', 'filetypes/file.png'); } - sizeColor = Math.round(200-size/(1024*1024)*2); - lastModifiedTime=Math.round(lastModified.getTime() / 1000); - modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*14); - html+=''+simpleSize+''; - html+=''+relative_modified_date(lastModified.getTime() / 1000)+''; - html+=''; - FileList.insertElement(name,'file',$(html).attr('data-file',name)); + var tr = this.createRow( + 'file', + name, + imgurl, + OC.Router.generate('download', { file: $('#dir').val()+'/'+name }), + size, + lastModified, + $('#permissions').val() + ); + + FileList.insertElement(name, 'file', tr.attr('data-file',name)); var row = $('tr').filterAttr('data-file',name); if(loading){ row.data('loading',true); @@ -44,30 +101,18 @@ var FileList={ FileActions.display(row.find('td.filename')); }, addDir:function(name,size,lastModified,hidden){ - var html, td, link_elem, sizeColor, lastModifiedTime, modifiedColor; - html = $('').attr({ "data-type": "dir", "data-size": size, "data-file": name, "data-permissions": $('#permissions').val()}); - td = $('').attr({"class": "filename", "style": 'background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')' }); - td.append(''); - link_elem = $('').attr({ "class": "name", "href": OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/') }); - link_elem.append($('').addClass('nametext').text(name)); - link_elem.append($('').attr({'class': 'uploadtext', 'currentUploads': 0})); - td.append(link_elem); - html.append(td); - if(size!='Pending'){ - simpleSize=simpleFileSize(size); - }else{ - simpleSize='Pending'; - } - sizeColor = Math.round(200-Math.pow((size/(1024*1024)),2)); - lastModifiedTime=Math.round(lastModified.getTime() / 1000); - modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*5); - td = $('').attr({ "class": "filesize", "title": humanFileSize(size), "style": 'color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')'}).text(simpleSize); - html.append(td); - td = $('').attr({ "class": "date" }); - td.append($('').attr({ "class": "modified", "title": formatDate(lastModified), "style": 'color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')' }).text( relative_modified_date(lastModified.getTime() / 1000) )); - html.append(td); - FileList.insertElement(name,'dir',html); + var tr = this.createRow( + 'dir', + name, + OC.imagePath('core', 'filetypes/folder.png'), + OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/'), + size, + lastModified, + $('#permissions').val() + ); + + FileList.insertElement(name,'dir',tr); var row = $('tr').filterAttr('data-file',name); row.find('td.filename').draggable(dragOptions); row.find('td.filename').droppable(folderDropOptions); @@ -201,14 +246,17 @@ var FileList={ }, checkName:function(oldName, newName, isNewFile) { if (isNewFile || $('tr').filterAttr('data-file', newName).length > 0) { - $('#notification').data('oldName', oldName); - $('#notification').data('newName', newName); - $('#notification').data('isNewFile', isNewFile); - if (isNewFile) { - OC.Notification.showHtml(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'suggest name')+''+t('files', 'cancel')+''); - } else { - OC.Notification.showHtml(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'cancel')+''); - } + var html; + if(isNewFile){ + html = t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'suggest name')+' '+t('files', 'cancel')+''; + }else{ + html = t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'cancel')+''; + } + html = $('' + html + ''); + html.attr('data-oldName', oldName); + html.attr('data-newName', newName); + html.attr('data-isNewFile', isNewFile); + OC.Notification.showHtml(html); return true; } else { return false; @@ -246,9 +294,7 @@ var FileList={ FileList.lastAction = function() { FileList.finishReplace(); }; - if (isNewFile) { - OC.Notification.showHtml(t('files', 'replaced {new_name}', {new_name: newName})+''+t('files', 'undo')+''); - } else { + if (!isNewFile) { OC.Notification.showHtml(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+''+t('files', 'undo')+''); } }, @@ -270,8 +316,8 @@ var FileList={ do_delete:function(files){ if(files.substr){ files=[files]; - } - for (var i in files) { + } + for (var i=0; i'; @@ -289,7 +335,7 @@ var FileList={ if (result.status == 'success') { $.each(files,function(index,file){ var files = $('tr').filterAttr('data-file',file); - files.hide(); + files.remove(); files.find('input[type="checkbox"]').removeAttr('checked'); files.removeClass('selected'); }); @@ -299,7 +345,7 @@ var FileList={ var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash"); deleteAction[0].outerHTML = oldHTML; }); - } + } }); } }; @@ -331,19 +377,19 @@ $(document).ready(function(){ FileList.lastAction = null; OC.Notification.hide(); }); - $('#notification').on('click', '.replace', function() { + $('#notification:first-child').on('click', '.replace', function() { OC.Notification.hide(function() { - FileList.replace($('#notification').data('oldName'), $('#notification').data('newName'), $('#notification').data('isNewFile')); + FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); }); }); - $('#notification').on('click', '.suggest', function() { - $('tr').filterAttr('data-file', $('#notification').data('oldName')).show(); + $('#notification:first-child').on('click', '.suggest', function() { + $('tr').filterAttr('data-file', $('#notification > span').attr('data-oldName')).show(); OC.Notification.hide(); }); - $('#notification').on('click', '.cancel', function() { - if ($('#notification').data('isNewFile')) { + $('#notification:first-child').on('click', '.cancel', function() { + if ($('#notification > span').attr('data-isNewFile')) { FileList.deleteCanceled = false; - FileList.deleteFiles = [$('#notification').data('oldName')]; + FileList.deleteFiles = [$('#notification > span').attr('data-oldName')]; } }); FileList.useUndo=(window.onbeforeunload)?true:false; diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 5c5b430a8d4c96f890bb71658a70af949fb52f3b..464f77036857c6ec85982976cf5bfc92a8848f89 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -114,7 +114,7 @@ $(document).ready(function() { $(this).parent().children('#file_upload_start').trigger('click'); return false; }); - + // Show trash bin $('#trash a').live('click', function() { window.location=OC.filePath('files_trashbin', '', 'index.php'); @@ -162,9 +162,10 @@ $(document).ready(function() { var tr=$('tr').filterAttr('data-file',filename); var renaming=tr.data('renaming'); if(!renaming && !FileList.isLoading(filename)){ - var mime=$(this).parent().parent().data('mime'); - var type=$(this).parent().parent().data('type'); - var permissions = $(this).parent().parent().data('permissions'); + FileActions.currentFile = $(this).parent(); + var mime=FileActions.getCurrentMimeType(); + var type=FileActions.getCurrentType(); + var permissions = FileActions.getCurrentPermissions(); var action=FileActions.getDefault(mime,type, permissions); if(action){ event.preventDefault(); @@ -523,7 +524,7 @@ $(document).ready(function() { crumb.text(text); } - $(window).click(function(){ + $(document).click(function(){ $('#new>ul').hide(); $('#new').removeClass('active'); $('#new li').each(function(i,element){ @@ -594,7 +595,7 @@ $(document).ready(function() { var date=new Date(); FileList.addFile(name,0,date,false,hidden); var tr=$('tr').filterAttr('data-file',name); - tr.data('mime','text/plain').data('id',result.data.id); + tr.attr('data-mime','text/plain'); tr.attr('data-id', result.data.id); getMimeIcon('text/plain',function(path){ tr.find('td.filename').attr('style','background-image:url('+path+')'); @@ -685,9 +686,10 @@ $(document).ready(function() { breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth; }); - if ($('#controls .actions').length > 0) { - breadcrumbsWidth += $('#controls .actions').get(0).offsetWidth; - } + + $.each($('#controls .actions>div'), function(index, action) { + breadcrumbsWidth += $(action).get(0).offsetWidth; + }); function resizeBreadcrumbs(firstRun) { var width = $(this).width(); @@ -815,26 +817,26 @@ var createDragShadow = function(event){ //select dragged file $(event.target).parents('tr').find('td input:first').prop('checked',true); } - + var selectedFiles = getSelectedFiles(); - + if (!isDragSelected && selectedFiles.length == 1) { //revert the selection $(event.target).parents('tr').find('td input:first').prop('checked',false); } - + //also update class when we dragged more than one file if (selectedFiles.length > 1) { $(event.target).parents('tr').addClass('selected'); } - + // build dragshadow var dragshadow = $('
'); var tbody = $(''); dragshadow.append(tbody); - + var dir=$('#dir').val(); - + $(selectedFiles).each(function(i,elem){ var newtr = $('' +''+elem.name+''+humanFileSize(elem.size)+'' @@ -848,7 +850,7 @@ var createDragShadow = function(event){ }); } }); - + return dragshadow; } @@ -861,6 +863,10 @@ var dragOptions={ $('#fileList tr td.filename').addClass('ui-draggable'); } } +// sane browsers support using the distance option +if ( ! $.browser.msie) { + dragOptions['distance'] = 20; +} var folderDropOptions={ drop: function( event, ui ) { @@ -868,9 +874,9 @@ var folderDropOptions={ if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { return false; } - + var target=$.trim($(this).find('.nametext').text()); - + var files = ui.helper.find('tr'); $(files).each(function(i,row){ var dir = $(row).data('dir'); diff --git a/apps/files/js/jquery-visibility.js b/apps/files/js/jquery-visibility.js index a824bf6873076b284def2bd5a47c1d6ae52a5611..18f57d1f2bd19eaac9f3f2fa24c6833b1de46630 100644 --- a/apps/files/js/jquery-visibility.js +++ b/apps/files/js/jquery-visibility.js @@ -3,7 +3,7 @@ var prefix, property, -// In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior + // In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior eventName = 'onfocusin' in document && 'hasFocus' in document ? 'focusin focusout' : 'focus blur', prefixes = ['', 'moz', 'ms', 'o', 'webkit'], $support = $.support, @@ -19,12 +19,11 @@ $(/blur$/.test(eventName) ? window : document).on(eventName, function (event) { var type = event.type, - originalEvent = event.originalEvent, - toElement = originalEvent.toElement; -// If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement` should both be `null` or `undefined`; -// else, the page visibility hasn’t changed, but the user just clicked somewhere in the doc. -// In IE9, we need to check the `relatedTarget` property instead. - if (!/^focus./.test(type) || (toElement == undefined && originalEvent.fromElement == undefined && originalEvent.relatedTarget == undefined)) { + originalEvent = event.originalEvent; + // If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement` should both be `null` or `undefined`; + // else, the page visibility hasn’t changed, but the user just clicked somewhere in the doc. + // In IE9, we need to check the `relatedTarget` property instead. + if (!/^focus./.test(type) || originalEvent == undefined || (originalEvent.toElement == undefined && originalEvent.fromElement == undefined && originalEvent.relatedTarget == undefined)) { $event.trigger((property && document[property] || /^(?:blur|focusout)$/.test(type) ? 'hide' : 'show') + '.visibility'); } }); diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index b741815be458d781049689f6504260952e9b4af4..ce8a34acedb3ef8462d1659bde80f0145b526d14 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -5,7 +5,6 @@ "No file was uploaded" => "لم يتم ترفيع أي من الملفات", "Missing a temporary folder" => "المجلد المؤقت غير موجود", "Files" => "الملفات", -"Unshare" => "إلغاء مشاركة", "Delete" => "محذوف", "Close" => "إغلق", "Name" => "الاسم", @@ -19,6 +18,7 @@ "Folder" => "مجلد", "Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرفع بعض الملفات!", "Download" => "تحميل", +"Unshare" => "إلغاء مشاركة", "Upload too large" => "حجم الترفيع أعلى من المسموح", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "حجم الملفات التي تريد ترفيعها أعلى من المسموح على الخادم." ); diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php index ae49f5169992c6cd93d22c41dd9da060f90afc49..a4d23e5afb54e317b8a7b15ddd8aea2f343de914 100644 --- a/apps/files/l10n/bg_BG.php +++ b/apps/files/l10n/bg_BG.php @@ -1,21 +1,33 @@ "Липсва временна папка", +"Failed to write to disk" => "Възникна проблем при запис в диска", +"Invalid directory." => "Невалидна директория.", "Files" => "Файлове", +"Delete permanently" => "Изтриване завинаги", "Delete" => "Изтриване", "Rename" => "Преименуване", +"Pending" => "Чакащо", "replace" => "препокриване", "cancel" => "отказ", "undo" => "възтановяване", +"Upload Error" => "Възникна грешка при качването", +"Close" => "Затвори", "Upload cancelled." => "Качването е спряно.", "Name" => "Име", "Size" => "Размер", "Modified" => "Променено", +"1 folder" => "1 папка", +"{count} folders" => "{count} папки", +"1 file" => "1 файл", +"{count} files" => "{count} файла", "Upload" => "Качване", "Maximum upload size" => "Максимален размер за качване", "0 is unlimited" => "Ползвайте 0 за без ограничения", "Save" => "Запис", "New" => "Ново", +"Text file" => "Текстов файл", "Folder" => "Папка", +"Cancel upload" => "Спри качването", "Nothing in here. Upload something!" => "Няма нищо тук. Качете нещо.", "Download" => "Изтегляне", "Upload too large" => "Файлът който сте избрали за качване е прекалено голям" diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index dbff81cef6c5b424872c058622bf6fc67c214c45..aec5d7f9d9215e934c10358d6e782ad844d5c4b4 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -1,4 +1,7 @@ "%s কে স্থানান্তর করা সম্ভব হলো না - এই নামের ফাইল বিদ্যমান", +"Could not move %s" => "%s কে স্থানান্তর করা সম্ভব হলো না", +"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না", "No file was uploaded. Unknown error" => "কোন ফাইল আপলোড করা হয় নি। সমস্যা অজ্ঞাত।", "There is no error, the file uploaded with success" => "কোন সমস্যা নেই, ফাইল আপলোড সুসম্পন্ন হয়েছে", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "আপলোড করা ফাইলটি php.ini তে বর্ণিত upload_max_filesize নির্দেশিত আয়তন অতিক্রম করছেঃ", @@ -9,23 +12,21 @@ "Failed to write to disk" => "ডিস্কে লিখতে ব্যর্থ", "Invalid directory." => "ভুল ডিরেক্টরি", "Files" => "ফাইল", -"Unshare" => "ভাগাভাগি বাতিল ", "Delete" => "মুছে ফেল", "Rename" => "পূনঃনামকরণ", +"Pending" => "মুলতুবি", "{new_name} already exists" => "{new_name} টি বিদ্যমান", "replace" => "প্রতিস্থাপন", "suggest name" => "নাম সুপারিশ করুন", "cancel" => "বাতিল", -"replaced {new_name}" => "{new_name} প্রতিস্থাপন করা হয়েছে", -"undo" => "ক্রিয়া প্রত্যাহার", "replaced {new_name} with {old_name}" => "{new_name} কে {old_name} নামে প্রতিস্থাপন করা হয়েছে", +"undo" => "ক্রিয়া প্রত্যাহার", "'.' is an invalid file name." => "টি একটি অননুমোদিত নাম।", "File name cannot be empty." => "ফাইলের নামটি ফাঁকা রাখা যাবে না।", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "নামটি সঠিক নয়, '\\', '/', '<', '>', ':', '\"', '|', '?' এবং '*' অনুমোদিত নয়।", "Unable to upload your file as it is a directory or has 0 bytes" => "আপনার ফাইলটি আপলোড করা সম্ভব হলো না, কেননা এটি হয় একটি ফোল্ডার কিংবা এর আকার ০ বাইট", "Upload Error" => "আপলোড করতে সমস্যা ", "Close" => "বন্ধ", -"Pending" => "মুলতুবি", "1 file uploading" => "১টি ফাইল আপলোড করা হচ্ছে", "{count} files uploading" => "{count} টি ফাইল আপলোড করা হচ্ছে", "Upload cancelled." => "আপলোড বাতিল করা হয়েছে।", @@ -55,6 +56,7 @@ "Cancel upload" => "আপলোড বাতিল কর", "Nothing in here. Upload something!" => "এখানে কিছুই নেই। কিছু আপলোড করুন !", "Download" => "ডাউনলোড", +"Unshare" => "ভাগাভাগি বাতিল ", "Upload too large" => "আপলোডের আকারটি অনেক বড়", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "আপনি এই সার্ভারে আপলোড করার জন্য অনুমোদিত ফাইলের সর্বোচ্চ আকারের চেয়ে বৃহদাকার ফাইল আপলোড করার চেষ্টা করছেন ", "Files are being scanned, please wait." => "ফাইলগুলো স্ক্যান করা হচ্ছে, দয়া করে অপেক্ষা করুন।", diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index 49ea7f73abb451c083fecb0f8d8f74dfa4b074c0..43aaea31e8a722e7268338990b0e9fe610752ef7 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -1,4 +1,7 @@ "No s'ha pogut moure %s - Ja hi ha un fitxer amb aquest nom", +"Could not move %s" => " No s'ha pogut moure %s", +"Unable to rename file" => "No es pot canviar el nom del fitxer", "No file was uploaded. Unknown error" => "No s'ha carregat cap fitxer. Error desconegut", "There is no error, the file uploaded with success" => "El fitxer s'ha pujat correctament", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "L’arxiu que voleu carregar supera el màxim definit en la directiva upload_max_filesize del php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "El fitxer no s'ha pujat", "Missing a temporary folder" => "S'ha perdut un fitxer temporal", "Failed to write to disk" => "Ha fallat en escriure al disc", +"Not enough storage available" => "No hi ha prou espai disponible", "Invalid directory." => "Directori no vàlid.", "Files" => "Fitxers", -"Unshare" => "Deixa de compartir", "Delete permanently" => "Esborra permanentment", "Delete" => "Suprimeix", "Rename" => "Reanomena", +"Pending" => "Pendents", "{new_name} already exists" => "{new_name} ja existeix", "replace" => "substitueix", "suggest name" => "sugereix un nom", "cancel" => "cancel·la", -"replaced {new_name}" => "s'ha substituït {new_name}", -"undo" => "desfés", "replaced {new_name} with {old_name}" => "s'ha substituït {old_name} per {new_name}", +"undo" => "desfés", "perform delete operation" => "executa d'operació d'esborrar", "'.' is an invalid file name." => "'.' és un nom no vàlid per un fitxer.", "File name cannot be empty." => "El nom del fitxer no pot ser buit.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "No es pot pujar el fitxer perquè és una carpeta o té 0 bytes", "Upload Error" => "Error en la pujada", "Close" => "Tanca", -"Pending" => "Pendents", "1 file uploading" => "1 fitxer pujant", "{count} files uploading" => "{count} fitxers en pujada", "Upload cancelled." => "La pujada s'ha cancel·lat.", @@ -57,10 +59,12 @@ "Text file" => "Fitxer de text", "Folder" => "Carpeta", "From link" => "Des d'enllaç", -"Trash" => "Esborra", +"Deleted files" => "Fitxers esborrats", "Cancel upload" => "Cancel·la la pujada", +"You don’t have write permissions here." => "No teniu permisos d'escriptura aquí.", "Nothing in here. Upload something!" => "Res per aquí. Pugeu alguna cosa!", "Download" => "Baixa", +"Unshare" => "Deixa de compartir", "Upload too large" => "La pujada és massa gran", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada del servidor", "Files are being scanned, please wait." => "S'estan escanejant els fitxers, espereu", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index c2085a3aa9aad3a50b2499f050c41a50ed39a258..4c7dc898cc9a97ea213c69c2969e5a151d931968 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -1,4 +1,7 @@ "Nelze přesunout %s - existuje soubor se stejným názvem", +"Could not move %s" => "Nelze přesunout %s", +"Unable to rename file" => "Nelze přejmenovat soubor", "No file was uploaded. Unknown error" => "Soubor nebyl odeslán. Neznámá chyba", "There is no error, the file uploaded with success" => "Soubor byl odeslán úspěšně", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Odesílaný soubor přesahuje velikost upload_max_filesize povolenou v php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Žádný soubor nebyl odeslán", "Missing a temporary folder" => "Chybí adresář pro dočasné soubory", "Failed to write to disk" => "Zápis na disk selhal", +"Not enough storage available" => "Nedostatek dostupného úložného prostoru", "Invalid directory." => "Neplatný adresář", "Files" => "Soubory", -"Unshare" => "Zrušit sdílení", "Delete permanently" => "Trvale odstranit", "Delete" => "Smazat", "Rename" => "Přejmenovat", +"Pending" => "Čekající", "{new_name} already exists" => "{new_name} již existuje", "replace" => "nahradit", "suggest name" => "navrhnout název", "cancel" => "zrušit", -"replaced {new_name}" => "nahrazeno {new_name}", -"undo" => "zpět", "replaced {new_name} with {old_name}" => "nahrazeno {new_name} s {old_name}", +"undo" => "zpět", "perform delete operation" => "provést smazání", "'.' is an invalid file name." => "'.' je neplatným názvem souboru.", "File name cannot be empty." => "Název souboru nemůže být prázdný řetězec.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nelze odeslat Váš soubor, protože je to adresář nebo má velikost 0 bajtů", "Upload Error" => "Chyba odesílání", "Close" => "Zavřít", -"Pending" => "Čekající", "1 file uploading" => "odesílá se 1 soubor", "{count} files uploading" => "odesílám {count} souborů", "Upload cancelled." => "Odesílání zrušeno.", @@ -57,10 +59,11 @@ "Text file" => "Textový soubor", "Folder" => "Složka", "From link" => "Z odkazu", -"Trash" => "Koš", +"Deleted files" => "Odstraněné soubory", "Cancel upload" => "Zrušit odesílání", "Nothing in here. Upload something!" => "Žádný obsah. Nahrajte něco.", "Download" => "Stáhnout", +"Unshare" => "Zrušit sdílení", "Upload too large" => "Odeslaný soubor je příliš velký", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažíte odeslat, překračují limit velikosti odesílání na tomto serveru.", "Files are being scanned, please wait." => "Soubory se prohledávají, prosím čekejte.", diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index 71a5a56de57acb02c2ce801223c8c34b82c76fdf..c9042e30b4effcf0d3872b39801feb64d3d94d83 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -1,4 +1,7 @@ "Kunne ikke flytte %s - der findes allerede en fil med dette navn", +"Could not move %s" => "Kunne ikke flytte %s", +"Unable to rename file" => "Kunne ikke omdøbe fil", "No file was uploaded. Unknown error" => "Ingen fil blev uploadet. Ukendt fejl.", "There is no error, the file uploaded with success" => "Der er ingen fejl, filen blev uploadet med success", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uploadede fil overstiger upload_max_filesize direktivet i php.ini", @@ -7,18 +10,20 @@ "No file was uploaded" => "Ingen fil blev uploadet", "Missing a temporary folder" => "Mangler en midlertidig mappe", "Failed to write to disk" => "Fejl ved skrivning til disk.", +"Not enough storage available" => "Der er ikke nok plads til rådlighed", "Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", -"Unshare" => "Fjern deling", +"Delete permanently" => "Slet permanent", "Delete" => "Slet", "Rename" => "Omdøb", +"Pending" => "Afventer", "{new_name} already exists" => "{new_name} eksisterer allerede", "replace" => "erstat", "suggest name" => "foreslå navn", "cancel" => "fortryd", -"replaced {new_name}" => "erstattede {new_name}", -"undo" => "fortryd", "replaced {new_name} with {old_name}" => "erstattede {new_name} med {old_name}", +"undo" => "fortryd", +"perform delete operation" => "udfør slet operation", "'.' is an invalid file name." => "'.' er et ugyldigt filnavn.", "File name cannot be empty." => "Filnavnet kan ikke stå tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldigt navn, '\\', '/', '<', '>', ':' | '?', '\"', '', og '*' er ikke tilladt.", @@ -28,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Kunne ikke uploade din fil, da det enten er en mappe eller er tom", "Upload Error" => "Fejl ved upload", "Close" => "Luk", -"Pending" => "Afventer", "1 file uploading" => "1 fil uploades", "{count} files uploading" => "{count} filer uploades", "Upload cancelled." => "Upload afbrudt.", @@ -58,8 +62,10 @@ "Cancel upload" => "Fortryd upload", "Nothing in here. Upload something!" => "Her er tomt. Upload noget!", "Download" => "Download", +"Unshare" => "Fjern deling", "Upload too large" => "Upload for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne, du prøver at uploade, er større end den maksimale størrelse for fil-upload på denne server.", "Files are being scanned, please wait." => "Filerne bliver indlæst, vent venligst.", -"Current scanning" => "Indlæser" +"Current scanning" => "Indlæser", +"Upgrading filesystem cache..." => "Opgraderer filsystems cachen..." ); diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 4b38619eaa572a99961447380be459acb64fbaae..b78ffe1c168d298eeb4160a575d7ea34f418d01b 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -1,4 +1,7 @@ "%s konnte nicht verschoben werden - eine Datei mit diesem Namen existiert bereits.", +"Could not move %s" => "%s konnte nicht verschoben werden", +"Unable to rename file" => "Die Datei konnte nicht umbenannt werden", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", "There is no error, the file uploaded with success" => "Datei fehlerfrei hochgeladen.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", @@ -7,18 +10,19 @@ "No file was uploaded" => "Es wurde keine Datei hochgeladen.", "Missing a temporary folder" => "Temporärer Ordner fehlt.", "Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", +"Not enough storage available" => "Nicht genug Speicherplatz verfügbar", "Invalid directory." => "Ungültiges Verzeichnis.", "Files" => "Dateien", -"Unshare" => "Nicht mehr freigeben", +"Delete permanently" => "Permanent löschen", "Delete" => "Löschen", "Rename" => "Umbenennen", +"Pending" => "Ausstehend", "{new_name} already exists" => "{new_name} existiert bereits", "replace" => "ersetzen", "suggest name" => "Name vorschlagen", "cancel" => "abbrechen", -"replaced {new_name}" => "{new_name} wurde ersetzt", -"undo" => "rückgängig machen", "replaced {new_name} with {old_name}" => "{old_name} ersetzt durch {new_name}", +"undo" => "rückgängig machen", "perform delete operation" => "Löschvorgang ausführen", "'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", "File name cannot be empty." => "Der Dateiname darf nicht leer sein.", @@ -29,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Deine Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", "Upload Error" => "Fehler beim Upload", "Close" => "Schließen", -"Pending" => "Ausstehend", "1 file uploading" => "Eine Datei wird hoch geladen", "{count} files uploading" => "{count} Dateien werden hochgeladen", "Upload cancelled." => "Upload abgebrochen.", @@ -56,10 +59,11 @@ "Text file" => "Textdatei", "Folder" => "Ordner", "From link" => "Von einem Link", -"Trash" => "Papierkorb", +"Deleted files" => "Gelöschte Dateien", "Cancel upload" => "Upload abbrechen", "Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!", "Download" => "Herunterladen", +"Unshare" => "Nicht mehr freigeben", "Upload too large" => "Upload zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 71f24eba4c8fa2d11ae0074fdaeb777f2bcabfa9..dd5dd28472d1c4e72c14fa594cb48d0ce7e03120 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -1,4 +1,7 @@ "Konnte %s nicht verschieben - Datei mit diesem Namen existiert bereits", +"Could not move %s" => "Konnte %s nicht verschieben", +"Unable to rename file" => "Konnte Datei nicht umbenennen", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", "There is no error, the file uploaded with success" => "Es sind keine Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", @@ -7,20 +10,20 @@ "No file was uploaded" => "Es wurde keine Datei hochgeladen.", "Missing a temporary folder" => "Der temporäre Ordner fehlt.", "Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", +"Not enough storage available" => "Nicht genug Speicher vorhanden.", "Invalid directory." => "Ungültiges Verzeichnis.", "Files" => "Dateien", -"Unshare" => "Nicht mehr freigeben", "Delete permanently" => "Entgültig löschen", "Delete" => "Löschen", "Rename" => "Umbenennen", +"Pending" => "Ausstehend", "{new_name} already exists" => "{new_name} existiert bereits", "replace" => "ersetzen", "suggest name" => "Name vorschlagen", "cancel" => "abbrechen", -"replaced {new_name}" => "{new_name} wurde ersetzt", -"undo" => "rückgängig machen", "replaced {new_name} with {old_name}" => "{old_name} wurde ersetzt durch {new_name}", -"perform delete operation" => "Führe das Löschen aus", +"undo" => "rückgängig machen", +"perform delete operation" => "führe das Löschen aus", "'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", "File name cannot be empty." => "Der Dateiname darf nicht leer sein.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", "Upload Error" => "Fehler beim Upload", "Close" => "Schließen", -"Pending" => "Ausstehend", "1 file uploading" => "1 Datei wird hochgeladen", "{count} files uploading" => "{count} Dateien wurden hochgeladen", "Upload cancelled." => "Upload abgebrochen.", @@ -57,13 +59,14 @@ "Text file" => "Textdatei", "Folder" => "Ordner", "From link" => "Von einem Link", -"Trash" => "Abfall", +"Deleted files" => "Gelöschte Dateien", "Cancel upload" => "Upload abbrechen", "Nothing in here. Upload something!" => "Alles leer. Bitte laden Sie etwas hoch!", "Download" => "Herunterladen", +"Unshare" => "Nicht mehr freigeben", "Upload too large" => "Der Upload ist zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", "Current scanning" => "Scanne", -"Upgrading filesystem cache..." => "Aktualisiere den Dateisystem-Cache" +"Upgrading filesystem cache..." => "Aktualisiere den Dateisystem-Cache..." ); diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index a9c5fda0981a08c24d8d1da6399dc309981c10b0..63759f1201eed295f2c01c994604f811d7d963cd 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -1,4 +1,7 @@ "Αδυναμία μετακίνησης του %s - υπάρχει ήδη αρχείο με αυτό το όνομα", +"Could not move %s" => "Αδυναμία μετακίνησης του %s", +"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου", "No file was uploaded. Unknown error" => "Δεν ανέβηκε κάποιο αρχείο. Άγνωστο σφάλμα", "There is no error, the file uploaded with success" => "Δεν υπάρχει σφάλμα, το αρχείο εστάλει επιτυχώς", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Το απεσταλμένο αρχείο ξεπερνά την οδηγία upload_max_filesize στο php.ini:", @@ -7,18 +10,20 @@ "No file was uploaded" => "Κανένα αρχείο δεν στάλθηκε", "Missing a temporary folder" => "Λείπει ο προσωρινός φάκελος", "Failed to write to disk" => "Αποτυχία εγγραφής στο δίσκο", +"Not enough storage available" => "Μη επαρκής διαθέσιμος αποθηκευτικός χώρος", "Invalid directory." => "Μη έγκυρος φάκελος.", "Files" => "Αρχεία", -"Unshare" => "Διακοπή κοινής χρήσης", +"Delete permanently" => "Μόνιμη διαγραφή", "Delete" => "Διαγραφή", "Rename" => "Μετονομασία", +"Pending" => "Εκκρεμεί", "{new_name} already exists" => "{new_name} υπάρχει ήδη", "replace" => "αντικατέστησε", "suggest name" => "συνιστώμενο όνομα", "cancel" => "ακύρωση", -"replaced {new_name}" => "{new_name} αντικαταστάθηκε", -"undo" => "αναίρεση", "replaced {new_name} with {old_name}" => "αντικαταστάθηκε το {new_name} με {old_name}", +"undo" => "αναίρεση", +"perform delete operation" => "εκτέλεση διαδικασία διαγραφής", "'.' is an invalid file name." => "'.' είναι μη έγκυρο όνομα αρχείου.", "File name cannot be empty." => "Το όνομα αρχείου δεν πρέπει να είναι κενό.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Μη έγκυρο όνομα, '\\', '/', '<', '>', ':', '\"', '|', '?' και '*' δεν επιτρέπονται.", @@ -28,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Αδυναμία στην αποστολή του αρχείου σας αφού είναι φάκελος ή έχει 0 bytes", "Upload Error" => "Σφάλμα Αποστολής", "Close" => "Κλείσιμο", -"Pending" => "Εκκρεμεί", "1 file uploading" => "1 αρχείο ανεβαίνει", "{count} files uploading" => "{count} αρχεία ανεβαίνουν", "Upload cancelled." => "Η αποστολή ακυρώθηκε.", @@ -55,11 +59,14 @@ "Text file" => "Αρχείο κειμένου", "Folder" => "Φάκελος", "From link" => "Από σύνδεσμο", +"Deleted files" => "Διαγραμμένα αρχεία", "Cancel upload" => "Ακύρωση αποστολής", -"Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανέβασε κάτι!", +"Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Μεταφορτώστε κάτι!", "Download" => "Λήψη", +"Unshare" => "Διακοπή κοινής χρήσης", "Upload too large" => "Πολύ μεγάλο αρχείο προς αποστολή", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος αποστολής αρχείων σε αυτόν τον διακομιστή.", "Files are being scanned, please wait." => "Τα αρχεία σαρώνονται, παρακαλώ περιμένετε", -"Current scanning" => "Τρέχουσα αναζήτηση " +"Current scanning" => "Τρέχουσα αναζήτηση ", +"Upgrading filesystem cache..." => "Αναβάθμιση μνήμης cache του συστήματος αρχείων..." ); diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index ba78e8b56d7d65624178fd339750d89db068f5ce..225408f9a76e8ef9b664bc1e25e18572016cb8f1 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -1,4 +1,7 @@ "Ne eblis movi %s: dosiero kun ĉi tiu nomo jam ekzistas", +"Could not move %s" => "Ne eblis movi %s", +"Unable to rename file" => "Ne eblis alinomigi dosieron", "No file was uploaded. Unknown error" => "Neniu dosiero alŝutiĝis. Nekonata eraro.", "There is no error, the file uploaded with success" => "Ne estas eraro, la dosiero alŝutiĝis sukcese", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "La dosiero alŝutita superas la regulon upload_max_filesize el php.ini: ", @@ -9,16 +12,15 @@ "Failed to write to disk" => "Malsukcesis skribo al disko", "Invalid directory." => "Nevalida dosierujo.", "Files" => "Dosieroj", -"Unshare" => "Malkunhavigi", "Delete" => "Forigi", "Rename" => "Alinomigi", +"Pending" => "Traktotaj", "{new_name} already exists" => "{new_name} jam ekzistas", "replace" => "anstataŭigi", "suggest name" => "sugesti nomon", "cancel" => "nuligi", -"replaced {new_name}" => "anstataŭiĝis {new_name}", -"undo" => "malfari", "replaced {new_name} with {old_name}" => "anstataŭiĝis {new_name} per {old_name}", +"undo" => "malfari", "'.' is an invalid file name." => "'.' ne estas valida dosiernomo.", "File name cannot be empty." => "Dosiernomo devas ne malpleni.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nevalida nomo: “\\”, “/”, “<”, “>”, “:”, “\"”, “|”, “?” kaj “*” ne permesatas.", @@ -26,7 +28,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Ne eblis alŝuti vian dosieron ĉar ĝi estas dosierujo aŭ havas 0 duumokojn", "Upload Error" => "Alŝuta eraro", "Close" => "Fermi", -"Pending" => "Traktotaj", "1 file uploading" => "1 dosiero estas alŝutata", "{count} files uploading" => "{count} dosieroj alŝutatas", "Upload cancelled." => "La alŝuto nuliĝis.", @@ -56,6 +57,7 @@ "Cancel upload" => "Nuligi alŝuton", "Nothing in here. Upload something!" => "Nenio estas ĉi tie. Alŝutu ion!", "Download" => "Elŝuti", +"Unshare" => "Malkunhavigi", "Upload too large" => "Elŝuto tro larĝa", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "La dosieroj, kiujn vi provas alŝuti, transpasas la maksimuman grandon por dosieralŝutoj en ĉi tiu servilo.", "Files are being scanned, please wait." => "Dosieroj estas skanataj, bonvolu atendi.", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index 9d45e6035c701f2af58c3344198224e24bd4f0f6..e70a1afd0ef50bf1e505c99660a9655392d312a6 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -1,4 +1,7 @@ "No se puede mover %s - Ya existe un archivo con ese nombre", +"Could not move %s" => "No se puede mover %s", +"Unable to rename file" => "No se puede renombrar el archivo", "No file was uploaded. Unknown error" => "Fallo no se subió el fichero", "There is no error, the file uploaded with success" => "No se ha producido ningún error, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentas subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini", @@ -7,19 +10,19 @@ "No file was uploaded" => "No se ha subido ningún archivo", "Missing a temporary folder" => "Falta un directorio temporal", "Failed to write to disk" => "La escritura en disco ha fallado", +"Not enough storage available" => "No hay suficiente espacio disponible", "Invalid directory." => "Directorio invalido.", "Files" => "Archivos", -"Unshare" => "Dejar de compartir", "Delete permanently" => "Eliminar permanentemente", "Delete" => "Eliminar", "Rename" => "Renombrar", +"Pending" => "Pendiente", "{new_name} already exists" => "{new_name} ya existe", "replace" => "reemplazar", "suggest name" => "sugerir nombre", "cancel" => "cancelar", -"replaced {new_name}" => "reemplazado {new_name}", -"undo" => "deshacer", "replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}", +"undo" => "deshacer", "perform delete operation" => "Eliminar", "'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", "File name cannot be empty." => "El nombre de archivo no puede estar vacío.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes", "Upload Error" => "Error al subir el archivo", "Close" => "cerrrar", -"Pending" => "Pendiente", "1 file uploading" => "subiendo 1 archivo", "{count} files uploading" => "Subiendo {count} archivos", "Upload cancelled." => "Subida cancelada.", @@ -57,10 +59,11 @@ "Text file" => "Archivo de texto", "Folder" => "Carpeta", "From link" => "Desde el enlace", -"Trash" => "Basura", +"Deleted files" => "Archivos eliminados", "Cancel upload" => "Cancelar subida", "Nothing in here. Upload something!" => "Aquí no hay nada. ¡Sube algo!", "Download" => "Descargar", +"Unshare" => "Dejar de compartir", "Upload too large" => "El archivo es demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.", "Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.", diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index e805f24ce4c0ce22223a9ac7562f93854d77b567..f16385a652d5b4318036a5e1f7b035cbb9cadbfc 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -1,4 +1,7 @@ "No se pudo mover %s - Un archivo con este nombre ya existe", +"Could not move %s" => "No se pudo mover %s ", +"Unable to rename file" => "No fue posible cambiar el nombre al archivo", "No file was uploaded. Unknown error" => "El archivo no fue subido. Error desconocido", "There is no error, the file uploaded with success" => "No se han producido errores, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentás subir excede el tamaño definido por upload_max_filesize en el php.ini:", @@ -7,18 +10,19 @@ "No file was uploaded" => "El archivo no fue subido", "Missing a temporary folder" => "Falta un directorio temporal", "Failed to write to disk" => "Error al escribir en el disco", +"Not enough storage available" => "No hay suficiente capacidad de almacenamiento", "Invalid directory." => "Directorio invalido.", "Files" => "Archivos", -"Unshare" => "Dejar de compartir", +"Delete permanently" => "Borrar de manera permanente", "Delete" => "Borrar", "Rename" => "Cambiar nombre", +"Pending" => "Pendiente", "{new_name} already exists" => "{new_name} ya existe", "replace" => "reemplazar", "suggest name" => "sugerir nombre", "cancel" => "cancelar", -"replaced {new_name}" => "reemplazado {new_name}", -"undo" => "deshacer", "replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}", +"undo" => "deshacer", "perform delete operation" => "Eliminar", "'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", "File name cannot be empty." => "El nombre del archivo no puede quedar vacío.", @@ -29,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "No fue posible subir el archivo porque es un directorio o porque su tamaño es 0 bytes", "Upload Error" => "Error al subir el archivo", "Close" => "Cerrar", -"Pending" => "Pendiente", "1 file uploading" => "Subiendo 1 archivo", "{count} files uploading" => "Subiendo {count} archivos", "Upload cancelled." => "La subida fue cancelada", @@ -56,10 +59,11 @@ "Text file" => "Archivo de texto", "Folder" => "Carpeta", "From link" => "Desde enlace", -"Trash" => "Papelera", +"Deleted files" => "Archivos Borrados", "Cancel upload" => "Cancelar subida", "Nothing in here. Upload something!" => "No hay nada. ¡Subí contenido!", "Download" => "Descargar", +"Unshare" => "Dejar de compartir", "Upload too large" => "El archivo es demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que intentás subir sobrepasan el tamaño máximo ", "Files are being scanned, please wait." => "Se están escaneando los archivos, por favor esperá.", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 54dd7cfdc560ad5f72c481d078657989f3cd4b2f..f1c94e93aab7a02334a4f41f10c0f0f1d38cff6c 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -1,4 +1,6 @@ "%s liigutamine ebaõnnestus", +"Unable to rename file" => "Faili ümbernimetamine ebaõnnestus", "No file was uploaded. Unknown error" => "Ühtegi faili ei laetud üles. Tundmatu viga", "There is no error, the file uploaded with success" => "Ühtegi viga pole, fail on üles laetud", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Üles laetud faili suurus ületab HTML vormis määratud upload_max_filesize suuruse", @@ -6,22 +8,25 @@ "No file was uploaded" => "Ühtegi faili ei laetud üles", "Missing a temporary folder" => "Ajutiste failide kaust puudub", "Failed to write to disk" => "Kettale kirjutamine ebaõnnestus", +"Not enough storage available" => "Saadaval pole piisavalt ruumi", +"Invalid directory." => "Vigane kaust.", "Files" => "Failid", -"Unshare" => "Lõpeta jagamine", +"Delete permanently" => "Kustuta jäädavalt", "Delete" => "Kustuta", "Rename" => "ümber", +"Pending" => "Ootel", "{new_name} already exists" => "{new_name} on juba olemas", "replace" => "asenda", "suggest name" => "soovita nime", "cancel" => "loobu", -"replaced {new_name}" => "asendatud nimega {new_name}", -"undo" => "tagasi", "replaced {new_name} with {old_name}" => "asendas nime {old_name} nimega {new_name}", +"undo" => "tagasi", +"'.' is an invalid file name." => "'.' on vigane failinimi.", +"File name cannot be empty." => "Faili nimi ei saa olla tühi.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Vigane nimi, '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' pole lubatud.", "Unable to upload your file as it is a directory or has 0 bytes" => "Sinu faili üleslaadimine ebaõnnestus, kuna see on kaust või selle suurus on 0 baiti", "Upload Error" => "Üleslaadimise viga", "Close" => "Sulge", -"Pending" => "Ootel", "1 file uploading" => "1 faili üleslaadimisel", "{count} files uploading" => "{count} faili üleslaadimist", "Upload cancelled." => "Üleslaadimine tühistati.", @@ -50,6 +55,7 @@ "Cancel upload" => "Tühista üleslaadimine", "Nothing in here. Upload something!" => "Siin pole midagi. Lae midagi üles!", "Download" => "Lae alla", +"Unshare" => "Lõpeta jagamine", "Upload too large" => "Üleslaadimine on liiga suur", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Failid, mida sa proovid üles laadida, ületab serveri poolt üleslaetavatele failidele määratud maksimaalse suuruse.", "Files are being scanned, please wait." => "Faile skannitakse, palun oota", diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index 45c515814e71bc84b112d387542534c4ce2921b9..63c62ce9a55d92e747edcdd88cde30a3ad92d0ff 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -1,4 +1,7 @@ "Ezin da %s mugitu - Izen hau duen fitxategia dagoeneko existitzen da", +"Could not move %s" => "Ezin dira fitxategiak mugitu %s", +"Unable to rename file" => "Ezin izan da fitxategia berrizendatu", "No file was uploaded. Unknown error" => "Ez da fitxategirik igo. Errore ezezaguna", "There is no error, the file uploaded with success" => "Ez da arazorik izan, fitxategia ongi igo da", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Igotako fitxategiak php.ini fitxategian ezarritako upload_max_filesize muga gainditu du:", @@ -7,18 +10,20 @@ "No file was uploaded" => "Ez da fitxategirik igo", "Missing a temporary folder" => "Aldi baterako karpeta falta da", "Failed to write to disk" => "Errore bat izan da diskoan idazterakoan", +"Not enough storage available" => "Ez dago behar aina leku erabilgarri,", "Invalid directory." => "Baliogabeko karpeta.", "Files" => "Fitxategiak", -"Unshare" => "Ez elkarbanatu", +"Delete permanently" => "Ezabatu betirako", "Delete" => "Ezabatu", "Rename" => "Berrizendatu", +"Pending" => "Zain", "{new_name} already exists" => "{new_name} dagoeneko existitzen da", "replace" => "ordeztu", "suggest name" => "aholkatu izena", "cancel" => "ezeztatu", -"replaced {new_name}" => "ordezkatua {new_name}", -"undo" => "desegin", "replaced {new_name} with {old_name}" => " {new_name}-k {old_name} ordezkatu du", +"undo" => "desegin", +"perform delete operation" => "Ezabatu", "'.' is an invalid file name." => "'.' ez da fitxategi izen baliogarria.", "File name cannot be empty." => "Fitxategi izena ezin da hutsa izan.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "IZen aliogabea, '\\', '/', '<', '>', ':', '\"', '|', '?' eta '*' ez daude baimenduta.", @@ -28,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Ezin da zure fitxategia igo, karpeta bat da edo 0 byt ditu", "Upload Error" => "Igotzean errore bat suertatu da", "Close" => "Itxi", -"Pending" => "Zain", "1 file uploading" => "fitxategi 1 igotzen", "{count} files uploading" => "{count} fitxategi igotzen", "Upload cancelled." => "Igoera ezeztatuta", @@ -55,11 +59,15 @@ "Text file" => "Testu fitxategia", "Folder" => "Karpeta", "From link" => "Estekatik", +"Deleted files" => "Ezabatutako fitxategiak", "Cancel upload" => "Ezeztatu igoera", +"You don’t have write permissions here." => "Ez duzu hemen idazteko baimenik.", "Nothing in here. Upload something!" => "Ez dago ezer. Igo zerbait!", "Download" => "Deskargatu", +"Unshare" => "Ez elkarbanatu", "Upload too large" => "Igotakoa handiegia da", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Igotzen saiatzen ari zaren fitxategiak zerbitzari honek igotzeko onartzen duena baino handiagoak dira.", "Files are being scanned, please wait." => "Fitxategiak eskaneatzen ari da, itxoin mezedez.", -"Current scanning" => "Orain eskaneatzen ari da" +"Current scanning" => "Orain eskaneatzen ari da", +"Upgrading filesystem cache..." => "Fitxategi sistemaren katxea eguneratzen..." ); diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 2559d597a791e17235798972fa9e1fb464f6bf7d..f4e0af9576d269c298ee3ec0cbe3de814beb1871 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -1,4 +1,7 @@ "%s نمی تواند حرکت کند - در حال حاضر پرونده با این نام وجود دارد. ", +"Could not move %s" => "%s نمی تواند حرکت کند ", +"Unable to rename file" => "قادر به تغییر نام پرونده نیست.", "No file was uploaded. Unknown error" => "هیچ فایلی آپلود نشد.خطای ناشناس", "There is no error, the file uploaded with success" => "هیچ خطایی وجود ندارد فایل با موفقیت بار گذاری شد", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "پرونده آپلود شده بیش ازدستور ماکزیمم_حجم فایل_برای آپلود در php.ini استفاده کرده است.", @@ -9,16 +12,15 @@ "Failed to write to disk" => "نوشتن بر روی دیسک سخت ناموفق بود", "Invalid directory." => "فهرست راهنما نامعتبر می باشد.", "Files" => "فایل ها", -"Unshare" => "لغو اشتراک", "Delete" => "پاک کردن", "Rename" => "تغییرنام", +"Pending" => "در انتظار", "{new_name} already exists" => "{نام _جدید} در حال حاضر وجود دارد.", "replace" => "جایگزین", "suggest name" => "پیشنهاد نام", "cancel" => "لغو", -"replaced {new_name}" => "{نام _جدید} جایگزین شد ", -"undo" => "بازگشت", "replaced {new_name} with {old_name}" => "{نام_جدید} با { نام_قدیمی} جایگزین شد.", +"undo" => "بازگشت", "'.' is an invalid file name." => "'.' یک نام پرونده نامعتبر است.", "File name cannot be empty." => "نام پرونده نمی تواند خالی باشد.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "نام نامعتبر ، '\\', '/', '<', '>', ':', '\"', '|', '?' و '*' مجاز نمی باشند.", @@ -26,7 +28,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "ناتوان در بارگذاری یا فایل یک پوشه است یا 0بایت دارد", "Upload Error" => "خطا در بار گذاری", "Close" => "بستن", -"Pending" => "در انتظار", "1 file uploading" => "1 پرونده آپلود شد.", "{count} files uploading" => "{ شمار } فایل های در حال آپلود", "Upload cancelled." => "بار گذاری لغو شد", @@ -56,6 +57,7 @@ "Cancel upload" => "متوقف کردن بار گذاری", "Nothing in here. Upload something!" => "اینجا هیچ چیز نیست.", "Download" => "بارگیری", +"Unshare" => "لغو اشتراک", "Upload too large" => "حجم بارگذاری بسیار زیاد است", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "فایلها بیش از حد تعیین شده در این سرور هستند\nمترجم:با تغییر فایل php,ini میتوان این محدودیت را برطرف کرد", "Files are being scanned, please wait." => "پرونده ها در حال بازرسی هستند لطفا صبر کنید", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index 6a425e760909afb42b3e9557d6272755a5403bfa..6eb891d29d3b5b902b9090b93c21130f4c0c605b 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -1,4 +1,7 @@ "Kohteen %s siirto ei onnistunut - Tiedosto samalla nimellä on jo olemassa", +"Could not move %s" => "Kohteen %s siirto ei onnistunut", +"Unable to rename file" => "Tiedoston nimeäminen uudelleen ei onnistunut", "No file was uploaded. Unknown error" => "Tiedostoa ei lähetetty. Tuntematon virhe", "There is no error, the file uploaded with success" => "Ei virheitä, tiedosto lähetettiin onnistuneesti", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lähetetty tiedosto ylittää HTML-lomakkeessa määritetyn MAX_FILE_SIZE-arvon ylärajan", @@ -6,11 +9,13 @@ "No file was uploaded" => "Yhtäkään tiedostoa ei lähetetty", "Missing a temporary folder" => "Väliaikaiskansiota ei ole olemassa", "Failed to write to disk" => "Levylle kirjoitus epäonnistui", +"Not enough storage available" => "Tallennustilaa ei ole riittävästi käytettävissä", "Invalid directory." => "Virheellinen kansio.", "Files" => "Tiedostot", -"Unshare" => "Peru jakaminen", +"Delete permanently" => "Poista pysyvästi", "Delete" => "Poista", "Rename" => "Nimeä uudelleen", +"Pending" => "Odottaa", "{new_name} already exists" => "{new_name} on jo olemassa", "replace" => "korvaa", "suggest name" => "ehdota nimeä", @@ -26,7 +31,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio", "Upload Error" => "Lähetysvirhe.", "Close" => "Sulje", -"Pending" => "Odottaa", "Upload cancelled." => "Lähetys peruttu.", "File upload is in progress. Leaving the page now will cancel the upload." => "Tiedoston lähetys on meneillään. Sivulta poistuminen nyt peruu tiedoston lähetyksen.", "URL cannot be empty." => "Verkko-osoite ei voi olla tyhjä", @@ -50,10 +54,12 @@ "Text file" => "Tekstitiedosto", "Folder" => "Kansio", "From link" => "Linkistä", -"Trash" => "Roskakori", +"Deleted files" => "Poistetut tiedostot", "Cancel upload" => "Peru lähetys", +"You don’t have write permissions here." => "Tunnuksellasi ei ole kirjoitusoikeuksia tänne.", "Nothing in here. Upload something!" => "Täällä ei ole mitään. Lähetä tänne jotakin!", "Download" => "Lataa", +"Unshare" => "Peru jakaminen", "Upload too large" => "Lähetettävä tiedosto on liian suuri", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Lähetettäväksi valitsemasi tiedostot ylittävät palvelimen salliman tiedostokoon rajan.", "Files are being scanned, please wait." => "Tiedostoja tarkistetaan, odota hetki.", diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index 45281d277ffc67e64d56eb55b1671a8ac1f409e5..9849184441d861a43f605a4d580129fc5e665fc7 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -1,4 +1,7 @@ "Impossible de déplacer %s - Un fichier possédant ce nom existe déjà", +"Could not move %s" => "Impossible de déplacer %s", +"Unable to rename file" => "Impossible de renommer le fichier", "No file was uploaded. Unknown error" => "Aucun fichier n'a été chargé. Erreur inconnue", "There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été téléversé avec succès", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse la valeur upload_max_filesize située dans le fichier php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Aucun fichier n'a été téléversé", "Missing a temporary folder" => "Il manque un répertoire temporaire", "Failed to write to disk" => "Erreur d'écriture sur le disque", +"Not enough storage available" => "Plus assez d'espace de stockage disponible", "Invalid directory." => "Dossier invalide.", "Files" => "Fichiers", -"Unshare" => "Ne plus partager", "Delete permanently" => "Supprimer de façon définitive", "Delete" => "Supprimer", "Rename" => "Renommer", +"Pending" => "En cours", "{new_name} already exists" => "{new_name} existe déjà", "replace" => "remplacer", "suggest name" => "Suggérer un nom", "cancel" => "annuler", -"replaced {new_name}" => "{new_name} a été remplacé", -"undo" => "annuler", "replaced {new_name} with {old_name}" => "{new_name} a été remplacé par {old_name}", +"undo" => "annuler", "perform delete operation" => "effectuer l'opération de suppression", "'.' is an invalid file name." => "'.' n'est pas un nom de fichier valide.", "File name cannot be empty." => "Le nom de fichier ne peut être vide.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Impossible de charger vos fichiers car il s'agit d'un dossier ou le fichier fait 0 octet.", "Upload Error" => "Erreur de chargement", "Close" => "Fermer", -"Pending" => "En cours", "1 file uploading" => "1 fichier en cours de téléchargement", "{count} files uploading" => "{count} fichiers téléversés", "Upload cancelled." => "Chargement annulé.", @@ -57,10 +59,11 @@ "Text file" => "Fichier texte", "Folder" => "Dossier", "From link" => "Depuis le lien", -"Trash" => "Corbeille", +"Deleted files" => "Fichiers supprimés", "Cancel upload" => "Annuler l'envoi", "Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)", "Download" => "Télécharger", +"Unshare" => "Ne plus partager", "Upload too large" => "Fichier trop volumineux", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.", "Files are being scanned, please wait." => "Les fichiers sont en cours d'analyse, veuillez patienter.", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 362e92daceae512ad297f86cf93f6902b6de938d..d48839d0b6022788f0243af3f1054f1469ede71a 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -1,37 +1,44 @@ "Non se subiu ningún ficheiro. Erro descoñecido.", -"There is no error, the file uploaded with success" => "Non hai erros. O ficheiro enviouse correctamente", -"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro subido excede a directiva indicada polo tamaño_máximo_de_subida de php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado supera a directiva MAX_FILE_SIZE que foi indicada no formulario HTML", +"Could not move %s - File with this name already exists" => "Non se moveu %s - Xa existe un ficheiro con ese nome.", +"Could not move %s" => "Non foi posíbel mover %s", +"Unable to rename file" => "Non é posíbel renomear o ficheiro", +"No file was uploaded. Unknown error" => "Non foi enviado ningún ficheiro. Produciuse un erro descoñecido.", +"There is no error, the file uploaded with success" => "Non se produciu ningún erro. O ficheiro enviouse correctamente", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede a directiva indicada por upload_max_filesize de php.ini:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede a directiva MAX_FILE_SIZE que foi indicada no formulario HTML", "The uploaded file was only partially uploaded" => "O ficheiro enviado foi só parcialmente enviado", "No file was uploaded" => "Non se enviou ningún ficheiro", "Missing a temporary folder" => "Falta un cartafol temporal", -"Failed to write to disk" => "Erro ao escribir no disco", +"Failed to write to disk" => "Produciuse un erro ao escribir no disco", +"Not enough storage available" => "Non hai espazo de almacenamento abondo", "Invalid directory." => "O directorio é incorrecto.", "Files" => "Ficheiros", -"Unshare" => "Deixar de compartir", +"Delete permanently" => "Eliminar permanentemente", "Delete" => "Eliminar", -"Rename" => "Mudar o nome", -"{new_name} already exists" => "xa existe un {new_name}", +"Rename" => "Renomear", +"Pending" => "Pendentes", +"{new_name} already exists" => "Xa existe un {new_name}", "replace" => "substituír", "suggest name" => "suxerir nome", "cancel" => "cancelar", -"replaced {new_name}" => "substituír {new_name}", +"replaced {new_name} with {old_name}" => "substituír {new_name} por {old_name}", "undo" => "desfacer", -"replaced {new_name} with {old_name}" => "substituír {new_name} polo {old_name}", -"'.' is an invalid file name." => "'.' é un nonme de ficheiro non válido", -"File name cannot be empty." => "O nome de ficheiro non pode estar baldeiro", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome non válido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' non se permiten.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Non se puido subir o ficheiro pois ou é un directorio ou ten 0 bytes", -"Upload Error" => "Erro na subida", +"perform delete operation" => "realizar a operación de eliminación", +"'.' is an invalid file name." => "«.» é un nome de ficheiro incorrecto", +"File name cannot be empty." => "O nome de ficheiro non pode estar baleiro", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome incorrecto, non se permite «\\», «/», «<», «>», «:», «\"», «|», «?» e «*».", +"Your storage is full, files can not be updated or synced anymore!" => "O seu espazo de almacenamento está cheo, non é posíbel actualizar ou sincronizar máis os ficheiros!", +"Your storage is almost full ({usedSpacePercent}%)" => "O seu espazo de almacenamento está case cheo ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Está a prepararse a súa descarga. Isto pode levar bastante tempo se os ficheiros son grandes.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Non foi posíbel enviar o ficheiro pois ou é un directorio ou ten 0 bytes", +"Upload Error" => "Produciuse un erro no envío", "Close" => "Pechar", -"Pending" => "Pendentes", -"1 file uploading" => "1 ficheiro subíndose", -"{count} files uploading" => "{count} ficheiros subíndose", -"Upload cancelled." => "Subida cancelada.", -"File upload is in progress. Leaving the page now will cancel the upload." => "A subida do ficheiro está en curso. Saír agora da páxina cancelará a subida.", -"URL cannot be empty." => "URL non pode quedar baleiro.", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de cartafol non válido. O uso de 'Shared' está reservado por Owncloud", +"1 file uploading" => "Enviándose 1 ficheiro", +"{count} files uploading" => "Enviandose {count} ficheiros", +"Upload cancelled." => "Envío cancelado.", +"File upload is in progress. Leaving the page now will cancel the upload." => "O envío do ficheiro está en proceso. Saír agora da páxina cancelará o envío.", +"URL cannot be empty." => "O URL non pode quedar baleiro.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de cartafol incorrecto. O uso de «Shared» está reservado por Owncloud", "Name" => "Nome", "Size" => "Tamaño", "Modified" => "Modificado", @@ -41,22 +48,26 @@ "{count} files" => "{count} ficheiros", "Upload" => "Enviar", "File handling" => "Manexo de ficheiro", -"Maximum upload size" => "Tamaño máximo de envío", -"max. possible: " => "máx. posible: ", +"Maximum upload size" => "Tamaño máximo do envío", +"max. possible: " => "máx. posíbel: ", "Needed for multi-file and folder downloads." => "Precísase para a descarga de varios ficheiros e cartafoles.", "Enable ZIP-download" => "Habilitar a descarga-ZIP", "0 is unlimited" => "0 significa ilimitado", -"Maximum input size for ZIP files" => "Tamaño máximo de descarga para os ZIP", +"Maximum input size for ZIP files" => "Tamaño máximo de descarga para os ficheiros ZIP", "Save" => "Gardar", "New" => "Novo", "Text file" => "Ficheiro de texto", "Folder" => "Cartafol", -"From link" => "Dende a ligazón", -"Cancel upload" => "Cancelar a subida", -"Nothing in here. Upload something!" => "Nada por aquí. Envía algo.", +"From link" => "Desde a ligazón", +"Deleted files" => "Ficheiros eliminados", +"Cancel upload" => "Cancelar o envío", +"You don’t have write permissions here." => "Non ten permisos para escribir aquí.", +"Nothing in here. Upload something!" => "Aquí non hai nada. Envíe algo.", "Download" => "Descargar", +"Unshare" => "Deixar de compartir", "Upload too large" => "Envío demasiado grande", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que trata de subir superan o tamaño máximo permitido neste servidor", -"Files are being scanned, please wait." => "Estanse analizando os ficheiros. Agarda.", -"Current scanning" => "Análise actual" +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que tenta enviar exceden do tamaño máximo permitido neste servidor", +"Files are being scanned, please wait." => "Estanse analizando os ficheiros. Agarde.", +"Current scanning" => "Análise actual", +"Upgrading filesystem cache..." => "Anovando a caché do sistema de ficheiros..." ); diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index 94cddca000080bc4a05263b85c594e219108874c..9d6b411c415d826adc98c0f95df0b74cb83a1ab7 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -8,21 +8,20 @@ "Missing a temporary folder" => "תיקייה זמנית חסרה", "Failed to write to disk" => "הכתיבה לכונן נכשלה", "Files" => "קבצים", -"Unshare" => "הסר שיתוף", +"Delete permanently" => "מחק לצמיתות", "Delete" => "מחיקה", "Rename" => "שינוי שם", +"Pending" => "ממתין", "{new_name} already exists" => "{new_name} כבר קיים", "replace" => "החלפה", "suggest name" => "הצעת שם", "cancel" => "ביטול", -"replaced {new_name}" => "{new_name} הוחלף", -"undo" => "ביטול", "replaced {new_name} with {old_name}" => "{new_name} הוחלף ב־{old_name}", +"undo" => "ביטול", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "השם שגוי, אסור להשתמש בתווים '\\', '/', '<', '>', ':', '\"', '|', '?' ו־'*'.", "Unable to upload your file as it is a directory or has 0 bytes" => "לא יכול להעלות את הקובץ מכיוון שזו תקיה או שמשקל הקובץ 0 בתים", "Upload Error" => "שגיאת העלאה", "Close" => "סגירה", -"Pending" => "ממתין", "1 file uploading" => "קובץ אחד נשלח", "{count} files uploading" => "{count} קבצים נשלחים", "Upload cancelled." => "ההעלאה בוטלה.", @@ -51,6 +50,7 @@ "Cancel upload" => "ביטול ההעלאה", "Nothing in here. Upload something!" => "אין כאן שום דבר. אולי ברצונך להעלות משהו?", "Download" => "הורדה", +"Unshare" => "הסר שיתוף", "Upload too large" => "העלאה גדולה מידי", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "הקבצים שניסית להעלות חרגו מהגודל המקסימלי להעלאת קבצים על שרת זה.", "Files are being scanned, please wait." => "הקבצים נסרקים, נא להמתין.", diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php index 4f4546aaf07b4e3dae11d26eb3d8371bc34dd247..3516ab8c1e6a76377ca949e19c217fe8013a5ef4 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -6,9 +6,9 @@ "Missing a temporary folder" => "Nedostaje privremena mapa", "Failed to write to disk" => "Neuspjelo pisanje na disk", "Files" => "Datoteke", -"Unshare" => "Prekini djeljenje", "Delete" => "Briši", "Rename" => "Promjeni ime", +"Pending" => "U tijeku", "replace" => "zamjeni", "suggest name" => "predloži ime", "cancel" => "odustani", @@ -16,7 +16,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij", "Upload Error" => "Pogreška pri slanju", "Close" => "Zatvori", -"Pending" => "U tijeku", "1 file uploading" => "1 datoteka se učitava", "Upload cancelled." => "Slanje poništeno.", "File upload is in progress. Leaving the page now will cancel the upload." => "Učitavanje datoteke. Napuštanjem stranice će prekinuti učitavanje.", @@ -38,6 +37,7 @@ "Cancel upload" => "Prekini upload", "Nothing in here. Upload something!" => "Nema ničega u ovoj mapi. Pošalji nešto!", "Download" => "Preuzmi", +"Unshare" => "Prekini djeljenje", "Upload too large" => "Prijenos je preobiman", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke koje pokušavate prenijeti prelaze maksimalnu veličinu za prijenos datoteka na ovom poslužitelju.", "Files are being scanned, please wait." => "Datoteke se skeniraju, molimo pričekajte.", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index 26d564807903ef23614f44b9f116ebaac1606d59..54d5033f907d1cdce3825039c6a6877bce46befc 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -1,4 +1,7 @@ "%s áthelyezése nem sikerült - már létezik másik fájl ezzel a névvel", +"Could not move %s" => "Nem sikerült %s áthelyezése", +"Unable to rename file" => "Nem lehet átnevezni a fájlt", "No file was uploaded. Unknown error" => "Nem történt feltöltés. Ismeretlen hiba", "There is no error, the file uploaded with success" => "A fájlt sikerült feltölteni", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "A feltöltött fájl mérete meghaladja a php.ini állományban megadott upload_max_filesize paraméter értékét.", @@ -7,18 +10,20 @@ "No file was uploaded" => "Nem töltődött fel semmi", "Missing a temporary folder" => "Hiányzik egy ideiglenes mappa", "Failed to write to disk" => "Nem sikerült a lemezre történő írás", +"Not enough storage available" => "Nincs elég szabad hely.", "Invalid directory." => "Érvénytelen mappa.", "Files" => "Fájlok", -"Unshare" => "Megosztás visszavonása", +"Delete permanently" => "Végleges törlés", "Delete" => "Törlés", "Rename" => "Átnevezés", +"Pending" => "Folyamatban", "{new_name} already exists" => "{new_name} már létezik", "replace" => "írjuk fölül", "suggest name" => "legyen más neve", "cancel" => "mégse", -"replaced {new_name}" => "a(z) {new_name} állományt kicseréltük", -"undo" => "visszavonás", "replaced {new_name} with {old_name}" => "{new_name} fájlt kicseréltük ezzel: {old_name}", +"undo" => "visszavonás", +"perform delete operation" => "a törlés végrehajtása", "'.' is an invalid file name." => "'.' fájlnév érvénytelen.", "File name cannot be empty." => "A fájlnév nem lehet semmi.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Érvénytelen elnevezés. Ezek a karakterek nem használhatók: '\\', '/', '<', '>', ':', '\"', '|', '?' és '*'", @@ -28,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nem tölthető fel, mert mappa volt, vagy 0 byte méretű", "Upload Error" => "Feltöltési hiba", "Close" => "Bezárás", -"Pending" => "Folyamatban", "1 file uploading" => "1 fájl töltődik föl", "{count} files uploading" => "{count} fájl töltődik föl", "Upload cancelled." => "A feltöltést megszakítottuk.", @@ -55,11 +59,15 @@ "Text file" => "Szövegfájl", "Folder" => "Mappa", "From link" => "Feltöltés linkről", +"Deleted files" => "Törölt fájlok", "Cancel upload" => "A feltöltés megszakítása", +"You don’t have write permissions here." => "Itt nincs írásjoga.", "Nothing in here. Upload something!" => "Itt nincs semmi. Töltsön fel valamit!", "Download" => "Letöltés", +"Unshare" => "Megosztás visszavonása", "Upload too large" => "A feltöltés túl nagy", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "A feltöltendő állományok mérete meghaladja a kiszolgálón megengedett maximális méretet.", "Files are being scanned, please wait." => "A fájllista ellenőrzése zajlik, kis türelmet!", -"Current scanning" => "Ellenőrzés alatt" +"Current scanning" => "Ellenőrzés alatt", +"Upgrading filesystem cache..." => "A fájlrendszer gyorsítótárának frissítése zajlik..." ); diff --git a/apps/files/l10n/hy.php b/apps/files/l10n/hy.php new file mode 100644 index 0000000000000000000000000000000000000000..29c0cd8b8d0627dcd2b067b297f902c626f162ef --- /dev/null +++ b/apps/files/l10n/hy.php @@ -0,0 +1,6 @@ + "Ջնջել", +"Close" => "Փակել", +"Save" => "Պահպանել", +"Download" => "Բեռնել" +); diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 3ebb9983291dd1dd51920f59a3f851324421a0b9..aff1933e569e9d3b801b5769dcf8d083dbfadf7c 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -6,20 +6,23 @@ "Missing a temporary folder" => "Kehilangan folder temporer", "Failed to write to disk" => "Gagal menulis ke disk", "Files" => "Berkas", -"Unshare" => "batalkan berbagi", "Delete" => "Hapus", +"Pending" => "Menunggu", "replace" => "mengganti", "cancel" => "batalkan", "undo" => "batal dikerjakan", "Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas anda karena berupa direktori atau mempunyai ukuran 0 byte", "Upload Error" => "Terjadi Galat Pengunggahan", "Close" => "tutup", -"Pending" => "Menunggu", "Upload cancelled." => "Pengunggahan dibatalkan.", "URL cannot be empty." => "tautan tidak boleh kosong", "Name" => "Nama", "Size" => "Ukuran", "Modified" => "Dimodifikasi", +"1 folder" => "1 map", +"{count} folders" => "{count} map", +"1 file" => "1 berkas", +"{count} files" => "{count} berkas", "Upload" => "Unggah", "File handling" => "Penanganan berkas", "Maximum upload size" => "Ukuran unggah maksimum", @@ -35,6 +38,7 @@ "Cancel upload" => "Batal mengunggah", "Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!", "Download" => "Unduh", +"Unshare" => "batalkan berbagi", "Upload too large" => "Unggahan terlalu besar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan berkas di server ini.", "Files are being scanned, please wait." => "Berkas sedang dipindai, silahkan tunggu.", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index f8d9789cf0fa1c392058b85aa5c5907a19c3236b..9d735c3c5411ab29e43c2d0cde6f7c5290b09612 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -1,4 +1,7 @@ "Gat ekki fært %s - Skrá með þessu nafni er þegar til", +"Could not move %s" => "Gat ekki fært %s", +"Unable to rename file" => "Gat ekki endurskýrt skrá", "No file was uploaded. Unknown error" => "Engin skrá var send inn. Óþekkt villa.", "There is no error, the file uploaded with success" => "Engin villa, innsending heppnaðist", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Innsend skrá er stærri en upload_max stillingin í php.ini:", @@ -9,23 +12,21 @@ "Failed to write to disk" => "Tókst ekki að skrifa á disk", "Invalid directory." => "Ógild mappa.", "Files" => "Skrár", -"Unshare" => "Hætta deilingu", "Delete" => "Eyða", "Rename" => "Endurskýra", +"Pending" => "Bíður", "{new_name} already exists" => "{new_name} er þegar til", "replace" => "yfirskrifa", "suggest name" => "stinga upp á nafni", "cancel" => "hætta við", -"replaced {new_name}" => "endurskýrði {new_name}", -"undo" => "afturkalla", "replaced {new_name} with {old_name}" => "yfirskrifaði {new_name} með {old_name}", +"undo" => "afturkalla", "'.' is an invalid file name." => "'.' er ekki leyfilegt nafn.", "File name cannot be empty." => "Nafn skráar má ekki vera tómt", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ógilt nafn, táknin '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' eru ekki leyfð.", "Unable to upload your file as it is a directory or has 0 bytes" => "Innsending á skrá mistókst, hugsanlega sendir þú möppu eða skráin er 0 bæti.", "Upload Error" => "Villa við innsendingu", "Close" => "Loka", -"Pending" => "Bíður", "1 file uploading" => "1 skrá innsend", "{count} files uploading" => "{count} skrár innsendar", "Upload cancelled." => "Hætt við innsendingu.", @@ -55,6 +56,7 @@ "Cancel upload" => "Hætta við innsendingu", "Nothing in here. Upload something!" => "Ekkert hér. Settu eitthvað inn!", "Download" => "Niðurhal", +"Unshare" => "Hætta deilingu", "Upload too large" => "Innsend skrá er of stór", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Skrárnar sem þú ert að senda inn eru stærri en hámarks innsendingarstærð á þessum netþjóni.", "Files are being scanned, please wait." => "Verið er að skima skrár, vinsamlegast hinkraðu.", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 3d6eb254e59daee92a25dbc8cc803ab060f3be31..2aac4ef20f532176aa0fccbf4433ff377866aead 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -1,4 +1,7 @@ "Impossibile spostare %s - un file con questo nome esiste già", +"Could not move %s" => "Impossibile spostare %s", +"Unable to rename file" => "Impossibile rinominare il file", "No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto", "There is no error, the file uploaded with success" => "Non ci sono errori, file caricato con successo", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Il file caricato supera la direttiva upload_max_filesize in php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Nessun file è stato caricato", "Missing a temporary folder" => "Cartella temporanea mancante", "Failed to write to disk" => "Scrittura su disco non riuscita", +"Not enough storage available" => "Spazio di archiviazione insufficiente", "Invalid directory." => "Cartella non valida.", "Files" => "File", -"Unshare" => "Rimuovi condivisione", "Delete permanently" => "Elimina definitivamente", "Delete" => "Elimina", "Rename" => "Rinomina", +"Pending" => "In corso", "{new_name} already exists" => "{new_name} esiste già", "replace" => "sostituisci", "suggest name" => "suggerisci nome", "cancel" => "annulla", -"replaced {new_name}" => "sostituito {new_name}", -"undo" => "annulla", "replaced {new_name} with {old_name}" => "sostituito {new_name} con {old_name}", +"undo" => "annulla", "perform delete operation" => "esegui l'operazione di eliminazione", "'.' is an invalid file name." => "'.' non è un nome file valido.", "File name cannot be empty." => "Il nome del file non può essere vuoto.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte", "Upload Error" => "Errore di invio", "Close" => "Chiudi", -"Pending" => "In corso", "1 file uploading" => "1 file in fase di caricamento", "{count} files uploading" => "{count} file in fase di caricamentoe", "Upload cancelled." => "Invio annullato", @@ -57,10 +59,12 @@ "Text file" => "File di testo", "Folder" => "Cartella", "From link" => "Da collegamento", -"Trash" => "Cestino", +"Deleted files" => "File eliminati", "Cancel upload" => "Annulla invio", +"You don’t have write permissions here." => "Qui non hai i permessi di scrittura.", "Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!", "Download" => "Scarica", +"Unshare" => "Rimuovi condivisione", "Upload too large" => "Il file caricato è troppo grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.", "Files are being scanned, please wait." => "Scansione dei file in corso, attendi", diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index 1caa308c1b89b191f1f79eea352548e0f2b1666e..88349d1ba4022e84598b962386581d5188e5c24a 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -1,4 +1,7 @@ "%s を移動できませんでした ― この名前のファイルはすでに存在します", +"Could not move %s" => "%s を移動できませんでした", +"Unable to rename file" => "ファイル名の変更ができません", "No file was uploaded. Unknown error" => "ファイルは何もアップロードされていません。不明なエラー", "There is no error, the file uploaded with success" => "エラーはありません。ファイルのアップロードは成功しました", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "アップロードされたファイルはphp.ini の upload_max_filesize に設定されたサイズを超えています:", @@ -7,19 +10,19 @@ "No file was uploaded" => "ファイルはアップロードされませんでした", "Missing a temporary folder" => "テンポラリフォルダが見つかりません", "Failed to write to disk" => "ディスクへの書き込みに失敗しました", +"Not enough storage available" => "ストレージに十分な空き容量がありません", "Invalid directory." => "無効なディレクトリです。", "Files" => "ファイル", -"Unshare" => "共有しない", "Delete permanently" => "完全に削除する", "Delete" => "削除", "Rename" => "名前の変更", +"Pending" => "保留", "{new_name} already exists" => "{new_name} はすでに存在しています", "replace" => "置き換え", "suggest name" => "推奨名称", "cancel" => "キャンセル", -"replaced {new_name}" => "{new_name} を置換", -"undo" => "元に戻す", "replaced {new_name} with {old_name}" => "{old_name} を {new_name} に置換", +"undo" => "元に戻す", "perform delete operation" => "削除を実行", "'.' is an invalid file name." => "'.' は無効なファイル名です。", "File name cannot be empty." => "ファイル名を空にすることはできません。", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "ディレクトリもしくは0バイトのファイルはアップロードできません", "Upload Error" => "アップロードエラー", "Close" => "閉じる", -"Pending" => "保留", "1 file uploading" => "ファイルを1つアップロード中", "{count} files uploading" => "{count} ファイルをアップロード中", "Upload cancelled." => "アップロードはキャンセルされました。", @@ -57,10 +59,12 @@ "Text file" => "テキストファイル", "Folder" => "フォルダ", "From link" => "リンク", -"Trash" => "ゴミ箱", +"Deleted files" => "削除ファイル", "Cancel upload" => "アップロードをキャンセル", +"You don’t have write permissions here." => "あなたには書き込み権限がありません。", "Nothing in here. Upload something!" => "ここには何もありません。何かアップロードしてください。", "Download" => "ダウンロード", +"Unshare" => "共有しない", "Upload too large" => "ファイルサイズが大きすぎます", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップロードしようとしているファイルは、サーバで規定された最大サイズを超えています。", "Files are being scanned, please wait." => "ファイルをスキャンしています、しばらくお待ちください。", diff --git a/apps/files/l10n/ka.php b/apps/files/l10n/ka.php new file mode 100644 index 0000000000000000000000000000000000000000..148e688547a77524b2b5d762b2b1946469b54eac --- /dev/null +++ b/apps/files/l10n/ka.php @@ -0,0 +1,4 @@ + "ფაილები", +"Download" => "გადმოწერა" +); diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php index 7ab6122c659a82d0c4cd7c0e11ae6415146f0932..421c720a330024db5d36e0e4ac8eb5322695d234 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -6,20 +6,18 @@ "Missing a temporary folder" => "დროებითი საქაღალდე არ არსებობს", "Failed to write to disk" => "შეცდომა დისკზე ჩაწერისას", "Files" => "ფაილები", -"Unshare" => "გაზიარების მოხსნა", "Delete" => "წაშლა", "Rename" => "გადარქმევა", +"Pending" => "მოცდის რეჟიმში", "{new_name} already exists" => "{new_name} უკვე არსებობს", "replace" => "შეცვლა", "suggest name" => "სახელის შემოთავაზება", "cancel" => "უარყოფა", -"replaced {new_name}" => "{new_name} შეცვლილია", -"undo" => "დაბრუნება", "replaced {new_name} with {old_name}" => "{new_name} შეცვლილია {old_name}–ით", +"undo" => "დაბრუნება", "Unable to upload your file as it is a directory or has 0 bytes" => "თქვენი ფაილის ატვირთვა ვერ მოხერხდა. ის არის საქაღალდე და შეიცავს 0 ბაიტს", "Upload Error" => "შეცდომა ატვირთვისას", "Close" => "დახურვა", -"Pending" => "მოცდის რეჟიმში", "1 file uploading" => "1 ფაილის ატვირთვა", "{count} files uploading" => "{count} ფაილი იტვირთება", "Upload cancelled." => "ატვირთვა შეჩერებულ იქნა.", @@ -46,6 +44,7 @@ "Cancel upload" => "ატვირთვის გაუქმება", "Nothing in here. Upload something!" => "აქ არაფერი არ არის. ატვირთე რამე!", "Download" => "ჩამოტვირთვა", +"Unshare" => "გაზიარების მოხსნა", "Upload too large" => "ასატვირთი ფაილი ძალიან დიდია", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ფაილის ზომა რომლის ატვირთვასაც თქვენ აპირებთ, აჭარბებს სერვერზე დაშვებულ მაქსიმუმს.", "Files are being scanned, please wait." => "მიმდინარეობს ფაილების სკანირება, გთხოვთ დაელოდოთ.", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index 98d0d60280127fd0cbb6c0111bfbbf36a1c6fecf..ba45bdaa5d7f2b479f15f10354434d1b489560cf 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -1,4 +1,7 @@ "%s 항목을 이동시키지 못하였음 - 파일 이름이 이미 존재함", +"Could not move %s" => "%s 항목을 이딩시키지 못하였음", +"Unable to rename file" => "파일 이름바꾸기 할 수 없음", "No file was uploaded. Unknown error" => "파일이 업로드되지 않았습니다. 알 수 없는 오류입니다", "There is no error, the file uploaded with success" => "업로드에 성공하였습니다.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "업로드한 파일이 php.ini의 upload_max_filesize보다 큽니다:", @@ -9,16 +12,15 @@ "Failed to write to disk" => "디스크에 쓰지 못했습니다", "Invalid directory." => "올바르지 않은 디렉터리입니다.", "Files" => "파일", -"Unshare" => "공유 해제", "Delete" => "삭제", "Rename" => "이름 바꾸기", +"Pending" => "보류 중", "{new_name} already exists" => "{new_name}이(가) 이미 존재함", "replace" => "바꾸기", "suggest name" => "이름 제안", "cancel" => "취소", -"replaced {new_name}" => "{new_name}을(를) 대체함", -"undo" => "실행 취소", "replaced {new_name} with {old_name}" => "{old_name}이(가) {new_name}(으)로 대체됨", +"undo" => "실행 취소", "'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름 입니다.", "File name cannot be empty." => "파일 이름이 비어 있을 수 없습니다.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "폴더 이름이 올바르지 않습니다. 이름에 문자 '\\', '/', '<', '>', ':', '\"', '|', '? ', '*'는 사용할 수 없습니다.", @@ -28,7 +30,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉터리이거나 비어 있기 때문에 업로드할 수 없습니다", "Upload Error" => "업로드 오류", "Close" => "닫기", -"Pending" => "보류 중", "1 file uploading" => "파일 1개 업로드 중", "{count} files uploading" => "파일 {count}개 업로드 중", "Upload cancelled." => "업로드가 취소되었습니다.", @@ -58,6 +59,7 @@ "Cancel upload" => "업로드 취소", "Nothing in here. Upload something!" => "내용이 없습니다. 업로드할 수 있습니다!", "Download" => "다운로드", +"Unshare" => "공유 해제", "Upload too large" => "업로드 용량 초과", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "이 파일이 서버에서 허용하는 최대 업로드 가능 용량보다 큽니다.", "Files are being scanned, please wait." => "파일을 검색하고 있습니다. 기다려 주십시오.", diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php index 79ef4bc9417fd0729a574c476f6fd85c9be6b924..b052da3a027c540e4535f6ba41352bfcccaae3f5 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -6,7 +6,6 @@ "Missing a temporary folder" => "Et feelt en temporären Dossier", "Failed to write to disk" => "Konnt net op den Disk schreiwen", "Files" => "Dateien", -"Unshare" => "Net méi deelen", "Delete" => "Läschen", "replace" => "ersetzen", "cancel" => "ofbriechen", @@ -34,6 +33,7 @@ "Cancel upload" => "Upload ofbriechen", "Nothing in here. Upload something!" => "Hei ass näischt. Lued eppes rop!", "Download" => "Eroflueden", +"Unshare" => "Net méi deelen", "Upload too large" => "Upload ze grouss", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Déi Dateien déi Dir probéiert erop ze lueden sinn méi grouss wei déi Maximal Gréisst déi op dësem Server erlaabt ass.", "Files are being scanned, please wait." => "Fichieren gi gescannt, war weg.", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index f4ad655f421f0baae6b136d176fa111bc108e00d..2f16fc224200fe36a54171ea990ab6a02c9220ce 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -6,20 +6,18 @@ "Missing a temporary folder" => "Nėra laikinojo katalogo", "Failed to write to disk" => "Nepavyko įrašyti į diską", "Files" => "Failai", -"Unshare" => "Nebesidalinti", "Delete" => "Ištrinti", "Rename" => "Pervadinti", +"Pending" => "Laukiantis", "{new_name} already exists" => "{new_name} jau egzistuoja", "replace" => "pakeisti", "suggest name" => "pasiūlyti pavadinimą", "cancel" => "atšaukti", -"replaced {new_name}" => "pakeiskite {new_name}", -"undo" => "anuliuoti", "replaced {new_name} with {old_name}" => "pakeiskite {new_name} į {old_name}", +"undo" => "anuliuoti", "Unable to upload your file as it is a directory or has 0 bytes" => "Neįmanoma įkelti failo - jo dydis gali būti 0 bitų arba tai katalogas", "Upload Error" => "Įkėlimo klaida", "Close" => "Užverti", -"Pending" => "Laukiantis", "1 file uploading" => "įkeliamas 1 failas", "{count} files uploading" => "{count} įkeliami failai", "Upload cancelled." => "Įkėlimas atšauktas.", @@ -46,6 +44,7 @@ "Cancel upload" => "Atšaukti siuntimą", "Nothing in here. Upload something!" => "Čia tuščia. Įkelkite ką nors!", "Download" => "Atsisiųsti", +"Unshare" => "Nebesidalinti", "Upload too large" => "Įkėlimui failas per didelis", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis viršija maksimalų leidžiamą šiame serveryje", "Files are being scanned, please wait." => "Skenuojami failai, prašome palaukti.", diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index 57b391e444c451e96e6d4aa1ebea4618baa4b3ac..93c3593b0950827061d44254aec88d5e58cd539c 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -1,4 +1,7 @@ "Nevarēja pārvietot %s — jau eksistē datne ar tādu nosaukumu", +"Could not move %s" => "Nevarēja pārvietot %s", +"Unable to rename file" => "Nevarēja pārsaukt datni", "No file was uploaded. Unknown error" => "Netika augšupielādēta neviena datne. Nezināma kļūda", "There is no error, the file uploaded with success" => "Augšupielāde pabeigta bez kļūdām", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Augšupielādētā datne pārsniedz upload_max_filesize norādījumu php.ini datnē:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Neviena datne netika augšupielādēta", "Missing a temporary folder" => "Trūkst pagaidu mapes", "Failed to write to disk" => "Neizdevās saglabāt diskā", +"Not enough storage available" => "Nav pietiekami daudz vietas", "Invalid directory." => "Nederīga direktorija.", "Files" => "Datnes", -"Unshare" => "Pārtraukt dalīšanos", "Delete permanently" => "Dzēst pavisam", "Delete" => "Dzēst", "Rename" => "Pārsaukt", +"Pending" => "Gaida savu kārtu", "{new_name} already exists" => "{new_name} jau eksistē", "replace" => "aizvietot", "suggest name" => "ieteiktais nosaukums", "cancel" => "atcelt", -"replaced {new_name}" => "aizvietots {new_name}", -"undo" => "atsaukt", "replaced {new_name} with {old_name}" => "aizvietoja {new_name} ar {old_name}", +"undo" => "atsaukt", "perform delete operation" => "veikt dzēšanas darbību", "'.' is an invalid file name." => "'.' ir nederīgs datnes nosaukums.", "File name cannot be empty." => "Datnes nosaukums nevar būt tukšs.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nevar augšupielādēt jūsu datni, jo tā ir direktorija vai arī tās izmērs ir 0 baiti", "Upload Error" => "Kļūda augšupielādējot", "Close" => "Aizvērt", -"Pending" => "Gaida savu kārtu", "1 file uploading" => "Augšupielādē 1 datni", "{count} files uploading" => "augšupielādē {count} datnes", "Upload cancelled." => "Augšupielāde ir atcelta.", @@ -57,10 +59,11 @@ "Text file" => "Teksta datne", "Folder" => "Mape", "From link" => "No saites", -"Trash" => "Miskaste", +"Deleted files" => "Dzēstās datnes", "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", "Upload too large" => "Datne ir par lielu, lai to augšupielādētu", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Augšupielādējamās datnes pārsniedz servera pieļaujamo datņu augšupielādes apjomu", "Files are being scanned, please wait." => "Datnes šobrīd tiek caurskatītas, lūdzu, uzgaidiet.", diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index 2580d1e6a97bc4440f4aaf61b5ce5af71ae817c2..cf9ad8abafc75fdcf85a367e91786d0155dd62ca 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -8,21 +8,19 @@ "Missing a temporary folder" => "Не постои привремена папка", "Failed to write to disk" => "Неуспеав да запишам на диск", "Files" => "Датотеки", -"Unshare" => "Не споделувај", "Delete" => "Избриши", "Rename" => "Преименувај", +"Pending" => "Чека", "{new_name} already exists" => "{new_name} веќе постои", "replace" => "замени", "suggest name" => "предложи име", "cancel" => "откажи", -"replaced {new_name}" => "земенета {new_name}", -"undo" => "врати", "replaced {new_name} with {old_name}" => "заменета {new_name} со {old_name}", +"undo" => "врати", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправилно име. , '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не се дозволени.", "Unable to upload your file as it is a directory or has 0 bytes" => "Не може да се преземе вашата датотека бидејќи фолдерот во кој се наоѓа фајлот има големина од 0 бајти", "Upload Error" => "Грешка при преземање", "Close" => "Затвои", -"Pending" => "Чека", "1 file uploading" => "1 датотека се подига", "{count} files uploading" => "{count} датотеки се подигаат", "Upload cancelled." => "Преземањето е прекинато.", @@ -51,6 +49,7 @@ "Cancel upload" => "Откажи прикачување", "Nothing in here. Upload something!" => "Тука нема ништо. Снимете нешто!", "Download" => "Преземи", +"Unshare" => "Не споделувај", "Upload too large" => "Датотеката е премногу голема", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.", "Files are being scanned, please wait." => "Се скенираат датотеки, ве молам почекајте.", diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php index 4ac26d80918281f242aafe25f097d28ea350215d..b15a9111e70475f697e409c700c650d4e55f456a 100644 --- a/apps/files/l10n/ms_MY.php +++ b/apps/files/l10n/ms_MY.php @@ -8,12 +8,12 @@ "Failed to write to disk" => "Gagal untuk disimpan", "Files" => "fail", "Delete" => "Padam", +"Pending" => "Dalam proses", "replace" => "ganti", "cancel" => "Batal", "Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes", "Upload Error" => "Muat naik ralat", "Close" => "Tutup", -"Pending" => "Dalam proses", "Upload cancelled." => "Muatnaik dibatalkan.", "Name" => "Nama ", "Size" => "Saiz", diff --git a/apps/files/l10n/my_MM.php b/apps/files/l10n/my_MM.php new file mode 100644 index 0000000000000000000000000000000000000000..b791a134ccce079a6f7c47dbc4b5bb61eb1586fc --- /dev/null +++ b/apps/files/l10n/my_MM.php @@ -0,0 +1,4 @@ + "ဖိုင်များ", +"Download" => "ဒေါင်းလုတ်" +); diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php index a6ba6e9c03ff60e219d6c45c5d5f55a87f196ba8..dc267e36fb3973f0049a54dd52ee3c5c0eda3738 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -7,21 +7,19 @@ "Missing a temporary folder" => "Mangler en midlertidig mappe", "Failed to write to disk" => "Klarte ikke å skrive til disk", "Files" => "Filer", -"Unshare" => "Avslutt deling", "Delete" => "Slett", "Rename" => "Omdøp", +"Pending" => "Ventende", "{new_name} already exists" => "{new_name} finnes allerede", "replace" => "erstatt", "suggest name" => "foreslå navn", "cancel" => "avbryt", -"replaced {new_name}" => "erstatt {new_name}", -"undo" => "angre", "replaced {new_name} with {old_name}" => "erstatt {new_name} med {old_name}", +"undo" => "angre", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldig navn, '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' er ikke tillatt.", "Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes", "Upload Error" => "Opplasting feilet", "Close" => "Lukk", -"Pending" => "Ventende", "1 file uploading" => "1 fil lastes opp", "{count} files uploading" => "{count} filer laster opp", "Upload cancelled." => "Opplasting avbrutt.", @@ -50,6 +48,7 @@ "Cancel upload" => "Avbryt opplasting", "Nothing in here. Upload something!" => "Ingenting her. Last opp noe!", "Download" => "Last ned", +"Unshare" => "Avslutt deling", "Upload too large" => "Opplasting for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er for store for å laste opp til denne serveren.", "Files are being scanned, please wait." => "Skanner etter filer, vennligst vent.", diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index 9095149cd9db7ead23a84218eef5de2faf9b5662..1719350f9c7927866ccd359efce62131fc9b4411 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -1,4 +1,7 @@ "Kon %s niet verplaatsen - Er bestaat al een bestand met deze naam", +"Could not move %s" => "Kon %s niet verplaatsen", +"Unable to rename file" => "Kan bestand niet hernoemen", "No file was uploaded. Unknown error" => "Er was geen bestand geladen. Onbekende fout", "There is no error, the file uploaded with success" => "Geen fout opgetreden, bestand successvol geupload.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Het geüploade bestand overscheidt de upload_max_filesize optie in php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Geen bestand geüpload", "Missing a temporary folder" => "Een tijdelijke map mist", "Failed to write to disk" => "Schrijven naar schijf mislukt", +"Not enough storage available" => "Niet genoeg opslagruimte beschikbaar", "Invalid directory." => "Ongeldige directory.", "Files" => "Bestanden", -"Unshare" => "Stop delen", "Delete permanently" => "Verwijder definitief", "Delete" => "Verwijder", "Rename" => "Hernoem", +"Pending" => "Wachten", "{new_name} already exists" => "{new_name} bestaat al", "replace" => "vervang", "suggest name" => "Stel een naam voor", "cancel" => "annuleren", -"replaced {new_name}" => "verving {new_name}", -"undo" => "ongedaan maken", "replaced {new_name} with {old_name}" => "verving {new_name} met {old_name}", +"undo" => "ongedaan maken", "perform delete operation" => "uitvoeren verwijderactie", "'.' is an invalid file name." => "'.' is een ongeldige bestandsnaam.", "File name cannot be empty." => "Bestandsnaam kan niet leeg zijn.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "uploaden van de file mislukt, het is of een directory of de bestandsgrootte is 0 bytes", "Upload Error" => "Upload Fout", "Close" => "Sluit", -"Pending" => "Wachten", "1 file uploading" => "1 bestand wordt ge-upload", "{count} files uploading" => "{count} bestanden aan het uploaden", "Upload cancelled." => "Uploaden geannuleerd.", @@ -57,10 +59,11 @@ "Text file" => "Tekstbestand", "Folder" => "Map", "From link" => "Vanaf link", -"Trash" => "Verwijderen", +"Deleted files" => "Verwijderde bestanden", "Cancel upload" => "Upload afbreken", "Nothing in here. Upload something!" => "Er bevindt zich hier niets. Upload een bestand!", "Download" => "Download", +"Unshare" => "Stop delen", "Upload too large" => "Bestanden te groot", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.", "Files are being scanned, please wait." => "Bestanden worden gescand, even wachten.", diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php index 78045b299edd7ce5b727df71a15a4743240386e1..7a39e9399f5c3d7e49ff2af9d07c645996d74a7c 100644 --- a/apps/files/l10n/oc.php +++ b/apps/files/l10n/oc.php @@ -6,16 +6,15 @@ "Missing a temporary folder" => "Un dorsièr temporari manca", "Failed to write to disk" => "L'escriptura sul disc a fracassat", "Files" => "Fichièrs", -"Unshare" => "Non parteja", "Delete" => "Escafa", "Rename" => "Torna nomenar", +"Pending" => "Al esperar", "replace" => "remplaça", "suggest name" => "nom prepausat", "cancel" => "anulla", "undo" => "defar", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'amontcargar lo teu fichièr qu'es un repertòri o que ten pas que 0 octet.", "Upload Error" => "Error d'amontcargar", -"Pending" => "Al esperar", "1 file uploading" => "1 fichièr al amontcargar", "Upload cancelled." => "Amontcargar anullat.", "File upload is in progress. Leaving the page now will cancel the upload." => "Un amontcargar es a se far. Daissar aquesta pagina ara tamparà lo cargament. ", @@ -37,6 +36,7 @@ "Cancel upload" => " Anulla l'amontcargar", "Nothing in here. Upload something!" => "Pas res dedins. Amontcarga qualquaren", "Download" => "Avalcarga", +"Unshare" => "Non parteja", "Upload too large" => "Amontcargament tròp gròs", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los fichièrs que sias a amontcargar son tròp pesucs per la talha maxi pel servidor.", "Files are being scanned, please wait." => "Los fiichièrs son a èsser explorats, ", diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index 45d0f4366144fb0a73e8e32cca1750c9f862379d..89eb3421291944cdec37b27f1d8506c94c08d258 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -1,62 +1,73 @@ "Plik nie został załadowany. Nieznany błąd", +"Could not move %s - File with this name already exists" => "Nie można było przenieść %s - Plik o takiej nazwie już istnieje", +"Could not move %s" => "Nie można było przenieść %s", +"Unable to rename file" => "Nie można zmienić nazwy pliku", +"No file was uploaded. Unknown error" => "Żaden plik nie został załadowany. Nieznany błąd", "There is no error, the file uploaded with success" => "Przesłano plik", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Wgrany plik przekracza wartość upload_max_filesize zdefiniowaną w php.ini: ", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Rozmiar przesłanego pliku przekracza maksymalną wartość dyrektywy upload_max_filesize, zawartą formularzu HTML", -"The uploaded file was only partially uploaded" => "Plik przesłano tylko częściowo", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Wysłany plik przekracza wielkość dyrektywy MAX_FILE_SIZE określonej w formularzu HTML", +"The uploaded file was only partially uploaded" => "Załadowany plik został wysłany tylko częściowo.", "No file was uploaded" => "Nie przesłano żadnego pliku", "Missing a temporary folder" => "Brak katalogu tymczasowego", "Failed to write to disk" => "Błąd zapisu na dysk", +"Not enough storage available" => "Za mało dostępnego miejsca", "Invalid directory." => "Zła ścieżka.", "Files" => "Pliki", -"Unshare" => "Nie udostępniaj", -"Delete" => "Usuwa element", +"Delete permanently" => "Trwale usuń", +"Delete" => "Usuń", "Rename" => "Zmień nazwę", +"Pending" => "Oczekujące", "{new_name} already exists" => "{new_name} już istnieje", -"replace" => "zastap", +"replace" => "zastąp", "suggest name" => "zasugeruj nazwę", "cancel" => "anuluj", -"replaced {new_name}" => "zastąpiony {new_name}", -"undo" => "wróć", -"replaced {new_name} with {old_name}" => "zastąpiony {new_name} z {old_name}", -"'.' is an invalid file name." => "'.' jest nieprawidłową nazwą pliku.", +"replaced {new_name} with {old_name}" => "zastąpiono {new_name} przez {old_name}", +"undo" => "cofnij", +"perform delete operation" => "wykonaj operację usunięcia", +"'.' is an invalid file name." => "„.” jest nieprawidłową nazwą pliku.", "File name cannot be empty." => "Nazwa pliku nie może być pusta.", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Niepoprawna nazwa, Znaki '\\', '/', '<', '>', ':', '\"', '|', '?' oraz '*'są niedozwolone.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku jeśli jest katalogiem lub ma 0 bajtów", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nieprawidłowa nazwa. Znaki '\\', '/', '<', '>', ':', '\"', '|', '?' oraz '*' są niedozwolone.", +"Your storage is full, files can not be updated or synced anymore!" => "Magazyn jest pełny. Pliki nie mogą zostać zaktualizowane lub zsynchronizowane!", +"Your storage is almost full ({usedSpacePercent}%)" => "Twój magazyn jest prawie pełny ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Pobieranie jest przygotowywane. Może to zająć trochę czasu jeśli pliki są duże.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku, ponieważ jest on katalogiem lub ma 0 bajtów", "Upload Error" => "Błąd wczytywania", "Close" => "Zamknij", -"Pending" => "Oczekujące", -"1 file uploading" => "1 plik wczytany", -"{count} files uploading" => "{count} przesyłanie plików", +"1 file uploading" => "1 plik wczytywany", +"{count} files uploading" => "Ilość przesyłanych plików: {count}", "Upload cancelled." => "Wczytywanie anulowane.", -"File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Teraz opuszczając stronę wysyłanie zostanie anulowane.", +"File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Jeśli opuścisz tę stronę, wysyłanie zostanie przerwane.", "URL cannot be empty." => "URL nie może być pusty.", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nazwa folderu nieprawidłowa. Wykorzystanie \"Shared\" jest zarezerwowane przez Owncloud", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nieprawidłowa nazwa folderu. Korzystanie z nazwy „Shared” jest zarezerwowane dla ownCloud", "Name" => "Nazwa", "Size" => "Rozmiar", -"Modified" => "Czas modyfikacji", +"Modified" => "Modyfikacja", "1 folder" => "1 folder", -"{count} folders" => "{count} foldery", +"{count} folders" => "Ilość folderów: {count}", "1 file" => "1 plik", -"{count} files" => "{count} pliki", +"{count} files" => "Ilość plików: {count}", "Upload" => "Prześlij", "File handling" => "Zarządzanie plikami", "Maximum upload size" => "Maksymalny rozmiar wysyłanego pliku", -"max. possible: " => "max. możliwych", +"max. possible: " => "maks. możliwy:", "Needed for multi-file and folder downloads." => "Wymagany do pobierania wielu plików i folderów", "Enable ZIP-download" => "Włącz pobieranie ZIP-paczki", -"0 is unlimited" => "0 jest nielimitowane", +"0 is unlimited" => "0 - bez limitów", "Maximum input size for ZIP files" => "Maksymalna wielkość pliku wejściowego ZIP ", "Save" => "Zapisz", "New" => "Nowy", "Text file" => "Plik tekstowy", "Folder" => "Katalog", -"From link" => "Z linku", -"Cancel upload" => "Przestań wysyłać", -"Nothing in here. Upload something!" => "Brak zawartości. Proszę wysłać pliki!", -"Download" => "Pobiera element", +"From link" => "Z odnośnika", +"Deleted files" => "Pliki usunięte", +"Cancel upload" => "Anuluj wysyłanie", +"You don’t have write permissions here." => "Nie masz uprawnień do zapisu w tym miejscu.", +"Nothing in here. Upload something!" => "Pusto. Wyślij coś!", +"Download" => "Pobierz", +"Unshare" => "Nie udostępniaj", "Upload too large" => "Wysyłany plik ma za duży rozmiar", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki które próbujesz przesłać, przekraczają maksymalną, dopuszczalną wielkość.", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki, które próbujesz przesłać, przekraczają maksymalną dopuszczalną wielkość.", "Files are being scanned, please wait." => "Skanowanie plików, proszę czekać.", -"Current scanning" => "Aktualnie skanowane" +"Current scanning" => "Aktualnie skanowane", +"Upgrading filesystem cache..." => "Uaktualnianie plików pamięci podręcznej..." ); diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index 361e81052b94fa700c9a2d8a6e24341ee7025af4..cce5a916dbc1bbc978607076e3210688ac594d8f 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -1,4 +1,7 @@ "Não possível mover %s - Um arquivo com este nome já existe", +"Could not move %s" => "Não possível mover %s", +"Unable to rename file" => "Impossível renomear arquivo", "No file was uploaded. Unknown error" => "Nenhum arquivo foi transferido. Erro desconhecido", "There is no error, the file uploaded with success" => "Não houve nenhum erro, o arquivo foi transferido com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O arquivo enviado excede a diretiva upload_max_filesize no php.ini: ", @@ -7,26 +10,29 @@ "No file was uploaded" => "Nenhum arquivo foi transferido", "Missing a temporary folder" => "Pasta temporária não encontrada", "Failed to write to disk" => "Falha ao escrever no disco", +"Not enough storage available" => "Espaço de armazenamento insuficiente", "Invalid directory." => "Diretório inválido.", "Files" => "Arquivos", -"Unshare" => "Descompartilhar", +"Delete permanently" => "Excluir permanentemente", "Delete" => "Excluir", "Rename" => "Renomear", +"Pending" => "Pendente", "{new_name} already exists" => "{new_name} já existe", "replace" => "substituir", "suggest name" => "sugerir nome", "cancel" => "cancelar", -"replaced {new_name}" => "substituído {new_name}", -"undo" => "desfazer", "replaced {new_name} with {old_name}" => "Substituído {old_name} por {new_name} ", +"undo" => "desfazer", +"perform delete operation" => "realizar operação de exclusão", "'.' is an invalid file name." => "'.' é um nome de arquivo inválido.", "File name cannot be empty." => "O nome do arquivo não pode estar vazio.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome inválido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.", +"Your storage is full, files can not be updated or synced anymore!" => "Seu armazenamento está cheio, arquivos não serão mais atualizados nem sincronizados!", +"Your storage is almost full ({usedSpacePercent}%)" => "Seu armazenamento está quase cheio ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Seu download está sendo preparado. Isto pode levar algum tempo se os arquivos forem grandes.", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossível enviar seus arquivo como diretório ou ele tem 0 bytes.", "Upload Error" => "Erro de envio", "Close" => "Fechar", -"Pending" => "Pendente", "1 file uploading" => "enviando 1 arquivo", "{count} files uploading" => "Enviando {count} arquivos", "Upload cancelled." => "Envio cancelado.", @@ -53,11 +59,14 @@ "Text file" => "Arquivo texto", "Folder" => "Pasta", "From link" => "Do link", +"Deleted files" => "Arquivos apagados", "Cancel upload" => "Cancelar upload", "Nothing in here. Upload something!" => "Nada aqui.Carrege alguma coisa!", "Download" => "Baixar", +"Unshare" => "Descompartilhar", "Upload too large" => "Arquivo muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor.", "Files are being scanned, please wait." => "Arquivos sendo escaneados, por favor aguarde.", -"Current scanning" => "Scanning atual" +"Current scanning" => "Scanning atual", +"Upgrading filesystem cache..." => "Aprimorando cache do sistema de arquivos..." ); diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index 52c87ed728a918fb50db2f157f1b719e52705559..b8334db27254a791e6ac194855ece2af28fc8c25 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -1,4 +1,7 @@ "Não foi possível mover o ficheiro %s - Já existe um ficheiro com esse nome", +"Could not move %s" => "Não foi possível move o ficheiro %s", +"Unable to rename file" => "Não foi possível renomear o ficheiro", "No file was uploaded. Unknown error" => "Nenhum ficheiro foi carregado. Erro desconhecido", "There is no error, the file uploaded with success" => "Sem erro, ficheiro enviado com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede o limite permitido na directiva do php.ini upload_max_filesize", @@ -7,19 +10,19 @@ "No file was uploaded" => "Não foi enviado nenhum ficheiro", "Missing a temporary folder" => "Falta uma pasta temporária", "Failed to write to disk" => "Falhou a escrita no disco", +"Not enough storage available" => "Não há espaço suficiente em disco", "Invalid directory." => "Directório Inválido", "Files" => "Ficheiros", -"Unshare" => "Deixar de partilhar", "Delete permanently" => "Eliminar permanentemente", "Delete" => "Apagar", "Rename" => "Renomear", +"Pending" => "Pendente", "{new_name} already exists" => "O nome {new_name} já existe", "replace" => "substituir", "suggest name" => "sugira um nome", "cancel" => "cancelar", -"replaced {new_name}" => "{new_name} substituido", -"undo" => "desfazer", "replaced {new_name} with {old_name}" => "substituido {new_name} por {old_name}", +"undo" => "desfazer", "perform delete operation" => "Executar a tarefa de apagar", "'.' is an invalid file name." => "'.' não é um nome de ficheiro válido!", "File name cannot be empty." => "O nome do ficheiro não pode estar vazio.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Não é possível fazer o envio do ficheiro devido a ser uma pasta ou ter 0 bytes", "Upload Error" => "Erro no envio", "Close" => "Fechar", -"Pending" => "Pendente", "1 file uploading" => "A enviar 1 ficheiro", "{count} files uploading" => "A carregar {count} ficheiros", "Upload cancelled." => "Envio cancelado.", @@ -57,10 +59,11 @@ "Text file" => "Ficheiro de texto", "Folder" => "Pasta", "From link" => "Da ligação", -"Trash" => "Lixo", +"Deleted files" => "Ficheiros eliminados", "Cancel upload" => "Cancelar envio", "Nothing in here. Upload something!" => "Vazio. Envie alguma coisa!", "Download" => "Transferir", +"Unshare" => "Deixar de partilhar", "Upload too large" => "Envio muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que está a tentar enviar excedem o tamanho máximo de envio permitido neste servidor.", "Files are being scanned, please wait." => "Os ficheiros estão a ser analisados, por favor aguarde.", diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index 79ca1cf4f5121c99ae3e2c476589786de017cbd5..153caba2291e8938db91e0e8d0f3316f993df763 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -1,4 +1,7 @@ "Nu se poate de mutat %s - Fișier cu acest nume deja există", +"Could not move %s" => "Nu s-a putut muta %s", +"Unable to rename file" => "Nu s-a putut redenumi fișierul", "No file was uploaded. Unknown error" => "Nici un fișier nu a fost încărcat. Eroare necunoscută", "There is no error, the file uploaded with success" => "Nicio eroare, fișierul a fost încărcat cu succes", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Fisierul incarcat depaseste upload_max_filesize permisi in php.ini: ", @@ -9,16 +12,15 @@ "Failed to write to disk" => "Eroare la scriere pe disc", "Invalid directory." => "Director invalid.", "Files" => "Fișiere", -"Unshare" => "Anulează partajarea", "Delete" => "Șterge", "Rename" => "Redenumire", +"Pending" => "În așteptare", "{new_name} already exists" => "{new_name} deja exista", "replace" => "înlocuire", "suggest name" => "sugerează nume", "cancel" => "anulare", -"replaced {new_name}" => "inlocuit {new_name}", -"undo" => "Anulează ultima acțiune", "replaced {new_name} with {old_name}" => "{new_name} inlocuit cu {old_name}", +"undo" => "Anulează ultima acțiune", "'.' is an invalid file name." => "'.' este un nume invalid de fișier.", "File name cannot be empty." => "Numele fișierului nu poate rămâne gol.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nume invalid, '\\', '/', '<', '>', ':', '\"', '|', '?' si '*' nu sunt permise.", @@ -26,7 +28,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nu s-a putut încărca fișierul tău deoarece pare să fie un director sau are 0 bytes.", "Upload Error" => "Eroare la încărcare", "Close" => "Închide", -"Pending" => "În așteptare", "1 file uploading" => "un fișier se încarcă", "{count} files uploading" => "{count} fisiere incarcate", "Upload cancelled." => "Încărcare anulată.", @@ -56,6 +57,7 @@ "Cancel upload" => "Anulează încărcarea", "Nothing in here. Upload something!" => "Nimic aici. Încarcă ceva!", "Download" => "Descarcă", +"Unshare" => "Anulează partajarea", "Upload too large" => "Fișierul încărcat este prea mare", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fișierul care l-ai încărcat a depășită limita maximă admisă la încărcare pe acest server.", "Files are being scanned, please wait." => "Fișierele sunt scanate, te rog așteptă.", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 05542452e7fcfee0bfdae1017ed69ad83a511ffc..cf8ee7c6c75693c0bda14776b6f5372b6af9533c 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -1,4 +1,7 @@ "Невозможно переместить %s - файл с таким именем уже существует", +"Could not move %s" => "Невозможно переместить %s", +"Unable to rename file" => "Невозможно переименовать файл", "No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка", "There is no error, the file uploaded with success" => "Файл успешно загружен", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Файл превышает размер установленный upload_max_filesize в php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Файл не был загружен", "Missing a temporary folder" => "Невозможно найти временную папку", "Failed to write to disk" => "Ошибка записи на диск", +"Not enough storage available" => "Недостаточно доступного места в хранилище", "Invalid directory." => "Неправильный каталог.", "Files" => "Файлы", -"Unshare" => "Отменить публикацию", "Delete permanently" => "Удалено навсегда", "Delete" => "Удалить", "Rename" => "Переименовать", +"Pending" => "Ожидание", "{new_name} already exists" => "{new_name} уже существует", "replace" => "заменить", "suggest name" => "предложить название", "cancel" => "отмена", -"replaced {new_name}" => "заменено {new_name}", -"undo" => "отмена", "replaced {new_name} with {old_name}" => "заменено {new_name} на {old_name}", +"undo" => "отмена", "perform delete operation" => "выполняется операция удаления", "'.' is an invalid file name." => "'.' - неправильное имя файла.", "File name cannot be empty." => "Имя файла не может быть пустым.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Не удается загрузить файл размером 0 байт в каталог", "Upload Error" => "Ошибка загрузки", "Close" => "Закрыть", -"Pending" => "Ожидание", "1 file uploading" => "загружается 1 файл", "{count} files uploading" => "{count} файлов загружается", "Upload cancelled." => "Загрузка отменена.", @@ -57,10 +59,12 @@ "Text file" => "Текстовый файл", "Folder" => "Папка", "From link" => "Из ссылки", -"Trash" => "Корзина", +"Deleted files" => "Удалённые файлы", "Cancel upload" => "Отмена загрузки", +"You don’t have write permissions here." => "У вас нет разрешений на запись здесь.", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Скачать", +"Unshare" => "Отменить публикацию", "Upload too large" => "Файл слишком большой", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файлы, которые Вы пытаетесь загрузить, превышают лимит для файлов на этом сервере.", "Files are being scanned, please wait." => "Подождите, файлы сканируются.", diff --git a/apps/files/l10n/ru_RU.php b/apps/files/l10n/ru_RU.php index 9b2913970f2efbe6ff7d4b8e1f5978ce1be1347e..054ed8094db1b6d74d861565689a47aa736dc615 100644 --- a/apps/files/l10n/ru_RU.php +++ b/apps/files/l10n/ru_RU.php @@ -1,4 +1,7 @@ "Неполучается перенести %s - Файл с таким именем уже существует", +"Could not move %s" => "Неполучается перенести %s ", +"Unable to rename file" => "Невозможно переименовать файл", "No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка", "There is no error, the file uploaded with success" => "Ошибка отсутствует, файл загружен успешно.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Размер загружаемого файла превышает upload_max_filesize директиву в php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Файл не был загружен", "Missing a temporary folder" => "Отсутствует временная папка", "Failed to write to disk" => "Не удалось записать на диск", +"Not enough storage available" => "Недостаточно места в хранилище", "Invalid directory." => "Неверный каталог.", "Files" => "Файлы", -"Unshare" => "Скрыть", "Delete permanently" => "Удалить навсегда", "Delete" => "Удалить", "Rename" => "Переименовать", +"Pending" => "Ожидающий решения", "{new_name} already exists" => "{новое_имя} уже существует", "replace" => "отмена", "suggest name" => "подобрать название", "cancel" => "отменить", -"replaced {new_name}" => "заменено {новое_имя}", -"undo" => "отменить действие", "replaced {new_name} with {old_name}" => "заменено {новое_имя} с {старое_имя}", +"undo" => "отменить действие", "perform delete operation" => "выполняется процесс удаления", "'.' is an invalid file name." => "'.' является неверным именем файла.", "File name cannot be empty." => "Имя файла не может быть пустым.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Невозможно загрузить файл,\n так как он имеет нулевой размер или является директорией", "Upload Error" => "Ошибка загрузки", "Close" => "Закрыть", -"Pending" => "Ожидающий решения", "1 file uploading" => "загрузка 1 файла", "{count} files uploading" => "{количество} загружено файлов", "Upload cancelled." => "Загрузка отменена", @@ -57,10 +59,10 @@ "Text file" => "Текстовый файл", "Folder" => "Папка", "From link" => "По ссылке", -"Trash" => "Корзина", "Cancel upload" => "Отмена загрузки", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Загрузить", +"Unshare" => "Скрыть", "Upload too large" => "Загрузка слишком велика", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Размер файлов, которые Вы пытаетесь загрузить, превышает максимально допустимый размер для загрузки на данный сервер.", "Files are being scanned, please wait." => "Файлы сканируются, пожалуйста, подождите.", diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php index 316470d83965773a1e2f8cffc68db4bf63ec7694..de2b89068451e83d9833f7ec8494fea9b853bb54 100644 --- a/apps/files/l10n/si_LK.php +++ b/apps/files/l10n/si_LK.php @@ -7,7 +7,6 @@ "Missing a temporary folder" => "තාවකාලික ෆොල්ඩරයක් සොයාගත නොහැක", "Failed to write to disk" => "තැටිගත කිරීම අසාර්ථකයි", "Files" => "ගොනු", -"Unshare" => "නොබෙදු", "Delete" => "මකන්න", "Rename" => "නැවත නම් කරන්න", "replace" => "ප්‍රතිස්ථාපනය කරන්න", @@ -41,6 +40,7 @@ "Cancel upload" => "උඩුගත කිරීම අත් හරින්න", "Nothing in here. Upload something!" => "මෙහි කිසිවක් නොමැත. යමක් උඩුගත කරන්න", "Download" => "බාගත කිරීම", +"Unshare" => "නොබෙදු", "Upload too large" => "උඩුගත කිරීම විශාල වැඩිය", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ඔබ උඩුගත කිරීමට තැත් කරන ගොනු මෙම සේවාදායකයා උඩුගත කිරීමට ඉඩදී ඇති උපරිම ගොනු විශාලත්වයට වඩා වැඩිය", "Files are being scanned, please wait." => "ගොනු පරික්ෂා කෙරේ. මඳක් රැඳී සිටින්න", diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index be7f77adab076e38ff7b3525cd0ef46f20fe5b6e..a6cb2909111d3e9183acae000e50453492db5e89 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -1,4 +1,7 @@ "Nie je možné presunúť %s - súbor s týmto menom už existuje", +"Could not move %s" => "Nie je možné presunúť %s", +"Unable to rename file" => "Nemožno premenovať súbor", "No file was uploaded. Unknown error" => "Žiaden súbor nebol odoslaný. Neznáma chyba", "There is no error, the file uploaded with success" => "Nenastala žiadna chyba, súbor bol úspešne nahraný", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Nahraný súbor predčil konfiguračnú direktívu upload_max_filesize v súbore php.ini:", @@ -7,19 +10,19 @@ "No file was uploaded" => "Žiaden súbor nebol nahraný", "Missing a temporary folder" => "Chýbajúci dočasný priečinok", "Failed to write to disk" => "Zápis na disk sa nepodaril", -"Invalid directory." => "Neplatný adresár", +"Not enough storage available" => "Nedostatok dostupného úložného priestoru", +"Invalid directory." => "Neplatný priečinok", "Files" => "Súbory", -"Unshare" => "Nezdielať", "Delete permanently" => "Zmazať trvalo", "Delete" => "Odstrániť", "Rename" => "Premenovať", +"Pending" => "Čaká sa", "{new_name} already exists" => "{new_name} už existuje", "replace" => "nahradiť", "suggest name" => "pomôcť s menom", "cancel" => "zrušiť", -"replaced {new_name}" => "prepísaný {new_name}", -"undo" => "vrátiť", "replaced {new_name} with {old_name}" => "prepísaný {new_name} súborom {old_name}", +"undo" => "vrátiť", "perform delete operation" => "vykonať zmazanie", "'.' is an invalid file name." => "'.' je neplatné meno súboru.", "File name cannot be empty." => "Meno súboru nemôže byť prázdne", @@ -30,13 +33,12 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Nemôžem nahrať súbor lebo je to priečinok alebo má 0 bajtov.", "Upload Error" => "Chyba odosielania", "Close" => "Zavrieť", -"Pending" => "Čaká sa", "1 file uploading" => "1 súbor sa posiela ", "{count} files uploading" => "{count} súborov odosielaných", "Upload cancelled." => "Odosielanie zrušené", "File upload is in progress. Leaving the page now will cancel the upload." => "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "URL cannot be empty." => "URL nemôže byť prázdne", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatné meno adresára. Používanie mena 'Shared' je vyhradené len pre Owncloud", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatné meno priečinka. Používanie mena 'Shared' je vyhradené len pre Owncloud", "Name" => "Meno", "Size" => "Veľkosť", "Modified" => "Upravené", @@ -45,10 +47,10 @@ "1 file" => "1 súbor", "{count} files" => "{count} súborov", "Upload" => "Odoslať", -"File handling" => "Nastavenie správanie k súborom", +"File handling" => "Nastavenie správania sa k súborom", "Maximum upload size" => "Maximálna veľkosť odosielaného súboru", "max. possible: " => "najväčšie možné:", -"Needed for multi-file and folder downloads." => "Vyžadované pre sťahovanie viacerých súborov a adresárov.", +"Needed for multi-file and folder downloads." => "Vyžadované pre sťahovanie viacerých súborov a priečinkov.", "Enable ZIP-download" => "Povoliť sťahovanie ZIP súborov", "0 is unlimited" => "0 znamená neobmedzené", "Maximum input size for ZIP files" => "Najväčšia veľkosť ZIP súborov", @@ -57,13 +59,15 @@ "Text file" => "Textový súbor", "Folder" => "Priečinok", "From link" => "Z odkazu", -"Trash" => "Kôš", +"Deleted files" => "Zmazané súbory", "Cancel upload" => "Zrušiť odosielanie", +"You don’t have write permissions here." => "Nemáte oprávnenie na zápis.", "Nothing in here. Upload something!" => "Žiadny súbor. Nahrajte niečo!", "Download" => "Stiahnuť", +"Unshare" => "Nezdielať", "Upload too large" => "Odosielaný súbor je príliš veľký", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory, ktoré sa snažíte nahrať, presahujú maximálnu veľkosť pre nahratie súborov na tento server.", "Files are being scanned, please wait." => "Čakajte, súbory sú prehľadávané.", -"Current scanning" => "Práve prehliadané", +"Current scanning" => "Práve prezerané", "Upgrading filesystem cache..." => "Aktualizujem medzipamäť súborového systému..." ); diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index d55b4207d2b3d3f583541e2b4e0f3b4d8eed677b..6458a588aedfe2165b0836f1d837209c375fac64 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -8,21 +8,19 @@ "Missing a temporary folder" => "Manjka začasna mapa", "Failed to write to disk" => "Pisanje na disk je spodletelo", "Files" => "Datoteke", -"Unshare" => "Odstrani iz souporabe", "Delete" => "Izbriši", "Rename" => "Preimenuj", +"Pending" => "V čakanju ...", "{new_name} already exists" => "{new_name} že obstaja", "replace" => "zamenjaj", "suggest name" => "predlagaj ime", "cancel" => "prekliči", -"replaced {new_name}" => "zamenjano je ime {new_name}", -"undo" => "razveljavi", "replaced {new_name} with {old_name}" => "zamenjano ime {new_name} z imenom {old_name}", +"undo" => "razveljavi", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Neveljavno ime, znaki '\\', '/', '<', '>', ':', '\"', '|', '?' in '*' niso dovoljeni.", "Unable to upload your file as it is a directory or has 0 bytes" => "Pošiljanje ni mogoče, saj gre za mapo, ali pa je datoteka velikosti 0 bajtov.", "Upload Error" => "Napaka med nalaganjem", "Close" => "Zapri", -"Pending" => "V čakanju ...", "1 file uploading" => "Pošiljanje 1 datoteke", "{count} files uploading" => "nalagam {count} datotek", "Upload cancelled." => "Pošiljanje je preklicano.", @@ -51,6 +49,7 @@ "Cancel upload" => "Prekliči pošiljanje", "Nothing in here. Upload something!" => "Tukaj ni ničesar. Naložite kaj!", "Download" => "Prejmi", +"Unshare" => "Odstrani iz souporabe", "Upload too large" => "Nalaganje ni mogoče, ker je preveliko", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite naložiti, presegajo največjo dovoljeno velikost na tem strežniku.", "Files are being scanned, please wait." => "Poteka preučevanje datotek, počakajte ...", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index 188c8fc0da641203e0ab5c28ac0dbf6c41e26794..fe71ee9c90d1b05bcd9712f9b5ca340dd0157d43 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -7,21 +7,19 @@ "Missing a temporary folder" => "Недостаје привремена фасцикла", "Failed to write to disk" => "Не могу да пишем на диск", "Files" => "Датотеке", -"Unshare" => "Укини дељење", "Delete" => "Обриши", "Rename" => "Преименуј", +"Pending" => "На чекању", "{new_name} already exists" => "{new_name} већ постоји", "replace" => "замени", "suggest name" => "предложи назив", "cancel" => "откажи", -"replaced {new_name}" => "замењено {new_name}", -"undo" => "опозови", "replaced {new_name} with {old_name}" => "замењено {new_name} са {old_name}", +"undo" => "опозови", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неисправан назив. Следећи знакови нису дозвољени: \\, /, <, >, :, \", |, ? и *.", "Unable to upload your file as it is a directory or has 0 bytes" => "Не могу да отпремим датотеку као фасциклу или она има 0 бајтова", "Upload Error" => "Грешка при отпремању", "Close" => "Затвори", -"Pending" => "На чекању", "1 file uploading" => "Отпремам 1 датотеку", "{count} files uploading" => "Отпремам {count} датотеке/а", "Upload cancelled." => "Отпремање је прекинуто.", @@ -49,6 +47,7 @@ "Cancel upload" => "Прекини отпремање", "Nothing in here. Upload something!" => "Овде нема ничег. Отпремите нешто!", "Download" => "Преузми", +"Unshare" => "Укини дељење", "Upload too large" => "Датотека је превелика", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеке које желите да отпремите прелазе ограничење у величини.", "Files are being scanned, please wait." => "Скенирам датотеке…", diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index ebdaae9193f96cfdd23e2d7d63982a0f0790320b..c02a7818707df8cae651a79f2dc4d836395f5c29 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -1,4 +1,7 @@ "Kunde inte flytta %s - Det finns redan en fil med detta namn", +"Could not move %s" => "Kan inte flytta %s", +"Unable to rename file" => "Kan inte byta namn på filen", "No file was uploaded. Unknown error" => "Ingen fil uppladdad. Okänt fel", "There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uppladdade filen överskrider upload_max_filesize direktivet php.ini:", @@ -7,18 +10,19 @@ "No file was uploaded" => "Ingen fil blev uppladdad", "Missing a temporary folder" => "Saknar en tillfällig mapp", "Failed to write to disk" => "Misslyckades spara till disk", +"Not enough storage available" => "Inte tillräckligt med lagringsutrymme tillgängligt", "Invalid directory." => "Felaktig mapp.", "Files" => "Filer", -"Unshare" => "Sluta dela", +"Delete permanently" => "Radera permanent", "Delete" => "Radera", "Rename" => "Byt namn", +"Pending" => "Väntar", "{new_name} already exists" => "{new_name} finns redan", "replace" => "ersätt", "suggest name" => "föreslå namn", "cancel" => "avbryt", -"replaced {new_name}" => "ersatt {new_name}", -"undo" => "ångra", "replaced {new_name} with {old_name}" => "ersatt {new_name} med {old_name}", +"undo" => "ångra", "perform delete operation" => "utför raderingen", "'.' is an invalid file name." => "'.' är ett ogiltigt filnamn.", "File name cannot be empty." => "Filnamn kan inte vara tomt.", @@ -29,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Kunde inte ladda upp dina filer eftersom det antingen är en mapp eller har 0 bytes.", "Upload Error" => "Uppladdningsfel", "Close" => "Stäng", -"Pending" => "Väntar", "1 file uploading" => "1 filuppladdning", "{count} files uploading" => "{count} filer laddas upp", "Upload cancelled." => "Uppladdning avbruten.", @@ -56,10 +59,11 @@ "Text file" => "Textfil", "Folder" => "Mapp", "From link" => "Från länk", -"Trash" => "Papperskorgen", +"Deleted files" => "Raderade filer", "Cancel upload" => "Avbryt uppladdning", "Nothing in here. Upload something!" => "Ingenting här. Ladda upp något!", "Download" => "Ladda ner", +"Unshare" => "Sluta dela", "Upload too large" => "För stor uppladdning", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar på servern.", "Files are being scanned, please wait." => "Filer skannas, var god vänta", diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php index 383b4ef6f85c9031f9790a78870fc1d05ee16590..304453f1290b87b5b30e3df562bf0c77488625ef 100644 --- a/apps/files/l10n/ta_LK.php +++ b/apps/files/l10n/ta_LK.php @@ -7,21 +7,19 @@ "Missing a temporary folder" => "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை", "Failed to write to disk" => "வட்டில் எழுத முடியவில்லை", "Files" => "கோப்புகள்", -"Unshare" => "பகிரப்படாதது", "Delete" => "அழிக்க", "Rename" => "பெயர்மாற்றம்", +"Pending" => "நிலுவையிலுள்ள", "{new_name} already exists" => "{new_name} ஏற்கனவே உள்ளது", "replace" => "மாற்றிடுக", "suggest name" => "பெயரை பரிந்துரைக்க", "cancel" => "இரத்து செய்க", -"replaced {new_name}" => "மாற்றப்பட்டது {new_name}", -"undo" => "முன் செயல் நீக்கம் ", "replaced {new_name} with {old_name}" => "{new_name} ஆனது {old_name} இனால் மாற்றப்பட்டது", +"undo" => "முன் செயல் நீக்கம் ", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "செல்லுபடியற்ற பெயர்,'\\', '/', '<', '>', ':', '\"', '|', '?' மற்றும் '*' ஆகியன அனுமதிக்கப்படமாட்டாது.", "Unable to upload your file as it is a directory or has 0 bytes" => "அடைவு அல்லது 0 bytes ஐ கொண்டுள்ளதால் உங்களுடைய கோப்பை பதிவேற்ற முடியவில்லை", "Upload Error" => "பதிவேற்றல் வழு", "Close" => "மூடுக", -"Pending" => "நிலுவையிலுள்ள", "1 file uploading" => "1 கோப்பு பதிவேற்றப்படுகிறது", "{count} files uploading" => "{எண்ணிக்கை} கோப்புகள் பதிவேற்றப்படுகின்றது", "Upload cancelled." => "பதிவேற்றல் இரத்து செய்யப்பட்டுள்ளது", @@ -50,6 +48,7 @@ "Cancel upload" => "பதிவேற்றலை இரத்து செய்க", "Nothing in here. Upload something!" => "இங்கு ஒன்றும் இல்லை. ஏதாவது பதிவேற்றுக!", "Download" => "பதிவிறக்குக", +"Unshare" => "பகிரப்படாதது", "Upload too large" => "பதிவேற்றல் மிகப்பெரியது", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "நீங்கள் பதிவேற்ற முயற்சிக்கும் கோப்புகளானது இந்த சேவையகத்தில் கோப்பு பதிவேற்றக்கூடிய ஆகக்கூடிய அளவிலும் கூடியது.", "Files are being scanned, please wait." => "கோப்புகள் வருடப்படுகின்றன, தயவுசெய்து காத்திருங்கள்.", diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php index 5f880702b822e6d7b035728ba125359305d8e91a..2353501b4782dfe8ba4649a43ef9dabf282ea3e4 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -1,4 +1,7 @@ "ไม่สามารถย้าย %s ได้ - ไฟล์ที่ใช้ชื่อนี้มีอยู่แล้ว", +"Could not move %s" => "ไม่สามารถย้าย %s ได้", +"Unable to rename file" => "ไม่สามารถเปลี่ยนชื่อไฟล์ได้", "No file was uploaded. Unknown error" => "ยังไม่มีไฟล์ใดที่ถูกอัพโหลด เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ", "There is no error, the file uploaded with success" => "ไม่มีข้อผิดพลาดใดๆ ไฟล์ถูกอัพโหลดเรียบร้อยแล้ว", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "ขนาดไฟล์ที่อัพโหลดมีขนาดเกิน upload_max_filesize ที่ระบุไว้ใน php.ini", @@ -7,18 +10,18 @@ "No file was uploaded" => "ยังไม่มีไฟล์ที่ถูกอัพโหลด", "Missing a temporary folder" => "แฟ้มเอกสารชั่วคราวเกิดการสูญหาย", "Failed to write to disk" => "เขียนข้อมูลลงแผ่นดิสก์ล้มเหลว", +"Not enough storage available" => "เหลือพื้นที่ไม่เพียงสำหรับใช้งาน", "Invalid directory." => "ไดเร็กทอรี่ไม่ถูกต้อง", "Files" => "ไฟล์", -"Unshare" => "ยกเลิกการแชร์ข้อมูล", "Delete" => "ลบ", "Rename" => "เปลี่ยนชื่อ", +"Pending" => "อยู่ระหว่างดำเนินการ", "{new_name} already exists" => "{new_name} มีอยู่แล้วในระบบ", "replace" => "แทนที่", "suggest name" => "แนะนำชื่อ", "cancel" => "ยกเลิก", -"replaced {new_name}" => "แทนที่ {new_name} แล้ว", -"undo" => "เลิกทำ", "replaced {new_name} with {old_name}" => "แทนที่ {new_name} ด้วย {old_name} แล้ว", +"undo" => "เลิกทำ", "perform delete operation" => "ดำเนินการตามคำสั่งลบ", "'.' is an invalid file name." => "'.' เป็นชื่อไฟล์ที่ไม่ถูกต้อง", "File name cannot be empty." => "ชื่อไฟล์ไม่สามารถเว้นว่างได้", @@ -29,7 +32,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถอัพโหลดไฟล์ของคุณได้ เนื่องจากไฟล์ดังกล่าวเป็นไดเร็กทอรี่หรือมีขนาด 0 ไบต์", "Upload Error" => "เกิดข้อผิดพลาดในการอัพโหลด", "Close" => "ปิด", -"Pending" => "อยู่ระหว่างดำเนินการ", "1 file uploading" => "กำลังอัพโหลดไฟล์ 1 ไฟล์", "{count} files uploading" => "กำลังอัพโหลด {count} ไฟล์", "Upload cancelled." => "การอัพโหลดถูกยกเลิก", @@ -56,10 +58,10 @@ "Text file" => "ไฟล์ข้อความ", "Folder" => "แฟ้มเอกสาร", "From link" => "จากลิงก์", -"Trash" => "ถังขยะ", "Cancel upload" => "ยกเลิกการอัพโหลด", "Nothing in here. Upload something!" => "ยังไม่มีไฟล์ใดๆอยู่ที่นี่ กรุณาอัพโหลดไฟล์!", "Download" => "ดาวน์โหลด", +"Unshare" => "ยกเลิกการแชร์ข้อมูล", "Upload too large" => "ไฟล์ที่อัพโหลดมีขนาดใหญ่เกินไป", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะอัพโหลดมีขนาดเกินกว่าขนาดสูงสุดที่กำหนดไว้ให้อัพโหลดได้สำหรับเซิร์ฟเวอร์นี้", "Files are being scanned, please wait." => "ไฟล์กำลังอยู่ระหว่างการสแกน, กรุณารอสักครู่.", diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index 3325cbe1ee47b2b3a23559ffea0e90455f79ab7b..bcbef8daf3581dfd47cfe09cc87f59c863dcc9df 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -1,4 +1,7 @@ "%s taşınamadı. Bu isimde dosya zaten var.", +"Could not move %s" => "%s taşınamadı", +"Unable to rename file" => "Dosya adı değiştirilemedi", "No file was uploaded. Unknown error" => "Dosya yüklenmedi. Bilinmeyen hata", "There is no error, the file uploaded with success" => "Bir hata yok, dosya başarıyla yüklendi", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "php.ini dosyasında upload_max_filesize ile belirtilen dosya yükleme sınırı aşıldı.", @@ -7,26 +10,29 @@ "No file was uploaded" => "Hiç dosya yüklenmedi", "Missing a temporary folder" => "Geçici bir klasör eksik", "Failed to write to disk" => "Diske yazılamadı", +"Not enough storage available" => "Yeterli disk alanı yok", "Invalid directory." => "Geçersiz dizin.", "Files" => "Dosyalar", -"Unshare" => "Paylaşılmayan", +"Delete permanently" => "Kalıcı olarak sil", "Delete" => "Sil", "Rename" => "İsim değiştir.", +"Pending" => "Bekliyor", "{new_name} already exists" => "{new_name} zaten mevcut", "replace" => "değiştir", "suggest name" => "Öneri ad", "cancel" => "iptal", -"replaced {new_name}" => "değiştirilen {new_name}", -"undo" => "geri al", "replaced {new_name} with {old_name}" => "{new_name} ismi {old_name} ile değiştirildi", +"undo" => "geri al", +"perform delete operation" => "Silme işlemini gerçekleştir", "'.' is an invalid file name." => "'.' geçersiz dosya adı.", "File name cannot be empty." => "Dosya adı boş olamaz.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Geçersiz isim, '\\', '/', '<', '>', ':', '\"', '|', '?' ve '*' karakterlerine izin verilmemektedir.", +"Your storage is full, files can not be updated or synced anymore!" => "Depolama alanınız dolu, artık dosyalar güncellenmeyecek yada senkronizasyon edilmeyecek.", +"Your storage is almost full ({usedSpacePercent}%)" => "Depolama alanınız neredeyse dolu ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "İndirmeniz hazırlanıyor. Dosya büyük ise biraz zaman alabilir.", "Unable to upload your file as it is a directory or has 0 bytes" => "Dosyanızın boyutu 0 byte olduğundan veya bir dizin olduğundan yüklenemedi", "Upload Error" => "Yükleme hatası", "Close" => "Kapat", -"Pending" => "Bekliyor", "1 file uploading" => "1 dosya yüklendi", "{count} files uploading" => "{count} dosya yükleniyor", "Upload cancelled." => "Yükleme iptal edildi.", @@ -53,11 +59,14 @@ "Text file" => "Metin dosyası", "Folder" => "Klasör", "From link" => "Bağlantıdan", +"Deleted files" => "Dosyalar silindi", "Cancel upload" => "Yüklemeyi iptal et", "Nothing in here. Upload something!" => "Burada hiçbir şey yok. Birşeyler yükleyin!", "Download" => "İndir", +"Unshare" => "Paylaşılmayan", "Upload too large" => "Yüklemeniz çok büyük", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosyalar bu sunucudaki maksimum yükleme boyutunu aşıyor.", "Files are being scanned, please wait." => "Dosyalar taranıyor, lütfen bekleyin.", -"Current scanning" => "Güncel tarama" +"Current scanning" => "Güncel tarama", +"Upgrading filesystem cache..." => "Sistem dosyası önbelleği güncelleniyor" ); diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 4a76158c462785c90440290045222028e37204ab..05f279d8eb6dccff62e6ba7c94e1a6ff1fe5b143 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -1,4 +1,7 @@ "Не вдалося перемістити %s - Файл з таким ім'ям вже існує", +"Could not move %s" => "Не вдалося перемістити %s", +"Unable to rename file" => "Не вдалося перейменувати файл", "No file was uploaded. Unknown error" => "Не завантажено жодного файлу. Невідома помилка", "There is no error, the file uploaded with success" => "Файл успішно вивантажено без помилок.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Розмір звантаження перевищує upload_max_filesize параметра в php.ini: ", @@ -7,19 +10,19 @@ "No file was uploaded" => "Не відвантажено жодного файлу", "Missing a temporary folder" => "Відсутній тимчасовий каталог", "Failed to write to disk" => "Невдалося записати на диск", +"Not enough storage available" => "Місця більше немає", "Invalid directory." => "Невірний каталог.", "Files" => "Файли", -"Unshare" => "Заборонити доступ", "Delete permanently" => "Видалити назавжди", "Delete" => "Видалити", "Rename" => "Перейменувати", +"Pending" => "Очікування", "{new_name} already exists" => "{new_name} вже існує", "replace" => "заміна", "suggest name" => "запропонуйте назву", "cancel" => "відміна", -"replaced {new_name}" => "замінено {new_name}", -"undo" => "відмінити", "replaced {new_name} with {old_name}" => "замінено {new_name} на {old_name}", +"undo" => "відмінити", "perform delete operation" => "виконати операцію видалення", "'.' is an invalid file name." => "'.' це невірне ім'я файлу.", "File name cannot be empty." => " Ім'я файлу не може бути порожнім.", @@ -30,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Неможливо завантажити ваш файл тому, що він тека або файл розміром 0 байт", "Upload Error" => "Помилка завантаження", "Close" => "Закрити", -"Pending" => "Очікування", "1 file uploading" => "1 файл завантажується", "{count} files uploading" => "{count} файлів завантажується", "Upload cancelled." => "Завантаження перервано.", @@ -57,10 +59,11 @@ "Text file" => "Текстовий файл", "Folder" => "Папка", "From link" => "З посилання", -"Trash" => "Смітник", +"Deleted files" => "Видалено файлів", "Cancel upload" => "Перервати завантаження", "Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!", "Download" => "Завантажити", +"Unshare" => "Заборонити доступ", "Upload too large" => "Файл занадто великий", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файли,що ви намагаєтесь відвантажити перевищують максимальний дозволений розмір файлів на цьому сервері.", "Files are being scanned, please wait." => "Файли скануються, зачекайте, будь-ласка.", diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 0daf580a2f5a9c8fc1963d7cff6490b8b3e1f8a2..affca6c12f86b9109dcd20781ea2aeb209598fec 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -1,32 +1,44 @@ "Không thể di chuyển %s - Đã có tên file này trên hệ thống", +"Could not move %s" => "Không thể di chuyển %s", +"Unable to rename file" => "Không thể đổi tên file", "No file was uploaded. Unknown error" => "Không có tập tin nào được tải lên. Lỗi không xác định", "There is no error, the file uploaded with success" => "Không có lỗi, các tập tin đã được tải lên thành công", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "The uploaded file exceeds the upload_max_filesize directive in php.ini: ", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Kích thước những tập tin tải lên vượt quá MAX_FILE_SIZE đã được quy định", "The uploaded file was only partially uploaded" => "Tập tin tải lên mới chỉ tải lên được một phần", "No file was uploaded" => "Không có tập tin nào được tải lên", "Missing a temporary folder" => "Không tìm thấy thư mục tạm", "Failed to write to disk" => "Không thể ghi ", +"Not enough storage available" => "Không đủ không gian lưu trữ", +"Invalid directory." => "Thư mục không hợp lệ", "Files" => "Tập tin", -"Unshare" => "Không chia sẽ", +"Delete permanently" => "Xóa vĩnh vễn", "Delete" => "Xóa", "Rename" => "Sửa tên", +"Pending" => "Chờ", "{new_name} already exists" => "{new_name} đã tồn tại", "replace" => "thay thế", "suggest name" => "tên gợi ý", "cancel" => "hủy", -"replaced {new_name}" => "đã thay thế {new_name}", -"undo" => "lùi lại", "replaced {new_name} with {old_name}" => "đã thay thế {new_name} bằng {old_name}", +"undo" => "lùi lại", +"perform delete operation" => "thực hiện việc xóa", +"'.' is an invalid file name." => "'.' là một tên file không hợp lệ", +"File name cannot be empty." => "Tên file không được rỗng", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Tên không hợp lệ, '\\', '/', '<', '>', ':', '\"', '|', '?' và '*' thì không được phép dùng.", +"Your storage is full, files can not be updated or synced anymore!" => "Your storage is full, files can not be updated or synced anymore!", +"Your storage is almost full ({usedSpacePercent}%)" => "Your storage is almost full ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Your download is being prepared. This might take some time if the files are big.", "Unable to upload your file as it is a directory or has 0 bytes" => "Không thể tải lên tập tin này do nó là một thư mục hoặc kích thước tập tin bằng 0 byte", "Upload Error" => "Tải lên lỗi", "Close" => "Đóng", -"Pending" => "Chờ", "1 file uploading" => "1 tệp tin đang được tải lên", "{count} files uploading" => "{count} tập tin đang tải lên", "Upload cancelled." => "Hủy tải lên", "File upload is in progress. Leaving the page now will cancel the upload." => "Tập tin tải lên đang được xử lý. Nếu bạn rời khỏi trang bây giờ sẽ hủy quá trình này.", "URL cannot be empty." => "URL không được để trống.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Invalid folder name. Usage of 'Shared' is reserved by Owncloud", "Name" => "Tên", "Size" => "Kích cỡ", "Modified" => "Thay đổi", @@ -47,11 +59,14 @@ "Text file" => "Tập tin văn bản", "Folder" => "Thư mục", "From link" => "Từ liên kết", +"Deleted files" => "File đã bị xóa", "Cancel upload" => "Hủy upload", "Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên một cái gì đó !", "Download" => "Tải xuống", +"Unshare" => "Không chia sẽ", "Upload too large" => "Tập tin tải lên quá lớn", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Các tập tin bạn đang tải lên vượt quá kích thước tối đa cho phép trên máy chủ .", "Files are being scanned, please wait." => "Tập tin đang được quét ,vui lòng chờ.", -"Current scanning" => "Hiện tại đang quét" +"Current scanning" => "Hiện tại đang quét", +"Upgrading filesystem cache..." => "Upgrading filesystem cache..." ); diff --git a/apps/files/l10n/zh_CN.GB2312.php b/apps/files/l10n/zh_CN.GB2312.php index a38e2d3bc60c15df9d67b066a238599bc0f4559a..fa75627f141da59894425a31de973b64b97cde34 100644 --- a/apps/files/l10n/zh_CN.GB2312.php +++ b/apps/files/l10n/zh_CN.GB2312.php @@ -7,20 +7,18 @@ "Missing a temporary folder" => "丢失了一个临时文件夹", "Failed to write to disk" => "写磁盘失败", "Files" => "文件", -"Unshare" => "取消共享", "Delete" => "删除", "Rename" => "重命名", +"Pending" => "Pending", "{new_name} already exists" => "{new_name} 已存在", "replace" => "替换", "suggest name" => "推荐名称", "cancel" => "取消", -"replaced {new_name}" => "已替换 {new_name}", -"undo" => "撤销", "replaced {new_name} with {old_name}" => "已用 {old_name} 替换 {new_name}", +"undo" => "撤销", "Unable to upload your file as it is a directory or has 0 bytes" => "不能上传你指定的文件,可能因为它是个文件夹或者大小为0", "Upload Error" => "上传错误", "Close" => "关闭", -"Pending" => "Pending", "1 file uploading" => "1 个文件正在上传", "{count} files uploading" => "{count} 个文件正在上传", "Upload cancelled." => "上传取消了", @@ -49,6 +47,7 @@ "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里没有东西.上传点什么!", "Download" => "下载", +"Unshare" => "取消共享", "Upload too large" => "上传的文件太大了", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "你正在试图上传的文件超过了此服务器支持的最大的文件大小.", "Files are being scanned, please wait." => "正在扫描文件,请稍候.", diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index 3c87ee2b73fe92a6725e9a2c986bfe8ae0d8eef0..88fdc537c3aab28ae731fe0bcc9c7bd02f3651f5 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -1,4 +1,7 @@ "无法移动 %s - 同名文件已存在", +"Could not move %s" => "无法移动 %s", +"Unable to rename file" => "无法重命名文件", "No file was uploaded. Unknown error" => "没有文件被上传。未知错误", "There is no error, the file uploaded with success" => "没有发生错误,文件上传成功。", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上传文件大小已超过php.ini中upload_max_filesize所规定的值", @@ -9,16 +12,15 @@ "Failed to write to disk" => "写入磁盘失败", "Invalid directory." => "无效文件夹。", "Files" => "文件", -"Unshare" => "取消分享", "Delete" => "删除", "Rename" => "重命名", +"Pending" => "操作等待中", "{new_name} already exists" => "{new_name} 已存在", "replace" => "替换", "suggest name" => "建议名称", "cancel" => "取消", -"replaced {new_name}" => "替换 {new_name}", -"undo" => "撤销", "replaced {new_name} with {old_name}" => "已将 {old_name}替换成 {new_name}", +"undo" => "撤销", "'.' is an invalid file name." => "'.' 是一个无效的文件名。", "File name cannot be empty." => "文件名不能为空。", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "无效名称,'\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 不被允许使用。", @@ -26,7 +28,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "无法上传文件,因为它是一个目录或者大小为 0 字节", "Upload Error" => "上传错误", "Close" => "关闭", -"Pending" => "操作等待中", "1 file uploading" => "1个文件上传中", "{count} files uploading" => "{count} 个文件上传中", "Upload cancelled." => "上传已取消", @@ -56,6 +57,7 @@ "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里还什么都没有。上传些东西吧!", "Download" => "下载", +"Unshare" => "取消分享", "Upload too large" => "上传文件过大", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "您正尝试上传的文件超过了此服务器可以上传的最大容量限制", "Files are being scanned, please wait." => "文件正在被扫描,请稍候。", diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 439907821d48e49cb7acf075226911d8f43d2de4..793fedac41260ef5630419250e01b2bc6bad9c03 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -1,4 +1,7 @@ "無法移動 %s - 同名的檔案已經存在", +"Could not move %s" => "無法移動 %s", +"Unable to rename file" => "無法重新命名檔案", "No file was uploaded. Unknown error" => "沒有檔案被上傳。未知的錯誤。", "There is no error, the file uploaded with success" => "無錯誤,檔案上傳成功", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上傳的檔案大小超過 php.ini 當中 upload_max_filesize 參數的設定:", @@ -7,18 +10,19 @@ "No file was uploaded" => "無已上傳檔案", "Missing a temporary folder" => "遺失暫存資料夾", "Failed to write to disk" => "寫入硬碟失敗", +"Not enough storage available" => "儲存空間不足", "Invalid directory." => "無效的資料夾。", "Files" => "檔案", -"Unshare" => "取消共享", +"Delete permanently" => "永久刪除", "Delete" => "刪除", "Rename" => "重新命名", +"Pending" => "等候中", "{new_name} already exists" => "{new_name} 已經存在", "replace" => "取代", "suggest name" => "建議檔名", "cancel" => "取消", -"replaced {new_name}" => "已取代 {new_name}", -"undo" => "復原", "replaced {new_name} with {old_name}" => "使用 {new_name} 取代 {old_name}", +"undo" => "復原", "perform delete operation" => "進行刪除動作", "'.' is an invalid file name." => "'.' 是不合法的檔名。", "File name cannot be empty." => "檔名不能為空。", @@ -29,7 +33,6 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "無法上傳您的檔案因為它可能是一個目錄或檔案大小為0", "Upload Error" => "上傳發生錯誤", "Close" => "關閉", -"Pending" => "等候中", "1 file uploading" => "1 個檔案正在上傳", "{count} files uploading" => "{count} 個檔案正在上傳", "Upload cancelled." => "上傳取消", @@ -56,10 +59,10 @@ "Text file" => "文字檔", "Folder" => "資料夾", "From link" => "從連結", -"Trash" => "回收筒", "Cancel upload" => "取消上傳", "Nothing in here. Upload something!" => "沒有任何東西。請上傳內容!", "Download" => "下載", +"Unshare" => "取消共享", "Upload too large" => "上傳過大", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "您試圖上傳的檔案已超過伺服器的最大檔案大小限制。 ", "Files are being scanned, please wait." => "正在掃描檔案,請稍等。", diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php index ad69b5519d9a616a354256f4aea247ce3428d5b0..0ab931a467cbb5f51b9fb65524cb8e417ad8cac9 100644 --- a/apps/files/templates/admin.php +++ b/apps/files/templates/admin.php @@ -2,27 +2,27 @@
- t('File handling');?> + t('File handling')); ?> - - '/> + + '/> - (t('max. possible: '); echo $_['maxPossibleUploadSize'] ?>) + (t('max. possible: ')); p($_['maxPossibleUploadSize']) ?>)
checked="checked" /> -
+
- ' - title="t( '0 is unlimited' ); ?>" + ' + title="t( '0 is unlimited' )); ?>" disabled="disabled" />
- t( 'Maximum input size for ZIP files' ); ?>
+ t( 'Maximum input size for ZIP files' )); ?>
- + + value="t( 'Save' )); ?>"/>
diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 7cf65915af038b6bb323d8892c7b76a41656efaf..a53df4e2d3e808af55e8d55136e56868cf627342 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -1,96 +1,97 @@
- +
- t('New');?> + t('New'));?>
    -
  • t('Text file');?>

  • -
  • t('Folder');?>

  • -
  • t('From link');?>

  • +
  • t('Text file'));?>

  • +
  • t('Folder'));?>

  • +
  • t('From link'));?>

+ title="t('Upload') . ' max. '.$_['uploadMaxHumanFilesize']) ?>">
+ value=""> - + - + value="(max )"> +
- +
t('You don’t have write permissions here.'))?>
+ - +
-
t('Nothing in here. Upload something!')?>
+
t('Nothing in here. Upload something!'))?>
- +
- + - +
- t( 'Name' ); ?> + t( 'Name' )); ?> Download" /> - t('Download')?> + src="" /> + t('Download'))?> t( 'Size' ); ?>t( 'Size' )); ?> - t( 'Modified' ); ?> + t( 'Modified' )); ?> - t('Unshare')?> - <?php echo $l->t('Unshare')?>" /> + t('Unshare'))?> + <?php p($l->t('Unshare'))?>" /> - t('Delete')?> - <?php echo $l->t('Delete')?>" /> + t('Delete'))?> + <?php p($l->t('Delete'))?>" /> @@ -98,24 +99,24 @@
-
+

- t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?> + t('The files you are trying to upload exceed the maximum size for file uploads on this server.'));?>

- t('Files are being scanned, please wait.');?> + t('Files are being scanned, please wait.'));?>

- t('Current scanning');?> + t('Current scanning'));?>

- - + + diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 8778915be846f556c135ff7b2e8fc6f838a6e56a..7ea1755d1d7459c8ac8ce2d01c8013328e876671 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,7 +1,7 @@ @@ -9,8 +9,8 @@ $crumb = $_["breadcrumb"][$i]; $dir = str_replace('+', '%20', urlencode($crumb["dir"])); $dir = str_replace('%2F', '/', $dir); ?> -
svg" - data-dir=''> - +
svg" + data-dir=''> +
"> + - ' - data-permissions=''> + ' + data-permissions=''> - style="background-image:url()" + style="background-image:url()" - style="background-image:url()" + style="background-image:url()" > - + - + - + - + @@ -47,17 +46,17 @@ - + title="" + style="color:rgb()"> + " + style="color:rgb()"> - + .$relative_date_color) ?>)"> + diff --git a/apps/files/templates/upgrade.php b/apps/files/templates/upgrade.php index de6cc7130284ca37c0148a18c8059e1ebc625c11..e03f086e47d487518539afdc6d0b8c345c1de706 100644 --- a/apps/files/templates/upgrade.php +++ b/apps/files/templates/upgrade.php @@ -1,4 +1,4 @@
- t('Upgrading filesystem cache...');?> + t('Upgrading filesystem cache...'));?>
diff --git a/apps/files_encryption/ajax/mode.php b/apps/files_encryption/ajax/mode.php deleted file mode 100644 index 64c5be944012aa7d447f0644740160ff296bf88b..0000000000000000000000000000000000000000 --- a/apps/files_encryption/ajax/mode.php +++ /dev/null @@ -1,38 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -use OCA\Encryption\Keymanager; - -OCP\JSON::checkAppEnabled('files_encryption'); -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); - -$mode = $_POST['mode']; -$changePasswd = false; -$passwdChanged = false; - -if ( isset($_POST['newpasswd']) && isset($_POST['oldpasswd']) ) { - $oldpasswd = $_POST['oldpasswd']; - $newpasswd = $_POST['newpasswd']; - $changePasswd = true; - $passwdChanged = Keymanager::changePasswd($oldpasswd, $newpasswd); -} - -$query = \OC_DB::prepare( "SELECT mode FROM *PREFIX*encryption WHERE uid = ?" ); -$result = $query->execute(array(\OCP\User::getUser())); - -if ($result->fetchRow()){ - $query = OC_DB::prepare( 'UPDATE *PREFIX*encryption SET mode = ? WHERE uid = ?' ); -} else { - $query = OC_DB::prepare( 'INSERT INTO *PREFIX*encryption ( mode, uid ) VALUES( ?, ? )' ); -} - -if ( (!$changePasswd || $passwdChanged) && $query->execute(array($mode, \OCP\User::getUser())) ) { - OCP\JSON::success(); -} else { - OCP\JSON::error(); -} \ No newline at end of file diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index 3c100b4957dd0a6bdc6955b99f77db198e571588..3bdf6829a9b088563b622d2371c4c1a8b42d2125 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -13,7 +13,7 @@ OC_FileProxy::register( new OCA\Encryption\Proxy() ); // User-related hooks OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); -OCP\Util::connectHook( 'OC_User', 'pre_setPassword','OCA\Encryption\Hooks', 'setPassphrase' ); +OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' ); // Sharing-related hooks OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' ); @@ -44,6 +44,6 @@ if ( } -// Reguster settings scripts +// Register settings scripts OCP\App::registerAdmin( 'files_encryption', 'settings' ); -OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); \ No newline at end of file +OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 8bdeee0937b811b56ef599dae766f002da2887f6..2731d5a92f74ad4ed435d77a37d35cc9018cb7df 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -40,7 +40,7 @@ class Hooks { // Manually initialise Filesystem{} singleton with correct // fake root path, in order to avoid fatal webdav errors - \OC\Files\Filesystem::init( $params['uid'] . '/' . 'files' . '/' ); + \OC\Files\Filesystem::init( $params['uid'], $params['uid'] . '/' . 'files' . '/' ); $view = new \OC_FilesystemView( '/' ); @@ -165,16 +165,6 @@ class Hooks { * @brief */ public static function postShared( $params ) { - - // Delete existing catfile - Keymanager::deleteFileKey( ); - - // Generate new catfile and env keys - Crypt::multiKeyEncrypt( $plainContent, $publicKeys ); - - // Save env keys to user folders - - } /** diff --git a/apps/files_encryption/js/settings-personal.js b/apps/files_encryption/js/settings-personal.js deleted file mode 100644 index 1a53e99d2b4da326c10710ac2cb14617e3c68070..0000000000000000000000000000000000000000 --- a/apps/files_encryption/js/settings-personal.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2012, Bjoern Schiessle - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -$(document).ready(function(){ - $('input[name=encryption_mode]').change(function(){ - var prevmode = document.getElementById('prev_encryption_mode').value - var client=$('input[value="client"]:checked').val() - ,server=$('input[value="server"]:checked').val() - ,user=$('input[value="user"]:checked').val() - ,none=$('input[value="none"]:checked').val() - if (client) { - $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'client' }); - if (prevmode == 'server') { - OC.dialogs.info(t('encryption', 'Please switch to your ownCloud client and change your encryption password to complete the conversion.'), t('encryption', 'switched to client side encryption')); - } - } else if (server) { - if (prevmode == 'client') { - OC.dialogs.form([{text:'Login password', name:'newpasswd', type:'password'},{text:'Encryption password used on the client', name:'oldpasswd', type:'password'}],t('encryption', 'Change encryption password to login password'), function(data) { - $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'server', newpasswd: data[0].value, oldpasswd: data[1].value }, function(result) { - if (result.status != 'success') { - document.getElementById(prevmode+'_encryption').checked = true; - OC.dialogs.alert(t('encryption', 'Please check your passwords and try again.'), t('encryption', 'Could not change your file encryption password to your login password')) - } else { - console.log("alles super"); - } - }, true); - }); - } else { - $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'server' }); - } - } else { - $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'none' }); - } - }) -}) \ No newline at end of file diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js index 60563bde859cd59683bbb7706fb4c0992b3523f9..0be857bb73e9dd6d35a7ad30e702d3a5c25a08a6 100644 --- a/apps/files_encryption/js/settings.js +++ b/apps/files_encryption/js/settings.js @@ -9,38 +9,11 @@ $(document).ready(function(){ $('#encryption_blacklist').multiSelect({ oncheck:blackListChange, onuncheck:blackListChange, - createText:'...', + createText:'...' }); function blackListChange(){ var blackList=$('#encryption_blacklist').val().join(','); OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); } - - //TODO: Handle switch between client and server side encryption - $('input[name=encryption_mode]').change(function(){ - var client=$('input[value="client"]:checked').val() - ,server=$('input[value="server"]:checked').val() - ,user=$('input[value="user"]:checked').val() - ,none=$('input[value="none"]:checked').val() - ,disable=false - if (client) { - OC.AppConfig.setValue('files_encryption','mode','client'); - disable = true; - } else if (server) { - OC.AppConfig.setValue('files_encryption','mode','server'); - disable = true; - } else if (user) { - OC.AppConfig.setValue('files_encryption','mode','user'); - disable = true; - } else { - OC.AppConfig.setValue('files_encryption','mode','none'); - } - if (disable) { - document.getElementById('server_encryption').disabled = true; - document.getElementById('client_encryption').disabled = true; - document.getElementById('user_encryption').disabled = true; - document.getElementById('none_encryption').disabled = true; - } - }) }) \ No newline at end of file diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php index 1b888f7714bc91834a9aa4a16e4f6e9b53080de9..0c661353a776ce7644bc8ad3e5c954e2becc28ad 100644 --- a/apps/files_encryption/l10n/ca.php +++ b/apps/files_encryption/l10n/ca.php @@ -1,9 +1,4 @@ "Connecteu-vos al client ownCloud i canvieu la contrasenya d'encriptació per completar la conversió.", -"switched to client side encryption" => "s'ha commutat a l'encriptació per part del client", -"Change encryption password to login password" => "Canvia la contrasenya d'encriptació per la d'accés", -"Please check your passwords and try again." => "Comproveu les contrasenyes i proveu-ho de nou.", -"Could not change your file encryption password to your login password" => "No s'ha pogut canviar la contrasenya d'encriptació de fitxers per la d'accés", "Encryption" => "Encriptatge", "File encryption is enabled." => "L'encriptació de fitxers està activada.", "The following file types will not be encrypted:" => "Els tipus de fitxers següents no s'encriptaran:", diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php index 3278f13920ac636f1e0534a8010819138e5cfbe9..d225688a079d78356a3ec1ae5eb3ef9cf4041306 100644 --- a/apps/files_encryption/l10n/cs_CZ.php +++ b/apps/files_encryption/l10n/cs_CZ.php @@ -1,9 +1,4 @@ "Prosím přejděte na svého klienta ownCloud a nastavte šifrovací heslo pro dokončení konverze.", -"switched to client side encryption" => "přepnuto na šifrování na straně klienta", -"Change encryption password to login password" => "Změnit šifrovací heslo na přihlašovací", -"Please check your passwords and try again." => "Zkontrolujte, prosím, své heslo a zkuste to znovu.", -"Could not change your file encryption password to your login password" => "Nelze změnit šifrovací heslo na přihlašovací.", "Encryption" => "Šifrování", "File encryption is enabled." => "Šifrování je povoleno.", "The following file types will not be encrypted:" => "Následující typy souborů nebudou šifrovány:", diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php index c9255759cb8bd6664a22708f7b73bb04904239d7..b085381ea7b2b72f8fb66a9844c771ec9739733a 100644 --- a/apps/files_encryption/l10n/da.php +++ b/apps/files_encryption/l10n/da.php @@ -1,9 +1,7 @@ "Skift venligst til din ownCloud-klient og skift krypteringskoden for at fuldføre konverteringen.", -"switched to client side encryption" => "skiftet til kryptering på klientsiden", -"Change encryption password to login password" => "Udskift krypteringskode til login-adgangskode", -"Please check your passwords and try again." => "Check adgangskoder og forsøg igen.", -"Could not change your file encryption password to your login password" => "Kunne ikke udskifte krypteringskode med login-adgangskode", "Encryption" => "Kryptering", +"File encryption is enabled." => "Fil kryptering aktiveret.", +"The following file types will not be encrypted:" => "De følgende filtyper vil ikke blive krypteret:", +"Exclude the following file types from encryption:" => "Ekskluder de følgende fil typer fra kryptering:", "None" => "Ingen" ); diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php index c3c69e09007d2884cfa153f5bdc928e13fbf7522..cdcd8a40b23c87ae5c13660fd17539a8885b399e 100644 --- a/apps/files_encryption/l10n/de.php +++ b/apps/files_encryption/l10n/de.php @@ -1,9 +1,7 @@ "Bitte wechseln Sie nun zum ownCloud Client und ändern Sie ihr Verschlüsselungspasswort um die Konvertierung abzuschließen.", -"switched to client side encryption" => "Zur Clientseitigen Verschlüsselung gewechselt", -"Change encryption password to login password" => "Ändern des Verschlüsselungspasswortes zum Anmeldepasswort", -"Please check your passwords and try again." => "Bitte überprüfen sie Ihr Passwort und versuchen Sie es erneut.", -"Could not change your file encryption password to your login password" => "Ihr Verschlüsselungspasswort konnte nicht als Anmeldepasswort gesetzt werden.", "Encryption" => "Verschlüsselung", +"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert", +"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", +"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:", "None" => "Keine" ); diff --git a/apps/files_encryption/l10n/de_DE.php b/apps/files_encryption/l10n/de_DE.php index 465af23efddf13c3f920cd82f0ce495ce540e8e7..4f08b98eb29fdb50d0866b4b8ead9a1e9a468e38 100644 --- a/apps/files_encryption/l10n/de_DE.php +++ b/apps/files_encryption/l10n/de_DE.php @@ -1,12 +1,7 @@ "Bitte wechseln Sie nun zum ownCloud Client und ändern Sie ihr Verschlüsselungspasswort um die Konvertierung abzuschließen.", -"switched to client side encryption" => "Zur Clientseitigen Verschlüsselung gewechselt", -"Change encryption password to login password" => "Ändern des Verschlüsselungspasswortes zum Anmeldepasswort", -"Please check your passwords and try again." => "Bitte überprüfen sie Ihr Passwort und versuchen Sie es erneut.", -"Could not change your file encryption password to your login password" => "Ihr Verschlüsselungspasswort konnte nicht als Anmeldepasswort gesetzt werden.", "Encryption" => "Verschlüsselung", "File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert", -"The following file types will not be encrypted:" => "Die folgenden Datei-Typen werden nicht verschlüsselt:", -"Exclude the following file types from encryption:" => "Die folgenden Datei-Typen von der Verschlüsselung ausnehmen:", +"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", +"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:", "None" => "Keine" ); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php index 94bb68bcbca35b4810798d11f4f0633276e99e3c..0031a7319445a45b376950d0e4f1f38d379a3dc4 100644 --- a/apps/files_encryption/l10n/el.php +++ b/apps/files_encryption/l10n/el.php @@ -1,7 +1,7 @@ "Αλλαγή συνθηματικού κρυπτογράφησης στο συνθηματικό εισόδου ", -"Please check your passwords and try again." => "Παρακαλώ ελέγξτε το συνθηματικό σας και προσπαθήστε ξανά.", -"Could not change your file encryption password to your login password" => "Αδυναμία αλλαγής συνθηματικού κρυπτογράφησης αρχείων στο συνθηματικό εισόδου σας", "Encryption" => "Κρυπτογράφηση", +"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.", +"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:", +"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:", "None" => "Καμία" ); diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php index 73b5f273d1f9c1edd0faa677e81315c4a86a033c..4ea87b92e7c490b58b390070529ca0818bb4a367 100644 --- a/apps/files_encryption/l10n/es.php +++ b/apps/files_encryption/l10n/es.php @@ -1,9 +1,4 @@ "Por favor, cambie su cliente de ownCloud y cambie su clave de cifrado para completar la conversión.", -"switched to client side encryption" => "Cambiar a cifrado del lado del cliente", -"Change encryption password to login password" => "Cambie la clave de cifrado para su contraseña de inicio de sesión", -"Please check your passwords and try again." => "Por favor revise su contraseña e intentelo de nuevo.", -"Could not change your file encryption password to your login password" => "No se pudo cambiar la contraseña de cifrado de archivos de su contraseña de inicio de sesión", "Encryption" => "Cifrado", "File encryption is enabled." => "La encriptacion de archivo esta activada.", "The following file types will not be encrypted:" => "Los siguientes tipos de archivo no seran encriptados:", diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php index 8160db10df6ffe9b690971a1bb0e8f070d3a7161..af522879e16f7d4559413c22cef8ac754ffc1b4e 100644 --- a/apps/files_encryption/l10n/es_AR.php +++ b/apps/files_encryption/l10n/es_AR.php @@ -1,9 +1,7 @@ "Por favor, cambiá uu cliente de ownCloud y cambiá tu clave de encriptado para completar la conversión.", -"switched to client side encryption" => "Cambiado a encriptación por parte del cliente", -"Change encryption password to login password" => "Cambiá la clave de encriptado para tu contraseña de inicio de sesión", -"Please check your passwords and try again." => "Por favor, revisá tu contraseña e intentalo de nuevo.", -"Could not change your file encryption password to your login password" => "No se pudo cambiar la contraseña de encriptación de archivos de tu contraseña de inicio de sesión", "Encryption" => "Encriptación", +"File encryption is enabled." => "La encriptación de archivos no está habilitada", +"The following file types will not be encrypted:" => "Los siguientes tipos de archivos no serán encriptados", +"Exclude the following file types from encryption:" => "Excluir los siguientes tipos de archivos de encriptación:", "None" => "Ninguno" ); diff --git a/apps/files_encryption/l10n/et_EE.php b/apps/files_encryption/l10n/et_EE.php index 07f1a48fb0bb01dc978a9872b2dcdb4101cb7eea..0d189ac062ea4cbf9b42654edcd88867e38f3846 100644 --- a/apps/files_encryption/l10n/et_EE.php +++ b/apps/files_encryption/l10n/et_EE.php @@ -1,4 +1,7 @@ "Krüpteerimine", +"File encryption is enabled." => "Faili krüpteerimine on sisse lülitatud.", +"The following file types will not be encrypted:" => "Järgnevaid failitüüpe ei krüpteerita:", +"Exclude the following file types from encryption:" => "Järgnevaid failitüüpe ei krüpteerita:", "None" => "Pole" ); diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php index a2368816f52b79164a665e5e328c916eca6dd9c2..5a22b65728ef5d9d669b57dcd72559cda9830c60 100644 --- a/apps/files_encryption/l10n/eu.php +++ b/apps/files_encryption/l10n/eu.php @@ -1,5 +1,7 @@ "Mesedez egiaztatu zure pasahitza eta saia zaitez berriro:", "Encryption" => "Enkriptazioa", +"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.", +"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:", +"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:", "None" => "Bat ere ez" ); diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php index 2186c9025b4b16aa5e9f6598a6bf37f1c67e2773..21ad7e565667e07fb2b69f735dcac5b2f176a9eb 100644 --- a/apps/files_encryption/l10n/fa.php +++ b/apps/files_encryption/l10n/fa.php @@ -1,5 +1,4 @@ "لطفا گذرواژه خود را بررسی کنید و دوباره امتحان کنید.", "Encryption" => "رمزگذاری", "None" => "هیچ‌کدام" ); diff --git a/apps/files_encryption/l10n/fi_FI.php b/apps/files_encryption/l10n/fi_FI.php index 8a9dd30e6708b6cf4db6879dcbaa8cdeaa3a614d..6352d396b3c1ae65fecc57a1ba2a689aace7863b 100644 --- a/apps/files_encryption/l10n/fi_FI.php +++ b/apps/files_encryption/l10n/fi_FI.php @@ -1,5 +1,7 @@ "Tarkista salasanasi ja yritä uudelleen.", "Encryption" => "Salaus", +"File encryption is enabled." => "Tiedostojen salaus on käytössä.", +"The following file types will not be encrypted:" => "Seuraavia tiedostotyyppejä ei salata:", +"Exclude the following file types from encryption:" => "Älä salaa seuravia tiedostotyyppejä:", "None" => "Ei mitään" ); diff --git a/apps/files_encryption/l10n/fr.php b/apps/files_encryption/l10n/fr.php index 7d431e6e462b923778f1b358b3c12279f00ce27e..88f1e4a393f24ad432ca05cd3e8a17dbd497ef61 100644 --- a/apps/files_encryption/l10n/fr.php +++ b/apps/files_encryption/l10n/fr.php @@ -1,9 +1,4 @@ "Veuillez vous connecter depuis votre client de synchronisation ownCloud et changer votre mot de passe de chiffrement pour finaliser la conversion.", -"switched to client side encryption" => "Mode de chiffrement changé en chiffrement côté client", -"Change encryption password to login password" => "Convertir le mot de passe de chiffrement en mot de passe de connexion", -"Please check your passwords and try again." => "Veuillez vérifier vos mots de passe et réessayer.", -"Could not change your file encryption password to your login password" => "Impossible de convertir votre mot de passe de chiffrement en mot de passe de connexion", "Encryption" => "Chiffrement", "File encryption is enabled." => "Le chiffrement des fichiers est activé", "The following file types will not be encrypted:" => "Les fichiers de types suivants ne seront pas chiffrés :", diff --git a/apps/files_encryption/l10n/gl.php b/apps/files_encryption/l10n/gl.php index b240990f3d57fabbdd56abd3752432172a0209b4..3210f71545312e5b90b9677f90ecacd861e4d157 100644 --- a/apps/files_encryption/l10n/gl.php +++ b/apps/files_encryption/l10n/gl.php @@ -1,4 +1,7 @@ "Cifrado", -"None" => "Nada" +"File encryption is enabled." => "O cifrado de ficheiros está activado", +"The following file types will not be encrypted:" => "Os seguintes tipos de ficheiros non van seren cifrados:", +"Exclude the following file types from encryption:" => "Excluír os seguintes tipos de ficheiros do cifrado:", +"None" => "Ningún" ); diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php index fa62ae75fb6a2220f4c70852a0fee5c0357fae57..4043da108c0e0a991d2d1385f63eedb172779035 100644 --- a/apps/files_encryption/l10n/hu_HU.php +++ b/apps/files_encryption/l10n/hu_HU.php @@ -1,9 +1,7 @@ "Kérjük, hogy váltson át az ownCloud kliensére, és változtassa meg a titkosítási jelszót az átalakítás befejezéséhez.", -"switched to client side encryption" => "átváltva a kliens oldalai titkosításra", -"Change encryption password to login password" => "Titkosítási jelszó módosítása a bejelentkezési jelszóra", -"Please check your passwords and try again." => "Kérjük, ellenőrizze a jelszavait, és próbálja meg újra.", -"Could not change your file encryption password to your login password" => "Nem módosíthatja a fájltitkosítási jelszavát a bejelentkezési jelszavára", "Encryption" => "Titkosítás", +"File encryption is enabled." => "Az állományok titkosítása be van kapcsolva.", +"The following file types will not be encrypted:" => "A következő fájltípusok nem kerülnek titkosításra:", +"Exclude the following file types from encryption:" => "Zárjuk ki a titkosításból a következő fájltípusokat:", "None" => "Egyik sem" ); diff --git a/apps/files_encryption/l10n/id.php b/apps/files_encryption/l10n/id.php index 3f9a6c7d07f89c8f6d1ec9806981af61765b0251..6044348e72e335b5f813e25382185af6bfd9927f 100644 --- a/apps/files_encryption/l10n/id.php +++ b/apps/files_encryption/l10n/id.php @@ -1,4 +1,7 @@ "enkripsi", -"None" => "tidak ada" +"Encryption" => "Enkripsi", +"File encryption is enabled." => "Enkripsi berkas aktif.", +"The following file types will not be encrypted:" => "Tipe berkas berikut tidak akan dienkripsi:", +"Exclude the following file types from encryption:" => "Kecualikan tipe berkas berikut dari enkripsi:", +"None" => "Tidak ada" ); diff --git a/apps/files_encryption/l10n/it.php b/apps/files_encryption/l10n/it.php index ffa20b718d98b26537cd55da9df04315288f37e8..9ab9bc492a0fb599349802711ac6ada4df5a7237 100644 --- a/apps/files_encryption/l10n/it.php +++ b/apps/files_encryption/l10n/it.php @@ -1,9 +1,4 @@ "Passa al tuo client ownCloud e cambia la password di cifratura per completare la conversione.", -"switched to client side encryption" => "passato alla cifratura lato client", -"Change encryption password to login password" => "Converti la password di cifratura nella password di accesso", -"Please check your passwords and try again." => "Controlla la password e prova ancora.", -"Could not change your file encryption password to your login password" => "Impossibile convertire la password di cifratura nella password di accesso", "Encryption" => "Cifratura", "File encryption is enabled." => "La cifratura dei file è abilitata.", "The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:", diff --git a/apps/files_encryption/l10n/ja_JP.php b/apps/files_encryption/l10n/ja_JP.php index b7aeb8d8348c3a4a59b0d6050fef3b3c393554e5..35fba615aec29d6c6974cfd4b369655142e7be79 100644 --- a/apps/files_encryption/l10n/ja_JP.php +++ b/apps/files_encryption/l10n/ja_JP.php @@ -1,9 +1,4 @@ "変換を完了するために、ownCloud クライアントに切り替えて、暗号化パスワードを変更してください。", -"switched to client side encryption" => "クライアントサイドの暗号化に切り替えました", -"Change encryption password to login password" => "暗号化パスワードをログインパスワードに変更", -"Please check your passwords and try again." => "パスワードを確認してもう一度行なってください。", -"Could not change your file encryption password to your login password" => "ファイル暗号化パスワードをログインパスワードに変更できませんでした。", "Encryption" => "暗号化", "File encryption is enabled." => "ファイルの暗号化は有効です。", "The following file types will not be encrypted:" => "次のファイルタイプは暗号化されません:", diff --git a/apps/files_encryption/l10n/ko.php b/apps/files_encryption/l10n/ko.php index 625906d89d6b00833dc4e8f1781c0d3f6ae3a6ad..bd1580578c42146002ac808db852674b847b0c6b 100644 --- a/apps/files_encryption/l10n/ko.php +++ b/apps/files_encryption/l10n/ko.php @@ -1,9 +1,4 @@ "ownCloud로 전환한 다음 암호화에 사용할 암호를 변경하면 변환이 완료됩니다.", -"switched to client side encryption" => "클라이언트 암호화로 변경됨", -"Change encryption password to login password" => "암호화 암호를 로그인 암호로 변경", -"Please check your passwords and try again." => "암호를 확인한 다음 다시 시도하십시오.", -"Could not change your file encryption password to your login password" => "암호화 암호를 로그인 암호로 변경할 수 없습니다", "Encryption" => "암호화", "None" => "없음" ); diff --git a/apps/files_encryption/l10n/lv.php b/apps/files_encryption/l10n/lv.php index 1aae1377516dea0252bd05e6070e985f22630626..fc31ccdb92d25209689358bc5e00522f3f2b1e36 100644 --- a/apps/files_encryption/l10n/lv.php +++ b/apps/files_encryption/l10n/lv.php @@ -1,9 +1,4 @@ "Lūdzu, pārslēdzieties uz savu ownCloud klientu un maniet savu šifrēšanas paroli, lai pabeigtu pārveidošanu.", -"switched to client side encryption" => "Pārslēdzās uz klienta puses šifrēšanu", -"Change encryption password to login password" => "Mainīt šifrēšanas paroli uz ierakstīšanās paroli", -"Please check your passwords and try again." => "Lūdzu, pārbaudiet savas paroles un mēģiniet vēlreiz.", -"Could not change your file encryption password to your login password" => "Nevarēja mainīt datņu šifrēšanas paroli uz ierakstīšanās paroli", "Encryption" => "Šifrēšana", "File encryption is enabled." => "Datņu šifrēšana ir aktivēta.", "The following file types will not be encrypted:" => "Sekojošās datnes netiks šifrētas:", diff --git a/apps/files_encryption/l10n/nl.php b/apps/files_encryption/l10n/nl.php index c434330049bf124f806993b19ce71e867862a4c6..b1cba96aad775d5d5256796ab530cf674759651f 100644 --- a/apps/files_encryption/l10n/nl.php +++ b/apps/files_encryption/l10n/nl.php @@ -1,9 +1,4 @@ "Schakel om naar uw eigen ownCloud client en wijzig uw versleutelwachtwoord om de conversie af te ronden.", -"switched to client side encryption" => "overgeschakeld naar client side encryptie", -"Change encryption password to login password" => "Verander encryptie wachtwoord naar login wachtwoord", -"Please check your passwords and try again." => "Controleer uw wachtwoorden en probeer het opnieuw.", -"Could not change your file encryption password to your login password" => "Kon het bestandsencryptie wachtwoord niet veranderen naar het login wachtwoord", "Encryption" => "Versleuteling", "File encryption is enabled." => "Bestandsversleuteling geactiveerd.", "The following file types will not be encrypted:" => "De volgende bestandstypen zullen niet worden versleuteld:", diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php index 505e8659f0893acaf0a65c063b17456df9c29630..2fa86f454f9bfbc1bbf6a361e3c44bc9b9f68739 100644 --- a/apps/files_encryption/l10n/pl.php +++ b/apps/files_encryption/l10n/pl.php @@ -1,4 +1,7 @@ "Szyfrowanie", +"File encryption is enabled." => "Szyfrowanie plików jest włączone", +"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:", +"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:", "None" => "Brak" ); diff --git a/apps/files_encryption/l10n/pt_BR.php b/apps/files_encryption/l10n/pt_BR.php index 356419e0e7fb2ac8cf73593e9a7402e05188e823..28807db72ce820c067840756b9e19ef6a3cf6d2a 100644 --- a/apps/files_encryption/l10n/pt_BR.php +++ b/apps/files_encryption/l10n/pt_BR.php @@ -1,9 +1,7 @@ "Por favor, vá ao seu cliente ownCloud e mude sua criptografia de senha para completar a conversão.", -"switched to client side encryption" => "alterado para criptografia por parte do cliente", -"Change encryption password to login password" => "Mudar senha de criptografia para senha de login", -"Please check your passwords and try again." => "Por favor, verifique suas senhas e tente novamente.", -"Could not change your file encryption password to your login password" => "Não foi possível mudar sua senha de criptografia de arquivos para sua senha de login", "Encryption" => "Criptografia", +"File encryption is enabled." => "A criptografia de arquivos está ativada.", +"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:", +"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:", "None" => "Nenhuma" ); diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php index 4dac4d2273b230f8d5ed32d1dca65a9e75087eaa..1c46011fc10b48b628dd03826aa913e6f5e9d9a2 100644 --- a/apps/files_encryption/l10n/pt_PT.php +++ b/apps/files_encryption/l10n/pt_PT.php @@ -1,9 +1,7 @@ "Por favor, use o seu cliente de sincronização do ownCloud e altere a sua password de encriptação para concluír a conversão.", -"switched to client side encryption" => "Alterado para encriptação do lado do cliente", -"Change encryption password to login password" => "Alterar a password de encriptação para a password de login", -"Please check your passwords and try again." => "Por favor verifique as suas paswords e tente de novo.", -"Could not change your file encryption password to your login password" => "Não foi possível alterar a password de encriptação de ficheiros para a sua password de login", "Encryption" => "Encriptação", +"File encryption is enabled." => "A encriptação de ficheiros está ligada", +"The following file types will not be encrypted:" => "Os seguintes ficheiros não serão encriptados:", +"Exclude the following file types from encryption:" => "Excluir da encriptação os seguintes tipos de ficheiro:", "None" => "Nenhum" ); diff --git a/apps/files_encryption/l10n/ro.php b/apps/files_encryption/l10n/ro.php index 9a3acc18dd35bc4eca97bfbd0f9f18a6863e521f..a5a6fb3cb787e2440a8993be13150190e60d8888 100644 --- a/apps/files_encryption/l10n/ro.php +++ b/apps/files_encryption/l10n/ro.php @@ -1,9 +1,4 @@ "Te rugăm să mergi în clientul ownCloud și să schimbi parola pentru a finisa conversia", -"switched to client side encryption" => "setat la encriptare locală", -"Change encryption password to login password" => "Schimbă parola de ecriptare în parolă de acces", -"Please check your passwords and try again." => "Verifică te rog parolele și înceracă din nou.", -"Could not change your file encryption password to your login password" => "Nu s-a putut schimba parola de encripție a fișierelor ca parolă de acces", "Encryption" => "Încriptare", "None" => "Niciuna" ); diff --git a/apps/files_encryption/l10n/ru.php b/apps/files_encryption/l10n/ru.php index 651885fe022fbe44ea237e002b57d31d9c814b30..22c1e3da3747c957863a007ce8bc54708eebe2cf 100644 --- a/apps/files_encryption/l10n/ru.php +++ b/apps/files_encryption/l10n/ru.php @@ -1,9 +1,4 @@ "Пожалуйста переключитесь на Ваш клиент ownCloud и поменяйте пароль шиврования для завершения преобразования.", -"switched to client side encryption" => "переключён на шифрование со стороны клиента", -"Change encryption password to login password" => "Изменить пароль шифрования для пароля входа", -"Please check your passwords and try again." => "Пожалуйста проверьте пароли и попробуйте снова.", -"Could not change your file encryption password to your login password" => "Невозможно изменить Ваш пароль файла шифрования для пароля входа", "Encryption" => "Шифрование", "File encryption is enabled." => "Шифрование файла включено.", "The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:", diff --git a/apps/files_encryption/l10n/ru_RU.php b/apps/files_encryption/l10n/ru_RU.php index dbbb22ed9cf2fd23811e35c6e0eca130aadeadc9..7222235485c6f50a15b797ec924ef7ab27aa2645 100644 --- a/apps/files_encryption/l10n/ru_RU.php +++ b/apps/files_encryption/l10n/ru_RU.php @@ -1,7 +1,4 @@ "Пожалуйста, переключитесь на ownCloud-клиент и измените Ваш пароль шифрования для завершения конвертации.", -"switched to client side encryption" => "переключено на шифрование на клиентской стороне", -"Please check your passwords and try again." => "Пожалуйста, проверьте Ваш пароль и попробуйте снова", "Encryption" => "Шифрование", "None" => "Ни один" ); diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php index dc2907e704f8a9eb136daf14638b0ace55c18dda..bebb6234710dc2146021ca6a95dcc86a77a658cd 100644 --- a/apps/files_encryption/l10n/sk_SK.php +++ b/apps/files_encryption/l10n/sk_SK.php @@ -1,12 +1,7 @@ "Prosím, prejdite do svojho klienta ownCloud a zmente šifrovacie heslo na dokončenie konverzie.", -"switched to client side encryption" => "prepnuté na šifrovanie prostredníctvom klienta", -"Change encryption password to login password" => "Zmeniť šifrovacie heslo na prihlasovacie", -"Please check your passwords and try again." => "Skontrolujte si heslo a skúste to znovu.", -"Could not change your file encryption password to your login password" => "Nie je možné zmeniť šifrovacie heslo na prihlasovacie", "Encryption" => "Šifrovanie", -"File encryption is enabled." => "Kryptovanie súborov nastavené.", -"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú kryptované:", -"Exclude the following file types from encryption:" => "Nekryptovať uvedené typy súborov", +"File encryption is enabled." => "Šifrovanie súborov nastavené.", +"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:", +"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov", "None" => "Žiadne" ); diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php index e5294974e4efc9f35fda7d7e72f74bc1c6b24d6b..e214a937a1d4195883b87866080a523ca50acaf1 100644 --- a/apps/files_encryption/l10n/sv.php +++ b/apps/files_encryption/l10n/sv.php @@ -1,9 +1,4 @@ "Vänligen växla till ownCloud klienten och ändra ditt krypteringslösenord för att slutföra omvandlingen.", -"switched to client side encryption" => "Bytte till kryptering på klientsidan", -"Change encryption password to login password" => "Ändra krypteringslösenord till loginlösenord", -"Please check your passwords and try again." => "Kontrollera dina lösenord och försök igen.", -"Could not change your file encryption password to your login password" => "Kunde inte ändra ditt filkrypteringslösenord till ditt loginlösenord", "Encryption" => "Kryptering", "File encryption is enabled." => "Filkryptering är aktiverat.", "The following file types will not be encrypted:" => "Följande filtyper kommer inte att krypteras:", diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php index 28d9e30864fa5e41471430b20701c2b71b44dd35..e46d249118606f7f97352d974cd6054385262726 100644 --- a/apps/files_encryption/l10n/th_TH.php +++ b/apps/files_encryption/l10n/th_TH.php @@ -1,9 +1,4 @@ "กรุณาสลับไปที่โปรแกรมไคลเอนต์ ownCloud ของคุณ แล้วเปลี่ยนรหัสผ่านสำหรับการเข้ารหัสเพื่อแปลงข้อมูลให้เสร็จสมบูรณ์", -"switched to client side encryption" => "สลับไปใช้การเข้ารหัสจากโปรแกรมไคลเอนต์", -"Change encryption password to login password" => "เปลี่ยนรหัสผ่านสำหรับเข้ารหัสไปเป็นรหัสผ่านสำหรับการเข้าสู่ระบบ", -"Please check your passwords and try again." => "กรุณาตรวจสอบรหัสผ่านของคุณแล้วลองใหม่อีกครั้ง", -"Could not change your file encryption password to your login password" => "ไม่สามารถเปลี่ยนรหัสผ่านสำหรับการเข้ารหัสไฟล์ของคุณไปเป็นรหัสผ่านสำหรับการเข้าสู่ระบบของคุณได้", "Encryption" => "การเข้ารหัส", "None" => "ไม่ต้อง" ); diff --git a/apps/files_encryption/l10n/tr.php b/apps/files_encryption/l10n/tr.php index 0868d0a69053a9779b74f04d672f91d313cbb4c8..6b42c757e6577a64371ba5d57f679eb136d17c48 100644 --- a/apps/files_encryption/l10n/tr.php +++ b/apps/files_encryption/l10n/tr.php @@ -1,4 +1,7 @@ "Şifreleme", +"File encryption is enabled." => "Dosya şifreleme aktif.", +"The following file types will not be encrypted:" => "Belirtilen dosya tipleri şifrelenmeyecek:", +"Exclude the following file types from encryption:" => "Seçilen dosya tiplerini şifreleme:", "None" => "Hiçbiri" ); diff --git a/apps/files_encryption/l10n/uk.php b/apps/files_encryption/l10n/uk.php index 8236c5afefd415f0220732f3bf7ff483910d6206..d495714119163fc5709491d5491a6a2795609cbb 100644 --- a/apps/files_encryption/l10n/uk.php +++ b/apps/files_encryption/l10n/uk.php @@ -1,4 +1,7 @@ "Шифрування", +"File encryption is enabled." => "Увімкнуто шифрування файлів.", +"The following file types will not be encrypted:" => "Такі типи файлів шифруватись не будуть:", +"Exclude the following file types from encryption:" => "Виключити наступні типи файлів з ​​шифрування:", "None" => "Жоден" ); diff --git a/apps/files_encryption/l10n/vi.php b/apps/files_encryption/l10n/vi.php index b86cd8397831a7c30ef324a51743e68a28de9045..0a88d1b2db60b09f23e67d9ed317e9e4aa771cc7 100644 --- a/apps/files_encryption/l10n/vi.php +++ b/apps/files_encryption/l10n/vi.php @@ -1,4 +1,7 @@ "Mã hóa", +"File encryption is enabled." => "Mã hóa file đã mở", +"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa", +"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau", "None" => "Không có gì hết" ); diff --git a/apps/files_encryption/l10n/zh_TW.php b/apps/files_encryption/l10n/zh_TW.php index bd8257ed602ab2edd46cdb624d20efca41a4b0ac..1655e171433828affb536dc94ee161cf710ae901 100644 --- a/apps/files_encryption/l10n/zh_TW.php +++ b/apps/files_encryption/l10n/zh_TW.php @@ -1,9 +1,4 @@ "請至您的 ownCloud 客戶端程式修改您的加密密碼以完成轉換。", -"switched to client side encryption" => "已切換為客戶端加密", -"Change encryption password to login password" => "將加密密碼修改為登入密碼", -"Please check your passwords and try again." => "請檢查您的密碼並再試一次。", -"Could not change your file encryption password to your login password" => "無法變更您的檔案加密密碼為登入密碼", "Encryption" => "加密", "None" => "無" ); diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index d00f71b61414ce4e5360114443cfd7bf56f8e9ee..437a18669e5f2e8d67bb46a9b01752b0873f896b 100755 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -4,8 +4,8 @@ * ownCloud * * @author Sam Tuke, Frank Karlitschek, Robin Appelman - * @copyright 2012 Sam Tuke samtuke@owncloud.com, - * Robin Appelman icewind@owncloud.com, Frank Karlitschek + * @copyright 2012 Sam Tuke samtuke@owncloud.com, + * Robin Appelman icewind@owncloud.com, Frank Karlitschek * frank@owncloud.org * * This library is free software; you can redistribute it and/or @@ -47,15 +47,15 @@ class Crypt { public static function mode( $user = null ) { return 'server'; - + } - - /** - * @brief Create a new encryption keypair - * @return array publicKey, privatekey - */ + + /** + * @brief Create a new encryption keypair + * @return array publicKey, privatekey + */ public static function createKeypair() { - + $res = openssl_pkey_new(); // Get private key @@ -63,570 +63,543 @@ class Crypt { // Get public key $publicKey = openssl_pkey_get_details( $res ); - + $publicKey = $publicKey['key']; - + return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) ); - + } - - /** - * @brief Add arbitrary padding to encrypted data - * @param string $data data to be padded - * @return padded data - * @note In order to end up with data exactly 8192 bytes long we must - * add two letters. It is impossible to achieve exactly 8192 length - * blocks with encryption alone, hence padding is added to achieve the - * required length. - */ + + /** + * @brief Add arbitrary padding to encrypted data + * @param string $data data to be padded + * @return padded data + * @note In order to end up with data exactly 8192 bytes long we must + * add two letters. It is impossible to achieve exactly 8192 length + * blocks with encryption alone, hence padding is added to achieve the + * required length. + */ public static function addPadding( $data ) { - + $padded = $data . 'xx'; - + return $padded; - + } - - /** - * @brief Remove arbitrary padding to encrypted data - * @param string $padded padded data to remove padding from - * @return unpadded data on success, false on error - */ + + /** + * @brief Remove arbitrary padding to encrypted data + * @param string $padded padded data to remove padding from + * @return unpadded data on success, false on error + */ public static function removePadding( $padded ) { - + if ( substr( $padded, -2 ) == 'xx' ) { - + $data = substr( $padded, 0, -2 ); - + return $data; - + } else { - + // TODO: log the fact that unpadded data was submitted for removal of padding return false; - + } - + } - - /** - * @brief Check if a file's contents contains an IV and is symmetrically encrypted - * @return true / false - * @note see also OCA\Encryption\Util->isEncryptedPath() - */ + + /** + * @brief Check if a file's contents contains an IV and is symmetrically encrypted + * @return true / false + * @note see also OCA\Encryption\Util->isEncryptedPath() + */ public static function isCatfile( $content ) { - + + if ( !$content ) { + + return false; + + } + $noPadding = self::removePadding( $content ); - + // Fetch encryption metadata from end of file $meta = substr( $noPadding, -22 ); - + // Fetch IV from end of file $iv = substr( $meta, -16 ); - + // Fetch identifier from start of metadata $identifier = substr( $meta, 0, 6 ); - + if ( $identifier == '00iv00') { - + return true; - + } else { - + return false; - + } - + } - + /** * Check if a file is encrypted according to database file cache * @param string $path * @return bool */ public static function isEncryptedMeta( $path ) { - + // TODO: Use DI to get \OC\Files\Filesystem out of here - + // Fetch all file metadata from DB $metadata = \OC\Files\Filesystem::getFileInfo( $path, '' ); - + // Return encryption status return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted']; - + } - - /** - * @brief Check if a file is encrypted via legacy system - * @param string $relPath The path of the file, relative to user/data; - * e.g. filename or /Docs/filename, NOT admin/files/filename - * @return true / false - */ + + /** + * @brief Check if a file is encrypted via legacy system + * @param string $relPath The path of the file, relative to user/data; + * e.g. filename or /Docs/filename, NOT admin/files/filename + * @return true / false + */ public static function isLegacyEncryptedContent( $data, $relPath ) { - + // Fetch all file metadata from DB $metadata = \OC\Files\Filesystem::getFileInfo( $relPath, '' ); - + // If a file is flagged with encryption in DB, but isn't a // valid content + IV combination, it's probably using the // legacy encryption system - if ( - isset( $metadata['encrypted'] ) - and $metadata['encrypted'] === true - and ! self::isCatfile( $data ) + if ( + isset( $metadata['encrypted'] ) + and $metadata['encrypted'] === true + and ! self::isCatfile( $data ) ) { - + return true; - + } else { - + return false; - + } - + } - - /** - * @brief Symmetrically encrypt a string - * @returns encrypted file - */ + + /** + * @brief Symmetrically encrypt a string + * @returns encrypted file + */ public static function encrypt( $plainContent, $iv, $passphrase = '' ) { - + if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { return $encryptedContent; - + } else { - + \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR ); - + return false; - + } - + } - - /** - * @brief Symmetrically decrypt a string - * @returns decrypted file - */ + + /** + * @brief Symmetrically decrypt a string + * @returns decrypted file + */ public static function decrypt( $encryptedContent, $iv, $passphrase ) { - + if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { return $plainContent; - - + + } else { - + throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' ); - - return false; - + } - + } - - /** - * @brief Concatenate encrypted data with its IV and padding - * @param string $content content to be concatenated - * @param string $iv IV to be concatenated - * @returns string concatenated content - */ + + /** + * @brief Concatenate encrypted data with its IV and padding + * @param string $content content to be concatenated + * @param string $iv IV to be concatenated + * @returns string concatenated content + */ public static function concatIv ( $content, $iv ) { - + $combined = $content . '00iv00' . $iv; - + return $combined; - + } - - /** - * @brief Split concatenated data and IV into respective parts - * @param string $catFile concatenated data to be split - * @returns array keys: encrypted, iv - */ + + /** + * @brief Split concatenated data and IV into respective parts + * @param string $catFile concatenated data to be split + * @returns array keys: encrypted, iv + */ public static function splitIv ( $catFile ) { - + // Fetch encryption metadata from end of file $meta = substr( $catFile, -22 ); - + // Fetch IV from end of file $iv = substr( $meta, -16 ); - + // Remove IV and IV identifier text to expose encrypted content $encrypted = substr( $catFile, 0, -22 ); - + $split = array( 'encrypted' => $encrypted - , 'iv' => $iv + , 'iv' => $iv ); - + return $split; - + } - - /** - * @brief Symmetrically encrypts a string and returns keyfile content - * @param $plainContent content to be encrypted in keyfile - * @returns encrypted content combined with IV - * @note IV need not be specified, as it will be stored in the returned keyfile - * and remain accessible therein. - */ + + /** + * @brief Symmetrically encrypts a string and returns keyfile content + * @param $plainContent content to be encrypted in keyfile + * @returns encrypted content combined with IV + * @note IV need not be specified, as it will be stored in the returned keyfile + * and remain accessible therein. + */ public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) { - + if ( !$plainContent ) { - + return false; - + } - + $iv = self::generateIv(); - + if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { - - // Combine content to encrypt with IV identifier and actual IV - $catfile = self::concatIv( $encryptedContent, $iv ); - - $padded = self::addPadding( $catfile ); - - return $padded; - + + // Combine content to encrypt with IV identifier and actual IV + $catfile = self::concatIv( $encryptedContent, $iv ); + + $padded = self::addPadding( $catfile ); + + return $padded; + } else { - + \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR ); - + return false; - + } - + } /** - * @brief Symmetrically decrypts keyfile content - * @param string $source - * @param string $target - * @param string $key the decryption key - * @returns decrypted content - * - * This function decrypts a file - */ + * @brief Symmetrically decrypts keyfile content + * @param string $source + * @param string $target + * @param string $key the decryption key + * @returns decrypted content + * + * This function decrypts a file + */ public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) { - + if ( !$keyfileContent ) { - + throw new \Exception( 'Encryption library: no data provided for decryption' ); - + } - + // Remove padding $noPadding = self::removePadding( $keyfileContent ); - + // Split into enc data and catfile $catfile = self::splitIv( $noPadding ); - + if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { - + return $plainContent; - + } - + } - + /** - * @brief Creates symmetric keyfile content using a generated key - * @param string $plainContent content to be encrypted - * @returns array keys: key, encrypted - * @note symmetricDecryptFileContent() can be used to decrypt files created using this method - * - * This function decrypts a file - */ + * @brief Creates symmetric keyfile content using a generated key + * @param string $plainContent content to be encrypted + * @returns array keys: key, encrypted + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ public static function symmetricEncryptFileContentKeyfile( $plainContent ) { - + $key = self::generateKey(); - + if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) { - + return array( 'key' => $key - , 'encrypted' => $encryptedContent + , 'encrypted' => $encryptedContent ); - + } else { - + return false; - + } - + } - + /** - * @brief Create asymmetrically encrypted keyfile content using a generated key - * @param string $plainContent content to be encrypted - * @returns array keys: key, encrypted - * @note symmetricDecryptFileContent() can be used to decrypt files created using this method - * - * This function decrypts a file - */ + * @brief Create asymmetrically encrypted keyfile content using a generated key + * @param string $plainContent content to be encrypted + * @returns array keys: key, encrypted + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ public static function multiKeyEncrypt( $plainContent, array $publicKeys ) { - + // Set empty vars to be set by openssl by reference $sealed = ''; $envKeys = array(); - + if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) { - + return array( 'keys' => $envKeys - , 'encrypted' => $sealed + , 'encrypted' => $sealed ); - + } else { - + return false; - + } - + } - + /** - * @brief Asymmetrically encrypt a file using multiple public keys - * @param string $plainContent content to be encrypted - * @returns string $plainContent decrypted string - * @note symmetricDecryptFileContent() can be used to decrypt files created using this method - * - * This function decrypts a file - */ + * @brief Asymmetrically encrypt a file using multiple public keys + * @param string $plainContent content to be encrypted + * @returns string $plainContent decrypted string + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) { - + if ( !$encryptedContent ) { - + return false; - + } - + if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) { - + return $plainContent; - + } else { - + \OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR ); - + return false; - + } - + } - - /** - * @brief Asymetrically encrypt a string using a public key - * @returns encrypted file - */ + + /** + * @brief Asymmetrically encrypt a string using a public key + * @returns encrypted file + */ public static function keyEncrypt( $plainContent, $publicKey ) { - + openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey ); - + return $encryptedContent; - + } - - /** - * @brief Asymetrically decrypt a file using a private key - * @returns decrypted file - */ + + /** + * @brief Asymetrically decrypt a file using a private key + * @returns decrypted file + */ public static function keyDecrypt( $encryptedContent, $privatekey ) { - + openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey ); - + return $plainContent; - + } - /** - * @brief Encrypts content symmetrically and generates keyfile asymmetrically - * @returns array containing catfile and new keyfile. - * keys: data, key - * @note this method is a wrapper for combining other crypt class methods - */ + /** + * @brief Encrypts content symmetrically and generates keyfile asymmetrically + * @returns array containing catfile and new keyfile. + * keys: data, key + * @note this method is a wrapper for combining other crypt class methods + */ public static function keyEncryptKeyfile( $plainContent, $publicKey ) { - + // Encrypt plain data, generate keyfile & encrypted file $cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent ); - + // Encrypt keyfile $cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey ); - + return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey ); - + } - - /** - * @brief Takes catfile, keyfile, and private key, and - * performs decryption - * @returns decrypted content - * @note this method is a wrapper for combining other crypt class methods - */ + + /** + * @brief Takes catfile, keyfile, and private key, and + * performs decryption + * @returns decrypted content + * @note this method is a wrapper for combining other crypt class methods + */ public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) { - + // Decrypt the keyfile with the user's private key $decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey ); - + // Decrypt the catfile symmetrically using the decrypted keyfile $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile ); - + return $decryptedData; - + } - + /** - * @brief Symmetrically encrypt a file by combining encrypted component data blocks - */ + * @brief Symmetrically encrypt a file by combining encrypted component data blocks + */ public static function symmetricBlockEncryptFileContent( $plainContent, $key ) { - + $crypted = ''; - + $remaining = $plainContent; - + $testarray = array(); - + while( strlen( $remaining ) ) { - + //echo "\n\n\$block = ".substr( $remaining, 0, 6126 ); - + // Encrypt a chunk of unencrypted data and add it to the rest $block = self::symmetricEncryptFileContent( substr( $remaining, 0, 6126 ), $key ); - + $padded = self::addPadding( $block ); - + $crypted .= $block; - + $testarray[] = $block; - + // Remove the data already encrypted from remaining unencrypted data $remaining = substr( $remaining, 6126 ); - + } - - //echo "hags "; - - //echo "\n\n\n\$crypted = $crypted\n\n\n"; - - //print_r($testarray); - + return $crypted; } /** - * @brief Symmetrically decrypt a file by combining encrypted component data blocks - */ + * @brief Symmetrically decrypt a file by combining encrypted component data blocks + */ public static function symmetricBlockDecryptFileContent( $crypted, $key ) { - + $decrypted = ''; - + $remaining = $crypted; - + $testarray = array(); - + while( strlen( $remaining ) ) { - + $testarray[] = substr( $remaining, 0, 8192 ); - + // Decrypt a chunk of unencrypted data and add it to the rest $decrypted .= self::symmetricDecryptFileContent( $remaining, $key ); - + // Remove the data already encrypted from remaining unencrypted data $remaining = substr( $remaining, 8192 ); - + } - - //echo "\n\n\$testarray = "; print_r($testarray); - + return $decrypted; - + } - - /** - * @brief Generates a pseudo random initialisation vector - * @return String $iv generated IV - */ + + /** + * @brief Generates a pseudo random initialisation vector + * @return String $iv generated IV + */ public static function generateIv() { - + if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) { - + if ( !$strong ) { - + // If OpenSSL indicates randomness is insecure, log error \OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN ); - + } - + // We encode the iv purely for string manipulation // purposes - it gets decoded before use $iv = base64_encode( $random ); - + return $iv; - + } else { - - throw new Exception( 'Generating IV failed' ); - + + throw new \Exception( 'Generating IV failed' ); + } - + } - - /** - * @brief Generate a pseudo random 1024kb ASCII key - * @returns $key Generated key - */ + + /** + * @brief Generate a pseudo random 1024kb ASCII key + * @returns $key Generated key + */ public static function generateKey() { - + // Generate key if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { - + if ( !$strong ) { - + // If OpenSSL indicates randomness is insecure, log error - throw new Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' ); - + throw new \Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' ); + } - + return $key; - + } else { - + return false; - - } - - } - public static function changekeypasscode( $oldPassword, $newPassword ) { - - if ( \OCP\User::isLoggedIn() ) { - - $key = Keymanager::getPrivateKey( $user, $view ); - - if ( ( $key = Crypt::symmetricDecryptFileContent($key,$oldpasswd) ) ) { - - if ( ( $key = Crypt::symmetricEncryptFileContent( $key, $newpasswd ) ) ) { - - Keymanager::setPrivateKey( $key ); - - return true; - } - - } - } - - return false; - + } - + /** * @brief Get the blowfish encryption handeler for a key * @param $key string (optional) @@ -635,21 +608,21 @@ class Crypt { * if the key is left out, the default handeler will be used */ public static function getBlowfish( $key = '' ) { - + if ( $key ) { - + return new \Crypt_Blowfish( $key ); - + } else { - + return false; - + } - + } - + public static function legacyCreateKey( $passphrase ) { - + // Generate a random integer $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); @@ -657,9 +630,9 @@ class Crypt { $legacyEncKey = self::legacyEncrypt( $key, $passphrase ); return $legacyEncKey; - + } - + /** * @brief encrypts content using legacy blowfish system * @param $content the cleartext message you want to encrypt @@ -669,54 +642,54 @@ class Crypt { * This function encrypts an content */ public static function legacyEncrypt( $content, $passphrase = '' ) { - + $bf = self::getBlowfish( $passphrase ); - + return $bf->encrypt( $content ); - + } - + /** - * @brief decrypts content using legacy blowfish system - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key (optional) - * @returns cleartext content - * - * This function decrypts an content - */ + * @brief decrypts content using legacy blowfish system + * @param $content the cleartext message you want to decrypt + * @param $key the encryption key (optional) + * @returns cleartext content + * + * This function decrypts an content + */ public static function legacyDecrypt( $content, $passphrase = '' ) { - + $bf = self::getBlowfish( $passphrase ); - + $decrypted = $bf->decrypt( $content ); - + $trimmed = rtrim( $decrypted, "\0" ); - + return $trimmed; - + } - + public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) { - + $decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase ); - + $recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey ); - + return $recrypted; - + } - + /** - * @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV - * @param $legacyContent the legacy encrypted content to re-encrypt - * @returns cleartext content - * - * This function decrypts an content - */ + * @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV + * @param $legacyContent the legacy encrypted content to re-encrypt + * @returns cleartext content + * + * This function decrypts an content + */ public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) { - + // TODO: write me - + } - -} \ No newline at end of file + +} diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 43af70dacc2be7a6b502bc68dd397130cbb738a2..955877971540bf86144ec6048ec47742cefff752 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -1,325 +1,323 @@ - - * - * 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 . - * - */ - -namespace OCA\Encryption; - -/** - * @brief Class to manage storage and retrieval of encryption keys - * @note Where a method requires a view object, it's root must be '/' - */ -class Keymanager { - - /** - * @brief retrieve the ENCRYPTED private key from a user - * - * @return string private key or false - * @note the key returned by this method must be decrypted before use - */ - public static function getPrivateKey( \OC_FilesystemView $view, $user ) { - - $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key'; - - $key = $view->file_get_contents( $path ); - - return $key; - } - - /** - * @brief retrieve public key for a specified user - * @return string public key or false - */ - public static function getPublicKey( \OC_FilesystemView $view, $userId ) { - - return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); - - } - - /** - * @brief retrieve both keys from a user (private and public) - * @return array keys: privateKey, publicKey - */ - public static function getUserKeys( \OC_FilesystemView $view, $userId ) { - - return array( - 'publicKey' => self::getPublicKey( $view, $userId ) - , 'privateKey' => self::getPrivateKey( $view, $userId ) - ); - - } - - /** - * @brief Retrieve public keys of all users with access to a file - * @param string $path Path to file - * @return array of public keys for the given file - * @note Checks that the sharing app is enabled should be performed - * by client code, that isn't checked here - */ - public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) { - - $path = ltrim( $path, '/' ); - - $filepath = '/' . $userId . '/files/' . $filePath; - - // Check if sharing is enabled - if ( OC_App::isEnabled( 'files_sharing' ) ) { - - - - } else { - - // check if it is a file owned by the user and not shared at all - $userview = new \OC_FilesystemView( '/'.$userId.'/files/' ); - - if ( $userview->file_exists( $path ) ) { - - $users[] = $userId; - - } - - } - - $view = new \OC_FilesystemView( '/public-keys/' ); - - $keylist = array(); - - $count = 0; - - foreach ( $users as $user ) { - - $keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' ); - - } - - return $keylist; - - } - - /** - * @brief store file encryption key - * - * @param string $path relative path of the file, including filename - * @param string $key - * @return bool true/false - * @note The keyfile is not encrypted here. Client code must - * asymmetrically encrypt the keyfile before passing it to this method - */ - public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) { - - $basePath = '/' . $userId . '/files_encryption/keyfiles'; - - $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId ); - - if ( $view->is_dir( $basePath . '/' . $targetPath ) ) { - - - - } else { - - // Save the keyfile in parallel directory - return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); - - } - - } - - /** - * @brief retrieve keyfile for an encrypted file - * @param string file name - * @return string file key or false on failure - * @note The keyfile returned is asymmetrically encrypted. Decryption - * of the keyfile must be performed by client code - */ - public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { - - $filePath_f = ltrim( $filePath, '/' ); - - $catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key'; - - if ( $view->file_exists( $catfilePath ) ) { - - return $view->file_get_contents( $catfilePath ); - - } else { - - return false; - - } - - } - - /** - * @brief Delete a keyfile - * - * @param OC_FilesystemView $view - * @param string $userId username - * @param string $path path of the file the key belongs to - * @return bool Outcome of unlink operation - * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT - * /data/admin/files/mydoc.txt - */ - public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) { - - $trimmed = ltrim( $path, '/' ); - $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key'; - - // Unlink doesn't tell us if file was deleted (not found returns - // true), so we perform our own test - if ( $view->file_exists( $keyPath ) ) { - - return $view->unlink( $keyPath ); - - } else { - - \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); - - return false; - - } - - } - - /** - * @brief store private key from the user - * @param string key - * @return bool - * @note Encryption of the private key must be performed by client code - * as no encryption takes place here - */ - public static function setPrivateKey( $key ) { - - $user = \OCP\User::getUser(); - - $view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); - - \OC_FileProxy::$enabled = false; - - if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); - - return $view->file_put_contents( $user . '.private.key', $key ); - - \OC_FileProxy::$enabled = true; - - } - - /** - * @brief store private keys from the user - * - * @param string privatekey - * @param string publickey - * @return bool true/false - */ - public static function setUserKeys($privatekey, $publickey) { - - return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) ); - - } - - /** - * @brief store public key of the user - * - * @param string key - * @return bool true/false - */ - public static function setPublicKey( $key ) { - - $view = new \OC_FilesystemView( '/public-keys' ); - - \OC_FileProxy::$enabled = false; - - if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); - - return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); - - \OC_FileProxy::$enabled = true; - - } - - /** - * @note 'shareKey' is a more user-friendly name for env_key - */ - public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { - - $basePath = '/' . $userId . '/files_encryption/share-keys'; - - $shareKeyPath = self::keySetPreparation( $view, $path, $basePath, $userId ); - - return $view->file_put_contents( $basePath . '/' . $shareKeyPath . '.shareKey', $shareKey ); - - } - - /** - * @brief Make preparations to vars and filesystem for saving a keyfile - */ - public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) { - - $targetPath = ltrim( $path, '/' ); - - $path_parts = pathinfo( $targetPath ); - - // If the file resides within a subdirectory, create it - if ( - isset( $path_parts['dirname'] ) - && ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] ) - ) { - - $view->mkdir( $basePath . '/' . $path_parts['dirname'] ); - - } - - return $targetPath; - - } - - /** - * @brief change password of private encryption key - * - * @param string $oldpasswd old password - * @param string $newpasswd new password - * @return bool true/false - */ - public static function changePasswd($oldpasswd, $newpasswd) { - - if ( \OCP\User::checkPassword(\OCP\User::getUser(), $newpasswd) ) { - return Crypt::changekeypasscode($oldpasswd, $newpasswd); - } - return false; - - } - - /** - * @brief Fetch the legacy encryption key from user files - * @param string $login used to locate the legacy key - * @param string $passphrase used to decrypt the legacy key - * @return true / false - * - * if the key is left out, the default handeler will be used - */ - public function getLegacyKey() { - - $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView( '/' . $user ); - return $view->file_get_contents( 'encryption.key' ); - - } - + + * + * 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 . + * + */ + +namespace OCA\Encryption; + +/** + * @brief Class to manage storage and retrieval of encryption keys + * @note Where a method requires a view object, it's root must be '/' + */ +class Keymanager { + + /** + * @brief retrieve the ENCRYPTED private key from a user + * + * @return string private key or false + * @note the key returned by this method must be decrypted before use + */ + public static function getPrivateKey( \OC_FilesystemView $view, $user ) { + + $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key'; + + $key = $view->file_get_contents( $path ); + + return $key; + } + + /** + * @brief retrieve public key for a specified user + * @param \OC_FilesystemView $view + * @param $userId + * @return string public key or false + */ + public static function getPublicKey( \OC_FilesystemView $view, $userId ) { + + return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); + + } + + /** + * @brief retrieve both keys from a user (private and public) + * @param \OC_FilesystemView $view + * @param $userId + * @return array keys: privateKey, publicKey + */ + public static function getUserKeys( \OC_FilesystemView $view, $userId ) { + + return array( + 'publicKey' => self::getPublicKey( $view, $userId ) + , 'privateKey' => self::getPrivateKey( $view, $userId ) + ); + + } + + /** + * @brief Retrieve public keys of all users with access to a file + * @param string $path Path to file + * @return array of public keys for the given file + * @note Checks that the sharing app is enabled should be performed + * by client code, that isn't checked here + */ + public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) { + + $path = ltrim( $path, '/' ); + + $filepath = '/' . $userId . '/files/' . $filePath; + + // Check if sharing is enabled + if ( OC_App::isEnabled( 'files_sharing' ) ) { + + + + } else { + + // check if it is a file owned by the user and not shared at all + $userview = new \OC_FilesystemView( '/'.$userId.'/files/' ); + + if ( $userview->file_exists( $path ) ) { + + $users[] = $userId; + + } + + } + + $view = new \OC_FilesystemView( '/public-keys/' ); + + $keylist = array(); + + $count = 0; + + foreach ( $users as $user ) { + + $keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' ); + + } + + return $keylist; + + } + + /** + * @brief store file encryption key + * + * @param string $path relative path of the file, including filename + * @param string $key + * @return bool true/false + * @note The keyfile is not encrypted here. Client code must + * asymmetrically encrypt the keyfile before passing it to this method + */ + public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) { + + $basePath = '/' . $userId . '/files_encryption/keyfiles'; + + $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId ); + + if ( $view->is_dir( $basePath . '/' . $targetPath ) ) { + + + + } else { + + // Save the keyfile in parallel directory + return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); + + } + + } + + /** + * @brief retrieve keyfile for an encrypted file + * @param \OC_FilesystemView $view + * @param $userId + * @param $filePath + * @internal param \OCA\Encryption\file $string name + * @return string file key or false + * @note The keyfile returned is asymmetrically encrypted. Decryption + * of the keyfile must be performed by client code + */ + public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { + + $filePath_f = ltrim( $filePath, '/' ); + + $catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key'; + + if ( $view->file_exists( $catfilePath ) ) { + + return $view->file_get_contents( $catfilePath ); + + } else { + + return false; + + } + + } + + /** + * @brief Delete a keyfile + * + * @param OC_FilesystemView $view + * @param string $userId username + * @param string $path path of the file the key belongs to + * @return bool Outcome of unlink operation + * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT + * /data/admin/files/mydoc.txt + */ + public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) { + + $trimmed = ltrim( $path, '/' ); + $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key'; + + // Unlink doesn't tell us if file was deleted (not found returns + // true), so we perform our own test + if ( $view->file_exists( $keyPath ) ) { + + return $view->unlink( $keyPath ); + + } else { + + \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); + + return false; + + } + + } + + /** + * @brief store private key from the user + * @param string key + * @return bool + * @note Encryption of the private key must be performed by client code + * as no encryption takes place here + */ + public static function setPrivateKey( $key ) { + + $user = \OCP\User::getUser(); + + $view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); + + \OC_FileProxy::$enabled = false; + + if ( !$view->file_exists( '' ) ) + $view->mkdir( '' ); + + return $view->file_put_contents( $user . '.private.key', $key ); + + } + + /** + * @brief store private keys from the user + * + * @param string privatekey + * @param string publickey + * @return bool true/false + */ + public static function setUserKeys($privatekey, $publickey) { + + return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) ); + + } + + /** + * @brief store public key of the user + * + * @param string key + * @return bool true/false + */ + public static function setPublicKey( $key ) { + + $view = new \OC_FilesystemView( '/public-keys' ); + + \OC_FileProxy::$enabled = false; + + if ( !$view->file_exists( '' ) ) + $view->mkdir( '' ); + + return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); + + + } + + /** + * @brief store file encryption key + * + * @param string $path relative path of the file, including filename + * @param string $key + * @param null $view + * @param string $dbClassName + * @return bool true/false + * @note The keyfile is not encrypted here. Client code must + * asymmetrically encrypt the keyfile before passing it to this method + */ + public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { + + $basePath = '/' . $userId . '/files_encryption/share-keys'; + + $shareKeyPath = self::keySetPreparation( $view, $path, $basePath, $userId ); + + return $view->file_put_contents( $basePath . '/' . $shareKeyPath . '.shareKey', $shareKey ); + + } + + /** + * @brief Make preparations to vars and filesystem for saving a keyfile + */ + public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) { + + $targetPath = ltrim( $path, '/' ); + + $path_parts = pathinfo( $targetPath ); + + // If the file resides within a subdirectory, create it + if ( + isset( $path_parts['dirname'] ) + && ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] ) + ) { + + $view->mkdir( $basePath . '/' . $path_parts['dirname'] ); + + } + + return $targetPath; + + } + + /** + * @brief Fetch the legacy encryption key from user files + * @param string $login used to locate the legacy key + * @param string $passphrase used to decrypt the legacy key + * @return true / false + * + * if the key is left out, the default handler will be used + */ + public function getLegacyKey() { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView( '/' . $user ); + return $view->file_get_contents( 'encryption.key' ); + + } + } \ No newline at end of file diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index d4b993b4c0638a7a6596c56cfc5edd8fd7955814..65d7d57a05a740bd164e7476b1c3910afcd7f49f 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -173,7 +173,7 @@ class Stream { // $count will always be 8192 https://bugs.php.net/bug.php?id=21641 // This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed' - \OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', OCP\Util::FATAL ); + \OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL ); die(); @@ -209,7 +209,7 @@ class Stream { } /** - * @brief Encrypt and pad data ready for writting to disk + * @brief Encrypt and pad data ready for writing to disk * @param string $plainData data to be encrypted * @param string $key key to use for encryption * @return encrypted data on success, false on failure @@ -403,7 +403,7 @@ class Stream { $encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile ); // Write the data chunk to disk. This will be - // addended to the last data chunk if the file + // attended to the last data chunk if the file // being handled totals more than 6126 bytes fwrite( $this->handle, $encrypted ); diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php index 6fe4ea6d564c1b84ed64901941155aaf77c32ccf..af0273cfdc4dc4cf1a5c31f6155ff158c8b04d5b 100644 --- a/apps/files_encryption/settings-personal.php +++ b/apps/files_encryption/settings-personal.php @@ -12,8 +12,6 @@ $blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_b $tmpl->assign( 'blacklist', $blackList ); -OCP\Util::addscript('files_encryption','settings-personal'); - return $tmpl->fetchPage(); return null; diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index 1f71efb1735772e8c864c7886ae3d36c903f6471..5f0accaed5fd960c3c6a2a08b3493f483148b841 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -1,22 +1,22 @@
- t( 'Encryption' ); ?> + t( 'Encryption' )); ?>

- t( 'File encryption is enabled.' ); ?> + t( 'File encryption is enabled.' )); ?>

- t( 'The following file types will not be encrypted:' ); ?> + t( 'The following file types will not be encrypted:' )); ?>

  • - +
  • -

    +
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php index f7ef8a8efe65772bb44d0775766c214a0c3e2ffa..b873d7f5aafd8e081088de28b8ad009aabcc5a56 100644 --- a/apps/files_encryption/templates/settings.php +++ b/apps/files_encryption/templates/settings.php @@ -2,17 +2,17 @@

- t( 'Encryption' ); ?> + t( 'Encryption' )); ?> - t( "Exclude the following file types from encryption:" ); ?> + t( "Exclude the following file types from encryption:" )); ?>

diff --git a/apps/files_external/3rdparty/phpseclib/AUTHORS b/apps/files_external/3rdparty/phpseclib/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..7bae8ab94e1a2b05c0fe3f81f7897dfc114af773 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/AUTHORS @@ -0,0 +1,3 @@ +phpseclib Lead Developer: TerraFrost (Jim Wigginton) + +phpseclib Developers: monnerat (Patrick Monnerat) \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/LICENSE b/apps/files_external/3rdparty/phpseclib/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6ecd9b9bec11f7587fd76974810a90bb7faad537 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/LICENSE @@ -0,0 +1,21 @@ +Copyright 2007-2012 TerraFrost and other contributors +http://phpseclib.sourceforge.net/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/README.md b/apps/files_external/3rdparty/phpseclib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fbd58bd82b1f2c1b3cc25c13994359c12325e56c --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/README.md @@ -0,0 +1,16 @@ +# phpseclib - PHP Secure Communications Library + +[![Build Status](https://secure.travis-ci.org/phpseclib/phpseclib.png?branch=master)](http://travis-ci.org/phpseclib/phpseclib) + +MIT-licensed pure-PHP implementations of an arbitrary-precision integer +arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, +AES, SSH-1, SSH-2, SFTP, and X.509 + +* [Download (0.3.1)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.1.zip/download) +* [Browse Git](https://github.com/phpseclib/phpseclib) +* [Documentation](http://phpseclib.sourceforge.net/) +* [Support](http://www.frostjedi.com/phpbb/viewforum.php?f=46) +* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/latest/) + +PEAR Channel +PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm) diff --git a/apps/files_external/3rdparty/phpseclib/composer.json b/apps/files_external/3rdparty/phpseclib/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..11008cd81d1279b744b109df6bb094bb3d6d75e0 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/composer.json @@ -0,0 +1,48 @@ +{ + "name": "phpseclib/phpseclib", + "type": "library", + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "keywords": [ + "security", + "crypto", + "cryptography", + "encryption", + "signature", + "signing", + "rsa", + "aes", + "ssh", + "sftp", + "x509", + "x.509", + "asn1", + "asn.1", + "BigInteger" + ], + "homepage": "http://phpseclib.sourceforge.net", + "license": "MIT", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Developer" + } + ], + "require": { + "php": ">=5.0.0" + }, + "suggest": { + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "pear-pear/PHP_Compat": "Install PHP_Compat to get phpseclib working on PHP >= 4.3.3." + }, + "include-path": ["phpseclib/"], + "autoload": { + "psr-0": { + "Crypt": "phpseclib/", + "File": "phpseclib/", + "Math": "phpseclib/", + "Net": "phpseclib/" + } + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php new file mode 100644 index 0000000000000000000000000000000000000000..bc05adf67a710925749176d6c1c2a595a9c4f274 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php @@ -0,0 +1,946 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $aes->decrypt($aes->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_AES + * @author Jim Wigginton + * @copyright MMVIII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_Rijndael + */ +if (!class_exists('Crypt_Rijndael')) { + require_once 'Rijndael.php'; +} + +/**#@+ + * @access public + * @see Crypt_AES::encrypt() + * @see Crypt_AES::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_AES_MODE_CTR', -1); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_AES_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_AES_MODE_CBC', 2); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_AES_MODE_CFB', 3); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_AES_MODE_OFB', 4); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_AES::Crypt_AES() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_AES_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_AES_MODE_MCRYPT', 2); +/**#@-*/ + +/** + * Pure-PHP implementation of AES. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_AES + */ +class Crypt_AES extends Crypt_Rijndael { + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::encrypt() + * @var String + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::decrypt() + * @var String + * @access private + */ + var $demcrypt; + + /** + * mcrypt resource for CFB mode + * + * @see Crypt_AES::encrypt() + * @see Crypt_AES::decrypt() + * @var String + * @access private + */ + var $ecb; + + /** + * The SubByte S-Box + * + * @see Crypt_AES::_encryptBlock() + * @var Array + * @access intern + */ + var $sbox = array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ); + + /** + * The inverse SubByte S-Box + * + * @see Crypt_AES::_decryptBlock() + * @var Array + * @access intern + */ + var $isbox = array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ); + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_AES + * @access public + */ + function Crypt_AES($mode = CRYPT_AES_MODE_CBC) + { + if ( !defined('CRYPT_AES_MODE') ) { + switch (true) { + case extension_loaded('mcrypt') && in_array('rijndael-128', mcrypt_list_algorithms()): + define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT); + break; + default: + define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); + } + } + + switch ( CRYPT_AES_MODE ) { + case CRYPT_AES_MODE_MCRYPT: + switch ($mode) { + case CRYPT_AES_MODE_ECB: + $this->paddable = true; + $this->mode = MCRYPT_MODE_ECB; + break; + case CRYPT_AES_MODE_CTR: + // ctr doesn't have a constant associated with it even though it appears to be fairly widely + // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to + // include a compatibility layer. the layer has been implemented but, for now, is commented out. + $this->mode = 'ctr'; + //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR; + break; + case CRYPT_AES_MODE_CFB: + $this->mode = 'ncfb'; + break; + case CRYPT_AES_MODE_OFB: + $this->mode = MCRYPT_MODE_NOFB; + break; + case CRYPT_AES_MODE_CBC: + default: + $this->paddable = true; + $this->mode = MCRYPT_MODE_CBC; + } + + break; + default: + switch ($mode) { + case CRYPT_AES_MODE_ECB: + $this->paddable = true; + $this->mode = CRYPT_RIJNDAEL_MODE_ECB; + break; + case CRYPT_AES_MODE_CTR: + $this->mode = CRYPT_RIJNDAEL_MODE_CTR; + break; + case CRYPT_AES_MODE_CFB: + $this->mode = CRYPT_RIJNDAEL_MODE_CFB; + break; + case CRYPT_AES_MODE_OFB: + $this->mode = CRYPT_RIJNDAEL_MODE_OFB; + break; + case CRYPT_AES_MODE_CBC: + default: + $this->paddable = true; + $this->mode = CRYPT_RIJNDAEL_MODE_CBC; + } + } + + if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) { + parent::Crypt_Rijndael($this->mode); + } + } + + /** + * Extended Crypt_Rijndael::_setup() + * + * Optimizing the key schedule arrays ($w, $dw) for _encryptBlock() and _decryptBlock() after Crypt_Rijndael::_setup() + * + * @see Crypt_Rijndael::_setup() + * @access private + */ + function _setup() + { + if (!$this->changed) { + return; + } + + $this->w = $this->dw = array(); + parent::_setup(); + + $this->dw = array_reverse($this->dw); + $w = array_pop($this->w); + $dw = array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; + } + } + $this->w = $w; + $this->dw = $dw; + } + + /** + * Dummy function + * + * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything. + * + * @access public + * @param Integer $length + */ + function setBlockLength($length) + { + return; + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * @access public + * @param String $iv + */ + function setIV($iv) + { + parent::setIV($iv); + if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { + $this->changed = true; + } + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the + * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that + * length. + * + * @see Crypt_AES::decrypt() + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { + $this->_mcryptSetup(); + + // re: http://phpseclib.sourceforge.net/cfb-demo.phps + // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's + // rewritten CFB implementation the above outputs the same thing twice. + if ($this->mode == 'ncfb' && $this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 16 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; + } + if ($len >= 16) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > 280) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16)); + $iv = substr($ciphertext, -16); + $len%= 16; + } else { + while ($len >= 16) { + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 16); + $ciphertext.= $iv; + $len-= 16; + $i+= 16; + } + } + } + + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $block = $iv ^ substr($plaintext, -$len); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + + return $ciphertext; + } + + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); + } + + return $ciphertext; + } + + return parent::encrypt($plaintext); + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is. + * + * @see Crypt_AES::encrypt() + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { + $this->_mcryptSetup(); + + if ($this->mode == 'ncfb' && $this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 16 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= 16) { + $cb = substr($ciphertext, $i, $len - $len % 16); + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = substr($cb, -16); + $len%= 16; + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$len); + $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); + $pos = $len; + } + + return $plaintext; + } + + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0)); + } + + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + return parent::decrypt($ciphertext); + } + + /** + * Setup mcrypt + * + * Validates all the variables. + * + * @access private + */ + function _mcryptSetup() + { + if (!$this->changed) { + return; + } + + if (!$this->explicit_key_length) { + // this just copied from Crypt_Rijndael::_setup() + $length = strlen($this->key) >> 2; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; + } + $this->Nk = $length; + $this->key_size = $length << 2; + } + + switch ($this->Nk) { + case 4: // 128 + $this->key_size = 16; + break; + case 5: // 160 + case 6: // 192 + $this->key_size = 24; + break; + case 7: // 224 + case 8: // 256 + $this->key_size = 32; + } + + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0)); + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); + + if (!isset($this->enmcrypt)) { + $mode = $this->mode; + //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode; + + $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); + $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); + + if ($mode == 'ncfb') { + $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, ''); + } + + } // else should mcrypt_generic_deinit be called? + + mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); + + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); + } + + $this->changed = false; + } + + /** + * Encrypts a block + * + * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. + * + * @see Crypt_Rijndael::_encryptBlock() + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + $state = unpack('N*', $in); + + $sbox = $this->sbox; + $w = $this->w; + $t0 = $this->t0; + $t1 = $this->t1; + $t2 = $this->t2; + $t3 = $this->t3; + + // addRoundKey + $s0 = $state[1] ^ $w[4]; + $s1 = $state[2] ^ $w[5]; + $s2 = $state[3] ^ $w[6]; + $s3 = $state[4] ^ $w[7]; + + // shiftRows + subWord + mixColumns + addRoundKey + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[8]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[9]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[10]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[11]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[12]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[13]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[14]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[15]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[16]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[17]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[18]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[19]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[20]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[21]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[22]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[23]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[24]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[25]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[26]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[27]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[28]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[29]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[30]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[31]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[32]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[33]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[34]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[35]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[36]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[37]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[38]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[39]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[40]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[41]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[42]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[43]; + + switch ($this->Nr) { + case 10: + break; + + case 14: + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[56]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[57]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[58]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[59]; + break; + + case 12: + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; + break; + + case 13: + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; + $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; + + $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; + $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; + $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; + $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; + + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54]; + $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55]; + // Note: Here we skip $s3 but using $e3 + + $e0 = $s0; + $e1 = $s1; + $e2 = $s2; + // $e3 = $s3; + break; + + default: // 11 + $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; + $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; + $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; + $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; + // Note: Here we skip $s3 but using $e3 + + $e0 = $s0; + $e1 = $s1; + $e2 = $s2; + // $e3 = $s3; + } + + // subWord + $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24); + $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24); + $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24); + $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24); + + // shiftRows + addRoundKey + return pack('N*', + ($e0 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $w[0], + ($e1 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $w[1], + ($e2 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $w[2], + ($e3 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $w[3] + ); + } + + /** + * Decrypts a block + * + * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. + * + * @see Crypt_Rijndael::_decryptBlock() + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + $state = unpack('N*', $in); + + $sbox = $this->isbox; + $dw = $this->dw; + $dt0 = $this->dt0; + $dt1 = $this->dt1; + $dt2 = $this->dt2; + $dt3 = $this->dt3; + + // addRoundKey + $s0 = $state[1] ^ $dw[4]; + $s1 = $state[2] ^ $dw[5]; + $s2 = $state[3] ^ $dw[6]; + $s3 = $state[4] ^ $dw[7]; + + // invShiftRows + invSubBytes + invMixColumns + addRoundKey + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[8]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[9]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[10]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[11]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[12]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[13]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[14]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[15]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[16]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[17]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[18]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[19]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[20]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[21]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[22]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[23]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[24]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[25]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[26]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[27]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[28]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[29]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[30]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[31]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[32]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[33]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[34]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[35]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[36]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[37]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[38]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[39]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[40]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[41]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[42]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[43]; + + switch ($this->Nr) { + case 10: + break; + + case 14: + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[56]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[57]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[58]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[59]; + break; + + case 12: + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; + break; + + case 13: + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; + $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; + + $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; + $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; + $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; + $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; + + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54]; + $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55]; + // Note: Here we skip $s3 but using $e3 + + $e0 = $s0; + $e1 = $s1; + $e2 = $s2; + // $e3 = $s3; + break; + + default: // 11 + $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; + $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; + $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; + $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; + // Note: Here we skip $s3 but using $e3 + + $e0 = $s0; + $e1 = $s1; + $e2 = $s2; + // $e3 = $s3; + } + + // invSubWord + $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24); + $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24); + $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24); + $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24); + + // invShiftRows + addRoundKey + return pack('N*', + ($e0 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $dw[0], + ($e1 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $dw[1], + ($e2 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $dw[2], + ($e3 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $dw[3] + ); + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * The default behavior. + * + * @see Crypt_Rijndael::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + parent::enableContinuousBuffer(); + + if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) { + $this->enbuffer['enmcrypt_init'] = true; + $this->debuffer['demcrypt_init'] = true; + } + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see Crypt_Rijndael::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + parent::disableContinuousBuffer(); + + if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); + mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); + } + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php new file mode 100644 index 0000000000000000000000000000000000000000..1197a50ab72df4c032eb87a1168ce77458452f85 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php @@ -0,0 +1,1334 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_DES + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access private + * @see Crypt_DES::_prepareKey() + * @see Crypt_DES::_processBlock() + */ +/** + * Contains array_reverse($keys[CRYPT_DES_DECRYPT]) + */ +define('CRYPT_DES_ENCRYPT', 0); +/** + * Contains array_reverse($keys[CRYPT_DES_ENCRYPT]) + */ +define('CRYPT_DES_DECRYPT', 1); +/**#@-*/ + +/**#@+ + * @access public + * @see Crypt_DES::encrypt() + * @see Crypt_DES::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_DES_MODE_CTR', -1); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_DES_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_DES_MODE_CBC', 2); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_DES_MODE_CFB', 3); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_DES_MODE_OFB', 4); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_DES::Crypt_DES() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_DES_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_DES_MODE_MCRYPT', 2); +/**#@-*/ + +/** + * Pure-PHP implementation of DES. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_DES + */ +class Crypt_DES { + /** + * The Key Schedule + * + * @see Crypt_DES::setKey() + * @var Array + * @access private + */ + var $keys = "\0\0\0\0\0\0\0\0"; + + /** + * The Encryption Mode + * + * @see Crypt_DES::Crypt_DES() + * @var Integer + * @access private + */ + var $mode; + + /** + * Continuous Buffer status + * + * @see Crypt_DES::enableContinuousBuffer() + * @var Boolean + * @access private + */ + var $continuousBuffer = false; + + /** + * Padding status + * + * @see Crypt_DES::enablePadding() + * @var Boolean + * @access private + */ + var $padding = true; + + /** + * The Initialization Vector + * + * @see Crypt_DES::setIV() + * @var String + * @access private + */ + var $iv = "\0\0\0\0\0\0\0\0"; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_DES::enableContinuousBuffer() + * @var String + * @access private + */ + var $encryptIV = "\0\0\0\0\0\0\0\0"; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_DES::enableContinuousBuffer() + * @var String + * @access private + */ + var $decryptIV = "\0\0\0\0\0\0\0\0"; + + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_DES::encrypt() + * @var String + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_DES::decrypt() + * @var String + * @access private + */ + var $demcrypt; + + /** + * Does the enmcrypt resource need to be (re)initialized? + * + * @see Crypt_DES::setKey() + * @see Crypt_DES::setIV() + * @var Boolean + * @access private + */ + var $enchanged = true; + + /** + * Does the demcrypt resource need to be (re)initialized? + * + * @see Crypt_DES::setKey() + * @see Crypt_DES::setIV() + * @var Boolean + * @access private + */ + var $dechanged = true; + + /** + * Is the mode one that is paddable? + * + * @see Crypt_DES::Crypt_DES() + * @var Boolean + * @access private + */ + var $paddable = false; + + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_DES::encrypt() + * @var Array + * @access private + */ + var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_DES::decrypt() + * @var Array + * @access private + */ + var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + /** + * mcrypt resource for CFB mode + * + * @see Crypt_DES::encrypt() + * @see Crypt_DES::decrypt() + * @var String + * @access private + */ + var $ecb; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_DES + * @access public + */ + function Crypt_DES($mode = CRYPT_DES_MODE_CBC) + { + if ( !defined('CRYPT_DES_MODE') ) { + switch (true) { + case extension_loaded('mcrypt') && in_array('des', mcrypt_list_algorithms()): + define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT); + break; + default: + define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL); + } + } + + switch ( CRYPT_DES_MODE ) { + case CRYPT_DES_MODE_MCRYPT: + switch ($mode) { + case CRYPT_DES_MODE_ECB: + $this->paddable = true; + $this->mode = MCRYPT_MODE_ECB; + break; + case CRYPT_DES_MODE_CTR: + $this->mode = 'ctr'; + //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR; + break; + case CRYPT_DES_MODE_CFB: + $this->mode = 'ncfb'; + $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); + break; + case CRYPT_DES_MODE_OFB: + $this->mode = MCRYPT_MODE_NOFB; + break; + case CRYPT_DES_MODE_CBC: + default: + $this->paddable = true; + $this->mode = MCRYPT_MODE_CBC; + } + $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); + $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); + + break; + default: + switch ($mode) { + case CRYPT_DES_MODE_ECB: + case CRYPT_DES_MODE_CBC: + $this->paddable = true; + $this->mode = $mode; + break; + case CRYPT_DES_MODE_CTR: + case CRYPT_DES_MODE_CFB: + case CRYPT_DES_MODE_OFB: + $this->mode = $mode; + break; + default: + $this->paddable = true; + $this->mode = CRYPT_DES_MODE_CBC; + } + } + } + + /** + * Sets the key. + * + * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we + * only use the first eight, if $key has more then eight characters in it, and pad $key with the + * null byte if it is less then eight characters long. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * If the key is not explicitly set, it'll be assumed to be all zero's. + * + * @access public + * @param String $key + */ + function setKey($key) + { + $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key); + $this->enchanged = true; + $this->dechanged = true; + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * $hash, $salt, $count + * + * @param String $password + * @param optional String $method + * @access public + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + default: // 'pbkdf2' + list(, , $hash, $salt, $count) = func_get_args(); + if (!isset($hash)) { + $hash = 'sha1'; + } + // WPA and WPA2 use the SSID as the salt + if (!isset($salt)) { + $salt = 'phpseclib/salt'; + } + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + if (!isset($count)) { + $count = 1000; + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + + $i = 1; + while (strlen($key) < 8) { // $dkLen == 8 + //$dk.= $this->_pbkdf($password, $salt, $count, $i++); + $hmac = new Crypt_Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; $j++) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + } + + $this->setKey($key); + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * @access public + * @param String $iv + */ + function setIV($iv) + { + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0)); + $this->enchanged = true; + $this->dechanged = true; + } + + /** + * Generate CTR XOR encryption key + * + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. + * + * @see Crypt_DES::decrypt() + * @see Crypt_DES::encrypt() + * @access public + * @param String $iv + */ + function _generate_xor(&$iv) + { + $xor = $iv; + for ($j = 4; $j <= 8; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + + return $xor; + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the + * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that + * length. + * + * @see Crypt_DES::decrypt() + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { + if ($this->enchanged) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); + } + $this->enchanged = false; + } + + if ($this->mode != 'ncfb' || !$this->continuousBuffer) { + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); + } else { + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; + } + if ($len >= 8) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > 600) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8)); + $iv = substr($ciphertext, -8); + $len%= 8; + } else { + while ($len >= 8) { + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8); + $ciphertext.= $iv; + $len-= 8; + $i+= 8; + } + } + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $block = $iv ^ substr($plaintext, -$len); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + return $ciphertext; + } + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); + } + + return $ciphertext; + } + + if (!is_array($this->keys)) { + $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); + } + + $buffer = &$this->enbuffer; + $continuousBuffer = $this->continuousBuffer; + $ciphertext = ''; + switch ($this->mode) { + case CRYPT_DES_MODE_ECB: + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT); + } + break; + case CRYPT_DES_MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT); + $xor = $block; + $ciphertext.= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->encryptIV; + if (strlen($buffer['encrypted'])) { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $buffer['encrypted'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); + $key = $this->_string_shift($buffer['encrypted'], 8); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); + $ciphertext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) & 7) { + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; + } + } + break; + case CRYPT_DES_MODE_CFB: + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= 8) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT) ^ substr($plaintext, $i, 8); + $ciphertext.= $iv; + $len-= 8; + $i+= 8; + } + if ($len) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + return $ciphertext; + case CRYPT_DES_MODE_OFB: + $xor = $this->encryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); + $ciphertext.= substr($plaintext, $i, 8) ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); + $ciphertext.= substr($plaintext, $i, 8) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) & 7) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $ciphertext; + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is. + * + * @see Crypt_DES::encrypt() + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); + } + + if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { + if ($this->dechanged) { + mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); + } + $this->dechanged = false; + } + + if ($this->mode != 'ncfb' || !$this->continuousBuffer) { + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + } else { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= 8) { + $cb = substr($ciphertext, $i, $len - $len % 8); + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = substr($cb, -8); + $len%= 8; + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$len); + $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); + $pos = $len; + } + return $plaintext; + } + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if (!is_array($this->keys)) { + $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); + } + + $buffer = &$this->debuffer; + $continuousBuffer = $this->continuousBuffer; + $plaintext = ''; + switch ($this->mode) { + case CRYPT_DES_MODE_ECB: + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT); + } + break; + case CRYPT_DES_MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor; + $xor = $block; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->decryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $buffer['ciphertext'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); + $key = $this->_string_shift($buffer['ciphertext'], 8); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); + $plaintext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % 8) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case CRYPT_DES_MODE_CFB: + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= 8) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $cb = substr($ciphertext, $i, 8); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= 8; + $i+= 8; + } + if ($len) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); + $pos = $len; + } + return $plaintext; + case CRYPT_DES_MODE_OFB: + $xor = $this->decryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); + $plaintext.= substr($ciphertext, $i, 8) ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); + $plaintext.= substr($ciphertext, $i, 8) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % 8) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->encrypt(substr($plaintext, 8, 8)); + * + * + * echo $des->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see Crypt_DES::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + $this->continuousBuffer = true; + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see Crypt_DES::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + $this->continuousBuffer = false; + $this->encryptIV = $this->iv; + $this->decryptIV = $this->iv; + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + if (CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->iv); + mcrypt_generic_init($this->demcrypt, $this->keys, $this->iv); + } + } + + /** + * Pad "packets". + * + * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not + * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see Crypt_DES::disablePadding() + * @access public + */ + function enablePadding() + { + $this->padding = true; + } + + /** + * Do not pad packets. + * + * @see Crypt_DES::enablePadding() + * @access public + */ + function disablePadding() + { + $this->padding = false; + } + + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8). + * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see Crypt_DES::_unpad() + * @access private + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if (($length & 7) == 0) { + return $text; + } else { + user_error("The plaintext's length ($length) is not a multiple of the block size (8)"); + $this->padding = true; + } + } + + $pad = 8 - ($length & 7); + return str_pad($text, $length + $pad, chr($pad)); + } + + /** + * Unpads a string + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see Crypt_DES::_pad() + * @access private + */ + function _unpad($text) + { + if (!$this->padding) { + return $text; + } + + $length = ord($text[strlen($text) - 1]); + + if (!$length || $length > 8) { + return false; + } + + return substr($text, 0, -$length); + } + + /** + * Encrypts or decrypts a 64-bit block + * + * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See + * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general + * idea of what this function does. + * + * @access private + * @param String $block + * @param Integer $mode + * @return String + */ + function _processBlock($block, $mode) + { + // s-boxes. in the official DES docs, they're described as being matrices that + // one accesses by using the first and last bits to determine the row and the + // middle four bits to determine the column. in this implementation, they've + // been converted to vectors + static $sbox = array( + array( + 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, + 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, + 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, + 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 + ), + array( + 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, + 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, + 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, + 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 + ), + array( + 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, + 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, + 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, + 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 + ), + array( + 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, + 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, + 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, + 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 + ), + array( + 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, + 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, + 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, + 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 + ), + array( + 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, + 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, + 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, + 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 + ), + array( + 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, + 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, + 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, + 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 + ), + array( + 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, + 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, + 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, + 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 + ) + ); + + $keys = $this->keys; + + $temp = unpack('Na/Nb', $block); + $block = array($temp['a'], $temp['b']); + + // because php does arithmetic right shifts, if the most significant bits are set, right + // shifting those into the correct position will add 1's - not 0's. this will intefere + // with the | operation unless a second & is done. so we isolate these bits and left shift + // them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added + // for any other shifts. + $msb = array( + ($block[0] >> 31) & 1, + ($block[1] >> 31) & 1 + ); + $block[0] &= 0x7FFFFFFF; + $block[1] &= 0x7FFFFFFF; + + // we isolate the appropriate bit in the appropriate integer and shift as appropriate. in + // some cases, there are going to be multiple bits in the same integer that need to be shifted + // in the same way. we combine those into one shift operation. + $block = array( + (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) | + (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) | + (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) | + (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) | + (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) | + (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) | + (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) | + (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) | + (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) | + (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) | + (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) | + (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) | + (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) | + (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24) + , + (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) | + (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) | + (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) | + (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) | + ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) | + (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) | + (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) | + (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) | + (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) | + (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) | + (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) | + (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) | + (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) | + (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) | + ($msb[1] << 28) | ($msb[0] << 24) + ); + + for ($i = 0; $i < 16; $i++) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28) + | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24) + | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20) + | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16) + | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12) + | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8) + | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4) + | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]); + + $msb = ($temp >> 31) & 1; + $temp &= 0x7FFFFFFF; + $newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5) + | (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10) + | (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6) + | (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9) + | (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27) + | (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8) + | (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16) + | (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15) + | (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20) + | (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3) + | (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7) + | (($temp & 0x00200000) >> 19) | ($msb << 23); + // end of "the Feistel (F) function" - $newBlock is F's output + + $temp = $block[1]; + $block[1] = $block[0] ^ $newBlock; + $block[0] = $temp; + } + + $msb = array( + ($block[0] >> 31) & 1, + ($block[1] >> 31) & 1 + ); + $block[0] &= 0x7FFFFFFF; + $block[1] &= 0x7FFFFFFF; + + $block = array( + (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) | + (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) | + (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) | + (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) | + (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) | + (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) | + (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) | + (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) | + (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) | + (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) | + (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) | + (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) | + (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) | + (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9) + , + (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) | + (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) | + (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) | + (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) | + (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) | + ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) | + (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) | + (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) | + (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) | + (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) | + (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) | + (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) | + (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) | + ($msb[0] << 7) | ($msb[1] << 6) + ); + + return pack('NN', $block[0], $block[1]); + } + + /** + * Creates the key schedule. + * + * @access private + * @param String $key + * @return Array + */ + function _prepareKey($key) + { + static $shifts = array( // number of key bits shifted per round + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + ); + + // pad the key and remove extra characters as appropriate. + $key = str_pad(substr($key, 0, 8), 8, chr(0)); + + $temp = unpack('Na/Nb', $key); + $key = array($temp['a'], $temp['b']); + $msb = array( + ($key[0] >> 31) & 1, + ($key[1] >> 31) & 1 + ); + $key[0] &= 0x7FFFFFFF; + $key[1] &= 0x7FFFFFFF; + + $key = array( + (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) | + (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) | + (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) | + (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) | + (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) | + (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) | + (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) | + (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28) + , + (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) | + (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) | + (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) | + (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) | + (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) | + (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) | + (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) | + (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) | + (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) | + (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) | + (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) | + (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) | + (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) | + ($msb[1] << 24) | ($msb[0] << 20) + ); + + $keys = array(); + for ($i = 0; $i < 16; $i++) { + $key[0] <<= $shifts[$i]; + $temp = ($key[0] & 0xF0000000) >> 28; + $key[0] = ($key[0] | $temp) & 0x0FFFFFFF; + + $key[1] <<= $shifts[$i]; + $temp = ($key[1] & 0xF0000000) >> 28; + $key[1] = ($key[1] | $temp) & 0x0FFFFFFF; + + $temp = array( + (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) | + (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) | + (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23) + , + (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) | + (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) | + (($key[1] & 0x00000080) >> 6) + , + ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) | + (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) | + (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20) + , + (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) | + (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) | + (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26) + , + (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) | + (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) | + (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1) + , + (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) | + (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) | + (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8) + , + (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) | + (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) | + (($key[0] & 0x00400000) >> 21) + , + (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) | + (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) | + (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24) + ); + + $keys[] = $temp; + } + + $temp = array( + CRYPT_DES_ENCRYPT => $keys, + CRYPT_DES_DECRYPT => array_reverse($keys) + ); + + return $temp; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php new file mode 100644 index 0000000000000000000000000000000000000000..c5d314f009f87ab00cc283d629a9f52ad4572764 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php @@ -0,0 +1,825 @@ + + * setKey('abcdefg'); + * + * echo base64_encode($hash->hash('abcdefg')); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_Hash + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access private + * @see Crypt_Hash::Crypt_Hash() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_HASH_MODE_INTERNAL', 1); +/** + * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. + */ +define('CRYPT_HASH_MODE_MHASH', 2); +/** + * Toggles the hash() implementation, which works on PHP 5.1.2+. + */ +define('CRYPT_HASH_MODE_HASH', 3); +/**#@-*/ + +/** + * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_Hash + */ +class Crypt_Hash { + /** + * Byte-length of compression blocks / key (Internal HMAC) + * + * @see Crypt_Hash::setAlgorithm() + * @var Integer + * @access private + */ + var $b; + + /** + * Byte-length of hash output (Internal HMAC) + * + * @see Crypt_Hash::setHash() + * @var Integer + * @access private + */ + var $l = false; + + /** + * Hash Algorithm + * + * @see Crypt_Hash::setHash() + * @var String + * @access private + */ + var $hash; + + /** + * Key + * + * @see Crypt_Hash::setKey() + * @var String + * @access private + */ + var $key = false; + + /** + * Outer XOR (Internal HMAC) + * + * @see Crypt_Hash::setKey() + * @var String + * @access private + */ + var $opad; + + /** + * Inner XOR (Internal HMAC) + * + * @see Crypt_Hash::setKey() + * @var String + * @access private + */ + var $ipad; + + /** + * Default Constructor. + * + * @param optional String $hash + * @return Crypt_Hash + * @access public + */ + function Crypt_Hash($hash = 'sha1') + { + if ( !defined('CRYPT_HASH_MODE') ) { + switch (true) { + case extension_loaded('hash'): + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH); + break; + case extension_loaded('mhash'): + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH); + break; + default: + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL); + } + } + + $this->setHash($hash); + } + + /** + * Sets the key for HMACs + * + * Keys can be of any length. + * + * @access public + * @param String $key + */ + function setKey($key = false) + { + $this->key = $key; + } + + /** + * Sets the hash function. + * + * @access public + * @param String $hash + */ + function setHash($hash) + { + $hash = strtolower($hash); + switch ($hash) { + case 'md5-96': + case 'sha1-96': + $this->l = 12; // 96 / 8 = 12 + break; + case 'md2': + case 'md5': + $this->l = 16; + break; + case 'sha1': + $this->l = 20; + break; + case 'sha256': + $this->l = 32; + break; + case 'sha384': + $this->l = 48; + break; + case 'sha512': + $this->l = 64; + } + + switch ($hash) { + case 'md2': + $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ? + CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL; + break; + case 'sha384': + case 'sha512': + $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + break; + default: + $mode = CRYPT_HASH_MODE; + } + + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + switch ($hash) { + case 'md5': + case 'md5-96': + $this->hash = MHASH_MD5; + break; + case 'sha256': + $this->hash = MHASH_SHA256; + break; + case 'sha1': + case 'sha1-96': + default: + $this->hash = MHASH_SHA1; + } + return; + case CRYPT_HASH_MODE_HASH: + switch ($hash) { + case 'md5': + case 'md5-96': + $this->hash = 'md5'; + return; + case 'md2': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = $hash; + return; + case 'sha1': + case 'sha1-96': + default: + $this->hash = 'sha1'; + } + return; + } + + switch ($hash) { + case 'md2': + $this->b = 16; + $this->hash = array($this, '_md2'); + break; + case 'md5': + case 'md5-96': + $this->b = 64; + $this->hash = array($this, '_md5'); + break; + case 'sha256': + $this->b = 64; + $this->hash = array($this, '_sha256'); + break; + case 'sha384': + case 'sha512': + $this->b = 128; + $this->hash = array($this, '_sha512'); + break; + case 'sha1': + case 'sha1-96': + default: + $this->b = 64; + $this->hash = array($this, '_sha1'); + } + + $this->ipad = str_repeat(chr(0x36), $this->b); + $this->opad = str_repeat(chr(0x5C), $this->b); + } + + /** + * Compute the HMAC. + * + * @access public + * @param String $text + * @return String + */ + function hash($text) + { + $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + + if (!empty($this->key) || is_string($this->key)) { + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text, $this->key); + break; + case CRYPT_HASH_MODE_HASH: + $output = hash_hmac($this->hash, $text, $this->key, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + /* "Applications that use keys longer than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC." + + -- http://tools.ietf.org/html/rfc2104#section-2 */ + $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key; + + $key = str_pad($key, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 + } + } else { + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text); + break; + case CRYPT_HASH_MODE_HASH: + $output = hash($this->hash, $text, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + $output = call_user_func($this->hash, $text); + } + } + + return substr($output, 0, $this->l); + } + + /** + * Returns the hash length (in bytes) + * + * @access public + * @return Integer + */ + function getLength() + { + return $this->l; + } + + /** + * Wrapper for MD5 + * + * @access private + * @param String $text + */ + function _md5($m) + { + return pack('H*', md5($m)); + } + + /** + * Wrapper for SHA1 + * + * @access private + * @param String $text + */ + function _sha1($m) + { + return pack('H*', sha1($m)); + } + + /** + * Pure-PHP implementation of MD2 + * + * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. + * + * @access private + * @param String $text + */ + function _md2($m) + { + static $s = array( + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 + ); + + // Step 1. Append Padding Bytes + $pad = 16 - (strlen($m) & 0xF); + $m.= str_repeat(chr($pad), $pad); + + $length = strlen($m); + + // Step 2. Append Checksum + $c = str_repeat(chr(0), 16); + $l = chr(0); + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + // RFC1319 incorrectly states that C[j] should be set to S[c xor L] + //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); + // per , however, C[j] should be set to S[c xor L] xor C[j] + $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); + $l = $c[$j]; + } + } + $m.= $c; + + $length+= 16; + + // Step 3. Initialize MD Buffer + $x = str_repeat(chr(0), 48); + + // Step 4. Process Message in 16-Byte Blocks + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + $x[$j + 16] = $m[$i + $j]; + $x[$j + 32] = $x[$j + 16] ^ $x[$j]; + } + $t = chr(0); + for ($j = 0; $j < 18; $j++) { + for ($k = 0; $k < 48; $k++) { + $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); + //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); + } + $t = chr(ord($t) + $j); + } + } + + // Step 5. Output + return substr($x, 0, 16); + } + + /** + * Pure-PHP implementation of SHA256 + * + * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. + * + * @access private + * @param String $text + */ + function _sha256($m) + { + if (extension_loaded('suhosin')) { + return pack('H*', sha256($m)); + } + + // Initialize variables + $hash = array( + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ); + // Initialize table of round constants + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) + static $k = array( + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ); + + // Pre-processing + $length = strlen($m); + // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 + $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N2', 0, $length << 3); + + // Process the message in successive 512-bit chunks + $chunks = str_split($m, 64); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into sixty-four 32-bit words + for ($i = 16; $i < 64; $i++) { + $s0 = $this->_rightRotate($w[$i - 15], 7) ^ + $this->_rightRotate($w[$i - 15], 18) ^ + $this->_rightShift( $w[$i - 15], 3); + $s1 = $this->_rightRotate($w[$i - 2], 17) ^ + $this->_rightRotate($w[$i - 2], 19) ^ + $this->_rightShift( $w[$i - 2], 10); + $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); + + } + + // Initialize hash value for this chunk + list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; + + // Main loop + for ($i = 0; $i < 64; $i++) { + $s0 = $this->_rightRotate($a, 2) ^ + $this->_rightRotate($a, 13) ^ + $this->_rightRotate($a, 22); + $maj = ($a & $b) ^ + ($a & $c) ^ + ($b & $c); + $t2 = $this->_add($s0, $maj); + + $s1 = $this->_rightRotate($e, 6) ^ + $this->_rightRotate($e, 11) ^ + $this->_rightRotate($e, 25); + $ch = ($e & $f) ^ + ($this->_not($e) & $g); + $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); + + $h = $g; + $g = $f; + $f = $e; + $e = $this->_add($d, $t1); + $d = $c; + $c = $b; + $b = $a; + $a = $this->_add($t1, $t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $this->_add($hash[0], $a), + $this->_add($hash[1], $b), + $this->_add($hash[2], $c), + $this->_add($hash[3], $d), + $this->_add($hash[4], $e), + $this->_add($hash[5], $f), + $this->_add($hash[6], $g), + $this->_add($hash[7], $h) + ); + } + + // Produce the final hash value (big-endian) + return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); + } + + /** + * Pure-PHP implementation of SHA384 and SHA512 + * + * @access private + * @param String $text + */ + function _sha512($m) + { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + + static $init384, $init512, $k; + + if (!isset($k)) { + // Initialize variables + $init384 = array( // initial values for SHA384 + 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', + '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' + ); + $init512 = array( // initial values for SHA512 + '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', + '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179' + ); + + for ($i = 0; $i < 8; $i++) { + $init384[$i] = new Math_BigInteger($init384[$i], 16); + $init384[$i]->setPrecision(64); + $init512[$i] = new Math_BigInteger($init512[$i], 16); + $init512[$i]->setPrecision(64); + } + + // Initialize table of round constants + // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) + $k = array( + '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', + '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', + 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', + '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', + 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', + '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', + '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', + 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', + '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', + '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', + 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', + 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', + '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', + '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', + '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', + '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', + 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', + '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', + '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', + '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' + ); + + for ($i = 0; $i < 80; $i++) { + $k[$i] = new Math_BigInteger($k[$i], 16); + } + } + + $hash = $this->l == 48 ? $init384 : $init512; + + // Pre-processing + $length = strlen($m); + // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 + $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N4', 0, 0, 0, $length << 3); + + // Process the message in successive 1024-bit chunks + $chunks = str_split($m, 128); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256); + $temp->setPrecision(64); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into eighty 32-bit words + for ($i = 16; $i < 80; $i++) { + $temp = array( + $w[$i - 15]->bitwise_rightRotate(1), + $w[$i - 15]->bitwise_rightRotate(8), + $w[$i - 15]->bitwise_rightShift(7) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $w[$i - 2]->bitwise_rightRotate(19), + $w[$i - 2]->bitwise_rightRotate(61), + $w[$i - 2]->bitwise_rightShift(6) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $w[$i] = $w[$i - 16]->copy(); + $w[$i] = $w[$i]->add($s0); + $w[$i] = $w[$i]->add($w[$i - 7]); + $w[$i] = $w[$i]->add($s1); + } + + // Initialize hash value for this chunk + $a = $hash[0]->copy(); + $b = $hash[1]->copy(); + $c = $hash[2]->copy(); + $d = $hash[3]->copy(); + $e = $hash[4]->copy(); + $f = $hash[5]->copy(); + $g = $hash[6]->copy(); + $h = $hash[7]->copy(); + + // Main loop + for ($i = 0; $i < 80; $i++) { + $temp = array( + $a->bitwise_rightRotate(28), + $a->bitwise_rightRotate(34), + $a->bitwise_rightRotate(39) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $a->bitwise_and($b), + $a->bitwise_and($c), + $b->bitwise_and($c) + ); + $maj = $temp[0]->bitwise_xor($temp[1]); + $maj = $maj->bitwise_xor($temp[2]); + $t2 = $s0->add($maj); + + $temp = array( + $e->bitwise_rightRotate(14), + $e->bitwise_rightRotate(18), + $e->bitwise_rightRotate(41) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $temp = array( + $e->bitwise_and($f), + $g->bitwise_and($e->bitwise_not()) + ); + $ch = $temp[0]->bitwise_xor($temp[1]); + $t1 = $h->add($s1); + $t1 = $t1->add($ch); + $t1 = $t1->add($k[$i]); + $t1 = $t1->add($w[$i]); + + $h = $g->copy(); + $g = $f->copy(); + $f = $e->copy(); + $e = $d->add($t1); + $d = $c->copy(); + $c = $b->copy(); + $b = $a->copy(); + $a = $t1->add($t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $hash[0]->add($a), + $hash[1]->add($b), + $hash[2]->add($c), + $hash[3]->add($d), + $hash[4]->add($e), + $hash[5]->add($f), + $hash[6]->add($g), + $hash[7]->add($h) + ); + } + + // Produce the final hash value (big-endian) + // (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . + $hash[4]->toBytes() . $hash[5]->toBytes(); + if ($this->l != 48) { + $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); + } + + return $temp; + } + + /** + * Right Rotate + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightRotate($int, $amt) + { + $invamt = 32 - $amt; + $mask = (1 << $invamt) - 1; + return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); + } + + /** + * Right Shift + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightShift($int, $amt) + { + $mask = (1 << (32 - $amt)) - 1; + return ($int >> $amt) & $mask; + } + + /** + * Not + * + * @access private + * @param Integer $int + * @see _sha256() + * @return Integer + */ + function _not($int) + { + return ~$int & 0xFFFFFFFF; + } + + /** + * Add + * + * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the + * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster. + * + * @param String $string + * @param optional Integer $index + * @return String + * @see _sha256() + * @access private + */ + function _add() + { + static $mod; + if (!isset($mod)) { + $mod = pow(2, 32); + } + + $result = 0; + $arguments = func_get_args(); + foreach ($arguments as $argument) { + $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; + } + + return fmod($result, $mod); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php new file mode 100644 index 0000000000000000000000000000000000000000..390108e04800c99bde2053ec924cb7a53fd8459e --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php @@ -0,0 +1,519 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rc4->decrypt($rc4->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_RC4 + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access private + * @see Crypt_RC4::Crypt_RC4() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_RC4_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_RC4_MODE_MCRYPT', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_RC4::_crypt() + */ +define('CRYPT_RC4_ENCRYPT', 0); +define('CRYPT_RC4_DECRYPT', 1); +/**#@-*/ + +/** + * Pure-PHP implementation of RC4. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_RC4 + */ +class Crypt_RC4 { + /** + * The Key + * + * @see Crypt_RC4::setKey() + * @var String + * @access private + */ + var $key = "\0"; + + /** + * The Key Stream for encryption + * + * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object + * + * @see Crypt_RC4::setKey() + * @var Array + * @access private + */ + var $encryptStream = false; + + /** + * The Key Stream for decryption + * + * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object + * + * @see Crypt_RC4::setKey() + * @var Array + * @access private + */ + var $decryptStream = false; + + /** + * The $i and $j indexes for encryption + * + * @see Crypt_RC4::_crypt() + * @var Integer + * @access private + */ + var $encryptIndex = 0; + + /** + * The $i and $j indexes for decryption + * + * @see Crypt_RC4::_crypt() + * @var Integer + * @access private + */ + var $decryptIndex = 0; + + /** + * The Encryption Algorithm + * + * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR. + * + * @see Crypt_RC4::Crypt_RC4() + * @var Integer + * @access private + */ + var $mode; + + /** + * Continuous Buffer status + * + * @see Crypt_RC4::enableContinuousBuffer() + * @var Boolean + * @access private + */ + var $continuousBuffer = false; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * @return Crypt_RC4 + * @access public + */ + function Crypt_RC4() + { + if ( !defined('CRYPT_RC4_MODE') ) { + switch (true) { + case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()): + define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT); + break; + default: + define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL); + } + } + + switch ( CRYPT_RC4_MODE ) { + case CRYPT_RC4_MODE_MCRYPT: + switch (true) { + case defined('MCRYPT_ARCFOUR'): + $this->mode = MCRYPT_ARCFOUR; + break; + case defined('MCRYPT_RC4'); + $this->mode = MCRYPT_RC4; + } + $this->encryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, ''); + $this->decryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, ''); + + } + } + + /** + * Sets the key. + * + * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will + * be used. If no key is explicitly set, it'll be assumed to be a single null byte. + * + * @access public + * @param String $key + */ + function setKey($key) + { + $this->key = $key; + + if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { + mcrypt_generic_init($this->encryptStream, $this->key, ''); + mcrypt_generic_init($this->decryptStream, $this->key, ''); + return; + } + + $keyLength = strlen($key); + $keyStream = array(); + for ($i = 0; $i < 256; $i++) { + $keyStream[$i] = $i; + } + $j = 0; + for ($i = 0; $i < 256; $i++) { + $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; + $temp = $keyStream[$i]; + $keyStream[$i] = $keyStream[$j]; + $keyStream[$j] = $temp; + } + + $this->encryptIndex = $this->decryptIndex = array(0, 0); + $this->encryptStream = $this->decryptStream = $keyStream; + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * $hash, $salt, $count, $dkLen + * + * @param String $password + * @param optional String $method + * @access public + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + default: // 'pbkdf2' + list(, , $hash, $salt, $count) = func_get_args(); + if (!isset($hash)) { + $hash = 'sha1'; + } + // WPA and WPA2 use the SSID as the salt + if (!isset($salt)) { + $salt = 'phpseclib/salt'; + } + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + if (!isset($count)) { + $count = 1000; + } + if (!isset($dkLen)) { + $dkLen = 128; + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + + $i = 1; + while (strlen($key) < $dkLen) { + //$dk.= $this->_pbkdf($password, $salt, $count, $i++); + $hmac = new Crypt_Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; $j++) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + } + + $this->setKey(substr($key, 0, $dkLen)); + } + + /** + * Dummy function. + * + * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1]. + * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before + * calling setKey(). + * + * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol, + * the IV's are relatively easy to predict, an attack described by + * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir} + * can be used to quickly guess at the rest of the key. The following links elaborate: + * + * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009} + * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack} + * + * @param String $iv + * @see Crypt_RC4::setKey() + * @access public + */ + function setIV($iv) + { + } + + /** + * Encrypts a message. + * + * @see Crypt_RC4::_crypt() + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT); + } + + /** + * Decrypts a message. + * + * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). + * Atleast if the continuous buffer is disabled. + * + * @see Crypt_RC4::_crypt() + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT); + } + + /** + * Encrypts or decrypts a message. + * + * @see Crypt_RC4::encrypt() + * @see Crypt_RC4::decrypt() + * @access private + * @param String $text + * @param Integer $mode + */ + function _crypt($text, $mode) + { + if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { + $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream'; + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->$keyStream, $this->key, ''); + } + + return mcrypt_generic($this->$keyStream, $text); + } + + if ($this->encryptStream === false) { + $this->setKey($this->key); + } + + switch ($mode) { + case CRYPT_RC4_ENCRYPT: + $keyStream = $this->encryptStream; + list($i, $j) = $this->encryptIndex; + break; + case CRYPT_RC4_DECRYPT: + $keyStream = $this->decryptStream; + list($i, $j) = $this->decryptIndex; + } + + $newText = ''; + for ($k = 0; $k < strlen($text); $k++) { + $i = ($i + 1) & 255; + $j = ($j + $keyStream[$i]) & 255; + $temp = $keyStream[$i]; + $keyStream[$i] = $keyStream[$j]; + $keyStream[$j] = $temp; + $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255]; + $newText.= chr(ord($text[$k]) ^ $temp); + } + + if ($this->continuousBuffer) { + switch ($mode) { + case CRYPT_RC4_ENCRYPT: + $this->encryptStream = $keyStream; + $this->encryptIndex = array($i, $j); + break; + case CRYPT_RC4_DECRYPT: + $this->decryptStream = $keyStream; + $this->decryptIndex = array($i, $j); + } + } + + return $newText; + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $rc4->encrypt(substr($plaintext, 0, 8)); + * echo $rc4->encrypt(substr($plaintext, 8, 8)); + * + * + * echo $rc4->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $rc4->encrypt(substr($plaintext, 0, 8)); + * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see Crypt_RC4::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { + mcrypt_generic_init($this->encryptStream, $this->key, ''); + mcrypt_generic_init($this->decryptStream, $this->key, ''); + } + + $this->continuousBuffer = true; + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see Crypt_RC4::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) { + $this->encryptIndex = $this->decryptIndex = array(0, 0); + $this->encryptStream = $this->decryptStream = false; + } + + $this->continuousBuffer = false; + } + + /** + * Dummy function. + * + * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is + * included is so that you can switch between a block cipher and a stream cipher transparently. + * + * @see Crypt_RC4::disablePadding() + * @access public + */ + function enablePadding() + { + } + + /** + * Dummy function. + * + * @see Crypt_RC4::enablePadding() + * @access public + */ + function disablePadding() + { + } + + /** + * Class destructor. + * + * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really + * needs to be called if mcrypt is being used. + * + * @access public + */ + function __destruct() + { + if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { + $this->_closeMCrypt(); + } + } + + /** + * Properly close the MCrypt objects. + * + * @access prviate + */ + function _closeMCrypt() + { + mcrypt_module_close($this->encryptStream); + mcrypt_module_close($this->decryptStream); + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php new file mode 100644 index 0000000000000000000000000000000000000000..db1ba1581b684001eaadad51f2bc086171036746 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php @@ -0,0 +1,2646 @@ + + * createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $ciphertext = $rsa->encrypt($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->decrypt($ciphertext); + * ?> + * + * + * Here's an example of how to create signatures and verify signatures with this library: + * + * createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $signature = $rsa->sign($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_RSA + * @author Jim Wigginton + * @copyright MMIX Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Math_BigInteger + */ +if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); +} + +/** + * Include Crypt_Random + */ +// the class_exists() will only be called if the crypt_random_string function hasn't been defined and +// will trigger a call to __autoload() if you're wanting to auto-load classes +// call function_exists() a second time to stop the require_once from being called outside +// of the auto loader +if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); +} + +/** + * Include Crypt_Hash + */ +if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); +} + +/**#@+ + * @access public + * @see Crypt_RSA::encrypt() + * @see Crypt_RSA::decrypt() + */ +/** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha1 by default. + * + * @see Crypt_RSA::setHash() + * @see Crypt_RSA::setMGFHash() + */ +define('CRYPT_RSA_ENCRYPTION_OAEP', 1); +/** + * Use PKCS#1 padding. + * + * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatability with protocols (like SSH-1) written before OAEP's introduction. + */ +define('CRYPT_RSA_ENCRYPTION_PKCS1', 2); +/**#@-*/ + +/**#@+ + * @access public + * @see Crypt_RSA::sign() + * @see Crypt_RSA::verify() + * @see Crypt_RSA::setHash() + */ +/** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha1 by default. + * + * @see Crypt_RSA::setSaltLength() + * @see Crypt_RSA::setMGFHash() + */ +define('CRYPT_RSA_SIGNATURE_PSS', 1); +/** + * Use the PKCS#1 scheme by default. + * + * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards + * compatability with protocols (like SSH-2) written before PSS's introduction. + */ +define('CRYPT_RSA_SIGNATURE_PKCS1', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_RSA::createKey() + */ +/** + * ASN1 Integer + */ +define('CRYPT_RSA_ASN1_INTEGER', 2); +/** + * ASN1 Bit String + */ +define('CRYPT_RSA_ASN1_BITSTRING', 3); +/** + * ASN1 Sequence (with the constucted bit set) + */ +define('CRYPT_RSA_ASN1_SEQUENCE', 48); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_RSA::Crypt_RSA() + */ +/** + * To use the pure-PHP implementation + */ +define('CRYPT_RSA_MODE_INTERNAL', 1); +/** + * To use the OpenSSL library + * + * (if enabled; otherwise, the internal implementation will be used) + */ +define('CRYPT_RSA_MODE_OPENSSL', 2); +/**#@-*/ + +/** + * Default openSSL configuration file. + */ +define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf'); + + +/**#@+ + * @access public + * @see Crypt_RSA::createKey() + * @see Crypt_RSA::setPrivateKeyFormat() + */ +/** + * PKCS#1 formatted private key + * + * Used by OpenSSH + */ +define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0); +/** + * PuTTY formatted private key + */ +define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1); +/** + * XML formatted private key + */ +define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2); +/**#@-*/ + +/**#@+ + * @access public + * @see Crypt_RSA::createKey() + * @see Crypt_RSA::setPublicKeyFormat() + */ +/** + * Raw public key + * + * An array containing two Math_BigInteger objects. + * + * The exponent can be indexed with any of the following: + * + * 0, e, exponent, publicExponent + * + * The modulus can be indexed with any of the following: + * + * 1, n, modulo, modulus + */ +define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3); +/** + * PKCS#1 formatted public key (raw) + * + * Used by File/X509.php + */ +define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4); +/** + * XML formatted public key + */ +define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5); +/** + * OpenSSH formatted public key + * + * Place in $HOME/.ssh/authorized_keys + */ +define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6); +/** + * PKCS#1 formatted public key (encapsulated) + * + * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) + */ +define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 7); +/**#@-*/ + +/** + * Pure-PHP PKCS#1 compliant implementation of RSA. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_RSA + */ +class Crypt_RSA { + /** + * Precomputed Zero + * + * @var Array + * @access private + */ + var $zero; + + /** + * Precomputed One + * + * @var Array + * @access private + */ + var $one; + + /** + * Private Key Format + * + * @var Integer + * @access private + */ + var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1; + + /** + * Public Key Format + * + * @var Integer + * @access public + */ + var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1; + + /** + * Modulus (ie. n) + * + * @var Math_BigInteger + * @access private + */ + var $modulus; + + /** + * Modulus length + * + * @var Math_BigInteger + * @access private + */ + var $k; + + /** + * Exponent (ie. e or d) + * + * @var Math_BigInteger + * @access private + */ + var $exponent; + + /** + * Primes for Chinese Remainder Theorem (ie. p and q) + * + * @var Array + * @access private + */ + var $primes; + + /** + * Exponents for Chinese Remainder Theorem (ie. dP and dQ) + * + * @var Array + * @access private + */ + var $exponents; + + /** + * Coefficients for Chinese Remainder Theorem (ie. qInv) + * + * @var Array + * @access private + */ + var $coefficients; + + /** + * Hash name + * + * @var String + * @access private + */ + var $hashName; + + /** + * Hash function + * + * @var Crypt_Hash + * @access private + */ + var $hash; + + /** + * Length of hash function output + * + * @var Integer + * @access private + */ + var $hLen; + + /** + * Length of salt + * + * @var Integer + * @access private + */ + var $sLen; + + /** + * Hash function for the Mask Generation Function + * + * @var Crypt_Hash + * @access private + */ + var $mgfHash; + + /** + * Length of MGF hash function output + * + * @var Integer + * @access private + */ + var $mgfHLen; + + /** + * Encryption mode + * + * @var Integer + * @access private + */ + var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP; + + /** + * Signature mode + * + * @var Integer + * @access private + */ + var $signatureMode = CRYPT_RSA_SIGNATURE_PSS; + + /** + * Public Exponent + * + * @var Mixed + * @access private + */ + var $publicExponent = false; + + /** + * Password + * + * @var String + * @access private + */ + var $password = false; + + /** + * Components + * + * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - + * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. + * + * @see Crypt_RSA::_start_element_handler() + * @var Array + * @access private + */ + var $components = array(); + + /** + * Current String + * + * For use with parsing XML formatted keys. + * + * @see Crypt_RSA::_character_handler() + * @see Crypt_RSA::_stop_element_handler() + * @var Mixed + * @access private + */ + var $current; + + /** + * OpenSSL configuration file name. + * + * Set to NULL to use system configuration file. + * @see Crypt_RSA::createKey() + * @var Mixed + * @Access public + */ + var $configFile; + + /** + * The constructor + * + * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason + * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires + * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. + * + * @return Crypt_RSA + * @access public + */ + function Crypt_RSA() + { + $this->configFile = CRYPT_RSA_OPENSSL_CONFIG; + + if ( !defined('CRYPT_RSA_MODE') ) { + switch (true) { + case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='): + define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + } + } + + if (!defined('CRYPT_RSA_COMMENT')) { + define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key'); + } + + $this->zero = new Math_BigInteger(); + $this->one = new Math_BigInteger(1); + + $this->hash = new Crypt_Hash('sha1'); + $this->hLen = $this->hash->getLength(); + $this->hashName = 'sha1'; + $this->mgfHash = new Crypt_Hash('sha1'); + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Create public / private key pair + * + * Returns an array with the following three elements: + * - 'privatekey': The private key. + * - 'publickey': The public key. + * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). + * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing. + * + * @access public + * @param optional Integer $bits + * @param optional Integer $timeout + * @param optional Math_BigInteger $p + */ + function createKey($bits = 1024, $timeout = false, $partial = array()) + { + if (!defined('CRYPT_RSA_EXPONENT')) { + // http://en.wikipedia.org/wiki/65537_%28number%29 + define('CRYPT_RSA_EXPONENT', '65537'); + } + // per , this number ought not result in primes smaller + // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME + // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if + // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then + // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key + // generation when there's a chance neither gmp nor OpenSSL are installed) + if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { + define('CRYPT_RSA_SMALLEST_PRIME', 4096); + } + + // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum + if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { + $config = array(); + if (isset($this->configFile)) { + $config['config'] = $this->configFile; + } + $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); + openssl_pkey_export($rsa, $privatekey, NULL, $config); + $publickey = openssl_pkey_get_details($rsa); + $publickey = $publickey['key']; + + $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1))); + $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1))); + + // clear the buffer of error strings stemming from a minimalistic openssl.cnf + while (openssl_error_string() !== false); + + return array( + 'privatekey' => $privatekey, + 'publickey' => $publickey, + 'partialkey' => false + ); + } + + static $e; + if (!isset($e)) { + $e = new Math_BigInteger(CRYPT_RSA_EXPONENT); + } + + extract($this->_generateMinMax($bits)); + $absoluteMin = $min; + $temp = $bits >> 1; // divide by two to see how many bits P and Q would be + if ($temp > CRYPT_RSA_SMALLEST_PRIME) { + $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME); + $temp = CRYPT_RSA_SMALLEST_PRIME; + } else { + $num_primes = 2; + } + extract($this->_generateMinMax($temp + $bits % $temp)); + $finalMax = $max; + extract($this->_generateMinMax($temp)); + + $generator = new Math_BigInteger(); + + $n = $this->one->copy(); + if (!empty($partial)) { + extract(unserialize($partial)); + } else { + $exponents = $coefficients = $primes = array(); + $lcm = array( + 'top' => $this->one->copy(), + 'bottom' => false + ); + } + + $start = time(); + $i0 = count($primes) + 1; + + do { + for ($i = $i0; $i <= $num_primes; $i++) { + if ($timeout !== false) { + $timeout-= time() - $start; + $start = time(); + if ($timeout <= 0) { + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )) + ); + } + } + + if ($i == $num_primes) { + list($min, $temp) = $absoluteMin->divide($n); + if (!$temp->equals($this->zero)) { + $min = $min->add($this->one); // ie. ceil() + } + $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); + } else { + $primes[$i] = $generator->randomPrime($min, $max, $timeout); + } + + if ($primes[$i] === false) { // if we've reached the timeout + if (count($primes) > 1) { + $partialkey = ''; + } else { + array_pop($primes); + $partialkey = serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )); + } + + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => $partialkey + ); + } + + // the first coefficient is calculated differently from the rest + // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) + if ($i > 2) { + $coefficients[$i] = $n->modInverse($primes[$i]); + } + + $n = $n->multiply($primes[$i]); + + $temp = $primes[$i]->subtract($this->one); + + // textbook RSA implementations use Euler's totient function instead of the least common multiple. + // see http://en.wikipedia.org/wiki/Euler%27s_totient_function + $lcm['top'] = $lcm['top']->multiply($temp); + $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); + + $exponents[$i] = $e->modInverse($temp); + } + + list($lcm) = $lcm['top']->divide($lcm['bottom']); + $gcd = $lcm->gcd($e); + $i0 = 1; + } while (!$gcd->equals($this->one)); + + $d = $e->modInverse($lcm); + + $coefficients[2] = $primes[2]->modInverse($primes[1]); + + // from : + // RSAPrivateKey ::= SEQUENCE { + // version Version, + // modulus INTEGER, -- n + // publicExponent INTEGER, -- e + // privateExponent INTEGER, -- d + // prime1 INTEGER, -- p + // prime2 INTEGER, -- q + // exponent1 INTEGER, -- d mod (p-1) + // exponent2 INTEGER, -- d mod (q-1) + // coefficient INTEGER, -- (inverse of q) mod p + // otherPrimeInfos OtherPrimeInfos OPTIONAL + // } + + return array( + 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), + 'publickey' => $this->_convertPublicKey($n, $e), + 'partialkey' => false + ); + } + + /** + * Convert a private key to the appropriate format. + * + * @access private + * @see setPrivateKeyFormat() + * @param String $RSAPrivateKey + * @return String + */ + function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true), + 'privateExponent' => $d->toBytes(true), + 'prime1' => $primes[1]->toBytes(true), + 'prime2' => $primes[2]->toBytes(true), + 'exponent1' => $exponents[1]->toBytes(true), + 'exponent2' => $exponents[2]->toBytes(true), + 'coefficient' => $coefficients[2]->toBytes(true) + ); + + // if the format in question does not support multi-prime rsa and multi-prime rsa was used, + // call _convertPublicKey() instead. + switch ($this->privateKeyFormat) { + case CRYPT_RSA_PRIVATE_FORMAT_XML: + if ($num_primes != 2) { + return false; + } + return "\r\n" . + ' ' . base64_encode($raw['modulus']) . "\r\n" . + ' ' . base64_encode($raw['publicExponent']) . "\r\n" . + '

' . base64_encode($raw['prime1']) . "

\r\n" . + ' ' . base64_encode($raw['prime2']) . "\r\n" . + ' ' . base64_encode($raw['exponent1']) . "\r\n" . + ' ' . base64_encode($raw['exponent2']) . "\r\n" . + ' ' . base64_encode($raw['coefficient']) . "\r\n" . + ' ' . base64_encode($raw['privateExponent']) . "\r\n" . + '
'; + break; + case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: + if ($num_primes != 2) { + return false; + } + $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; + $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; + $key.= $encryption; + $key.= "\r\nComment: " . CRYPT_RSA_COMMENT . "\r\n"; + $public = pack('Na*Na*Na*', + strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus'] + ); + $source = pack('Na*Na*Na*Na*', + strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption, + strlen(CRYPT_RSA_COMMENT), CRYPT_RSA_COMMENT, strlen($public), $public + ); + $public = base64_encode($public); + $key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n"; + $key.= chunk_split($public, 64); + $private = pack('Na*Na*Na*Na*', + strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], + strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient'] + ); + if (empty($this->password) && !is_string($this->password)) { + $source.= pack('Na*', strlen($private), $private); + $hashkey = 'putty-private-key-file-mac-key'; + } else { + $private.= crypt_random_string(16 - (strlen($private) & 15)); + $source.= pack('Na*', strlen($private), $private); + if (!class_exists('Crypt_AES')) { + require_once('Crypt/AES.php'); + } + $sequence = 0; + $symkey = ''; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new Crypt_AES(); + + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $hashkey = 'putty-private-key-file-mac-key' . $this->password; + } + + $private = base64_encode($private); + $key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n"; + $key.= chunk_split($private, 64); + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $hash = new Crypt_Hash('sha1'); + $hash->setKey(pack('H*', sha1($hashkey))); + $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; + + return $key; + default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1 + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if (!empty($this->password) || is_string($this->password)) { + $iv = crypt_random_string(8); + $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key + $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } + $des = new Crypt_TripleDES(); + $des->setKey($symkey); + $des->setIV($iv); + $iv = strtoupper(bin2hex($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: DES-EDE3-CBC,$iv\r\n" . + "\r\n" . + chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + } + + /** + * Convert a public key to the appropriate format + * + * @access private + * @see setPublicKeyFormat() + * @param String $RSAPrivateKey + * @return String + */ + function _convertPublicKey($n, $e) + { + $modulus = $n->toBytes(true); + $publicExponent = $e->toBytes(true); + + switch ($this->publicKeyFormat) { + case CRYPT_RSA_PUBLIC_FORMAT_RAW: + return array('e' => $e->copy(), 'n' => $n->copy()); + case CRYPT_RSA_PUBLIC_FORMAT_XML: + return "\r\n" . + ' ' . base64_encode($modulus) . "\r\n" . + ' ' . base64_encode($publicExponent) . "\r\n" . + ''; + break; + case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: + // from : + // string "ssh-rsa" + // mpint e + // mpint n + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT; + + return $RSAPublicKey; + default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1 + // from : + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack('Ca*a*a*', + CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], $components['publicExponent'] + ); + + if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack('Ca*a*', + CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey + ); + } + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + + return $RSAPublicKey; + } + } + + /** + * Break a public or private key down into its constituant components + * + * @access private + * @see _convertPublicKey() + * @see _convertPrivateKey() + * @param String $key + * @param Integer $type + * @return Array + */ + function _parseKey($key, $type) + { + if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) { + return false; + } + + switch ($type) { + case CRYPT_RSA_PUBLIC_FORMAT_RAW: + if (!is_array($key)) { + return false; + } + $components = array(); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']->copy(); + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']->copy(); + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']->copy(); + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]->copy(); + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']->copy(); + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']->copy(); + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']->copy(); + break; + case isset($key[1]): + $components['modulus'] = $key[1]->copy(); + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + case CRYPT_RSA_PRIVATE_FORMAT_PKCS1: + case CRYPT_RSA_PUBLIC_FORMAT_PKCS1: + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = pack('H*', trim($matches[2])); + $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key + $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key); + $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false; + if ($ciphertext === false) { + $ciphertext = $key; + } + switch ($matches[1]) { + case 'AES-128-CBC': + if (!class_exists('Crypt_AES')) { + require_once('Crypt/AES.php'); + } + $symkey = substr($symkey, 0, 16); + $crypto = new Crypt_AES(); + break; + case 'DES-EDE3-CFB': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } + $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB); + break; + case 'DES-EDE3-CBC': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } + $crypto = new Crypt_TripleDES(); + break; + case 'DES-CBC': + if (!class_exists('Crypt_DES')) { + require_once('Crypt/DES.php'); + } + $crypto = new Crypt_DES(); + break; + default: + return false; + } + $crypto->setKey($symkey); + $crypto->setIV($iv); + $decoded = $crypto->decrypt($ciphertext); + } else { + $decoded = preg_replace('#-.+-|[\r\n]| #', '', $key); + $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false; + } + + if ($decoded !== false) { + $key = $decoded; + } + + $components = array(); + + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord($this->_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING */ + + if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + $this->_string_shift($key, 3); + $tag = CRYPT_RSA_ASN1_SEQUENCE; + } + + if ($tag == CRYPT_RSA_ASN1_SEQUENCE) { + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $this->_string_shift($key, $this->_decodeLength($key)); + $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of + // unused bits in the final subsequent octet. The number shall be in the range zero to seven." + // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) + if ($tag == CRYPT_RSA_ASN1_BITSTRING) { + $this->_string_shift($key); + } + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord($this->_string_shift($key)); + } + if ($tag != CRYPT_RSA_ASN1_INTEGER) { + return false; + } + + $length = $this->_decodeLength($key); + $temp = $this->_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new Math_BigInteger($temp, 256); + $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER + $length = $this->_decodeLength($key); + $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256); + + return $components; + } + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) { + return false; + } + $length = $this->_decodeLength($key); + $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + while (!empty($key)) { + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + $key = substr($key, 1); + $length = $this->_decodeLength($key); + $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256); + } + } + + return $components; + case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: + $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key)); + if ($key === false) { + return false; + } + + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; + + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256); + + if ($cleanup && strlen($key)) { + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256); + return strlen($key) ? false : array( + 'modulus' => $realModulus, + 'publicExponent' => $modulus + ); + } else { + return strlen($key) ? false : array( + 'modulus' => $modulus, + 'publicExponent' => $publicExponent + ); + } + // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue + // http://en.wikipedia.org/wiki/XML_Signature + case CRYPT_RSA_PRIVATE_FORMAT_XML: + case CRYPT_RSA_PUBLIC_FORMAT_XML: + $this->components = array(); + + $xml = xml_parser_create('UTF-8'); + xml_set_object($xml, $this); + xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); + xml_set_character_data_handler($xml, '_data_handler'); + // add to account for "dangling" tags like ... that are sometimes added + if (!xml_parse($xml, '' . $key . '')) { + return false; + } + + return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; + // from PuTTY's SSHPUBK.C + case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: + $components = array(); + $key = preg_split('#\r\n|\r|\n#', $key); + $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256); + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); + $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); + + switch ($encryption) { + case 'aes256-cbc': + if (!class_exists('Crypt_AES')) { + require_once('Crypt/AES.php'); + } + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new Crypt_AES(); + } + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256)); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256)); + + return $components; + } + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return Integer + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + + /** + * Start Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param Resource $parser + * @param String $name + * @param Array $attribs + */ + function _start_element_handler($parser, $name, $attribs) + { + //$name = strtoupper($name); + switch ($name) { + case 'MODULUS': + $this->current = &$this->components['modulus']; + break; + case 'EXPONENT': + $this->current = &$this->components['publicExponent']; + break; + case 'P': + $this->current = &$this->components['primes'][1]; + break; + case 'Q': + $this->current = &$this->components['primes'][2]; + break; + case 'DP': + $this->current = &$this->components['exponents'][1]; + break; + case 'DQ': + $this->current = &$this->components['exponents'][2]; + break; + case 'INVERSEQ': + $this->current = &$this->components['coefficients'][2]; + break; + case 'D': + $this->current = &$this->components['privateExponent']; + break; + default: + unset($this->current); + } + $this->current = ''; + } + + /** + * Stop Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param Resource $parser + * @param String $name + */ + function _stop_element_handler($parser, $name) + { + //$name = strtoupper($name); + if ($name == 'RSAKEYVALUE') { + return; + } + $this->current = new Math_BigInteger(base64_decode($this->current), 256); + } + + /** + * Data Handler + * + * Called by xml_set_character_data_handler() + * + * @access private + * @param Resource $parser + * @param String $data + */ + function _data_handler($parser, $data) + { + if (!isset($this->current) || is_object($this->current)) { + return; + } + $this->current.= trim($data); + } + + /** + * Loads a public or private key + * + * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) + * + * @access public + * @param String $key + * @param Integer $type optional + */ + function loadKey($key, $type = false) + { + if ($type === false) { + $types = array( + CRYPT_RSA_PUBLIC_FORMAT_RAW, + CRYPT_RSA_PRIVATE_FORMAT_PKCS1, + CRYPT_RSA_PRIVATE_FORMAT_XML, + CRYPT_RSA_PRIVATE_FORMAT_PUTTY, + CRYPT_RSA_PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + return false; + } + + $this->modulus = $components['modulus']; + $this->k = strlen($this->modulus->toBytes()); + $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; + if (isset($components['primes'])) { + $this->primes = $components['primes']; + $this->exponents = $components['exponents']; + $this->coefficients = $components['coefficients']; + $this->publicExponent = $components['publicExponent']; + } else { + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + $this->publicExponent = false; + } + + return true; + } + + /** + * Sets the password + * + * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. + * Or rather, pass in $password such that empty($password) && !is_string($password) is true. + * + * @see createKey() + * @see loadKey() + * @access public + * @param String $password + */ + function setPassword($password = false) + { + $this->password = $password; + } + + /** + * Defines the public key + * + * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when + * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a + * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys + * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public + * exponent this won't work unless you manually add the public exponent. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see getPublicKey() + * @access public + * @param String $key optional + * @param Integer $type optional + * @return Boolean + */ + function setPublicKey($key = false, $type = false) + { + if ($key === false && !empty($this->modulus)) { + $this->publicExponent = $this->exponent; + return true; + } + + if ($type === false) { + $types = array( + CRYPT_RSA_PUBLIC_FORMAT_RAW, + CRYPT_RSA_PUBLIC_FORMAT_PKCS1, + CRYPT_RSA_PUBLIC_FORMAT_XML, + CRYPT_RSA_PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + return false; + } + + if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { + $this->modulus = $components['modulus']; + $this->exponent = $this->publicExponent = $components['publicExponent']; + return true; + } + + $this->publicExponent = $components['publicExponent']; + + return true; + } + + /** + * Returns the public key + * + * The public key is only returned under two circumstances - if the private key had the public key embedded within it + * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this + * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. + * + * @see getPublicKey() + * @access public + * @param String $key + * @param Integer $type optional + */ + function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + { + if (empty($this->modulus) || empty($this->publicExponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $type; + $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see getPublicKey() + * @access public + * @param String $key + * @param Integer $type optional + */ + function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + { + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); + $this->privateKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns a minimalistic private key + * + * Returns the private key without the prime number constituants. Structurally identical to a public key that + * hasn't been set as the public key + * + * @see getPrivateKey() + * @access private + * @param String $key + * @param Integer $type optional + */ + function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $mode; + $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * __toString() magic method + * + * @access public + */ + function __toString() + { + $key = $this->getPrivateKey($this->privateKeyFormat); + if ($key !== false) { + return $key; + } + $key = $this->_getPrivatePublicKey($this->publicKeyFormat); + return $key !== false ? $key : ''; + } + + /** + * Generates the smallest and largest numbers requiring $bits bits + * + * @access private + * @param Integer $bits + * @return Array + */ + function _generateMinMax($bits) + { + $bytes = $bits >> 3; + $min = str_repeat(chr(0), $bytes); + $max = str_repeat(chr(0xFF), $bytes); + $msb = $bits & 7; + if ($msb) { + $min = chr(1 << ($msb - 1)) . $min; + $max = chr((1 << $msb) - 1) . $max; + } else { + $min[0] = chr(0x80); + } + + return array( + 'min' => new Math_BigInteger($min, 256), + 'max' => new Math_BigInteger($max, 256) + ); + } + + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information. + * + * @access private + * @param String $string + * @return Integer + */ + function _decodeLength(&$string) + { + $length = ord($this->_string_shift($string)); + if ( $length & 0x80 ) { // definite length, long form + $length&= 0x7F; + $temp = $this->_string_shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information. + * + * @access private + * @param Integer $length + * @return String + */ + function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Determines the private key format + * + * @see createKey() + * @access public + * @param Integer $format + */ + function setPrivateKeyFormat($format) + { + $this->privateKeyFormat = $format; + } + + /** + * Determines the public key format + * + * @see createKey() + * @access public + * @param Integer $format + */ + function setPublicKeyFormat($format) + { + $this->publicKeyFormat = $format; + } + + /** + * Determines which hashing function should be used + * + * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and + * decryption. If $hash isn't supported, sha1 is used. + * + * @access public + * @param String $hash + */ + function setHash($hash) + { + // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = new Crypt_Hash($hash); + $this->hashName = $hash; + break; + default: + $this->hash = new Crypt_Hash('sha1'); + $this->hashName = 'sha1'; + } + $this->hLen = $this->hash->getLength(); + } + + /** + * Determines which hashing function should be used for the mask generation function + * + * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's + * best if Hash and MGFHash are set to the same thing this is not a requirement. + * + * @access public + * @param String $hash + */ + function setMGFHash($hash) + { + // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->mgfHash = new Crypt_Hash($hash); + break; + default: + $this->mgfHash = new Crypt_Hash('sha1'); + } + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Determines the salt length + * + * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: + * + * Typical salt lengths in octets are hLen (the length of the output + * of the hash function Hash) and 0. + * + * @access public + * @param Integer $format + */ + function setSaltLength($sLen) + { + $this->sLen = $sLen; + } + + /** + * Integer-to-Octet-String primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. + * + * @access private + * @param Math_BigInteger $x + * @param Integer $xLen + * @return String + */ + function _i2osp($x, $xLen) + { + $x = $x->toBytes(); + if (strlen($x) > $xLen) { + user_error('Integer too large'); + return false; + } + return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); + } + + /** + * Octet-String-to-Integer primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. + * + * @access private + * @param String $x + * @return Math_BigInteger + */ + function _os2ip($x) + { + return new Math_BigInteger($x, 256); + } + + /** + * Exponentiate with or without Chinese Remainder Theorem + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. + * + * @access private + * @param Math_BigInteger $x + * @return Math_BigInteger + */ + function _exponentiate($x) + { + if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) { + return $x->modPow($this->exponent, $this->modulus); + } + + $num_primes = count($this->primes); + + if (defined('CRYPT_RSA_DISABLE_BLINDING')) { + $m_i = array( + 1 => $x->modPow($this->exponents[1], $this->primes[1]), + 2 => $x->modPow($this->exponents[2], $this->primes[2]) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } else { + $smallest = $this->primes[1]; + for ($i = 2; $i <= $num_primes; $i++) { + if ($smallest->compare($this->primes[$i]) > 0) { + $smallest = $this->primes[$i]; + } + } + + $one = new Math_BigInteger(1); + + $r = $one->random($one, $smallest->subtract($one)); + + $m_i = array( + 1 => $this->_blind($x, $r, 1), + 2 => $this->_blind($x, $r, 2) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $this->_blind($x, $r, $i); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } + + return $m; + } + + /** + * Performs RSA Blinding + * + * Protects against timing attacks by employing RSA Blinding. + * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) + * + * @access private + * @param Math_BigInteger $x + * @param Math_BigInteger $r + * @param Integer $i + * @return Math_BigInteger + */ + function _blind($x, $r, $i) + { + $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); + $x = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->modInverse($this->primes[$i]); + $x = $x->multiply($r); + list(, $x) = $x->divide($this->primes[$i]); + + return $x; + } + + /** + * Performs blinded RSA equality testing + * + * Protects against a particular type of timing attack described. + * + * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Dont use MessageDigest.isEquals)} + * + * Thanks for the heads up singpolyma! + * + * @access private + * @param String $x + * @param String $y + * @return Boolean + */ + function _equals($x, $y) + { + if (strlen($x) != strlen($y)) { + return false; + } + + $result = 0; + for ($i = 0; $i < strlen($x); $i++) { + $result |= ord($x[$i]) ^ ord($y[$i]); + } + + return $result == 0; + } + + /** + * RSAEP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. + * + * @access private + * @param Math_BigInteger $m + * @return Math_BigInteger + */ + function _rsaep($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSADP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. + * + * @access private + * @param Math_BigInteger $c + * @return Math_BigInteger + */ + function _rsadp($c) + { + if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { + user_error('Ciphertext representative out of range'); + return false; + } + return $this->_exponentiate($c); + } + + /** + * RSASP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. + * + * @access private + * @param Math_BigInteger $m + * @return Math_BigInteger + */ + function _rsasp1($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSAVP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. + * + * @access private + * @param Math_BigInteger $s + * @return Math_BigInteger + */ + function _rsavp1($s) + { + if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { + user_error('Signature representative out of range'); + return false; + } + return $this->_exponentiate($s); + } + + /** + * MGF1 + * + * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. + * + * @access private + * @param String $mgfSeed + * @param Integer $mgfLen + * @return String + */ + function _mgf1($mgfSeed, $maskLen) + { + // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. + + $t = ''; + $count = ceil($maskLen / $this->mgfHLen); + for ($i = 0; $i < $count; $i++) { + $c = pack('N', $i); + $t.= $this->mgfHash->hash($mgfSeed . $c); + } + + return substr($t, 0, $maskLen); + } + + /** + * RSAES-OAEP-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and + * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. + * + * @access private + * @param String $m + * @param String $l + * @return String + */ + function _rsaes_oaep_encrypt($m, $l = '') + { + $mLen = strlen($m); + + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if ($mLen > $this->k - 2 * $this->hLen - 2) { + user_error('Message too long'); + return false; + } + + // EME-OAEP encoding + + $lHash = $this->hash->hash($l); + $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); + $db = $lHash . $ps . chr(1) . $m; + $seed = crypt_random_string($this->hLen); + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $maskedSeed = $seed ^ $seedMask; + $em = chr(0) . $maskedSeed . $maskedDB; + + // RSA encryption + + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-OAEP-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error + * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: + * + * Note. Care must be taken to ensure that an opponent cannot + * distinguish the different error conditions in Step 3.g, whether by + * error message or timing, or, more generally, learn partial + * information about the encoded message EM. Otherwise an opponent may + * be able to obtain useful information about the decryption of the + * ciphertext C, leading to a chosen-ciphertext attack such as the one + * observed by Manger [36]. + * + * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: + * + * Both the encryption and the decryption operations of RSAES-OAEP take + * the value of a label L as input. In this version of PKCS #1, L is + * the empty string; other uses of the label are outside the scope of + * this document. + * + * @access private + * @param String $c + * @param String $l + * @return String + */ + function _rsaes_oaep_decrypt($c, $l = '') + { + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-OAEP decoding + + $lHash = $this->hash->hash($l); + $y = ord($em[0]); + $maskedSeed = substr($em, 1, $this->hLen); + $maskedDB = substr($em, $this->hLen + 1); + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $seed = $maskedSeed ^ $seedMask; + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $lHash2 = substr($db, 0, $this->hLen); + $m = substr($db, $this->hLen); + if ($lHash != $lHash2) { + user_error('Decryption error'); + return false; + } + $m = ltrim($m, chr(0)); + if (ord($m[0]) != 1) { + user_error('Decryption error'); + return false; + } + + // Output the message M + + return substr($m, 1); + } + + /** + * RSAES-PKCS1-V1_5-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. + * + * @access private + * @param String $m + * @return String + */ + function _rsaes_pkcs1_v1_5_encrypt($m) + { + $mLen = strlen($m); + + // Length checking + + if ($mLen > $this->k - 11) { + user_error('Message too long'); + return false; + } + + // EME-PKCS1-v1_5 encoding + $psLen = $this->k - $mLen - 3; + $ps = ''; + while (strlen($ps) != $psLen) { + $temp = crypt_random_string($psLen - strlen($ps)); + $temp = str_replace("\x00", '', $temp); + $ps.= $temp; + } + $em = chr(0) . chr(2) . $ps . chr(0) . $m; + + // RSA encryption + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-PKCS1-V1_5-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. + * + * For compatability purposes, this function departs slightly from the description given in RFC3447. + * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the + * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the + * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed + * to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the + * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. + * + * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt + * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but + * not private key encrypted ciphertext's. + * + * @access private + * @param String $c + * @return String + */ + function _rsaes_pkcs1_v1_5_decrypt($c) + { + // Length checking + + if (strlen($c) != $this->k) { // or if k < 11 + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-PKCS1-v1_5 decoding + + if (ord($em[0]) != 0 || ord($em[1]) > 2) { + user_error('Decryption error'); + return false; + } + + $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); + $m = substr($em, strlen($ps) + 3); + + if (strlen($ps) < 8) { + user_error('Decryption error'); + return false; + } + + // Output M + + return $m; + } + + /** + * EMSA-PSS-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. + * + * @access private + * @param String $m + * @param Integer $emBits + */ + function _emsa_pss_encode($m, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) + $sLen = $this->sLen == false ? $this->hLen : $this->sLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + user_error('Encoding error'); + return false; + } + + $salt = crypt_random_string($sLen); + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h = $this->hash->hash($m2); + $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); + $db = $ps . chr(1) . $salt; + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; + $em = $maskedDB . $h . chr(0xBC); + + return $em; + } + + /** + * EMSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. + * + * @access private + * @param String $m + * @param String $em + * @param Integer $emBits + * @return String + */ + function _emsa_pss_verify($m, $em, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); + $sLen = $this->sLen == false ? $this->hLen : $this->sLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + return false; + } + + if ($em[strlen($em) - 1] != chr(0xBC)) { + return false; + } + + $maskedDB = substr($em, 0, -$this->hLen - 1); + $h = substr($em, -$this->hLen - 1, $this->hLen); + $temp = chr(0xFF << ($emBits & 7)); + if ((~$maskedDB[0] & $temp) != $temp) { + return false; + } + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; + $temp = $emLen - $this->hLen - $sLen - 2; + if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { + return false; + } + $salt = substr($db, $temp + 1); // should be $sLen long + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h2 = $this->hash->hash($m2); + return $this->_equals($h, $h2); + } + + /** + * RSASSA-PSS-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. + * + * @access private + * @param String $m + * @return String + */ + function _rsassa_pss_sign($m) + { + // EMSA-PSS encoding + + $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1); + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. + * + * @access private + * @param String $m + * @param String $s + * @return String + */ + function _rsassa_pss_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $modBits = 8 * $this->k; + + $s2 = $this->_os2ip($s); + $m2 = $this->_rsavp1($s2); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $modBits >> 3); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PSS verification + + return $this->_emsa_pss_verify($m, $em, $modBits - 1); + } + + /** + * EMSA-PKCS1-V1_5-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. + * + * @access private + * @param String $m + * @param Integer $emLen + * @return String + */ + function _emsa_pkcs1_v1_5_encode($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + // see http://tools.ietf.org/html/rfc3447#page-43 + switch ($this->hashName) { + case 'md2': + $t = pack('H*', '3020300c06082a864886f70d020205000410'); + break; + case 'md5': + $t = pack('H*', '3020300c06082a864886f70d020505000410'); + break; + case 'sha1': + $t = pack('H*', '3021300906052b0e03021a05000414'); + break; + case 'sha256': + $t = pack('H*', '3031300d060960864801650304020105000420'); + break; + case 'sha384': + $t = pack('H*', '3041300d060960864801650304020205000430'); + break; + case 'sha512': + $t = pack('H*', '3051300d060960864801650304020305000440'); + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + + /** + * RSASSA-PKCS1-V1_5-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. + * + * @access private + * @param String $m + * @return String + */ + function _rsassa_pkcs1_v1_5_sign($m) + { + // EMSA-PKCS1-v1_5 encoding + + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em === false) { + user_error('RSA modulus too short'); + return false; + } + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PKCS1-V1_5-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. + * + * @access private + * @param String $m + * @return String + */ + function _rsassa_pkcs1_v1_5_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $s = $this->_os2ip($s); + $m2 = $this->_rsavp1($s); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PKCS1-v1_5 encoding + + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em2 === false) { + user_error('RSA modulus too short'); + return false; + } + + // Compare + return $this->_equals($em, $em2); + } + + /** + * Set Encryption Mode + * + * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1. + * + * @access public + * @param Integer $mode + */ + function setEncryptionMode($mode) + { + $this->encryptionMode = $mode; + } + + /** + * Set Signature Mode + * + * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1 + * + * @access public + * @param Integer $mode + */ + function setSignatureMode($mode) + { + $this->signatureMode = $mode; + } + + /** + * Encryption + * + * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. + * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will + * be concatenated together. + * + * @see decrypt() + * @access public + * @param String $plaintext + * @return String + */ + function encrypt($plaintext) + { + switch ($this->encryptionMode) { + case CRYPT_RSA_ENCRYPTION_PKCS1: + $length = $this->k - 11; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); + } + return $ciphertext; + //case CRYPT_RSA_ENCRYPTION_OAEP: + default: + $length = $this->k - 2 * $this->hLen - 2; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_oaep_encrypt($m); + } + return $ciphertext; + } + } + + /** + * Decryption + * + * @see encrypt() + * @access public + * @param String $plaintext + * @return String + */ + function decrypt($ciphertext) + { + if ($this->k <= 0) { + return false; + } + + $ciphertext = str_split($ciphertext, $this->k); + $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); + + $plaintext = ''; + + switch ($this->encryptionMode) { + case CRYPT_RSA_ENCRYPTION_PKCS1: + $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; + break; + //case CRYPT_RSA_ENCRYPTION_OAEP: + default: + $decrypt = '_rsaes_oaep_decrypt'; + } + + foreach ($ciphertext as $c) { + $temp = $this->$decrypt($c); + if ($temp === false) { + return false; + } + $plaintext.= $temp; + } + + return $plaintext; + } + + /** + * Create a signature + * + * @see verify() + * @access public + * @param String $message + * @return String + */ + function sign($message) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case CRYPT_RSA_SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_sign($message); + //case CRYPT_RSA_SIGNATURE_PSS: + default: + return $this->_rsassa_pss_sign($message); + } + } + + /** + * Verifies a signature + * + * @see sign() + * @access public + * @param String $message + * @param String $signature + * @return Boolean + */ + function verify($message, $signature) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case CRYPT_RSA_SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); + //case CRYPT_RSA_SIGNATURE_PSS: + default: + return $this->_rsassa_pss_verify($message, $signature); + } + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php new file mode 100644 index 0000000000000000000000000000000000000000..a60857df95ddf12870d91934822cb4aaad3cd330 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php @@ -0,0 +1,243 @@ + + * + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_Random + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Generate a random string. + * + * Although microoptimizations are generally discouraged as they impair readability this function is ripe with + * microoptimizations because this function has the potential of being called a huge number of times. + * eg. for RSA key generation. + * + * @param Integer $length + * @return String + * @access public + */ +function crypt_random_string($length) { + // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster + if ((PHP_OS & "\xDF\xDF\xDF") === 'WIN') { + // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. + // ie. class_alias is a function that was introduced in PHP 5.3 + if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) { + return mcrypt_create_iv($length); + } + // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, + // to quote , "possible blocking behavior". as of 5.3.4 + // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both + // call php_win32_get_random_bytes(): + // + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008 + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392 + // + // php_win32_get_random_bytes() is defined thusly: + // + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80 + // + // we're calling it, all the same, in the off chance that the mcrypt extension is not available + if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4', '>=')) { + return openssl_random_pseudo_bytes($length); + } + } else { + // method 1. the fastest + if (function_exists('openssl_random_pseudo_bytes')) { + return openssl_random_pseudo_bytes($length); + } + // method 2 + static $fp = true; + if ($fp === true) { + // warning's will be output unles the error suppression operator is used. errors such as + // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc. + $fp = @fopen('/dev/urandom', 'rb'); + } + if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() + return fread($fp, $length); + } + // method 3. pretty much does the same thing as method 2 per the following url: + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 + // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're + // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir + // restrictions or some such + if (function_exists('mcrypt_create_iv')) { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } + } + // at this point we have no choice but to use a pure-PHP CSPRNG + + // cascade entropy across multiple PHP instances by fixing the session and collecting all + // environmental variables, including the previous session data and the current session + // data. + // + // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) + // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but + // PHP isn't low level to be able to use those as sources and on a web server there's not likely + // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use + // however. a ton of people visiting the website. obviously you don't want to base your seeding + // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled + // by the user and (2) this isn't just looking at the data sent by the current user - it's based + // on the data sent by all users. one user requests the page and a hash of their info is saved. + // another user visits the page and the serialization of their data is utilized along with the + // server envirnment stuff and a hash of the previous http request data (which itself utilizes + // a hash of the session data before that). certainly an attacker should be assumed to have + // full control over his own http requests. he, however, is not going to have control over + // everyone's http requests. + static $crypto = false, $v; + if ($crypto === false) { + // save old session data + $old_session_id = session_id(); + $old_use_cookies = ini_get('session.use_cookies'); + $old_session_cache_limiter = session_cache_limiter(); + if (isset($_SESSION)) { + $_OLD_SESSION = $_SESSION; + } + if ($old_session_id != '') { + session_write_close(); + } + + session_id(1); + ini_set('session.use_cookies', 0); + session_cache_limiter(''); + session_start(); + + $v = $seed = $_SESSION['seed'] = pack('H*', sha1( + serialize($_SERVER) . + serialize($_POST) . + serialize($_GET) . + serialize($_COOKIE) . + serialize($_GLOBAL) . + serialize($_SESSION) . + serialize($_OLD_SESSION) + )); + if (!isset($_SESSION['count'])) { + $_SESSION['count'] = 0; + } + $_SESSION['count']++; + + session_write_close(); + + // restore old session data + if ($old_session_id != '') { + session_id($old_session_id); + session_start(); + ini_set('session.use_cookies', $old_use_cookies); + session_cache_limiter($old_session_cache_limiter); + } else { + if (isset($_OLD_SESSION)) { + $_SESSION = $_OLD_SESSION; + unset($_OLD_SESSION); + } else { + unset($_SESSION); + } + } + + // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. + // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. + // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the + // original hash and the current hash. we'll be emulating that. for more info see the following URL: + // + // http://tools.ietf.org/html/rfc4253#section-7.2 + // + // see the is_string($crypto) part for an example of how to expand the keys + $key = pack('H*', sha1($seed . 'A')); + $iv = pack('H*', sha1($seed . 'C')); + + // ciphers are used as per the nist.gov link below. also, see this link: + // + // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives + switch (true) { + case class_exists('Crypt_AES'): + $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR); + break; + case class_exists('Crypt_TripleDES'): + $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); + break; + case class_exists('Crypt_DES'): + $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR); + break; + case class_exists('Crypt_RC4'): + $crypto = new Crypt_RC4(); + break; + default: + $crypto = $seed; + return crypt_random_string($length); + } + + $crypto->setKey($key); + $crypto->setIV($iv); + $crypto->enableContinuousBuffer(); + } + + if (is_string($crypto)) { + // the following is based off of ANSI X9.31: + // + // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf + // + // OpenSSL uses that same standard for it's random numbers: + // + // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c + // (do a search for "ANS X9.31 A.2.4") + // + // ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see + // later on in the code) but if they're not we'll use sha1 + $result = ''; + while (strlen($result) < $length) { // each loop adds 20 bytes + // microtime() isn't packed as "densely" as it could be but then neither is that the idea. + // the idea is simply to ensure that each "block" has a unique element to it. + $i = pack('H*', sha1(microtime())); + $r = pack('H*', sha1($i ^ $v)); + $v = pack('H*', sha1($r ^ $i)); + $result.= $r; + } + return substr($result, 0, $length); + } + + //return $crypto->encrypt(str_repeat("\0", $length)); + + $result = ''; + while (strlen($result) < $length) { + $i = $crypto->encrypt(microtime()); + $r = $crypto->encrypt($i ^ $v); + $v = $crypto->encrypt($r ^ $i); + $result.= $r; + } + return substr($result, 0, $length); +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php new file mode 100644 index 0000000000000000000000000000000000000000..335d5233c4d6c537d4cdf72c16a02f5ce6c4f58a --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php @@ -0,0 +1,1525 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rijndael->decrypt($rijndael->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_Rijndael + * @author Jim Wigginton + * @copyright MMVIII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access public + * @see Crypt_Rijndael::encrypt() + * @see Crypt_Rijndael::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_RIJNDAEL_MODE_CTR', -1); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_RIJNDAEL_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_RIJNDAEL_MODE_CBC', 2); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_RIJNDAEL_MODE_CFB', 3); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_RIJNDAEL_MODE_OFB', 4); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_Rijndael::Crypt_Rijndael() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2); +/**#@-*/ + +/** + * Pure-PHP implementation of Rijndael. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_Rijndael + */ +class Crypt_Rijndael { + /** + * The Encryption Mode + * + * @see Crypt_Rijndael::Crypt_Rijndael() + * @var Integer + * @access private + */ + var $mode; + + /** + * The Key + * + * @see Crypt_Rijndael::setKey() + * @var String + * @access private + */ + var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + /** + * The Initialization Vector + * + * @see Crypt_Rijndael::setIV() + * @var String + * @access private + */ + var $iv = ''; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_Rijndael::enableContinuousBuffer() + * @var String + * @access private + */ + var $encryptIV = ''; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_Rijndael::enableContinuousBuffer() + * @var String + * @access private + */ + var $decryptIV = ''; + + /** + * Continuous Buffer status + * + * @see Crypt_Rijndael::enableContinuousBuffer() + * @var Boolean + * @access private + */ + var $continuousBuffer = false; + + /** + * Padding status + * + * @see Crypt_Rijndael::enablePadding() + * @var Boolean + * @access private + */ + var $padding = true; + + /** + * Does the key schedule need to be (re)calculated? + * + * @see setKey() + * @see setBlockLength() + * @see setKeyLength() + * @var Boolean + * @access private + */ + var $changed = true; + + /** + * Has the key length explicitly been set or should it be derived from the key, itself? + * + * @see setKeyLength() + * @var Boolean + * @access private + */ + var $explicit_key_length = false; + + /** + * The Key Schedule + * + * @see _setup() + * @var Array + * @access private + */ + var $w; + + /** + * The Inverse Key Schedule + * + * @see _setup() + * @var Array + * @access private + */ + var $dw; + + /** + * The Block Length + * + * @see setBlockLength() + * @var Integer + * @access private + * @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with + * $Nb because we need this value and not $Nb to pad strings appropriately. + */ + var $block_size = 16; + + /** + * The Block Length divided by 32 + * + * @see setBlockLength() + * @var Integer + * @access private + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size + * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could + * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + * + */ + var $Nb = 4; + + /** + * The Key Length + * + * @see setKeyLength() + * @var Integer + * @access private + * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size + * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could + * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + */ + var $key_size = 16; + + /** + * The Key Length divided by 32 + * + * @see setKeyLength() + * @var Integer + * @access private + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 + */ + var $Nk = 4; + + /** + * The Number of Rounds + * + * @var Integer + * @access private + * @internal The max value is 14, the min value is 10. + */ + var $Nr; + + /** + * Shift offsets + * + * @var Array + * @access private + */ + var $c; + + /** + * Precomputed mixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $t0; + + /** + * Precomputed mixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $t1; + + /** + * Precomputed mixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $t2; + + /** + * Precomputed mixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $t3; + + /** + * Precomputed invMixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $dt0; + + /** + * Precomputed invMixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $dt1; + + /** + * Precomputed invMixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $dt2; + + /** + * Precomputed invMixColumns table + * + * @see Crypt_Rijndael() + * @var Array + * @access private + */ + var $dt3; + + /** + * Is the mode one that is paddable? + * + * @see Crypt_Rijndael::Crypt_Rijndael() + * @var Boolean + * @access private + */ + var $paddable = false; + + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_Rijndael::encrypt() + * @var String + * @access private + */ + var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0); + + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_Rijndael::decrypt() + * @var String + * @access private + */ + var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0); + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_Rijndael + * @access public + */ + function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC) + { + switch ($mode) { + case CRYPT_RIJNDAEL_MODE_ECB: + case CRYPT_RIJNDAEL_MODE_CBC: + $this->paddable = true; + $this->mode = $mode; + break; + case CRYPT_RIJNDAEL_MODE_CTR: + case CRYPT_RIJNDAEL_MODE_CFB: + case CRYPT_RIJNDAEL_MODE_OFB: + $this->mode = $mode; + break; + default: + $this->paddable = true; + $this->mode = CRYPT_RIJNDAEL_MODE_CBC; + } + + $t3 = &$this->t3; + $t2 = &$this->t2; + $t1 = &$this->t1; + $t0 = &$this->t0; + + $dt3 = &$this->dt3; + $dt2 = &$this->dt2; + $dt1 = &$this->dt1; + $dt0 = &$this->dt0; + + // according to (section 5.2.1), + // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + // those are the names we'll use. + $t3 = array( + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + ); + + $dt3 = array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, + 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, + 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, + 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, + 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, + 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, + 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, + 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, + 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, + 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, + 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, + 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, + 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, + 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, + 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, + 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, + 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, + 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + ); + + for ($i = 0; $i < 256; $i++) { + $t2[] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF); + $t1[] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF); + $t0[] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF); + + $dt2[] = (($dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF); + $dt1[] = (($dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF); + $dt0[] = (($dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF); + } + } + + /** + * Sets the key. + * + * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and + * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length + * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the + * excess bits. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * @access public + * @param String $key + */ + function setKey($key) + { + $this->key = $key; + $this->changed = true; + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * @access public + * @param String $iv + */ + function setIV($iv) + { + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0)); + } + + /** + * Sets the key length + * + * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount. + * + * @access public + * @param Integer $length + */ + function setKeyLength($length) + { + $length >>= 5; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; + } + $this->Nk = $length; + $this->key_size = $length << 2; + + $this->explicit_key_length = true; + $this->changed = true; + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * $hash, $salt, $method + * Set $dkLen by calling setKeyLength() + * + * @param String $password + * @param optional String $method + * @access public + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + default: // 'pbkdf2' + list(, , $hash, $salt, $count) = func_get_args(); + if (!isset($hash)) { + $hash = 'sha1'; + } + // WPA and WPA2 use the SSID as the salt + if (!isset($salt)) { + $salt = 'phpseclib'; + } + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + if (!isset($count)) { + $count = 1000; + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + + $i = 1; + while (strlen($key) < $this->key_size) { // $dkLen == $this->key_size + //$dk.= $this->_pbkdf($password, $salt, $count, $i++); + $hmac = new Crypt_Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; $j++) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + } + + $this->setKey(substr($key, 0, $this->key_size)); + } + + /** + * Sets the block length + * + * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount. + * + * @access public + * @param Integer $length + */ + function setBlockLength($length) + { + $length >>= 5; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; + } + $this->Nb = $length; + $this->block_size = $length << 2; + $this->changed = true; + } + + /** + * Generate CTR XOR encryption key + * + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. + * + * @see Crypt_Rijndael::decrypt() + * @see Crypt_Rijndael::encrypt() + * @access public + * @param Integer $length + * @param String $iv + */ + function _generate_xor($length, &$iv) + { + $xor = ''; + $block_size = $this->block_size; + $num_blocks = floor(($length + ($block_size - 1)) / $block_size); + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= $block_size; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + } + + return $xor; + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael + * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's + * necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that + * length. + * + * @see Crypt_Rijndael::decrypt() + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + $this->_setup(); + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + $block_size = $this->block_size; + $buffer = &$this->enbuffer; + $ciphertext = ''; + switch ($this->mode) { + case CRYPT_RIJNDAEL_MODE_ECB: + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); + } + break; + case CRYPT_RIJNDAEL_MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $block = $this->_encryptBlock($block ^ $xor); + $xor = $block; + $ciphertext.= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case CRYPT_RIJNDAEL_MODE_CTR: + $xor = $this->encryptIV; + if (strlen($buffer['encrypted'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $buffer['encrypted'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $key = $this->_string_shift($buffer['encrypted'], $block_size); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $ciphertext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; + } + } + break; + case CRYPT_RIJNDAEL_MODE_CFB: + // cfb loosely routines inspired by openssl's: + // http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1 + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + break; + case CRYPT_RIJNDAEL_MODE_OFB: + $xor = $this->encryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], $block_size); + $ciphertext.= substr($plaintext, $i, $block_size) ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $ciphertext; + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until + * it is. + * + * @see Crypt_Rijndael::encrypt() + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + $this->_setup(); + + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0)); + } + + $block_size = $this->block_size; + $buffer = &$this->debuffer; + $plaintext = ''; + switch ($this->mode) { + case CRYPT_RIJNDAEL_MODE_ECB: + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); + } + break; + case CRYPT_RIJNDAEL_MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $plaintext.= $this->_decryptBlock($block) ^ $xor; + $xor = $block; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case CRYPT_RIJNDAEL_MODE_CTR: + $xor = $this->decryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $buffer['ciphertext'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $key = $this->_string_shift($buffer['ciphertext'], $block_size); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $plaintext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case CRYPT_RIJNDAEL_MODE_CFB: + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv); + $cb = substr($ciphertext, $i, $block_size); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); + $pos = $len; + } + break; + case CRYPT_RIJNDAEL_MODE_OFB: + $xor = $this->decryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], $block_size); + $plaintext.= substr($ciphertext, $i, $block_size) ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + /** + * Encrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + $state = array(); + $words = unpack('N*word', $in); + + $w = $this->w; + $t0 = $this->t0; + $t1 = $this->t1; + $t2 = $this->t2; + $t3 = $this->t3; + $Nb = $this->Nb; + $Nr = $this->Nr; + $c = $this->c; + + // addRoundKey + $i = -1; + foreach ($words as $word) { + $state[] = $word ^ $w[0][++$i]; + } + + // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - + // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding + // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf. + // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization. + // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], + // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well. + + // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf + $temp = array(); + for ($round = 1; $round < $Nr; ++$round) { + $i = 0; // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + + while ($i < $Nb) { + $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^ + $t1[$state[$j] >> 16 & 0x000000FF] ^ + $t2[$state[$k] >> 8 & 0x000000FF] ^ + $t3[$state[$l] & 0x000000FF] ^ + $w[$round][$i]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + + // subWord + for ($i = 0; $i < $Nb; ++$i) { + $state[$i] = $this->_subWord($state[$i]); + } + + // shiftRows + addRoundKey + $i = 0; // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + while ($i < $Nb) { + $temp[$i] = ($state[$i] & 0xFF000000) ^ + ($state[$j] & 0x00FF0000) ^ + ($state[$k] & 0x0000FF00) ^ + ($state[$l] & 0x000000FF) ^ + $w[$Nr][$i]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + + // 100% ugly switch/case code... but ~5% faster (meaning: ~half second faster de/encrypting 1MB text, tested with php5.4.9 on linux/32bit with an AMD Athlon II P360 CPU) then the commented smart code below. Don't know it's worth or not + switch ($Nb) { + case 8: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); + case 7: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); + case 6: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); + case 5: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); + default: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); + } + /* + $state = $temp; + + array_unshift($state, 'N*'); + + return call_user_func_array('pack', $state); + */ + } + + /** + * Decrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + $state = array(); + $words = unpack('N*word', $in); + + $dw = $this->dw; + $dt0 = $this->dt0; + $dt1 = $this->dt1; + $dt2 = $this->dt2; + $dt3 = $this->dt3; + $Nb = $this->Nb; + $Nr = $this->Nr; + $c = $this->c; + + // addRoundKey + $i = -1; + foreach ($words as $word) { + $state[] = $word ^ $dw[$Nr][++$i]; + } + + $temp = array(); + for ($round = $Nr - 1; $round > 0; --$round) { + $i = 0; // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + + while ($i < $Nb) { + $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^ + $dt1[$state[$j] >> 16 & 0x000000FF] ^ + $dt2[$state[$k] >> 8 & 0x000000FF] ^ + $dt3[$state[$l] & 0x000000FF] ^ + $dw[$round][$i]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + + // invShiftRows + invSubWord + addRoundKey + $i = 0; // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + + while ($i < $Nb) { + $temp[$i] = $dw[0][$i] ^ + $this->_invSubWord(($state[$i] & 0xFF000000) | + ($state[$j] & 0x00FF0000) | + ($state[$k] & 0x0000FF00) | + ($state[$l] & 0x000000FF)); + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + + switch ($Nb) { + case 8: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); + case 7: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); + case 6: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); + case 5: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); + default: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); + } + /* + $state = $temp; + + array_unshift($state, 'N*'); + + return call_user_func_array('pack', $state); + */ + } + + /** + * Setup Rijndael + * + * Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key + * key schedule. + * + * @access private + */ + function _setup() + { + // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field. + // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse + static $rcon = array(0, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, + 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000, + 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000, + 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000, + 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 + ); + + if (!$this->changed) { + return; + } + + if (!$this->explicit_key_length) { + // we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits + $length = strlen($this->key) >> 2; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; + } + $this->Nk = $length; + $this->key_size = $length << 2; + } + + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0)); + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0)); + + // see Rijndael-ammended.pdf#page=44 + $this->Nr = max($this->Nk, $this->Nb) + 6; + + // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44, + // "Table 8: Shift offsets in Shiftrow for the alternative block lengths" + // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14, + // "Table 2: Shift offsets for different block lengths" + switch ($this->Nb) { + case 4: + case 5: + case 6: + $this->c = array(0, 1, 2, 3); + break; + case 7: + $this->c = array(0, 1, 2, 4); + break; + case 8: + $this->c = array(0, 1, 3, 4); + } + + $key = $this->key; + + $w = array_values(unpack('N*words', $key)); + + $length = $this->Nb * ($this->Nr + 1); + for ($i = $this->Nk; $i < $length; $i++) { + $temp = $w[$i - 1]; + if ($i % $this->Nk == 0) { + // according to , "the size of an integer is platform-dependent". + // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine, + // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and' + // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. + $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord + $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk]; + } else if ($this->Nk > 6 && $i % $this->Nk == 4) { + $temp = $this->_subWord($temp); + } + $w[$i] = $w[$i - $this->Nk] ^ $temp; + } + + // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns + // and generate the inverse key schedule. more specifically, + // according to (section 5.3.3), + // "The key expansion for the Inverse Cipher is defined as follows: + // 1. Apply the Key Expansion. + // 2. Apply InvMixColumn to all Round Keys except the first and the last one." + // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" + $temp = array(); + for ($i = $row = $col = 0; $i < $length; $i++, $col++) { + if ($col == $this->Nb) { + if ($row == 0) { + $this->dw[0] = $this->w[0]; + } else { + // subWord + invMixColumn + invSubWord = invMixColumn + $j = 0; + while ($j < $this->Nb) { + $dw = $this->_subWord($this->w[$row][$j]); + $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^ + $this->dt1[$dw >> 16 & 0x000000FF] ^ + $this->dt2[$dw >> 8 & 0x000000FF] ^ + $this->dt3[$dw & 0x000000FF]; + $j++; + } + $this->dw[$row] = $temp; + } + + $col = 0; + $row++; + } + $this->w[$row][$col] = $w[$i]; + } + + $this->dw[$row] = $this->w[$row]; + + $this->changed = false; + } + + /** + * Performs S-Box substitutions + * + * @access private + */ + function _subWord($word) + { + static $sbox0, $sbox1, $sbox2, $sbox3; + + if (empty($sbox0)) { + $sbox0 = array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ); + + $sbox1 = array(); + $sbox2 = array(); + $sbox3 = array(); + + for ($i = 0; $i < 256; $i++) { + $sbox1[] = $sbox0[$i] << 8; + $sbox2[] = $sbox0[$i] << 16; + $sbox3[] = $sbox0[$i] << 24; + } + } + + return $sbox0[$word & 0x000000FF] | + $sbox1[$word >> 8 & 0x000000FF] | + $sbox2[$word >> 16 & 0x000000FF] | + $sbox3[$word >> 24 & 0x000000FF]; + } + + + /** + * Performs inverse S-Box substitutions + * + * @access private + */ + function _invSubWord($word) + { + static $sbox0, $sbox1, $sbox2, $sbox3; + + if (empty($sbox0)) { + $sbox0 = array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ); + + $sbox1 = array(); + $sbox2 = array(); + $sbox3 = array(); + + for ($i = 0; $i < 256; $i++) { + $sbox1[] = $sbox0[$i] << 8; + $sbox2[] = $sbox0[$i] << 16; + $sbox3[] = $sbox0[$i] << 24; + } + } + + return $sbox0[$word & 0x000000FF] | + $sbox1[$word >> 8 & 0x000000FF] | + $sbox2[$word >> 16 & 0x000000FF] | + $sbox3[$word >> 24 & 0x000000FF]; + } + + /** + * Pad "packets". + * + * Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple + * of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to + * pad the input so that it is of the proper length. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see Crypt_Rijndael::disablePadding() + * @access public + */ + function enablePadding() + { + $this->padding = true; + } + + /** + * Do not pad packets. + * + * @see Crypt_Rijndael::enablePadding() + * @access public + */ + function disablePadding() + { + $this->padding = false; + } + + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. + * $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to + * chr($block_size - (strlen($text) % $block_size) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see Crypt_Rijndael::_unpad() + * @access private + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if ($length % $this->block_size == 0) { + return $text; + } else { + user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); + $this->padding = true; + } + } + + $pad = $this->block_size - ($length % $this->block_size); + + return str_pad($text, $length + $pad, chr($pad)); + } + + /** + * Unpads a string. + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see Crypt_Rijndael::_pad() + * @access private + */ + function _unpad($text) + { + if (!$this->padding) { + return $text; + } + + $length = ord($text[strlen($text) - 1]); + + if (!$length || $length > $this->block_size) { + return false; + } + + return substr($text, 0, -$length); + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->encrypt(substr($plaintext, 16, 16)); + * + * + * echo $rijndael->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16))); + * + * + * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see Crypt_Rijndael::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + $this->continuousBuffer = true; + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see Crypt_Rijndael::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + $this->continuousBuffer = false; + $this->encryptIV = $this->iv; + $this->decryptIV = $this->iv; + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php new file mode 100644 index 0000000000000000000000000000000000000000..3b4c8c3622fdd4e275b24c50543cf03cd72c6ccd --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php @@ -0,0 +1,1080 @@ + + * setKey('abcdefghijklmnopqrstuvwx'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_TripleDES + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_DES + */ +if (!class_exists('Crypt_DES')) { + require_once('DES.php'); +} + +/** + * Encrypt / decrypt using inner chaining + * + * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3). + */ +define('CRYPT_DES_MODE_3CBC', -2); + +/** + * Encrypt / decrypt using outer chaining + * + * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC. + */ +define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC); + +/** + * Pure-PHP implementation of Triple DES. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_TerraDES + */ +class Crypt_TripleDES { + /** + * The Three Keys + * + * @see Crypt_TripleDES::setKey() + * @var String + * @access private + */ + var $key = "\0\0\0\0\0\0\0\0"; + + /** + * The Encryption Mode + * + * @see Crypt_TripleDES::Crypt_TripleDES() + * @var Integer + * @access private + */ + var $mode = CRYPT_DES_MODE_CBC; + + /** + * Continuous Buffer status + * + * @see Crypt_TripleDES::enableContinuousBuffer() + * @var Boolean + * @access private + */ + var $continuousBuffer = false; + + /** + * Padding status + * + * @see Crypt_TripleDES::enablePadding() + * @var Boolean + * @access private + */ + var $padding = true; + + /** + * The Initialization Vector + * + * @see Crypt_TripleDES::setIV() + * @var String + * @access private + */ + var $iv = "\0\0\0\0\0\0\0\0"; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_TripleDES::enableContinuousBuffer() + * @var String + * @access private + */ + var $encryptIV = "\0\0\0\0\0\0\0\0"; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_TripleDES::enableContinuousBuffer() + * @var String + * @access private + */ + var $decryptIV = "\0\0\0\0\0\0\0\0"; + + /** + * The Crypt_DES objects + * + * @var Array + * @access private + */ + var $des; + + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_TripleDES::encrypt() + * @var String + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_TripleDES::decrypt() + * @var String + * @access private + */ + var $demcrypt; + + /** + * Does the enmcrypt resource need to be (re)initialized? + * + * @see Crypt_TripleDES::setKey() + * @see Crypt_TripleDES::setIV() + * @var Boolean + * @access private + */ + var $enchanged = true; + + /** + * Does the demcrypt resource need to be (re)initialized? + * + * @see Crypt_TripleDES::setKey() + * @see Crypt_TripleDES::setIV() + * @var Boolean + * @access private + */ + var $dechanged = true; + + /** + * Is the mode one that is paddable? + * + * @see Crypt_TripleDES::Crypt_TripleDES() + * @var Boolean + * @access private + */ + var $paddable = false; + + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_TripleDES::encrypt() + * @var Array + * @access private + */ + var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_TripleDES::decrypt() + * @var Array + * @access private + */ + var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + /** + * mcrypt resource for CFB mode + * + * @see Crypt_TripleDES::encrypt() + * @see Crypt_TripleDES::decrypt() + * @var String + * @access private + */ + var $ecb; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_TripleDES + * @access public + */ + function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC) + { + if ( !defined('CRYPT_DES_MODE') ) { + switch (true) { + case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()): + define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT); + break; + default: + define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL); + } + } + + if ( $mode == CRYPT_DES_MODE_3CBC ) { + $this->mode = CRYPT_DES_MODE_3CBC; + $this->des = array( + new Crypt_DES(CRYPT_DES_MODE_CBC), + new Crypt_DES(CRYPT_DES_MODE_CBC), + new Crypt_DES(CRYPT_DES_MODE_CBC) + ); + $this->paddable = true; + + // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects + $this->des[0]->disablePadding(); + $this->des[1]->disablePadding(); + $this->des[2]->disablePadding(); + + return; + } + + switch ( CRYPT_DES_MODE ) { + case CRYPT_DES_MODE_MCRYPT: + switch ($mode) { + case CRYPT_DES_MODE_ECB: + $this->paddable = true; + $this->mode = MCRYPT_MODE_ECB; + break; + case CRYPT_DES_MODE_CTR: + $this->mode = 'ctr'; + break; + case CRYPT_DES_MODE_CFB: + $this->mode = 'ncfb'; + $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); + break; + case CRYPT_DES_MODE_OFB: + $this->mode = MCRYPT_MODE_NOFB; + break; + case CRYPT_DES_MODE_CBC: + default: + $this->paddable = true; + $this->mode = MCRYPT_MODE_CBC; + } + $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); + $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); + + break; + default: + $this->des = array( + new Crypt_DES(CRYPT_DES_MODE_ECB), + new Crypt_DES(CRYPT_DES_MODE_ECB), + new Crypt_DES(CRYPT_DES_MODE_ECB) + ); + + // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects + $this->des[0]->disablePadding(); + $this->des[1]->disablePadding(); + $this->des[2]->disablePadding(); + + switch ($mode) { + case CRYPT_DES_MODE_ECB: + case CRYPT_DES_MODE_CBC: + $this->paddable = true; + $this->mode = $mode; + break; + case CRYPT_DES_MODE_CTR: + case CRYPT_DES_MODE_CFB: + case CRYPT_DES_MODE_OFB: + $this->mode = $mode; + break; + default: + $this->paddable = true; + $this->mode = CRYPT_DES_MODE_CBC; + } + } + } + + /** + * Sets the key. + * + * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or + * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * If the key is not explicitly set, it'll be assumed to be all zero's. + * + * @access public + * @param String $key + */ + function setKey($key) + { + $length = strlen($key); + if ($length > 8) { + $key = str_pad($key, 24, chr(0)); + // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: + // http://php.net/function.mcrypt-encrypt#47973 + //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); + } else { + $key = str_pad($key, 8, chr(0)); + } + $this->key = $key; + switch (true) { + case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL: + case $this->mode == CRYPT_DES_MODE_3CBC: + $this->des[0]->setKey(substr($key, 0, 8)); + $this->des[1]->setKey(substr($key, 8, 8)); + $this->des[2]->setKey(substr($key, 16, 8)); + } + $this->enchanged = $this->dechanged = true; + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * $hash, $salt, $method + * + * @param String $password + * @param optional String $method + * @access public + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + default: // 'pbkdf2' + list(, , $hash, $salt, $count) = func_get_args(); + if (!isset($hash)) { + $hash = 'sha1'; + } + // WPA and WPA2 use the SSID as the salt + if (!isset($salt)) { + $salt = 'phpseclib'; + } + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + if (!isset($count)) { + $count = 1000; + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + + $i = 1; + while (strlen($key) < 24) { // $dkLen == 24 + $hmac = new Crypt_Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; $j++) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + } + + $this->setKey($key); + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * @access public + * @param String $iv + */ + function setIV($iv) + { + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0)); + if ($this->mode == CRYPT_DES_MODE_3CBC) { + $this->des[0]->setIV($iv); + $this->des[1]->setIV($iv); + $this->des[2]->setIV($iv); + } + $this->enchanged = $this->dechanged = true; + } + + /** + * Generate CTR XOR encryption key + * + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. + * + * @see Crypt_TripleDES::decrypt() + * @see Crypt_TripleDES::encrypt() + * @access private + * @param String $iv + */ + function _generate_xor(&$iv) + { + $xor = $iv; + for ($j = 4; $j <= 8; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + + return $xor; + } + + /** + * Encrypts a message. + * + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + // if the key is smaller then 8, do what we'd normally do + if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { + $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext))); + + return $ciphertext; + } + + if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { + if ($this->enchanged) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); + } + $this->enchanged = false; + } + + if ($this->mode != 'ncfb' || !$this->continuousBuffer) { + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); + } else { + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; + } + if ($len >= 8) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8)); + $iv = substr($ciphertext, -8); + $i = strlen($ciphertext); + $len%= 8; + } else { + while ($len >= 8) { + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8); + $ciphertext.= $iv; + $len-= 8; + $i+= 8; + } + } + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + return $ciphertext; + } + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + } + + return $ciphertext; + } + + if (strlen($this->key) <= 8) { + $this->des[0]->mode = $this->mode; + + return $this->des[0]->encrypt($plaintext); + } + + $des = $this->des; + + $buffer = &$this->enbuffer; + $continuousBuffer = $this->continuousBuffer; + $ciphertext = ''; + switch ($this->mode) { + case CRYPT_DES_MODE_ECB: + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something. + // only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that + // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make + // encryption and decryption take more time, per this: + // + // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html + $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); + $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT); + $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); + $ciphertext.= $block; + } + break; + case CRYPT_DES_MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8) ^ $xor; + $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); + $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT); + $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); + $xor = $block; + $ciphertext.= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->encryptIV; + if (strlen($buffer['encrypted'])) { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $key = $this->_generate_xor($xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $buffer['encrypted'].= $key; + $key = $this->_string_shift($buffer['encrypted'], 8); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $key = $this->_generate_xor($xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $ciphertext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) & 7) { + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; + } + } + break; + case CRYPT_DES_MODE_CFB: + if (strlen($buffer['xor'])) { + $ciphertext = $plaintext ^ $buffer['xor']; + $iv = $buffer['encrypted'] . $ciphertext; + $start = strlen($ciphertext); + $buffer['encrypted'].= $ciphertext; + $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); + } else { + $ciphertext = ''; + $iv = $this->encryptIV; + $start = 0; + } + + for ($i = $start; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT); + $iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT); + $xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT); + + $iv = $block ^ $xor; + if ($continuousBuffer && strlen($iv) != 8) { + $buffer = array( + 'encrypted' => $iv, + 'xor' => substr($xor, strlen($iv)) + ); + } + $ciphertext.= $iv; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; + case CRYPT_DES_MODE_OFB: + $xor = $this->encryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); + $ciphertext.= substr($plaintext, $i, 8) ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $ciphertext.= substr($plaintext, $i, 8) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) & 7) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $ciphertext; + } + + /** + * Decrypts a message. + * + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { + $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext))); + + return $this->_unpad($plaintext); + } + + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); + } + + if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { + if ($this->dechanged) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); + } + $this->dechanged = false; + } + + if ($this->mode != 'ncfb' || !$this->continuousBuffer) { + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + } else { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= 8) { + $cb = substr($ciphertext, $i, $len - $len % 8); + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = substr($cb, -8); + $len%= 8; + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $cb = substr($ciphertext, -$len); + $plaintext.= $iv ^ $cb; + $iv = substr_replace($iv, $cb, 0, $len); + $pos = $len; + } + return $plaintext; + } + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if (strlen($this->key) <= 8) { + $this->des[0]->mode = $this->mode; + $plaintext = $this->des[0]->decrypt($ciphertext); + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + $des = $this->des; + + $buffer = &$this->debuffer; + $continuousBuffer = $this->continuousBuffer; + $plaintext = ''; + switch ($this->mode) { + case CRYPT_DES_MODE_ECB: + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT); + $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); + $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT); + $plaintext.= $block; + } + break; + case CRYPT_DES_MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $orig = $block = substr($ciphertext, $i, 8); + $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT); + $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); + $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT); + $plaintext.= $block ^ $xor; + $xor = $orig; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->decryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $key = $this->_generate_xor($xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $buffer['ciphertext'].= $key; + $key = $this->_string_shift($buffer['ciphertext'], 8); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $key = $this->_generate_xor($xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $plaintext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($plaintext) & 7) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case CRYPT_DES_MODE_CFB: + if (strlen($buffer['ciphertext'])) { + $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext'])); + $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext)); + if (strlen($buffer['ciphertext']) != 8) { + $block = $this->decryptIV; + } else { + $block = $buffer['ciphertext']; + $xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $buffer['ciphertext'] = ''; + } + $start = strlen($plaintext); + } else { + $plaintext = ''; + $xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $start = 0; + } + + for ($i = $start; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $plaintext.= $block ^ $xor; + if ($continuousBuffer && strlen($block) != 8) { + $buffer['ciphertext'].= $block; + $block = $xor; + } else if (strlen($block) == 8) { + $xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $block; + } + break; + case CRYPT_DES_MODE_OFB: + $xor = $this->decryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); + $plaintext.= substr($ciphertext, $i, 8) ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); + $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); + $plaintext.= substr($ciphertext, $i, 8) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) & 7) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->encrypt(substr($plaintext, 8, 8)); + * + * + * echo $des->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see Crypt_TripleDES::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + $this->continuousBuffer = true; + if ($this->mode == CRYPT_DES_MODE_3CBC) { + $this->des[0]->enableContinuousBuffer(); + $this->des[1]->enableContinuousBuffer(); + $this->des[2]->enableContinuousBuffer(); + } + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see Crypt_TripleDES::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + $this->continuousBuffer = false; + $this->encryptIV = $this->iv; + $this->decryptIV = $this->iv; + $this->enchanged = true; + $this->dechanged = true; + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + if ($this->mode == CRYPT_DES_MODE_3CBC) { + $this->des[0]->disableContinuousBuffer(); + $this->des[1]->disableContinuousBuffer(); + $this->des[2]->disableContinuousBuffer(); + } + } + + /** + * Pad "packets". + * + * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not + * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see Crypt_TripleDES::disablePadding() + * @access public + */ + function enablePadding() + { + $this->padding = true; + } + + /** + * Do not pad packets. + * + * @see Crypt_TripleDES::enablePadding() + * @access public + */ + function disablePadding() + { + $this->padding = false; + } + + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8). + * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see Crypt_TripleDES::_unpad() + * @access private + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if (($length & 7) == 0) { + return $text; + } else { + user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE); + $this->padding = true; + } + } + + $pad = 8 - ($length & 7); + return str_pad($text, $length + $pad, chr($pad)); + } + + /** + * Unpads a string + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see Crypt_TripleDES::_pad() + * @access private + */ + function _unpad($text) + { + if (!$this->padding) { + return $text; + } + + $length = ord($text[strlen($text) - 1]); + + if (!$length || $length > 8) { + return false; + } + + return substr($text, 0, -$length); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php new file mode 100644 index 0000000000000000000000000000000000000000..29ad949e104061604c75a3c1ae91b134f12c4f56 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php @@ -0,0 +1,540 @@ + + * @copyright MMXII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id$ + * @link htp://phpseclib.sourceforge.net + */ + +/** + * Pure-PHP ANSI Decoder + * + * @author Jim Wigginton + * @version 0.3.0 + * @access public + * @package File_ANSI + */ +class File_ANSI { + /** + * Max Width + * + * @var Integer + * @access private + */ + var $max_x; + + /** + * Max Height + * + * @var Integer + * @access private + */ + var $max_y; + + /** + * Max History + * + * @var Integer + * @access private + */ + var $max_history; + + /** + * History + * + * @var Array + * @access private + */ + var $history; + + /** + * History Attributes + * + * @var Array + * @access private + */ + var $history_attrs; + + /** + * Current Column + * + * @var Integer + * @access private + */ + var $x; + + /** + * Current Row + * + * @var Integer + * @access private + */ + var $y; + + /** + * Old Column + * + * @var Integer + * @access private + */ + var $old_x; + + /** + * Old Row + * + * @var Integer + * @access private + */ + var $old_y; + + /** + * An empty attribute row + * + * @var Array + * @access private + */ + var $attr_row; + + /** + * The current screen text + * + * @var Array + * @access private + */ + var $screen; + + /** + * The current screen attributes + * + * @var Array + * @access private + */ + var $attrs; + + /** + * The current foreground color + * + * @var String + * @access private + */ + var $foreground; + + /** + * The current background color + * + * @var String + * @access private + */ + var $background; + + /** + * Bold flag + * + * @var Boolean + * @access private + */ + var $bold; + + /** + * Underline flag + * + * @var Boolean + * @access private + */ + var $underline; + + /** + * Blink flag + * + * @var Boolean + * @access private + */ + var $blink; + + /** + * Reverse flag + * + * @var Boolean + * @access private + */ + var $reverse; + + /** + * Color flag + * + * @var Boolean + * @access private + */ + var $color; + + /** + * Current ANSI code + * + * @var String + * @access private + */ + var $ansi; + + /** + * Default Constructor. + * + * @return File_ANSI + * @access public + */ + function File_ANSI() + { + $this->setHistory(200); + $this->setDimensions(80, 24); + } + + /** + * Set terminal width and height + * + * Resets the screen as well + * + * @param Integer $x + * @param Integer $y + * @access public + */ + function setDimensions($x, $y) + { + $this->max_x = $x - 1; + $this->max_y = $y - 1; + $this->x = $this->y = 0; + $this->history = $this->history_attrs = array(); + $this->attr_row = array_fill(0, $this->max_x + 1, ''); + $this->screen = array_fill(0, $this->max_y + 1, ''); + $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); + $this->foreground = 'white'; + $this->background = 'black'; + $this->bold = false; + $this->underline = false; + $this->blink = false; + $this->reverse = false; + $this->color = false; + + $this->ansi = ''; + } + + /** + * Set the number of lines that should be logged past the terminal height + * + * @param Integer $x + * @param Integer $y + * @access public + */ + function setHistory($history) + { + $this->max_history = $history; + } + + /** + * Load a string + * + * @param String $source + * @access public + */ + function loadString($source) + { + $this->setDimensions($this->max_x + 1, $this->max_y + 1); + $this->appendString($source); + } + + /** + * Appdend a string + * + * @param String $source + * @access public + */ + function appendString($source) + { + for ($i = 0; $i < strlen($source); $i++) { + if (strlen($this->ansi)) { + $this->ansi.= $source[$i]; + $chr = ord($source[$i]); + // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements + // single character CSI's not currently supported + switch (true) { + case $this->ansi == "\x1B=": + $this->ansi = ''; + continue 2; + case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): + case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: + break; + default: + continue 2; + } + // http://ascii-table.com/ansi-escape-sequences-vt-100.php + switch ($this->ansi) { + case "\x1B[H": + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $this->y = 0; + break; + case "\x1B[J": + $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); + $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); + + $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); + $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); + + if (count($this->history) == $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + case "\x1B[K": + $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); + + array_splice($this->attrs[$this->y], $this->x + 1); + break; + case "\x1B[?1h": // set cursor key to application + break; + default: + switch (true) { + case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $match[2] - 1; + $this->y = $match[1] - 1; + break; + case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): + $this->old_x = $this->x; + $x = $match[1] - 1; + break; + case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window + break; + case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): + $mods = explode(';', $match[1]); + foreach ($mods as $mod) { + switch ($mod) { + case 0: + $this->attrs[$this->y][$this->x] = ''; + + if ($this->bold) $this->attrs[$this->y][$this->x].= ''; + if ($this->underline) $this->attrs[$this->y][$this->x].= ''; + if ($this->blink) $this->attrs[$this->y][$this->x].= ''; + if ($this->color) $this->attrs[$this->y][$this->x].= ''; + + if ($this->reverse) { + $temp = $this->background; + $this->background = $this->foreground; + $this->foreground = $temp; + } + + $this->bold = $this->underline = $this->blink = $this->color = $this->reverse = false; + break; + case 1: + if (!$this->bold) { + $this->attrs[$this->y][$this->x] = ''; + $this->bold = true; + } + break; + case 4: + if (!$this->underline) { + $this->attrs[$this->y][$this->x] = ''; + $this->underline = true; + } + break; + case 5: + if (!$this->blink) { + $this->attrs[$this->y][$this->x] = ''; + $this->blink = true; + } + break; + case 7: + $this->reverse = !$this->reverse; + $temp = $this->background; + $this->background = $this->foreground; + $this->foreground = $temp; + $this->attrs[$this->y][$this->x] = ''; + if ($this->color) { + $this->attrs[$this->y][$this->x] = '' . $this->attrs[$this->y][$this->x]; + } + $this->color = true; + break; + default: + //$front = $this->reverse ? &$this->background : &$this->foreground; + $front = &$this->{ $this->reverse ? 'background' : 'foreground' }; + //$back = $this->reverse ? &$this->foreground : &$this->background; + $back = &$this->{ $this->reverse ? 'foreground' : 'background' }; + switch ($mod) { + case 30: $front = 'black'; break; + case 31: $front = 'red'; break; + case 32: $front = 'green'; break; + case 33: $front = 'yellow'; break; + case 34: $front = 'blue'; break; + case 35: $front = 'magenta'; break; + case 36: $front = 'cyan'; break; + case 37: $front = 'white'; break; + + case 40: $back = 'black'; break; + case 41: $back = 'red'; break; + case 42: $back = 'green'; break; + case 43: $back = 'yellow'; break; + case 44: $back = 'blue'; break; + case 45: $back = 'magenta'; break; + case 46: $back = 'cyan'; break; + case 47: $back = 'white'; break; + + default: + user_error('Unsupported attribute: ' . $mod); + $this->ansi = ''; + break 2; + } + + unset($temp); + $this->attrs[$this->y][$this->x] = ''; + if ($this->color) { + $this->attrs[$this->y][$this->x] = '' . $this->attrs[$this->y][$this->x]; + } + $this->color = true; + } + } + break; + default: + echo "{$this->ansi} unsupported\r\n"; + } + } + $this->ansi = ''; + continue; + } + + switch ($source[$i]) { + case "\r": + $this->x = 0; + break; + case "\n": + //if ($this->y < $this->max_y) { + // $this->y++; + //} + + while ($this->y >= $this->max_y) { + $this->history = array_merge($this->history, array(array_shift($this->screen))); + $this->screen[] = ''; + + $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); + $this->attrs[] = $this->attr_row; + + if (count($this->history) >= $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + + $this->y--; + } + $this->y++; + break; + case "\x0F": // shift + break; + case "\x1B": // start ANSI escape code + $this->ansi.= "\x1B"; + break; + default: + $this->screen[$this->y] = substr_replace( + $this->screen[$this->y], + $source[$i], + $this->x, + 1 + ); + + if ($this->x > $this->max_x) { + $this->x = 0; + $this->y++; + } else { + $this->x++; + } + } + } + } + + /** + * Returns the current screen without preformating + * + * @access private + * @return String + */ + function _getScreen() + { + $output = ''; + for ($i = 0; $i <= $this->max_y; $i++) { + for ($j = 0; $j <= $this->max_x + 1; $j++) { + if (isset($this->attrs[$i][$j])) { + $output.= $this->attrs[$i][$j]; + } + if (isset($this->screen[$i][$j])) { + $output.= htmlspecialchars($this->screen[$i][$j]); + } + } + $output.= "\r\n"; + } + return rtrim($output); + } + + /** + * Returns the current screen + * + * @access public + * @return String + */ + function getScreen() + { + return '
' . $this->_getScreen() . '
'; + } + + /** + * Returns the current screen and the x previous lines + * + * @access public + * @return String + */ + function getHistory() + { + $scrollback = ''; + for ($i = 0; $i < count($this->history); $i++) { + for ($j = 0; $j <= $this->max_x + 1; $j++) { + if (isset($this->history_attrs[$i][$j])) { + $scrollback.= $this->history_attrs[$i][$j]; + } + if (isset($this->history[$i][$j])) { + $scrollback.= htmlspecialchars($this->history[$i][$j]); + } + } + $scrollback.= "\r\n"; + } + $scrollback.= $this->_getScreen(); + + return '
' . $scrollback . '
'; + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php new file mode 100644 index 0000000000000000000000000000000000000000..766c6e7ebf448f14abcb4c471191fd7018acbb58 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php @@ -0,0 +1,1277 @@ + + * @copyright MMXII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id$ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Math_BigInteger + */ +if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); +} + +/**#@+ + * Tag Classes + * + * @access private + * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 + */ +define('FILE_ASN1_CLASS_UNIVERSAL', 0); +define('FILE_ASN1_CLASS_APPLICATION', 1); +define('FILE_ASN1_CLASS_CONTEXT_SPECIFIC', 2); +define('FILE_ASN1_CLASS_PRIVATE', 3); +/**#@-*/ + +/**#@+ + * Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node124.html + */ +define('FILE_ASN1_TYPE_BOOLEAN', 1); +define('FILE_ASN1_TYPE_INTEGER', 2); +define('FILE_ASN1_TYPE_BIT_STRING', 3); +define('FILE_ASN1_TYPE_OCTET_STRING', 4); +define('FILE_ASN1_TYPE_NULL', 5); +define('FILE_ASN1_TYPE_OBJECT_IDENTIFIER',6); +//define('FILE_ASN1_TYPE_OBJECT_DESCRIPTOR',7); +//define('FILE_ASN1_TYPE_INSTANCE_OF', 8); // EXTERNAL +define('FILE_ASN1_TYPE_REAL', 9); +define('FILE_ASN1_TYPE_ENUMERATED', 10); +//define('FILE_ASN1_TYPE_EMBEDDED', 11); +define('FILE_ASN1_TYPE_UTF8_STRING', 12); +//define('FILE_ASN1_TYPE_RELATIVE_OID', 13); +define('FILE_ASN1_TYPE_SEQUENCE', 16); // SEQUENCE OF +define('FILE_ASN1_TYPE_SET', 17); // SET OF +/**#@-*/ +/**#@+ + * More Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node10.html + */ +define('FILE_ASN1_TYPE_NUMERIC_STRING', 18); +define('FILE_ASN1_TYPE_PRINTABLE_STRING',19); +define('FILE_ASN1_TYPE_TELETEX_STRING', 20); // T61String +define('FILE_ASN1_TYPE_VIDEOTEX_STRING', 21); +define('FILE_ASN1_TYPE_IA5_STRING', 22); +define('FILE_ASN1_TYPE_UTC_TIME', 23); +define('FILE_ASN1_TYPE_GENERALIZED_TIME',24); +define('FILE_ASN1_TYPE_GRAPHIC_STRING', 25); +define('FILE_ASN1_TYPE_VISIBLE_STRING', 26); // ISO646String +define('FILE_ASN1_TYPE_GENERAL_STRING', 27); +define('FILE_ASN1_TYPE_UNIVERSAL_STRING',28); +//define('FILE_ASN1_TYPE_CHARACTER_STRING',29); +define('FILE_ASN1_TYPE_BMP_STRING', 30); +/**#@-*/ + +/**#@+ + * Tag Aliases + * + * These tags are kinda place holders for other tags. + * + * @access private + */ +define('FILE_ASN1_TYPE_CHOICE', -1); +define('FILE_ASN1_TYPE_ANY', -2); +/**#@-*/ + +/** + * ASN.1 Element + * + * Bypass normal encoding rules in File_ASN1::encodeDER() + * + * @author Jim Wigginton + * @version 0.3.0 + * @access public + * @package File_ASN1 + */ +class File_ASN1_Element { + /** + * Raw element value + * + * @var String + * @access private + */ + var $element; + + /** + * Constructor + * + * @param String $encoded + * @return File_ASN1_Element + * @access public + */ + function File_ASN1_Element($encoded) + { + $this->element = $encoded; + } +} + +/** + * Pure-PHP ASN.1 Parser + * + * @author Jim Wigginton + * @version 0.3.0 + * @access public + * @package File_ASN1 + */ +class File_ASN1 { + /** + * ASN.1 object identifier + * + * @var Array + * @access private + * @link http://en.wikipedia.org/wiki/Object_identifier + */ + var $oids = array(); + + /** + * Default date format + * + * @var String + * @access private + * @link http://php.net/class.datetime + */ + var $format = 'D, d M y H:i:s O'; + + /** + * Default date format + * + * @var Array + * @access private + * @see File_ASN1::setTimeFormat() + * @see File_ASN1::asn1map() + * @link http://php.net/class.datetime + */ + var $encoded; + + /** + * Filters + * + * If the mapping type is FILE_ASN1_TYPE_ANY what do we actually encode it as? + * + * @var Array + * @access private + * @see File_ASN1::_encode_der() + */ + var $filters; + + /** + * Type mapping table for the ANY type. + * + * Structured or unknown types are mapped to a FILE_ASN1_Element. + * Unambiguous types get the direct mapping (int/real/bool). + * Others are mapped as a choice, with an extra indexing level. + * + * @var Array + * @access public + */ + var $ANYmap = array( + FILE_ASN1_TYPE_BOOLEAN => true, + FILE_ASN1_TYPE_INTEGER => true, + FILE_ASN1_TYPE_BIT_STRING => 'bitString', + FILE_ASN1_TYPE_OCTET_STRING => 'octetString', + FILE_ASN1_TYPE_NULL => 'null', + FILE_ASN1_TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', + FILE_ASN1_TYPE_REAL => true, + FILE_ASN1_TYPE_ENUMERATED => 'enumerated', + FILE_ASN1_TYPE_UTF8_STRING => 'utf8String', + FILE_ASN1_TYPE_NUMERIC_STRING => 'numericString', + FILE_ASN1_TYPE_PRINTABLE_STRING => 'printableString', + FILE_ASN1_TYPE_TELETEX_STRING => 'teletexString', + FILE_ASN1_TYPE_VIDEOTEX_STRING => 'videotexString', + FILE_ASN1_TYPE_IA5_STRING => 'ia5String', + FILE_ASN1_TYPE_UTC_TIME => 'utcTime', + FILE_ASN1_TYPE_GENERALIZED_TIME => 'generalTime', + FILE_ASN1_TYPE_GRAPHIC_STRING => 'graphicString', + FILE_ASN1_TYPE_VISIBLE_STRING => 'visibleString', + FILE_ASN1_TYPE_GENERAL_STRING => 'generalString', + FILE_ASN1_TYPE_UNIVERSAL_STRING => 'universalString', + //FILE_ASN1_TYPE_CHARACTER_STRING => 'characterString', + FILE_ASN1_TYPE_BMP_STRING => 'bmpString' + ); + + /** + * String type to character size mapping table. + * + * Non-convertable types are absent from this table. + * size == 0 indicates variable length encoding. + * + * @var Array + * @access public + */ + var $stringTypeSize = array( + FILE_ASN1_TYPE_UTF8_STRING => 0, + FILE_ASN1_TYPE_BMP_STRING => 2, + FILE_ASN1_TYPE_UNIVERSAL_STRING => 4, + FILE_ASN1_TYPE_PRINTABLE_STRING => 1, + FILE_ASN1_TYPE_TELETEX_STRING => 1, + FILE_ASN1_TYPE_IA5_STRING => 1, + FILE_ASN1_TYPE_VISIBLE_STRING => 1, + ); + + /** + * Parse BER-encoding + * + * Serves a similar purpose to openssl's asn1parse + * + * @param String $encoded + * @return Array + * @access public + */ + function decodeBER($encoded) + { + if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') { + $encoded = $encoded->element; + } + + $this->encoded = $encoded; + return $this->_decode_ber($encoded); + } + + /** + * Parse BER-encoding (Helper function) + * + * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. + * $encoded is passed by reference for the recursive calls done for FILE_ASN1_TYPE_BIT_STRING and + * FILE_ASN1_TYPE_OCTET_STRING. In those cases, the indefinite length is used. + * + * @param String $encoded + * @param Integer $start + * @return Array + * @access private + */ + function _decode_ber(&$encoded, $start = 0) + { + $decoded = array(); + + while ( strlen($encoded) ) { + $current = array('start' => $start); + + $type = ord($this->_string_shift($encoded)); + $start++; + + $constructed = ($type >> 5) & 1; + + $tag = $type & 0x1F; + if ($tag == 0x1F) { + $tag = 0; + // process septets (since the eighth bit is ignored, it's not an octet) + do { + $loop = ord($encoded[0]) >> 7; + $tag <<= 7; + $tag |= ord($this->_string_shift($encoded)) & 0x7F; + $start++; + } while ( $loop ); + } + + // Length, as discussed in 8.1.3 of X.690-0207.pdf#page=13 + $length = ord($this->_string_shift($encoded)); + $start++; + if ( $length == 0x80 ) { // indefinite length + // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all + // immediately available." -- 8.1.3.2.c + //if ( !$constructed ) { + // return false; + //} + $length = strlen($encoded); + } elseif ( $length & 0x80 ) { // definite length, long form + // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only + // support it up to four. + $length&= 0x7F; + $temp = $this->_string_shift($encoded, $length); + $start+= $length; + extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } + + // End-of-content, see 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + if (!$type && !$length) { + return $decoded; + } + $content = $this->_string_shift($encoded, $length); + + /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 + built-in types. It defines an application-independent data type that must be distinguishable from all other + data types. The other three classes are user defined. The APPLICATION class distinguishes data types that + have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within + a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the + alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this + data type; the term CONTEXT-SPECIFIC does not appear. + + -- http://www.obj-sys.com/asn1tutorial/node12.html */ + $class = ($type >> 6) & 3; + switch ($class) { + case FILE_ASN1_CLASS_APPLICATION: + case FILE_ASN1_CLASS_PRIVATE: + case FILE_ASN1_CLASS_CONTEXT_SPECIFIC: + $decoded[] = array( + 'type' => $class, + 'constant' => $tag, + 'content' => $constructed ? $this->_decode_ber($content, $start) : $content, + 'length' => $length + $start - $current['start'] + ) + $current; + continue 2; + } + + $current+= array('type' => $tag); + + // decode UNIVERSAL tags + switch ($tag) { + case FILE_ASN1_TYPE_BOOLEAN: + // "The contents octets shall consist of a single octet." -- 8.2.1 + //if (strlen($content) != 1) { + // return false; + //} + $current['content'] = (bool) ord($content[0]); + break; + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: + $current['content'] = new Math_BigInteger($content, -256); + break; + case FILE_ASN1_TYPE_REAL: // not currently supported + return false; + case FILE_ASN1_TYPE_BIT_STRING: + // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + // the number of unused bits in the final subsequent octet. The number shall be in the range zero to + // seven. + if (!$constructed) { + $current['content'] = $content; + } else { + $temp = $this->_decode_ber($content, $start); + $length-= strlen($content); + $last = count($temp) - 1; + for ($i = 0; $i < $last; $i++) { + // all subtags should be bit strings + //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + // return false; + //} + $current['content'].= substr($temp[$i]['content'], 1); + } + // all subtags should be bit strings + //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + // return false; + //} + $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); + } + break; + case FILE_ASN1_TYPE_OCTET_STRING: + if (!$constructed) { + $current['content'] = $content; + } else { + $temp = $this->_decode_ber($content, $start); + $length-= strlen($content); + for ($i = 0, $size = count($temp); $i < $size; $i++) { + // all subtags should be octet strings + //if ($temp[$i]['type'] != FILE_ASN1_TYPE_OCTET_STRING) { + // return false; + //} + $current['content'].= $temp[$i]['content']; + } + // $length = + } + break; + case FILE_ASN1_TYPE_NULL: + // "The contents octets shall not contain any octets." -- 8.8.2 + //if (strlen($content)) { + // return false; + //} + break; + case FILE_ASN1_TYPE_SEQUENCE: + case FILE_ASN1_TYPE_SET: + $current['content'] = $this->_decode_ber($content, $start); + break; + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + $temp = ord($this->_string_shift($content)); + $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); + $valuen = 0; + // process septets + while (strlen($content)) { + $temp = ord($this->_string_shift($content)); + $valuen <<= 7; + $valuen |= $temp & 0x7F; + if (~$temp & 0x80) { + $current['content'].= ".$valuen"; + $valuen = 0; + } + } + // the eighth bit of the last byte should not be 1 + //if ($temp >> 7) { + // return false; + //} + break; + /* Each character string type shall be encoded as if it had been declared: + [UNIVERSAL x] IMPLICIT OCTET STRING + + -- X.690-0207.pdf#page=23 ( 8.21.3) + + Per that, we're not going to do any validation. If there are any illegal characters in the string, + we don't really care */ + case FILE_ASN1_TYPE_NUMERIC_STRING: + // 0,1,2,3,4,5,6,7,8,9, and space + case FILE_ASN1_TYPE_PRINTABLE_STRING: + // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, + // hyphen, full stop, solidus, colon, equal sign, question mark + case FILE_ASN1_TYPE_TELETEX_STRING: + // The Teletex character set in CCITT's T61, space, and delete + // see http://en.wikipedia.org/wiki/Teletex#Character_sets + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + // The Videotex character set in CCITT's T.100 and T.101, space, and delete + case FILE_ASN1_TYPE_VISIBLE_STRING: + // Printing character sets of international ASCII, and space + case FILE_ASN1_TYPE_IA5_STRING: + // International Alphabet 5 (International ASCII) + case FILE_ASN1_TYPE_GRAPHIC_STRING: + // All registered G sets, and space + case FILE_ASN1_TYPE_GENERAL_STRING: + // All registered C and G sets, space and delete + case FILE_ASN1_TYPE_UTF8_STRING: + // ???? + case FILE_ASN1_TYPE_BMP_STRING: + $current['content'] = $content; + break; + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: + $current['content'] = $this->_decodeTime($content, $tag); + default: + + } + + $start+= $length; + $decoded[] = $current + array('length' => $start - $current['start']); + } + + return $decoded; + } + + /** + * ASN.1 Decode + * + * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. + * + * @param Array $decoded + * @param Array $mapping + * @return Array + * @access public + */ + function asn1map($decoded, $mapping) + { + if (isset($mapping['explicit'])) { + $decoded = $decoded['content'][0]; + } + + switch (true) { + case $mapping['type'] == FILE_ASN1_TYPE_ANY: + $intype = $decoded['type']; + if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) { + return new File_ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length'])); + } + $inmap = $this->ANYmap[$intype]; + if (is_string($inmap)) { + return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping)); + } + break; + case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: + foreach ($mapping['children'] as $key => $option) { + switch (true) { + case isset($option['constant']) && $option['constant'] == $decoded['constant']: + case !isset($option['constant']) && $option['type'] == $decoded['type']: + $value = $this->asn1map($decoded, $option); + break; + case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: + $v = $this->asn1map($decoded, $option); + if (isset($v)) { + $value = $v; + } + } + if (isset($value)) { + return array($key => $value); + } + } + return NULL; + case isset($mapping['implicit']): + case isset($mapping['explicit']): + case $decoded['type'] == $mapping['type']: + break; + default: + return NULL; + } + + if (isset($mapping['implicit'])) { + $decoded['type'] = $mapping['type']; + } + + switch ($decoded['type']) { + case FILE_ASN1_TYPE_SEQUENCE: + $map = array(); + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = $this->asn1map($content, $child)) === NULL) { + return NULL; + } + } + + return $map; + } + + $n = count($decoded['content']); + $i = 0; + + foreach ($mapping['children'] as $key => $child) { + $maymatch = $i < $n; // Match only existing input. + if ($maymatch) { + $temp = $decoded['content'][$i]; + + if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { + // Get the mapping and input class & constant. + $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + $constant = NULL; + if (isset($temp['constant'])) { + $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + } + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } + elseif (isset($child['constant'])) { + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; + } + } + } + + if ($maymatch) { + // Attempt submapping. + $candidate = $this->asn1map($temp, $child); + $maymatch = $candidate !== NULL; + } + + if ($maymatch) { + // Got the match: use it. + $map[$key] = $candidate; + $i++; + } elseif (isset($child['default'])) { + $map[$key] = $child['default']; // Use default. + } elseif (!isset($child['optional'])) { + return NULL; // Syntax error. + } + } + + // Fail mapping if all input items have not been consumed. + return $i < $n? NULL: $map; + + // the main diff between sets and sequences is the encapsulation of the foreach in another for loop + case FILE_ASN1_TYPE_SET: + $map = array(); + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = $this->asn1map($content, $child)) === NULL) { + return NULL; + } + } + + return $map; + } + + for ($i = 0; $i < count($decoded['content']); $i++) { + $temp = $decoded['content'][$i]; + $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + if (isset($temp['constant'])) { + $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + } + + foreach ($mapping['children'] as $key => $child) { + if (isset($map[$key])) { + continue; + } + $maymatch = true; + if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { + $childClass = FILE_ASN1_CLASS_UNIVERSAL; + $constant = NULL; + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } + elseif (isset($child['constant'])) { + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; + } + } + + if ($maymatch) { + // Attempt submapping. + $candidate = $this->asn1map($temp, $child); + $maymatch = $candidate !== NULL; + } + + if (!$maymatch) { + break; + } + + // Got the match: use it. + $map[$key] = $candidate; + break; + } + } + + foreach ($mapping['children'] as $key => $child) { + if (!isset($map[$key])) { + if (isset($child['default'])) { + $map[$key] = $child['default']; + } elseif (!isset($child['optional'])) { + return NULL; + } + } + } + return $map; + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: + if (isset($mapping['implicit'])) { + $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); + } + return @date($this->format, $decoded['content']); + case FILE_ASN1_TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $offset = ord($decoded['content'][0]); + $size = (strlen($decoded['content']) - 1) * 8 - $offset; + /* + From X.680-0207.pdf#page=46 (21.7): + + "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove) + arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should + therefore ensure that different semantics are not associated with such values which differ only in the number of trailing + 0 bits." + */ + $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false); + for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) { + $current = ord($decoded['content'][$i]); + for ($j = $offset; $j < 8; $j++) { + $bits[] = (bool) ($current & (1 << $j)); + } + $offset = 0; + } + $values = array(); + $map = array_reverse($mapping['mapping']); + foreach ($map as $i => $value) { + if ($bits[$i]) { + $values[] = $value; + } + } + return $values; + } + case FILE_ASN1_TYPE_OCTET_STRING: + return base64_encode($decoded['content']); + case FILE_ASN1_TYPE_NULL: + return ''; + case FILE_ASN1_TYPE_BOOLEAN: + return $decoded['content']; + case FILE_ASN1_TYPE_NUMERIC_STRING: + case FILE_ASN1_TYPE_PRINTABLE_STRING: + case FILE_ASN1_TYPE_TELETEX_STRING: + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + case FILE_ASN1_TYPE_IA5_STRING: + case FILE_ASN1_TYPE_GRAPHIC_STRING: + case FILE_ASN1_TYPE_VISIBLE_STRING: + case FILE_ASN1_TYPE_GENERAL_STRING: + case FILE_ASN1_TYPE_UNIVERSAL_STRING: + case FILE_ASN1_TYPE_UTF8_STRING: + case FILE_ASN1_TYPE_BMP_STRING: + return $decoded['content']; + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: + $temp = $decoded['content']; + if (isset($mapping['implicit'])) { + $temp = new Math_BigInteger($decoded['content'], -256); + } + if (isset($mapping['mapping'])) { + $temp = (int) $temp->toString(); + return isset($mapping['mapping'][$temp]) ? + $mapping['mapping'][$temp] : + false; + } + return $temp; + } + } + + /** + * ASN.1 Encode + * + * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function + * an ASN.1 compiler. + * + * @param String $source + * @param String $mapping + * @param Integer $idx + * @return String + * @access public + */ + function encodeDER($source, $mapping) + { + $this->location = array(); + return $this->_encode_der($source, $mapping); + } + + /** + * ASN.1 Encode (Helper function) + * + * @param String $source + * @param String $mapping + * @param Integer $idx + * @return String + * @access private + */ + function _encode_der($source, $mapping, $idx = NULL) + { + if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { + return $source->element; + } + + // do not encode (implicitly optional) fields with value set to default + if (isset($mapping['default']) && $source === $mapping['default']) { + return ''; + } + + if (isset($idx)) { + $this->location[] = $idx; + } + + $tag = $mapping['type']; + + switch ($tag) { + case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence. + case FILE_ASN1_TYPE_SEQUENCE: + $tag|= 0x20; // set the constructed bit + $value = ''; + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + + foreach ($source as $content) { + $temp = $this->_encode_der($content, $child); + if ($temp === false) { + return false; + } + $value.= $temp; + } + break; + } + + foreach ($mapping['children'] as $key => $child) { + if (!isset($source[$key])) { + if (!isset($child['optional'])) { + return false; + } + continue; + } + + $temp = $this->_encode_der($source[$key], $child, $key); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + /* + From X.680-0207.pdf#page=58 (30.6): + + "The tagging construction specifies explicit tagging if any of the following holds: + ... + c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or + AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or + an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." + */ + if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); + $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; + } else { + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $temp = $subtag . substr($temp, 1); + } + } + $value.= $temp; + } + break; + case FILE_ASN1_TYPE_CHOICE: + $temp = false; + + foreach ($mapping['children'] as $key => $child) { + if (!isset($source[$key])) { + continue; + } + + $temp = $this->_encode_der($source[$key], $child, $key); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + + $tag = ord($temp[0]); + + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); + $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; + } else { + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $temp = $subtag . substr($temp, 1); + } + } + } + + if (isset($idx)) { + array_pop($this->location); + } + + if ($temp && isset($mapping['cast'])) { + $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']); + } + + return $temp; + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: + if (!isset($mapping['mapping'])) { + $value = $source->toBytes(true); + } else { + $value = array_search($source, $mapping['mapping']); + if ($value === false) { + return false; + } + $value = new Math_BigInteger($value); + $value = $value->toBytes(true); + } + break; + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: + $format = $mapping['type'] == FILE_ASN1_TYPE_UTC_TIME ? 'y' : 'Y'; + $format.= 'mdHis'; + $value = @gmdate($format, strtotime($source)) . 'Z'; + break; + case FILE_ASN1_TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $bits = array_fill(0, count($mapping['mapping']), 0); + $size = 0; + for ($i = 0; $i < count($mapping['mapping']); $i++) { + if (in_array($mapping['mapping'][$i], $source)) { + $bits[$i] = 1; + $size = $i; + } + } + + $offset = 8 - (($size + 1) & 7); + $offset = $offset !== 8 ? $offset : 0; + + $value = chr($offset); + + for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { + unset($bits[$i]); + } + + $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); + $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); + foreach ($bytes as $byte) { + $value.= chr(bindec($byte)); + } + + break; + } + case FILE_ASN1_TYPE_OCTET_STRING: + /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. + + -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ + $value = base64_decode($source); + break; + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); + if ($oid === false) { + user_error('Invalid OID'); + return false; + } + $value = ''; + $parts = explode('.', $oid); + $value = chr(40 * $parts[0] + $parts[1]); + for ($i = 2; $i < count($parts); $i++) { + $temp = ''; + if (!$parts[$i]) { + $temp = "\0"; + } else { + while ($parts[$i]) { + $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp; + $parts[$i] >>= 7; + } + $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); + } + $value.= $temp; + } + break; + case FILE_ASN1_TYPE_ANY: + $loc = $this->location; + if (isset($idx)) { + array_pop($this->location); + } + + switch (true) { + case !isset($source): + return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping); + case is_int($source): + case is_object($source) && strtolower(get_class($source)) == 'math_biginteger': + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping); + case is_float($source): + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping); + case is_bool($source): + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping); + case is_array($source) && count($source) == 1: + $typename = implode('', array_keys($source)); + $outtype = array_search($typename, $this->ANYmap, true); + if ($outtype !== false) { + return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping); + } + } + + $filters = $this->filters; + foreach ($loc as $part) { + if (!isset($filters[$part])) { + $filters = false; + break; + } + $filters = $filters[$part]; + } + if ($filters === false) { + user_error('No filters defined for ' . implode('/', $loc)); + return false; + } + return $this->_encode_der($source, $filters + $mapping); + case FILE_ASN1_TYPE_NULL: + $value = ''; + break; + case FILE_ASN1_TYPE_NUMERIC_STRING: + case FILE_ASN1_TYPE_TELETEX_STRING: + case FILE_ASN1_TYPE_PRINTABLE_STRING: + case FILE_ASN1_TYPE_UNIVERSAL_STRING: + case FILE_ASN1_TYPE_UTF8_STRING: + case FILE_ASN1_TYPE_BMP_STRING: + case FILE_ASN1_TYPE_IA5_STRING: + case FILE_ASN1_TYPE_VISIBLE_STRING: + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + case FILE_ASN1_TYPE_GRAPHIC_STRING: + case FILE_ASN1_TYPE_GENERAL_STRING: + $value = $source; + break; + case FILE_ASN1_TYPE_BOOLEAN: + $value = $source ? "\xFF" : "\x00"; + break; + default: + user_error('Mapping provides no type definition for ' . implode('/', $this->location)); + return false; + } + + if (isset($idx)) { + array_pop($this->location); + } + + if (isset($mapping['cast'])) { + $tag = ($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']; + } + + return chr($tag) . $this->_encodeLength(strlen($value)) . $value; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 8.1.3} for more information. + * + * @access private + * @param Integer $length + * @return String + */ + function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * BER-decode the time + * + * Called by _decode_ber() and in the case of implicit tags asn1map(). + * + * @access private + * @param String $content + * @param Integer $tag + * @return String + */ + function _decodeTime($content, $tag) + { + /* UTCTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + http://www.obj-sys.com/asn1tutorial/node15.html + + GeneralizedTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 + http://www.obj-sys.com/asn1tutorial/node14.html */ + + $pattern = $tag == FILE_ASN1_TYPE_UTC_TIME ? + '#(..)(..)(..)(..)(..)(..)(.*)#' : + '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#'; + + preg_match($pattern, $content, $matches); + + list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches; + + if ($tag == FILE_ASN1_TYPE_UTC_TIME) { + $year = $year >= 50 ? "19$year" : "20$year"; + } + + if ($timezone == 'Z') { + $mktime = 'gmmktime'; + $timezone = 0; + } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) { + $mktime = 'gmmktime'; + $timezone = 60 * $matches[3] + 3600 * $matches[2]; + if ($matches[1] == '-') { + $timezone = -$timezone; + } + } else { + $mktime = 'mktime'; + $timezone = 0; + } + + return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone; + } + + /** + * Set the time format + * + * Sets the time / date format for asn1map(). + * + * @access public + * @param String $format + */ + function setTimeFormat($format) + { + $this->format = $format; + } + + /** + * Load OIDs + * + * Load the relevant OIDs for a particular ASN.1 semantic mapping. + * + * @access public + * @param Array $oids + */ + function loadOIDs($oids) + { + $this->oids = $oids; + } + + /** + * Load filters + * + * See File_X509, etc, for an example. + * + * @access public + * @param Array $filters + */ + function loadFilters($filters) + { + $this->filters = $filters; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * String type conversion + * + * This is a lazy conversion, dealing only with character size. + * No real conversion table is used. + * + * @param String $in + * @param optional Integer $from + * @param optional Integer $to + * @return String + * @access public + */ + function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING) + { + if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) { + return false; + } + $insize = $this->stringTypeSize[$from]; + $outsize = $this->stringTypeSize[$to]; + $inlength = strlen($in); + $out = ''; + + for ($i = 0; $i < $inlength;) { + if ($inlength - $i < $insize) { + return false; + } + + // Get an input character as a 32-bit value. + $c = ord($in[$i++]); + switch (true) { + case $insize == 4: + $c = ($c << 8) | ord($in[$i++]); + $c = ($c << 8) | ord($in[$i++]); + case $insize == 2: + $c = ($c << 8) | ord($in[$i++]); + case $insize == 1: + break; + case ($c & 0x80) == 0x00: + break; + case ($c & 0x40) == 0x00: + return false; + default: + $bit = 6; + do { + if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) { + return false; + } + $c = ($c << 6) | (ord($in[$i++]) & 0x3F); + $bit += 5; + $mask = 1 << $bit; + } while ($c & $bit); + $c &= $mask - 1; + break; + } + + // Convert and append the character to output string. + $v = ''; + switch (true) { + case $outsize == 4: + $v .= chr($c & 0xFF); + $c >>= 8; + $v .= chr($c & 0xFF); + $c >>= 8; + case $outsize == 2: + $v .= chr($c & 0xFF); + $c >>= 8; + case $outsize == 1: + $v .= chr($c & 0xFF); + $c >>= 8; + if ($c) { + return false; + } + break; + case ($c & 0x80000000) != 0: + return false; + case $c >= 0x04000000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x04000000; + case $c >= 0x00200000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00200000; + case $c >= 0x00010000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00010000; + case $c >= 0x00000800: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00000800; + case $c >= 0x00000080: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x000000C0; + default: + $v .= chr($c); + break; + } + $out .= strrev($v); + } + return $out; + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php new file mode 100644 index 0000000000000000000000000000000000000000..278da62e26208c9d1a547040b3479f010ea9029c --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php @@ -0,0 +1,4323 @@ + + * @copyright MMXII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id$ + * @link htp://phpseclib.sourceforge.net + */ + +/** + * Include File_ASN1 + */ +if (!class_exists('File_ASN1')) { + require_once('File/ASN1.php'); +} + +/** + * Flag to only accept signatures signed by certificate authorities + * + * @access public + * @see File_X509::validateSignature() + */ +define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); + +/**#@+ + * @access public + * @see File_X509::getDN() + */ +/** + * Return internal array representation + */ +define('FILE_X509_DN_ARRAY', 0); +/** + * Return string + */ +define('FILE_X509_DN_STRING', 1); +/** + * Return ASN.1 name string + */ +define('FILE_X509_DN_ASN1', 2); +/** + * Return OpenSSL compatible array + */ +define('FILE_X509_DN_OPENSSL', 3); +/** + * Return canonical ASN.1 RDNs string + */ +define('FILE_X509_DN_CANON', 4); +/** + * Return name hash for file indexing + */ +define('FILE_X509_DN_HASH', 5); +/**#@-*/ + +/**#@+ + * @access public + * @see File_X509::saveX509() + * @see File_X509::saveCSR() + * @see File_X509::saveCRL() + */ +/** + * Save as PEM + * + * ie. a base64-encoded PEM with a header and a footer + */ +define('FILE_X509_FORMAT_PEM', 0); +/** + * Save as DER + */ +define('FILE_X509_FORMAT_DER', 1); +/** + * Save as a SPKAC + * + * Only works on CSRs. Not currently supported. + */ +define('FILE_X509_FORMAT_SPKAC', 2); +/**#@-*/ + +/** + * Attribute value disposition. + * If disposition is >= 0, this is the index of the target value. + */ +define('FILE_X509_ATTR_ALL', -1); // All attribute values (array). +define('FILE_X509_ATTR_APPEND', -2); // Add a value. +define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value. + +/** + * Pure-PHP X.509 Parser + * + * @author Jim Wigginton + * @version 0.3.1 + * @access public + * @package File_X509 + */ +class File_X509 { + /** + * ASN.1 syntax for X.509 certificates + * + * @var Array + * @access private + */ + var $Certificate; + + /**#@+ + * ASN.1 syntax for various extensions + * + * @access private + */ + var $DirectoryString; + var $PKCS9String; + var $AttributeValue; + var $Extensions; + var $KeyUsage; + var $ExtKeyUsageSyntax; + var $BasicConstraints; + var $KeyIdentifier; + var $CRLDistributionPoints; + var $AuthorityKeyIdentifier; + var $CertificatePolicies; + var $AuthorityInfoAccessSyntax; + var $SubjectAltName; + var $PrivateKeyUsagePeriod; + var $IssuerAltName; + var $PolicyMappings; + var $NameConstraints; + + var $CPSuri; + var $UserNotice; + + var $netscape_cert_type; + var $netscape_comment; + var $netscape_ca_policy_url; + + var $Name; + var $RelativeDistinguishedName; + var $CRLNumber; + var $CRLReason; + var $IssuingDistributionPoint; + var $InvalidityDate; + var $CertificateIssuer; + var $HoldInstructionCode; + var $SignedPublicKeyAndChallenge; + /**#@-*/ + + /** + * ASN.1 syntax for Certificate Signing Requests (RFC2986) + * + * @var Array + * @access private + */ + var $CertificationRequest; + + /** + * ASN.1 syntax for Certificate Revocation Lists (RFC5280) + * + * @var Array + * @access private + */ + var $CertificateList; + + /** + * Distinguished Name + * + * @var Array + * @access private + */ + var $dn; + + /** + * Public key + * + * @var String + * @access private + */ + var $publicKey; + + /** + * Private key + * + * @var String + * @access private + */ + var $privateKey; + + /** + * Object identifiers for X.509 certificates + * + * @var Array + * @access private + * @link http://en.wikipedia.org/wiki/Object_identifier + */ + var $oids; + + /** + * The certificate authorities + * + * @var Array + * @access private + */ + var $CAs; + + /** + * The currently loaded certificate + * + * @var Array + * @access private + */ + var $currentCert; + + /** + * The signature subject + * + * There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally + * encoded so we take save the portion of the original cert that the signature would have made for. + * + * @var String + * @access private + */ + var $signatureSubject; + + /** + * Certificate Start Date + * + * @var String + * @access private + */ + var $startDate; + + /** + * Certificate End Date + * + * @var String + * @access private + */ + var $endDate; + + /** + * Serial Number + * + * @var String + * @access private + */ + var $serialNumber; + + /** + * Key Identifier + * + * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and + * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. + * + * @var String + * @access private + */ + var $currentKeyIdentifier; + + /** + * CA Flag + * + * @var Boolean + * @access private + */ + var $caFlag = false; + + /** + * Default Constructor. + * + * @return File_X509 + * @access public + */ + function File_X509() + { + // Explicitly Tagged Module, 1988 Syntax + // http://tools.ietf.org/html/rfc5280#appendix-A.1 + + $this->DirectoryString = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'teletexString' => array('type' => FILE_ASN1_TYPE_TELETEX_STRING), + 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), + 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING), + 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING), + 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING) + ) + ); + + $this->PKCS9String = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), + 'directoryString' => $this->DirectoryString + ) + ); + + $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY); + + $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + + $AttributeTypeAndValue = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'type' => $AttributeType, + 'value'=> $this->AttributeValue + ) + ); + + /* + In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, + but they can be useful at times when either there is no unique attribute in the entry or you + want to ensure that the entry's DN contains some useful identifying information. + + - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName + */ + $this->RelativeDistinguishedName = array( + 'type' => FILE_ASN1_TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $AttributeTypeAndValue + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.2.4 + $RDNSequence = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + // RDNSequence does not define a min or a max, which means it doesn't have one + 'min' => 0, + 'max' => -1, + 'children' => $this->RelativeDistinguishedName + ); + + $this->Name = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'rdnSequence' => $RDNSequence + ) + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.1.2 + $AlgorithmIdentifier = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'parameters' => array( + 'type' => FILE_ASN1_TYPE_ANY, + 'optional' => true + ) + ) + ); + + /* + A certificate using system MUST reject the certificate if it encounters + a critical extension it does not recognize; however, a non-critical + extension may be ignored if it is not recognized. + + http://tools.ietf.org/html/rfc5280#section-4.2 + */ + $Extension = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'critical' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'optional' => true, + 'default' => false + ), + 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING) + ) + ); + + $this->Extensions = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + // technically, it's MAX, but we'll assume anything < 0 is MAX + 'max' => -1, + // if 'children' isn't an array then 'min' and 'max' must be defined + 'children' => $Extension + ); + + $SubjectPublicKeyInfo = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => $AlgorithmIdentifier, + 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING); + + $Time = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME), + 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME) + ) + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.2.5 + $Validity = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'notBefore' => $Time, + 'notAfter' => $Time + ) + ); + + $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER); + + $Version = array( + 'type' => FILE_ASN1_TYPE_INTEGER, + 'mapping' => array('v1', 'v2', 'v3') + ); + + // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) + $TBSCertificate = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + // technically, default implies optional, but we'll define it as being optional, none-the-less, just to + // reenforce that fact + 'version' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true, + 'default' => 'v1' + ) + $Version, + 'serialNumber' => $CertificateSerialNumber, + 'signature' => $AlgorithmIdentifier, + 'issuer' => $this->Name, + 'validity' => $Validity, + 'subject' => $this->Name, + 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo, + // implicit means that the T in the TLV structure is to be rewritten, regardless of the type + 'issuerUniqueID' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $UniqueIdentifier, + 'subjectUniqueID' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $UniqueIdentifier, + // doesn't use the EXPLICIT keyword but if + // it's not IMPLICIT, it's EXPLICIT + 'extensions' => array( + 'constant' => 3, + 'optional' => true, + 'explicit' => true + ) + $this->Extensions + ) + ); + + $this->Certificate = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'tbsCertificate' => $TBSCertificate, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + $this->KeyUsage = array( + 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'mapping' => array( + 'digitalSignature', + 'nonRepudiation', + 'keyEncipherment', + 'dataEncipherment', + 'keyAgreement', + 'keyCertSign', + 'cRLSign', + 'encipherOnly', + 'decipherOnly' + ) + ); + + $this->BasicConstraints = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'cA' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'optional' => true, + 'default' => false + ), + 'pathLenConstraint' => array( + 'type' => FILE_ASN1_TYPE_INTEGER, + 'optional' => true + ) + ) + ); + + $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING); + + $OrganizationalUnitNames = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, // ub-organizational-units + 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + ); + + $PersonalName = array( + 'type' => FILE_ASN1_TYPE_SET, + 'children' => array( + 'surname' => array( + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ), + 'given-name' => array( + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ), + 'initials' => array( + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ), + 'generation-qualifier' => array( + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + ) + ); + + $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); + + $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); + + $PrivateDomainName = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + ) + ); + + $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); + + $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); + + $AdministrationDomainName = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or + // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC + 'class' => FILE_ASN1_CLASS_APPLICATION, + 'cast' => 2, + 'children' => array( + 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + ) + ); + + $CountryName = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or + // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC + 'class' => FILE_ASN1_CLASS_APPLICATION, + 'cast' => 1, + 'children' => array( + 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + ) + ); + + $AnotherName = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'value' => array( + 'type' => FILE_ASN1_TYPE_ANY, + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + ) + ); + + $ExtensionAttribute = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'extension-attribute-type' => array( + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ), + 'extension-attribute-value' => array( + 'type' => FILE_ASN1_TYPE_ANY, + 'constant' => 1, + 'optional' => true, + 'explicit' => true + ) + ) + ); + + $ExtensionAttributes = array( + 'type' => FILE_ASN1_TYPE_SET, + 'min' => 1, + 'max' => 256, // ub-extension-attributes + 'children' => $ExtensionAttribute + ); + + $BuiltInDomainDefinedAttribute = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), + 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + ) + ); + + $BuiltInDomainDefinedAttributes = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, // ub-domain-defined-attributes + 'children' => $BuiltInDomainDefinedAttribute + ); + + $BuiltInStandardAttributes = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'country-name' => array('optional' => true) + $CountryName, + 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName, + 'network-address' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $NetworkAddress, + 'terminal-identifier' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $TerminalIdentifier, + 'private-domain-name' => array( + 'constant' => 2, + 'optional' => true, + 'explicit' => true + ) + $PrivateDomainName, + 'organization-name' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $OrganizationName, + 'numeric-user-identifier' => array( + 'constant' => 4, + 'optional' => true, + 'implicit' => true + ) + $NumericUserIdentifier, + 'personal-name' => array( + 'constant' => 5, + 'optional' => true, + 'implicit' => true + ) + $PersonalName, + 'organizational-unit-names' => array( + 'constant' => 6, + 'optional' => true, + 'implicit' => true + ) + $OrganizationalUnitNames + ) + ); + + $ORAddress = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'built-in-standard-attributes' => $BuiltInStandardAttributes, + 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes, + 'extension-attributes' => array('optional' => true) + $ExtensionAttributes + ) + ); + + $EDIPartyName = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'nameAssigner' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $this->DirectoryString, + // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and + // setting it to optional gets the job done in any event. + 'partyName' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $this->DirectoryString + ) + ); + + $GeneralName = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'otherName' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $AnotherName, + 'rfc822Name' => array( + 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ), + 'dNSName' => array( + 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ), + 'x400Address' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $ORAddress, + 'directoryName' => array( + 'constant' => 4, + 'optional' => true, + 'explicit' => true + ) + $this->Name, + 'ediPartyName' => array( + 'constant' => 5, + 'optional' => true, + 'implicit' => true + ) + $EDIPartyName, + 'uniformResourceIdentifier' => array( + 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'constant' => 6, + 'optional' => true, + 'implicit' => true + ), + 'iPAddress' => array( + 'type' => FILE_ASN1_TYPE_OCTET_STRING, + 'constant' => 7, + 'optional' => true, + 'implicit' => true + ), + 'registeredID' => array( + 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER, + 'constant' => 8, + 'optional' => true, + 'implicit' => true + ) + ) + ); + + $GeneralNames = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $GeneralName + ); + + $this->IssuerAltName = $GeneralNames; + + $ReasonFlags = array( + 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'mapping' => array( + 'unused', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + 'privilegeWithdrawn', + 'aACompromise' + ) + ); + + $DistributionPointName = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'fullName' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames, + 'nameRelativeToCRLIssuer' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $this->RelativeDistinguishedName + ) + ); + + $DistributionPoint = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'distributionPoint' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $DistributionPointName, + 'reasons' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $ReasonFlags, + 'cRLIssuer' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames + ) + ); + + $this->CRLDistributionPoints = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $DistributionPoint + ); + + $this->AuthorityKeyIdentifier = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'keyIdentifier' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $this->KeyIdentifier, + 'authorityCertIssuer' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames, + 'authorityCertSerialNumber' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $CertificateSerialNumber + ) + ); + + $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + + $PolicyQualifierInfo = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'policyQualifierId' => $PolicyQualifierId, + 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY) + ) + ); + + $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + + $PolicyInformation = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'policyIdentifier' => $CertPolicyId, + 'policyQualifiers' => array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 0, + 'max' => -1, + 'optional' => true, + 'children' => $PolicyQualifierInfo + ) + ) + ); + + $this->CertificatePolicies = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $PolicyInformation + ); + + $this->PolicyMappings = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'issuerDomainPolicy' => $CertPolicyId, + 'subjectDomainPolicy' => $CertPolicyId + ) + ) + ); + + $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + + $this->ExtKeyUsageSyntax = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $KeyPurposeId + ); + + $AccessDescription = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'accessLocation' => $GeneralName + ) + ); + + $this->AuthorityInfoAccessSyntax = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $AccessDescription + ); + + $this->SubjectAltName = $GeneralNames; + + $this->PrivateKeyUsagePeriod = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'notBefore' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true, + 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME), + 'notAfter' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true, + 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME) + ) + ); + + $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER); + + $GeneralSubtree = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'base' => $GeneralName, + 'minimum' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true, + 'default' => new Math_BigInteger(0) + ) + $BaseDistance, + 'maximum' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true, + ) + $BaseDistance + ) + ); + + $GeneralSubtrees = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $GeneralSubtree + ); + + $this->NameConstraints = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'permittedSubtrees' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $GeneralSubtrees, + 'excludedSubtrees' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $GeneralSubtrees + ) + ); + + $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING); + + $DisplayText = array( + 'type' => FILE_ASN1_TYPE_CHOICE, + 'children' => array( + 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), + 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING), + 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING), + 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING) + ) + ); + + $NoticeReference = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'organization' => $DisplayText, + 'noticeNumbers' => array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'min' => 1, + 'max' => 200, + 'children' => array('type' => FILE_ASN1_TYPE_INTEGER) + ) + ) + ); + + $this->UserNotice = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'noticeRef' => array( + 'optional' => true, + 'implicit' => true + ) + $NoticeReference, + 'explicitText' => array( + 'optional' => true, + 'implicit' => true + ) + $DisplayText + ) + ); + + // mapping is from + $this->netscape_cert_type = array( + 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'mapping' => array( + 'SSLClient', + 'SSLServer', + 'Email', + 'ObjectSigning', + 'Reserved', + 'SSLCA', + 'EmailCA', + 'ObjectSigningCA' + ) + ); + + $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING); + $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING); + + // attribute is used in RFC2986 but we're using the RFC5280 definition + + $Attribute = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'type' => $AttributeType, + 'value'=> array( + 'type' => FILE_ASN1_TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $this->AttributeValue + ) + ) + ); + + // adapted from + + $Attributes = array( + 'type' => FILE_ASN1_TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $Attribute + ); + + $CertificationRequestInfo = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'version' => array( + 'type' => FILE_ASN1_TYPE_INTEGER, + 'mapping' => array('v1') + ), + 'subject' => $this->Name, + 'subjectPKInfo' => $SubjectPublicKeyInfo, + 'attributes' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $Attributes, + ) + ); + + $this->CertificationRequest = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'certificationRequestInfo' => $CertificationRequestInfo, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + $RevokedCertificate = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'userCertificate' => $CertificateSerialNumber, + 'revocationDate' => $Time, + 'crlEntryExtensions' => array( + 'optional' => true + ) + $this->Extensions + ) + ); + + $TBSCertList = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'version' => array( + 'optional' => true, + 'default' => 'v1' + ) + $Version, + 'signature' => $AlgorithmIdentifier, + 'issuer' => $this->Name, + 'thisUpdate' => $Time, + 'nextUpdate' => array( + 'optional' => true + ) + $Time, + 'revokedCertificates' => array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'optional' => true, + 'min' => 0, + 'max' => -1, + 'children' => $RevokedCertificate + ), + 'crlExtensions' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $this->Extensions + ) + ); + + $this->CertificateList = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'tbsCertList' => $TBSCertList, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER); + + $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED, + 'mapping' => array( + 'unspecified', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + // Value 7 is not used. + 8 => 'removeFromCRL', + 'privilegeWithdrawn', + 'aACompromise' + ) + ); + + $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'distributionPoint' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $DistributionPointName, + 'onlyContainsUserCerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 1, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsCACerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 2, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlySomeReasons' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $ReasonFlags, + 'indirectCRL' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 4, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsAttributeCerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 5, + 'optional' => true, + 'default' => false, + 'implicit' => true + ) + ) + ); + + $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME); + + $this->CertificateIssuer = $GeneralNames; + + $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + + $PublicKeyAndChallenge = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'spki' => $SubjectPublicKeyInfo, + 'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING) + ) + ); + + $this->SignedPublicKeyAndChallenge = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'publicKeyAndChallenge' => $PublicKeyAndChallenge, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 + $this->oids = array( + '1.3.6.1.5.5.7' => 'id-pkix', + '1.3.6.1.5.5.7.1' => 'id-pe', + '1.3.6.1.5.5.7.2' => 'id-qt', + '1.3.6.1.5.5.7.3' => 'id-kp', + '1.3.6.1.5.5.7.48' => 'id-ad', + '1.3.6.1.5.5.7.2.1' => 'id-qt-cps', + '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice', + '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp', + '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers', + '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping', + '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository', + '2.5.4' => 'id-at', + '2.5.4.41' => 'id-at-name', + '2.5.4.4' => 'id-at-surname', + '2.5.4.42' => 'id-at-givenName', + '2.5.4.43' => 'id-at-initials', + '2.5.4.44' => 'id-at-generationQualifier', + '2.5.4.3' => 'id-at-commonName', + '2.5.4.7' => 'id-at-localityName', + '2.5.4.8' => 'id-at-stateOrProvinceName', + '2.5.4.10' => 'id-at-organizationName', + '2.5.4.11' => 'id-at-organizationalUnitName', + '2.5.4.12' => 'id-at-title', + '2.5.4.13' => 'id-at-description', + '2.5.4.46' => 'id-at-dnQualifier', + '2.5.4.6' => 'id-at-countryName', + '2.5.4.5' => 'id-at-serialNumber', + '2.5.4.65' => 'id-at-pseudonym', + '2.5.4.17' => 'id-at-postalCode', + '2.5.4.9' => 'id-at-streetAddress', + '2.5.4.45' => 'id-at-uniqueIdentifier', + '2.5.4.72' => 'id-at-role', + + '0.9.2342.19200300.100.1.25' => 'id-domainComponent', + '1.2.840.113549.1.9' => 'pkcs-9', + '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress', + '2.5.29' => 'id-ce', + '2.5.29.35' => 'id-ce-authorityKeyIdentifier', + '2.5.29.14' => 'id-ce-subjectKeyIdentifier', + '2.5.29.15' => 'id-ce-keyUsage', + '2.5.29.16' => 'id-ce-privateKeyUsagePeriod', + '2.5.29.32' => 'id-ce-certificatePolicies', + '2.5.29.32.0' => 'anyPolicy', + + '2.5.29.33' => 'id-ce-policyMappings', + '2.5.29.17' => 'id-ce-subjectAltName', + '2.5.29.18' => 'id-ce-issuerAltName', + '2.5.29.9' => 'id-ce-subjectDirectoryAttributes', + '2.5.29.19' => 'id-ce-basicConstraints', + '2.5.29.30' => 'id-ce-nameConstraints', + '2.5.29.36' => 'id-ce-policyConstraints', + '2.5.29.31' => 'id-ce-cRLDistributionPoints', + '2.5.29.37' => 'id-ce-extKeyUsage', + '2.5.29.37.0' => 'anyExtendedKeyUsage', + '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth', + '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth', + '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning', + '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection', + '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping', + '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning', + '2.5.29.54' => 'id-ce-inhibitAnyPolicy', + '2.5.29.46' => 'id-ce-freshestCRL', + '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess', + '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess', + '2.5.29.20' => 'id-ce-cRLNumber', + '2.5.29.28' => 'id-ce-issuingDistributionPoint', + '2.5.29.27' => 'id-ce-deltaCRLIndicator', + '2.5.29.21' => 'id-ce-cRLReasons', + '2.5.29.29' => 'id-ce-certificateIssuer', + '2.5.29.23' => 'id-ce-holdInstructionCode', + '1.2.840.10040.2' => 'holdInstruction', + '1.2.840.10040.2.1' => 'id-holdinstruction-none', + '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer', + '1.2.840.10040.2.3' => 'id-holdinstruction-reject', + '2.5.29.24' => 'id-ce-invalidityDate', + + '1.2.840.113549.2.2' => 'md2', + '1.2.840.113549.2.5' => 'md5', + '1.3.14.3.2.26' => 'id-sha1', + '1.2.840.10040.4.1' => 'id-dsa', + '1.2.840.10040.4.3' => 'id-dsa-with-sha1', + '1.2.840.113549.1.1' => 'pkcs-1', + '1.2.840.113549.1.1.1' => 'rsaEncryption', + '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption', + '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption', + '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption', + '1.2.840.10046.2.1' => 'dhpublicnumber', + '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm', + '1.2.840.10045' => 'ansi-X9-62', + '1.2.840.10045.4' => 'id-ecSigType', + '1.2.840.10045.4.1' => 'ecdsa-with-SHA1', + '1.2.840.10045.1' => 'id-fieldType', + '1.2.840.10045.1.1' => 'prime-field', + '1.2.840.10045.1.2' => 'characteristic-two-field', + '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis', + '1.2.840.10045.1.2.3.1' => 'gnBasis', + '1.2.840.10045.1.2.3.2' => 'tpBasis', + '1.2.840.10045.1.2.3.3' => 'ppBasis', + '1.2.840.10045.2' => 'id-publicKeyType', + '1.2.840.10045.2.1' => 'id-ecPublicKey', + '1.2.840.10045.3' => 'ellipticCurve', + '1.2.840.10045.3.0' => 'c-TwoCurve', + '1.2.840.10045.3.0.1' => 'c2pnb163v1', + '1.2.840.10045.3.0.2' => 'c2pnb163v2', + '1.2.840.10045.3.0.3' => 'c2pnb163v3', + '1.2.840.10045.3.0.4' => 'c2pnb176w1', + '1.2.840.10045.3.0.5' => 'c2pnb191v1', + '1.2.840.10045.3.0.6' => 'c2pnb191v2', + '1.2.840.10045.3.0.7' => 'c2pnb191v3', + '1.2.840.10045.3.0.8' => 'c2pnb191v4', + '1.2.840.10045.3.0.9' => 'c2pnb191v5', + '1.2.840.10045.3.0.10' => 'c2pnb208w1', + '1.2.840.10045.3.0.11' => 'c2pnb239v1', + '1.2.840.10045.3.0.12' => 'c2pnb239v2', + '1.2.840.10045.3.0.13' => 'c2pnb239v3', + '1.2.840.10045.3.0.14' => 'c2pnb239v4', + '1.2.840.10045.3.0.15' => 'c2pnb239v5', + '1.2.840.10045.3.0.16' => 'c2pnb272w1', + '1.2.840.10045.3.0.17' => 'c2pnb304w1', + '1.2.840.10045.3.0.18' => 'c2pnb359v1', + '1.2.840.10045.3.0.19' => 'c2pnb368w1', + '1.2.840.10045.3.0.20' => 'c2pnb431r1', + '1.2.840.10045.3.1' => 'primeCurve', + '1.2.840.10045.3.1.1' => 'prime192v1', + '1.2.840.10045.3.1.2' => 'prime192v2', + '1.2.840.10045.3.1.3' => 'prime192v3', + '1.2.840.10045.3.1.4' => 'prime239v1', + '1.2.840.10045.3.1.5' => 'prime239v2', + '1.2.840.10045.3.1.6' => 'prime239v3', + '1.2.840.10045.3.1.7' => 'prime256v1', + '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP', + '1.2.840.113549.1.1.9' => 'id-pSpecified', + '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS', + '1.2.840.113549.1.1.8' => 'id-mgf1', + '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', + '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption', + '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', + '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption', + '2.16.840.1.101.3.4.2.4' => 'id-sha224', + '2.16.840.1.101.3.4.2.1' => 'id-sha256', + '2.16.840.1.101.3.4.2.2' => 'id-sha384', + '2.16.840.1.101.3.4.2.3' => 'id-sha512', + '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94', + '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001', + '1.2.643.2.2.20' => 'id-GostR3410-2001', + '1.2.643.2.2.19' => 'id-GostR3410-94', + // Netscape Object Identifiers from "Netscape Certificate Extensions" + '2.16.840.1.113730' => 'netscape', + '2.16.840.1.113730.1' => 'netscape-cert-extension', + '2.16.840.1.113730.1.1' => 'netscape-cert-type', + '2.16.840.1.113730.1.13' => 'netscape-comment', + '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url', + // the following are X.509 extensions not supported by phpseclib + '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype', + '1.2.840.113533.7.65.0' => 'entrustVersInfo', + '2.16.840.1.113733.1.6.9' => 'verisignPrivate', + // for Certificate Signing Requests + // see http://tools.ietf.org/html/rfc2985 + '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name + '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', // Challenge password for certificate revocations + '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request + ); + } + + /** + * Load X.509 certificate + * + * Returns an associative array describing the X.509 cert or a false if the cert failed to load + * + * @param String $cert + * @access public + * @return Mixed + */ + function loadX509($cert) + { + if (is_array($cert) && isset($cert['tbsCertificate'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + $this->dn = $cert['tbsCertificate']['subject']; + if (!isset($this->dn)) { + return false; + } + $this->currentCert = $cert; + + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL; + + unset($this->signatureSubject); + + return $cert; + } + + $asn1 = new File_ASN1(); + + /* + X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie. + some may have the following preceeding the -----BEGIN CERTIFICATE----- line: + + subject=/O=organization/OU=org unit/CN=common name + issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $cert); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + if ($temp != false) { + $cert = $temp; + } + + if ($cert === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($cert); + + if (!empty($decoded)) { + $x509 = $asn1->asn1map($decoded[0], $this->Certificate); + } + if (!isset($x509) || $x509 === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + + $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; + $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); + + $this->currentCert = $x509; + $this->dn = $x509['tbsCertificate']['subject']; + + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL; + + return $x509; + } + + /** + * Save X.509 certificate + * + * @param Array $cert + * @param Integer $format optional + * @access public + * @return String + */ + function saveX509($cert, $format = FILE_X509_FORMAT_PEM) + { + if (!is_array($cert) || !isset($cert['tbsCertificate'])) { + return false; + } + + switch (true) { + // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()" + case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')): + case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] = + base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + } + } + + $asn1 = new File_ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['tbsCertificate']['signature']['parameters'] = + $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = + $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = + $filters['tbsCertificate']['subject']['rdnSequence']['value'] = + $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = + $filters['signatureAlgorithm']['parameters'] = + $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = + //$filters['policyQualifiers']['qualifier'] = + $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = + $filters['directoryName']['rdnSequence']['value'] = + array('type' => FILE_ASN1_TYPE_UTF8_STRING); + /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING. + FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random + characters. + */ + $filters['policyQualifiers']['qualifier'] = + array('type' => FILE_ASN1_TYPE_IA5_STRING); + + $asn1->loadFilters($filters); + + $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); + + $cert = $asn1->encodeDER($cert, $this->Certificate); + + switch ($format) { + case FILE_X509_FORMAT_DER: + return $cert; + // case FILE_X509_FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; + } + } + + /** + * Map extension values from octet string to extension-specific internal + * format. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapInExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArray($root, $path); + + if (is_array($extensions)) { + for ($i = 0; $i < count($extensions); $i++) { + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; + $value = base64_decode($value); + $decoded = $asn1->decodeBER($value); + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->_getMapping($id); + if (!is_bool($map)) { + $mapped = $asn1->asn1map($decoded[0], $map); + $value = $mapped === false ? $decoded[0] : $mapped; + + if ($id == 'id-ce-certificatePolicies') { + for ($j = 0; $j < count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->_getMapping($subid); + $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== false) { + $decoded = $asn1->decodeBER($subvalue); + $mapped = $asn1->asn1map($decoded[0], $map); + $subvalue = $mapped === false ? $decoded[0] : $mapped; + } + } + } + } + } elseif ($map) { + $value = base64_encode($value); + } + } + } + } + + /** + * Map extension values from extension-specific internal format to + * octet string. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapOutExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArray($root, $path); + + if (is_array($extensions)) { + $size = count($extensions); + for ($i = 0; $i < $size; $i++) { + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; + + switch ($id) { + case 'id-ce-certificatePolicies': + for ($j = 0; $j < count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->_getMapping($subid); + $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== false) { + // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's + // actual type is FILE_ASN1_TYPE_ANY + $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue, $map)); + } + } + } + break; + case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string + if (isset($value['authorityCertSerialNumber'])) { + if ($value['authorityCertSerialNumber']->toBytes() == '') { + $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; + $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp); + } + } + } + + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->_getMapping($id); + if (is_bool($map)) { + if (!$map) { + user_error($id . ' is not a currently supported extension'); + unset($extensions[$i]); + } + } else { + $temp = $asn1->encodeDER($value, $map); + $value = base64_encode($temp); + } + } + } + } + + /** + * Map attribute values from ANY type to attribute-specific internal + * format. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapInAttributes(&$root, $path, $asn1) + { + $attributes = &$this->_subArray($root, $path); + + if (is_array($attributes)) { + for ($i = 0; $i < count($attributes); $i++) { + $id = $attributes[$i]['type']; + /* $value contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $map = $this->_getMapping($id); + if (is_array($attributes[$i]['value'])) { + $values = &$attributes[$i]['value']; + for ($j = 0; $j < count($values); $j++) { + $value = $asn1->encodeDER($values[$j], $this->AttributeValue); + $decoded = $asn1->decodeBER($value); + if (!is_bool($map)) { + $mapped = $asn1->asn1map($decoded[0], $map); + if ($mapped !== false) { + $values[$j] = $mapped; + } + if ($id == 'pkcs-9-at-extensionRequest') { + $this->_mapInExtensions($values, $j, $asn1); + } + } elseif ($map) { + $values[$j] = base64_encode($value); + } + } + } + } + } + } + + /** + * Map attribute values from attribute-specific internal format to + * ANY type. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapOutAttributes(&$root, $path, $asn1) + { + $attributes = &$this->_subArray($root, $path); + + if (is_array($attributes)) { + $size = count($attributes); + for ($i = 0; $i < $size; $i++) { + /* [value] contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $id = $attributes[$i]['type']; + $map = $this->_getMapping($id); + if ($map === false) { + user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + unset($attributes[$i]); + } + elseif (is_array($attributes[$i]['value'])) { + $values = &$attributes[$i]['value']; + for ($j = 0; $j < count($values); $j++) { + switch ($id) { + case 'pkcs-9-at-extensionRequest': + $this->_mapOutExtensions($values, $j, $asn1); + break; + } + + if (!is_bool($map)) { + $temp = $asn1->encodeDER($values[$j], $map); + $decoded = $asn1->decodeBER($temp); + $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue); + } + } + } + } + } + } + + /** + * Associate an extension ID to an extension mapping + * + * @param String $extnId + * @access private + * @return Mixed + */ + function _getMapping($extnId) + { + if (!is_string($extnId)) { // eg. if it's a File_ASN1_Element object + return true; + } + + switch ($extnId) { + case 'id-ce-keyUsage': + return $this->KeyUsage; + case 'id-ce-basicConstraints': + return $this->BasicConstraints; + case 'id-ce-subjectKeyIdentifier': + return $this->KeyIdentifier; + case 'id-ce-cRLDistributionPoints': + return $this->CRLDistributionPoints; + case 'id-ce-authorityKeyIdentifier': + return $this->AuthorityKeyIdentifier; + case 'id-ce-certificatePolicies': + return $this->CertificatePolicies; + case 'id-ce-extKeyUsage': + return $this->ExtKeyUsageSyntax; + case 'id-pe-authorityInfoAccess': + return $this->AuthorityInfoAccessSyntax; + case 'id-ce-subjectAltName': + return $this->SubjectAltName; + case 'id-ce-privateKeyUsagePeriod': + return $this->PrivateKeyUsagePeriod; + case 'id-ce-issuerAltName': + return $this->IssuerAltName; + case 'id-ce-policyMappings': + return $this->PolicyMappings; + case 'id-ce-nameConstraints': + return $this->NameConstraints; + + case 'netscape-cert-type': + return $this->netscape_cert_type; + case 'netscape-comment': + return $this->netscape_comment; + case 'netscape-ca-policy-url': + return $this->netscape_ca_policy_url; + + // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets + // back around to asn1map() and we don't want it decoded again. + //case 'id-qt-cps': + // return $this->CPSuri; + case 'id-qt-unotice': + return $this->UserNotice; + + // the following OIDs are unsupported but we don't want them to give notices when calling saveX509(). + case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt + case 'entrustVersInfo': + // http://support.microsoft.com/kb/287547 + case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION + case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION + // "SET Secure Electronic Transaction Specification" + // http://www.maithean.com/docs/set_bk3.pdf + case '2.23.42.7.0': // id-set-hashedRootKey + return true; + + // CSR attributes + case 'pkcs-9-at-unstructuredName': + return $this->PKCS9String; + case 'pkcs-9-at-challengePassword': + return $this->DirectoryString; + case 'pkcs-9-at-extensionRequest': + return $this->Extensions; + + // CRL extensions. + case 'id-ce-cRLNumber': + return $this->CRLNumber; + case 'id-ce-deltaCRLIndicator': + return $this->CRLNumber; + case 'id-ce-issuingDistributionPoint': + return $this->IssuingDistributionPoint; + case 'id-ce-freshestCRL': + return $this->CRLDistributionPoints; + case 'id-ce-cRLReasons': + return $this->CRLReason; + case 'id-ce-invalidityDate': + return $this->InvalidityDate; + case 'id-ce-certificateIssuer': + return $this->CertificateIssuer; + case 'id-ce-holdInstructionCode': + return $this->HoldInstructionCode; + } + + return false; + } + + /** + * Load an X.509 certificate as a certificate authority + * + * @param String $cert + * @access public + * @return Boolean + */ + function loadCA($cert) + { + $olddn = $this->dn; + $oldcert = $this->currentCert; + $oldsigsubj = $this->signatureSubject; + $oldkeyid = $this->currentKeyIdentifier; + + $cert = $this->loadX509($cert); + if (!$cert) { + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + $this->currentKeyIdentifier = $oldkeyid; + + return false; + } + + /* From RFC5280 "PKIX Certificate and CRL Profile": + + If the keyUsage extension is present, then the subject public key + MUST NOT be used to verify signatures on certificates or CRLs unless + the corresponding keyCertSign or cRLSign bit is set. */ + //$keyUsage = $this->getExtension('id-ce-keyUsage'); + //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) { + // return false; + //} + + /* From RFC5280 "PKIX Certificate and CRL Profile": + + The cA boolean indicates whether the certified public key may be used + to verify certificate signatures. If the cA boolean is not asserted, + then the keyCertSign bit in the key usage extension MUST NOT be + asserted. If the basic constraints extension is not present in a + version 3 certificate, or the extension is present but the cA boolean + is not asserted, then the certified public key MUST NOT be used to + verify certificate signatures. */ + //$basicConstraints = $this->getExtension('id-ce-basicConstraints'); + //if (!$basicConstraints || !$basicConstraints['cA']) { + // return false; + //} + + $this->CAs[] = $cert; + + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + + return true; + } + + /** + * Validate an X.509 certificate against a URL + * + * From RFC2818 "HTTP over TLS": + * + * Matching is performed using the matching rules specified by + * [RFC2459]. If more than one identity of a given type is present in + * the certificate (e.g., more than one dNSName name, a match in any one + * of the set is considered acceptable.) Names may contain the wildcard + * character * which is considered to match any single domain name + * component or component fragment. E.g., *.a.com matches foo.a.com but + * not bar.foo.a.com. f*.com matches foo.com but not bar.com. + * + * @param String $url + * @access public + * @return Boolean + */ + function validateURL($url) + { + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + + $components = parse_url($url); + if (!isset($components['host'])) { + return false; + } + + if ($names = $this->getExtension('id-ce-subjectAltName')) { + foreach ($names as $key => $value) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); + switch ($key) { + case 'dNSName': + /* From RFC2818 "HTTP over TLS": + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. */ + if (preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + break; + case 'iPAddress': + /* From RFC2818 "HTTP over TLS": + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. */ + if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + } + } + return false; + } + + if ($value = $this->getDNProp('id-at-commonName')) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]); + return preg_match('#^' . $value . '$#', $components['host']); + } + + return false; + } + + /** + * Validate a date + * + * If $date isn't defined it is assumed to be the current date. + * + * @param Integer $date optional + * @access public + */ + function validateDate($date = NULL) + { + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + + if (!isset($date)) { + $date = time(); + } + + $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; + $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime']; + + $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; + $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; + + switch (true) { + case $date < @strtotime($notBefore): + case $date > @strtotime($notAfter): + return false; + } + + return true; + } + + /** + * Validate a signature + * + * Works on X.509 certs, CSR's and CRL's. + * Returns true if the signature is verified, false if it is not correct or NULL on error + * + * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. + * + * @param Integer $options optional + * @access public + * @return Mixed + */ + function validateSignature($options = 0) + { + if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { + return 0; + } + + /* TODO: + "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 + + implement pathLenConstraint in the id-ce-basicConstraints extension */ + + switch (true) { + case isset($this->currentCert['tbsCertificate']): + // self-signed cert + if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $this->currentCert; // working cert + } + } + + if (!empty($this->CAs)) { + for ($i = 0; $i < count($this->CAs); $i++) { + // even if the cert is a self-signed one we still want to see if it's a CA; + // if not, we'll conditionally return an error + $ca = $this->CAs[$i]; + if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 2; + } + } + } + if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + return false; + } + } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + return false; + } + return $this->_validateSignature( + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['certificationRequestInfo']): + return $this->_validateSignature( + $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], + $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['publicKeyAndChallenge']): + return $this->_validateSignature( + $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], + $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['tbsCertList']): + if (!empty($this->CAs)) { + for ($i = 0; $i < count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 2; + } + } + } + } + if (!isset($signingCert)) { + return false; + } + return $this->_validateSignature( + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + default: + return false; + } + } + + /** + * Validates a signature + * + * Returns true if the signature is verified, false if it is not correct or NULL on error + * + * @param String $publicKeyAlgorithm + * @param String $publicKey + * @param String $signatureAlgorithm + * @param String $signature + * @param String $signatureSubject + * @access private + * @return Integer + */ + function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) + { + switch ($publicKeyAlgorithm) { + case 'rsaEncryption': + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $rsa = new Crypt_RSA(); + $rsa->loadKey($publicKey); + + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); + if (!@$rsa->verify($signatureSubject, $signature)) { + return false; + } + break; + default: + return NULL; + } + break; + default: + return NULL; + } + + return true; + } + + /** + * Reformat public keys + * + * Reformats a public key to a format supported by phpseclib (if applicable) + * + * @param String $algorithm + * @param String $key + * @access private + * @return String + */ + function _reformatKey($algorithm, $key) + { + switch ($algorithm) { + case 'rsaEncryption': + return + "-----BEGIN PUBLIC KEY-----\r\n" . + // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits + // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox + // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do. + chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . + '-----END PUBLIC KEY-----'; + default: + return $key; + } + } + + /** + * "Normalizes" a Distinguished Name property + * + * @param String $propName + * @access private + * @return Mixed + */ + function _translateDNProp($propName) + { + switch (strtolower($propName)) { + case 'id-at-countryname': + case 'countryname': + case 'c': + return 'id-at-countryName'; + case 'id-at-organizationname': + case 'organizationname': + case 'o': + return 'id-at-organizationName'; + case 'id-at-dnqualifier': + case 'dnqualifier': + return 'id-at-dnQualifier'; + case 'id-at-commonname': + case 'commonname': + case 'cn': + return 'id-at-commonName'; + case 'id-at-stateorprovinceName': + case 'stateorprovincename': + case 'state': + case 'province': + case 'provincename': + case 'st': + return 'id-at-stateOrProvinceName'; + case 'id-at-localityname': + case 'localityname': + case 'l': + return 'id-at-localityName'; + case 'id-emailaddress': + case 'emailaddress': + return 'pkcs-9-at-emailAddress'; + case 'id-at-serialnumber': + case 'serialnumber': + return 'id-at-serialNumber'; + case 'id-at-postalcode': + case 'postalcode': + return 'id-at-postalCode'; + case 'id-at-streetaddress': + case 'streetaddress': + return 'id-at-streetAddress'; + case 'id-at-name': + case 'name': + return 'id-at-name'; + case 'id-at-givenname': + case 'givenname': + return 'id-at-givenName'; + case 'id-at-surname': + case 'surname': + case 'sn': + return 'id-at-surname'; + case 'id-at-initials': + case 'initials': + return 'id-at-initials'; + case 'id-at-generationqualifier': + case 'generationqualifier': + return 'id-at-generationQualifier'; + case 'id-at-organizationalunitname': + case 'organizationalunitname': + case 'ou': + return 'id-at-organizationalUnitName'; + case 'id-at-pseudonym': + case 'pseudonym': + return 'id-at-pseudonym'; + case 'id-at-title': + case 'title': + return 'id-at-title'; + case 'id-at-description': + case 'description': + return 'id-at-description'; + case 'id-at-role': + case 'role': + return 'id-at-role'; + case 'id-at-uniqueidentifier': + case 'uniqueidentifier': + case 'x500uniqueidentifier': + return 'id-at-uniqueIdentifier'; + default: + return false; + } + } + + /** + * Set a Distinguished Name property + * + * @param String $propName + * @param Mixed $propValue + * @param String $type optional + * @access public + * @return Boolean + */ + function setDNProp($propName, $propValue, $type = 'utf8String') + { + if (empty($this->dn)) { + $this->dn = array('rdnSequence' => array()); + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return false; + } + + foreach ((array) $propValue as $v) { + if (!is_array($v) && isset($type)) { + $v = array($type => $v); + } + $this->dn['rdnSequence'][] = array( + array( + 'type' => $propName, + 'value'=> $v + ) + ); + } + + return true; + } + + /** + * Remove Distinguished Name properties + * + * @param String $propName + * @access public + */ + function removeDNProp($propName) + { + if (empty($this->dn)) { + return; + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return; + } + + $dn = &$this->dn['rdnSequence']; + $size = count($dn); + for ($i = 0; $i < $size; $i++) { + if ($dn[$i][0]['type'] == $propName) { + unset($dn[$i]); + } + } + + $dn = array_values($dn); + } + + /** + * Get Distinguished Name properties + * + * @param String $propName + * @param Array $dn optional + * @param Boolean $withType optional + * @return Mixed + * @access public + */ + function getDNProp($propName, $dn = NULL, $withType = false) + { + if (!isset($dn)) { + $dn = $this->dn; + } + + if (empty($dn)) { + return false; + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return false; + } + + $dn = $dn['rdnSequence']; + $result = array(); + $asn1 = new File_ASN1(); + for ($i = 0; $i < count($dn); $i++) { + if ($dn[$i][0]['type'] == $propName) { + $v = $dn[$i][0]['value']; + if (!$withType && is_array($v)) { + foreach ($v as $type => $s) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $s = $asn1->convert($s, $type); + if ($s !== false) { + $v = $s; + break; + } + } + } + if (is_array($v)) { + $v = array_pop($v); // Always strip data type. + } + } + $result[] = $v; + } + } + + return $result; + } + + /** + * Set a Distinguished Name + * + * @param Mixed $dn + * @param Boolean $merge optional + * @param String $type optional + * @access public + * @return Boolean + */ + function setDN($dn, $merge = false, $type = 'utf8String') + { + if (!$merge) { + $this->dn = NULL; + } + + if (is_array($dn)) { + if (isset($dn['rdnSequence'])) { + $this->dn = $dn; // No merge here. + return true; + } + + // handles stuff generated by openssl_x509_parse() + foreach ($dn as $prop => $value) { + if (!$this->setDNProp($prop, $value, $type)) { + return false; + } + } + return true; + } + + // handles everything else + $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 1; $i < count($results); $i+=2) { + $prop = trim($results[$i], ', =/'); + $value = $results[$i + 1]; + if (!$this->setDNProp($prop, $value, $type)) { + return false; + } + } + + return true; + } + + /** + * Get the Distinguished Name for a certificates subject + * + * @param Mixed $format optional + * @param Array $dn optional + * @access public + * @return Boolean + */ + function getDN($format = FILE_X509_DN_ARRAY, $dn = NULL) + { + if (!isset($dn)) { + $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; + } + + switch ((int) $format) { + case FILE_X509_DN_ARRAY: + return $dn; + case FILE_X509_DN_ASN1: + $asn1 = new File_ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + return $asn1->encodeDER($dn, $this->Name); + case FILE_X509_DN_OPENSSL: + $dn = $this->getDN(FILE_X509_DN_STRING, $dn); + if ($dn === false) { + return false; + } + $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + $dn = array(); + for ($i = 1; $i < count($attrs); $i += 2) { + $prop = trim($attrs[$i], ', =/'); + $value = $attrs[$i + 1]; + if (!isset($dn[$prop])) { + $dn[$prop] = $value; + } else { + $dn[$prop] = array_merge((array) $dn[$prop], array($value)); + } + } + return $dn; + case FILE_X509_DN_CANON: + // No SEQUENCE around RDNs and all string values normalized as + // trimmed lowercase UTF-8 with all spacing as one blank. + $asn1 = new File_ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $result = ''; + foreach ($dn['rdnSequence'] as $rdn) { + foreach ($rdn as &$attr) { + if (is_array($attr['value'])) { + foreach ($attr['value'] as $type => $v) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $v = $asn1->convert($v, $type); + if ($v !== false) { + $v = preg_replace('/\s+/', ' ', $v); + $attr['value'] = strtolower(trim($v)); + break; + } + } + } + } + } + $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName); + } + return $result; + case FILE_X509_DN_HASH: + $dn = $this->getDN(FILE_X509_DN_CANON, $dn); + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $hash = new Crypt_Hash('sha1'); + $hash = $hash->hash($dn); + extract(unpack('Vhash', $hash)); + return strtolower(bin2hex(pack('N', $hash))); + } + + // Defaut is to return a string. + $start = true; + $output = ''; + $asn1 = new File_ASN1(); + foreach ($dn['rdnSequence'] as $field) { + $prop = $field[0]['type']; + $value = $field[0]['value']; + + $delim = ', '; + switch ($prop) { + case 'id-at-countryName': + $desc = 'C='; + break; + case 'id-at-stateOrProvinceName': + $desc = 'ST='; + break; + case 'id-at-organizationName': + $desc = 'O='; + break; + case 'id-at-organizationalUnitName': + $desc = 'OU='; + break; + case 'id-at-commonName': + $desc = 'CN='; + break; + case 'id-at-localityName': + $desc = 'L='; + break; + case 'id-at-surname': + $desc = 'SN='; + break; + case 'id-at-uniqueIdentifier': + $delim = '/'; + $desc = 'x500UniqueIdentifier='; + break; + default: + $delim = '/'; + $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '='; + } + + if (!$start) { + $output.= $delim; + } + if (is_array($value)) { + foreach ($value as $type => $v) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $v = $asn1->convert($v, $type); + if ($v !== false) { + $value = $v; + break; + } + } + } + if (is_array($value)) { + $value = array_pop($value); // Always strip data type. + } + } + $output.= $desc . $value; + $start = false; + } + + return $output; + } + + /** + * Get the Distinguished Name for a certificate/crl issuer + * + * @param Integer $format optional + * @access public + * @return Mixed + */ + function getIssuerDN($format = FILE_X509_DN_ARRAY) + { + switch (true) { + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']); + case isset($this->currentCert['tbsCertList']): + return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']); + } + + return false; + } + + /** + * Get the Distinguished Name for a certificate/csr subject + * Alias of getDN() + * + * @param Integer $format optional + * @access public + * @return Mixed + */ + function getSubjectDN($format = FILE_X509_DN_ARRAY) + { + switch (true) { + case !empty($this->dn): + return $this->getDN($format); + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']); + } + + return false; + } + + /** + * Get an individual Distinguished Name property for a certificate/crl issuer + * + * @param String $propName + * @param Boolean $withType optional + * @access public + * @return Mixed + */ + function getIssuerDNProp($propName, $withType = false) + { + switch (true) { + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propname, $this->currentCert['tbsCertificate']['issuer'], $withType); + case isset($this->currentCert['tbsCertList']): + return $this->getDNProp($propname, $this->currentCert['tbsCertList']['issuer'], $withType); + } + + return false; + } + + /** + * Get an individual Distinguished Name property for a certificate/csr subject + * + * @param String $propName + * @param Boolean $withType optional + * @access public + * @return Mixed + */ + function getSubjectDNProp($propName, $withType = false) + { + switch (true) { + case !empty($this->dn): + return $this->getDNProp($propName, NULL, $withType); + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDNProp($propname, $this->currentCert['certificationRequestInfo']['subject'], $withType); + } + + return false; + } + + /** + * Get the certificate chain for the current cert + * + * @access public + * @return Mixed + */ + function getChain() + { + $chain = array($this->currentCert); + + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + if (empty($this->CAs)) { + return $chain; + } + while (true) { + $currentCert = $chain[count($chain) - 1]; + for ($i = 0; $i < count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if ($currentCert === $ca) { + break 3; + } + $chain[] = $ca; + break 2; + } + } + } + if ($i == count($this->CAs)) { + break; + } + } + foreach ($chain as $key=>$value) { + $chain[$key] = new File_X509(); + $chain[$key]->loadX509($value); + } + return $chain; + } + + /** + * Set public key + * + * Key needs to be a Crypt_RSA object + * + * @param Object $key + * @access public + * @return Boolean + */ + function setPublicKey($key) + { + $this->publicKey = $key; + } + + /** + * Set private key + * + * Key needs to be a Crypt_RSA object + * + * @param Object $key + * @access public + */ + function setPrivateKey($key) + { + $this->privateKey = $key; + } + + /** + * Gets the public key + * + * Returns a Crypt_RSA object or a false. + * + * @access public + * @return Mixed + */ + function getPublicKey() + { + if (isset($this->publicKey)) { + return $this->publicKey; + } + + if (isset($this->currentCert) && is_array($this->currentCert)) { + foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) { + $keyinfo = $this->_subArray($this->currentCert, $path); + if (!empty($keyinfo)) { + break; + } + } + } + if (empty($keyinfo)) { + return false; + } + + $key = $keyinfo['subjectPublicKey']; + + switch ($keyinfo['algorithm']['algorithm']) { + case 'rsaEncryption': + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $publicKey = new Crypt_RSA(); + $publicKey->loadKey($key); + $publicKey->setPublicKey(); + break; + default: + return false; + } + + return $publicKey; + } + + /** + * Load a Certificate Signing Request + * + * @param String $csr + * @access public + * @return Mixed + */ + function loadCSR($csr) + { + if (is_array($csr) && isset($csr['certificationRequestInfo'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->dn = $csr['certificationRequestInfo']['subject']; + if (!isset($this->dn)) { + return false; + } + + $this->currentCert = $csr; + return $csr; + } + + // see http://tools.ietf.org/html/rfc2986 + + $asn1 = new File_ASN1(); + + $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $csr); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + if ($temp != false) { + $csr = $temp; + } + $orig = $csr; + + if ($csr === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($csr); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest); + if (!isset($csr) || $csr === false) { + $this->currentCert = false; + return false; + } + + $this->dn = $csr['certificationRequestInfo']['subject']; + $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']; + $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']; + $key = $this->_reformatKey($algorithm, $key); + + switch ($algorithm) { + case 'rsaEncryption': + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $this->publicKey = new Crypt_RSA(); + $this->publicKey->loadKey($key); + $this->publicKey->setPublicKey(); + break; + default: + $this->publicKey = NULL; + } + + $this->currentKeyIdentifier = NULL; + $this->currentCert = $csr; + + return $csr; + } + + /** + * Save CSR request + * + * @param Array $csr + * @param Integer $format optional + * @access public + * @return String + */ + function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) + { + if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { + return false; + } + + switch (true) { + case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): + case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']); + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] = + base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); + } + } + + $asn1 = new File_ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] = + array('type' => FILE_ASN1_TYPE_UTF8_STRING); + + $asn1->loadFilters($filters); + + $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1); + $csr = $asn1->encodeDER($csr, $this->CertificationRequest); + + switch ($format) { + case FILE_X509_FORMAT_DER: + return $csr; + // case FILE_X509_FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + } + } + + /** + * Load a SPKAC CSR + * + * SPKAC's are produced by the HTML5 keygen element: + * + * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen + * + * @param String $csr + * @access public + * @return Mixed + */ + function loadSPKAC($csr) + { + if (is_array($csr) && isset($csr['publicKeyAndChallenge'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->currentCert = $csr; + return $csr; + } + + // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge + + $asn1 = new File_ASN1(); + + $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $csr); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + if ($temp != false) { + $csr = $temp; + } + $orig = $csr; + + if ($csr === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($csr); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $csr = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); + + if (!isset($csr) || $csr === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $algorithm = &$csr['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; + $key = &$csr['publicKeyAndChallenge']['spki']['subjectPublicKey']; + $key = $this->_reformatKey($algorithm, $key); + + switch ($algorithm) { + case 'rsaEncryption': + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $this->publicKey = new Crypt_RSA(); + $this->publicKey->loadKey($key); + $this->publicKey->setPublicKey(); + break; + default: + $this->publicKey = NULL; + } + + $this->currentKeyIdentifier = NULL; + $this->currentCert = $csr; + + return $csr; + } + + /** + * Load a Certificate Revocation List + * + * @param String $crl + * @access public + * @return Mixed + */ + function loadCRL($crl) + { + if (is_array($crl) && isset($crl['tbsCertList'])) { + $this->currentCert = $crl; + unset($this->signatureSubject); + return $crl; + } + + $asn1 = new File_ASN1(); + + $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $crl); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + if ($temp != false) { + $crl = $temp; + } + $orig = $crl; + + if ($crl === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($crl); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $crl = $asn1->asn1map($decoded[0], $this->CertificateList); + if (!isset($crl) || $crl === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); + } + } + + $this->currentKeyIdentifier = NULL; + $this->currentCert = $crl; + + return $crl; + } + + /** + * Save Certificate Revocation List. + * + * @param Array $crl + * @param Integer $format optional + * @access public + * @return String + */ + function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) + { + if (!is_array($crl) || !isset($crl['tbsCertList'])) { + return false; + } + + $asn1 = new File_ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['tbsCertList']['issuer']['rdnSequence']['value'] = + $filters['tbsCertList']['signature']['parameters'] = + $filters['signatureAlgorithm']['parameters'] = + array('type' => FILE_ASN1_TYPE_UTF8_STRING); + + if (empty($crl['tbsCertList']['signature']['parameters'])) { + $filters['tbsCertList']['signature']['parameters'] = + array('type' => FILE_ASN1_TYPE_NULL); + } + + if (empty($crl['signatureAlgorithm']['parameters'])) { + $filters['signatureAlgorithm']['parameters'] = + array('type' => FILE_ASN1_TYPE_NULL); + } + + $asn1->loadFilters($filters); + + $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); + } + } + + $crl = $asn1->encodeDER($crl, $this->CertificateList); + + switch ($format) { + case FILE_X509_FORMAT_DER: + return $crl; + // case FILE_X509_FORMAT_PEM: + default: + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; + } + } + + /** + * Sign an X.509 certificate + * + * $issuer's private key needs to be loaded. + * $subject can be either an existing X.509 cert (if you want to resign it), + * a CSR or something with the DN and public key explicitly set. + * + * @param File_X509 $issuer + * @param File_X509 $subject + * @param String $signatureAlgorithm optional + * @access public + * @return Mixed + */ + function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($issuer->privateKey) || empty($issuer->dn)) { + return false; + } + + if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) { + return false; + } + + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL; + + if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { + $this->currentCert = $subject->currentCert; + $this->currentCert['tbsCertificate']['signature']['algorithm'] = + $this->currentCert['signatureAlgorithm']['algorithm'] = + $signatureAlgorithm; + if (!empty($this->startDate)) { + $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate; + unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']); + } + if (!empty($this->endDate)) { + $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate; + unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']); + } + if (!empty($this->serialNumber)) { + $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; + } + if (!empty($subject->dn)) { + $this->currentCert['tbsCertificate']['subject'] = $subject->dn; + } + if (!empty($subject->publicKey)) { + $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey; + } + $this->removeExtension('id-ce-authorityKeyIdentifier'); + if (isset($subject->domains)) { + $this->removeExtension('id-ce-subjectAltName'); + } + } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + return false; + } else { + if (!isset($subject->publicKey)) { + return false; + } + + $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); + $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M y H:i:s O', strtotime('+1 year')); + $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger(); + + $this->currentCert = array( + 'tbsCertificate' => + array( + 'version' => 'v3', + 'serialNumber' => $serialNumber, // $this->setserialNumber() + 'signature' => array('algorithm' => $signatureAlgorithm), + 'issuer' => false, // this is going to be overwritten later + 'validity' => array( + 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate() + 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate() + ), + 'subject' => $subject->dn, + 'subjectPublicKeyInfo' => $subjectPublicKey + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + + // Copy extensions from CSR. + $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0); + + if (!empty($csrexts)) { + $this->currentCert['tbsCertificate']['extensions'] = $csrexts; + } + } + + $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; + + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', array( + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier + ) + ); + //$extensions = &$this->currentCert['tbsCertificate']['extensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + + if (isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); + } + + if (isset($subject->domains) && count($subject->domains) > 1) { + $this->setExtension('id-ce-subjectAltName', + array_map(array('File_X509', '_dnsName'), $subject->domains)); + } + + if ($this->caFlag) { + $keyUsage = $this->getExtension('id-ce-keyUsage'); + if (!$keyUsage) { + $keyUsage = array(); + } + + $this->setExtension('id-ce-keyUsage', + array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) + ); + + $basicConstraints = $this->getExtension('id-ce-basicConstraints'); + if (!$basicConstraints) { + $basicConstraints = array(); + } + + $this->setExtension('id-ce-basicConstraints', + array_unique(array_merge(array('cA' => true), $basicConstraints)), true); + + if (!isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); + } + } + + // resync $this->signatureSubject + // save $tbsCertificate in case there are any File_ASN1_Element objects in it + $tbsCertificate = $this->currentCert['tbsCertificate']; + $this->loadX509($this->saveX509($this->currentCert)); + + $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); + $result['tbsCertificate'] = $tbsCertificate; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * Sign a CSR + * + * @access public + * @return Mixed + */ + function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($this->privateKey) || empty($this->dn)) { + return false; + } + + $origPublicKey = $this->publicKey; + $class = get_class($this->privateKey); + $this->publicKey = new $class(); + $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->setPublicKey(); + if (!($publicKey = $this->_formatSubjectPublicKey())) { + return false; + } + $this->publicKey = $origPublicKey; + + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL; + + if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { + $this->currentCert['signatureAlgorithm']['algorithm'] = + $signatureAlgorithm; + if (!empty($this->dn)) { + $this->currentCert['certificationRequestInfo']['subject'] = $this->dn; + } + $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; + } else { + $this->currentCert = array( + 'certificationRequestInfo' => + array( + 'version' => 'v1', + 'subject' => $this->dn, + 'subjectPKInfo' => $publicKey + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + // resync $this->signatureSubject + // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it + $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; + $this->loadCSR($this->saveCSR($this->currentCert)); + + $result = $this->_sign($this->privateKey, $signatureAlgorithm); + $result['certificationRequestInfo'] = $certificationRequestInfo; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * Sign a CRL + * + * $issuer's private key needs to be loaded. + * + * @param File_X509 $issuer + * @param File_X509 $crl + * @param String $signatureAlgorithm optional + * @access public + * @return Mixed + */ + function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($issuer->privateKey) || empty($issuer->dn)) { + return false; + } + + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : NULL; + $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); + + if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { + $this->currentCert = $crl->currentCert; + $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + } else { + $this->currentCert = array( + 'tbsCertList' => + array( + 'version' => 'v2', + 'signature' => array('algorithm' => $signatureAlgorithm), + 'issuer' => false, // this is going to be overwritten later + 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate() + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + $tbsCertList = &$this->currentCert['tbsCertList']; + $tbsCertList['issuer'] = $issuer->dn; + $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate); + + if (!empty($this->endDate)) { + $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate() + } else { + unset($tbsCertList['nextUpdate']); + } + + if (!empty($this->serialNumber)) { + $crlNumber = $this->serialNumber; + } + else { + $crlNumber = $this->getExtension('id-ce-cRLNumber'); + $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : NULL; + } + + $this->removeExtension('id-ce-authorityKeyIdentifier'); + $this->removeExtension('id-ce-issuerAltName'); + + // Be sure version >= v2 if some extension found. + $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; + if (!$version) { + if (!empty($tbsCertList['crlExtensions'])) { + $version = 1; // v2. + } + elseif (!empty($tbsCertList['revokedCertificates'])) { + foreach ($tbsCertList['revokedCertificates'] as $cert) { + if (!empty($cert['crlEntryExtensions'])) { + $version = 1; // v2. + } + } + } + + if ($version) { + $tbsCertList['version'] = $version; + } + } + + // Store additional extensions. + if (!empty($tbsCertList['version'])) { // At least v2. + if (!empty($crlNumber)) { + $this->setExtension('id-ce-cRLNumber', $crlNumber); + } + + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', array( + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier + ) + ); + //$extensions = &$tbsCertList['crlExtensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + + $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); + + if ($issuerAltName !== false) { + $this->setExtension('id-ce-issuerAltName', $issuerAltName); + } + } + + if (empty($tbsCertList['revokedCertificates'])) { + unset($tbsCertList['revokedCertificates']); + } + + unset($tbsCertList); + + // resync $this->signatureSubject + // save $tbsCertList in case there are any File_ASN1_Element objects in it + $tbsCertList = $this->currentCert['tbsCertList']; + $this->loadCRL($this->saveCRL($this->currentCert)); + + $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); + $result['tbsCertList'] = $tbsCertList; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * X.509 certificate signing helper function. + * + * @param Object $key + * @param File_X509 $subject + * @param String $signatureAlgorithm + * @access public + * @return Mixed + */ + function _sign($key, $signatureAlgorithm) + { + switch (strtolower(get_class($key))) { + case 'crypt_rsa': + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); + + $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); + return $this->currentCert; + } + default: + return false; + } + } + + /** + * Set certificate start date + * + * @param String $date + * @access public + */ + function setStartDate($date) + { + $this->startDate = @date('D, d M y H:i:s O', @strtotime($date)); + } + + /** + * Set certificate end date + * + * @param String $date + * @access public + */ + function setEndDate($date) + { + /* + To indicate that a certificate has no well-defined expiration date, + the notAfter SHOULD be assigned the GeneralizedTime value of + 99991231235959Z. + + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5 + */ + if (strtolower($date) == 'lifetime') { + $temp = '99991231235959Z'; + $asn1 = new File_ASN1(); + $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; + $this->endDate = new File_ASN1_Element($temp); + } else { + $this->endDate = @date('D, d M y H:i:s O', @strtotime($date)); + } + } + + /** + * Set Serial Number + * + * @param String $serial + * @param $base optional + * @access public + */ + function setSerialNumber($serial, $base = -256) + { + $this->serialNumber = new Math_BigInteger($serial, $base); + } + + /** + * Turns the certificate into a certificate authority + * + * @access public + */ + function makeCA() + { + $this->caFlag = true; + } + + /** + * Get a reference to a subarray + * + * @param array $root + * @param String $path absolute path with / as component separator + * @param Boolean $create optional + * @access private + * @return array item ref or false + */ + function &_subArray(&$root, $path, $create = false) + { + $false = false; + + if (!is_array($root)) { + return $false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return $false; + } + + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + + /** + * Get a reference to an extension subarray + * + * @param array $root + * @param String $path optional absolute path with / as component separator + * @param Boolean $create optional + * @access private + * @return array ref or false + */ + function &_extensions(&$root, $path = NULL, $create = false) + { + if (!isset($root)) { + $root = $this->currentCert; + } + + switch (true) { + case !empty($path): + case !is_array($root): + break; + case isset($root['tbsCertificate']): + $path = 'tbsCertificate/extensions'; + break; + case isset($root['tbsCertList']): + $path = 'tbsCertList/crlExtensions'; + break; + case isset($root['certificationRequestInfo']): + $pth = 'certificationRequestInfo/attributes'; + $attributes = &$this->_subArray($root, $pth, $create); + + if (is_array($attributes)) { + foreach ($attributes as $key => $value) { + if ($value['type'] == 'pkcs-9-at-extensionRequest') { + $path = "$pth/$key/value/0"; + break 2; + } + } + if ($create) { + $key = count($attributes); + $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array()); + $path = "$pth/$key/value/0"; + } + } + break; + } + + $extensions = &$this->_subArray($root, $path, $create); + + if (!is_array($extensions)) { + $false = false; + return $false; + } + + return $extensions; + } + + /** + * Remove an Extension + * + * @param String $id + * @param String $path optional + * @access private + * @return Boolean + */ + function _removeExtension($id, $path = NULL) + { + $extensions = &$this->_extensions($this->currentCert, $path); + + if (!is_array($extensions)) { + return false; + } + + $result = false; + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + unset($extensions[$key]); + $result = true; + } + } + + $extensions = array_values($extensions); + return $result; + } + + /** + * Get an Extension + * + * Returns the extension if it exists and false if not + * + * @param String $id + * @param Array $cert optional + * @param String $path optional + * @access private + * @return Mixed + */ + function _getExtension($id, $cert = NULL, $path = NULL) + { + $extensions = $this->_extensions($cert, $path); + + if (!is_array($extensions)) { + return false; + } + + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + return $value['extnValue']; + } + } + + return false; + } + + /** + * Returns a list of all extensions in use + * + * @param array $cert optional + * @param String $path optional + * @access private + * @return Array + */ + function _getExtensions($cert = NULL, $path = NULL) + { + $exts = $this->_extensions($cert, $path); + $extensions = array(); + + if (is_array($exts)) { + foreach ($exts as $extension) { + $extensions[] = $extension['extnId']; + } + } + + return $extensions; + } + + /** + * Set an Extension + * + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @param String $path optional + * @access private + * @return Boolean + */ + function _setExtension($id, $value, $critical = false, $replace = true, $path = NULL) + { + $extensions = &$this->_extensions($this->currentCert, $path, true); + + if (!is_array($extensions)) { + return false; + } + + $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value); + + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + if (!$replace) { + return false; + } + + $extensions[$key] = $newext; + return true; + } + } + + $extensions[] = $newext; + return true; + } + + /** + * Remove a certificate, CSR or CRL Extension + * + * @param String $id + * @access public + * @return Boolean + */ + function removeExtension($id) + { + return $this->_removeExtension($id); + } + + /** + * Get a certificate, CSR or CRL Extension + * + * Returns the extension if it exists and false if not + * + * @param String $id + * @param Array $cert optional + * @access public + * @return Mixed + */ + function getExtension($id, $cert = NULL) + { + return $this->_getExtension($id, $cert); + } + + /** + * Returns a list of all extensions in use in certificate, CSR or CRL + * + * @param array $cert optional + * @access public + * @return Array + */ + function getExtensions($cert = NULL) + { + return $this->_getExtensions($cert); + } + + /** + * Set a certificate, CSR or CRL Extension + * + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @access public + * @return Boolean + */ + function setExtension($id, $value, $critical = false, $replace = true) + { + return $this->_setExtension($id, $value, $critical, $replace); + } + + /** + * Remove a CSR attribute. + * + * @param String $id + * @param Integer $disposition optional + * @access public + * @return Boolean + */ + function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) + { + $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes'); + + if (!is_array($attributes)) { + return false; + } + + $result = false; + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == FILE_X509_ATTR_APPEND: + case $disposition == FILE_X509_ATTR_REPLACE: + return false; + case $disposition >= $n: + $disposition -= $n; + break; + case $disposition == FILE_X509_ATTR_ALL: + case $n == 1: + unset($attributes[$key]); + $result = true; + break; + default: + unset($attributes[$key]['value'][$disposition]); + $attributes[$key]['value'] = array_values($attributes[$key]['value']); + $result = true; + break; + } + if ($result && $disposition != FILE_X509_ATTR_ALL) { + break; + } + } + } + + $attributes = array_values($attributes); + return $result; + } + + /** + * Get a CSR attribute + * + * Returns the attribute if it exists and false if not + * + * @param String $id + * @param Integer $disposition optional + * @param Array $csr optional + * @access public + * @return Mixed + */ + function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = NULL) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + + $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); + + if (!is_array($attributes)) { + return false; + } + + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == FILE_X509_ATTR_APPEND: + case $disposition == FILE_X509_ATTR_REPLACE: + return false; + case $disposition == FILE_X509_ATTR_ALL: + return $attribute['value']; + case $disposition >= $n: + $disposition -= $n; + break; + default: + return $attribute['value'][$disposition]; + } + } + } + + return false; + } + + /** + * Returns a list of all CSR attributes in use + * + * @param array $csr optional + * @access public + * @return Array + */ + function getAttributes($csr = NULL) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + + $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); + $attrs = array(); + + if (is_array($attributes)) { + foreach ($attributes as $attribute) { + $attrs[] = $attribute['type']; + } + } + + return $attrs; + } + + /** + * Set a CSR attribute + * + * @param String $id + * @param Mixed $value + * @param Boolean $disposition optional + * @access public + * @return Boolean + */ + function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) + { + $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true); + + if (!is_array($attributes)) { + return false; + } + + switch ($disposition) { + case FILE_X509_ATTR_REPLACE: + $disposition = FILE_X509_ATTR_APPEND; + case FILE_X509_ATTR_ALL: + $this->removeAttribute($id); + break; + } + + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == FILE_X509_ATTR_APPEND: + $last = $key; + break; + case $disposition >= $n; + $disposition -= $n; + break; + default: + $attributes[$key]['value'][$disposition] = $value; + return true; + } + } + } + + switch (true) { + case $disposition >= 0: + return false; + case isset($last): + $attributes[$last]['value'][] = $value; + break; + default: + $attributes[] = array('type' => $id, 'value' => $disposition == FILE_X509_ATTR_ALL ? $value: array($value)); + break; + } + + return true; + } + + /** + * Sets the subject key identifier + * + * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. + * + * @param String $value + * @access public + */ + function setKeyIdentifier($value) + { + if (empty($value)) { + unset($this->currentKeyIdentifier); + } else { + $this->currentKeyIdentifier = base64_encode($value); + } + } + + /** + * Compute a public key identifier. + * + * Although key identifiers may be set to any unique value, this function + * computes key identifiers from public key according to the two + * recommended methods (4.2.1.2 RFC 3280). + * Highly polymorphic: try to accept all possible forms of key: + * - Key object + * - File_X509 object with public or private key defined + * - Certificate or CSR array + * - File_ASN1_Element object + * - PEM or DER string + * + * @param Mixed $key optional + * @param Integer $method optional + * @access public + * @return String binary key identifier + */ + function computeKeyIdentifier($key = NULL, $method = 1) + { + if (is_null($key)) { + $key = $this; + } + + switch (true) { + case is_string($key): + break; + case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); + case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); + case !is_object($key): + return false; + case strtolower(get_class($key)) == 'file_asn1_element': + $asn1 = new File_ASN1(); + $decoded = $asn1->decodeBER($cert); + if (empty($decoded)) { + return false; + } + $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + break; + case strtolower(get_class($key)) == 'file_x509': + if (isset($key->publicKey)) { + return $this->computeKeyIdentifier($key->publicKey, $method); + } + if (isset($key->privateKey)) { + return $this->computeKeyIdentifier($key->privateKey, $method); + } + if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { + return $this->computeKeyIdentifier($key->currentCert, $method); + } + return false; + default: // Should be a key object (i.e.: Crypt_RSA). + $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW); + break; + } + + // If in PEM format, convert to binary. + if (preg_match('#^-----BEGIN #', $key)) { + $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key)); + } + + // Now we have the key string: compute its sha-1 sum. + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $hash = new Crypt_Hash('sha1'); + $hash = $hash->hash($key); + + if ($method == 2) { + $hash = substr($hash, -8); + $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); + } + + return $hash; + } + + /** + * Format a public key as appropriate + * + * @access private + * @return Array + */ + function _formatSubjectPublicKey() + { + if (!isset($this->publicKey) || !is_object($this->publicKey)) { + return false; + } + + switch (strtolower(get_class($this->publicKey))) { + case 'crypt_rsa': + // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. + // the former is a good example of how to do fuzzing on the public key + //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); + return array( + 'algorithm' => array('algorithm' => 'rsaEncryption'), + 'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) + ); + default: + return false; + } + } + + /** + * Set the domain name's which the cert is to be valid for + * + * @access public + * @return Array + */ + function setDomain() + { + $this->domains = func_get_args(); + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->domains[0]); + } + + /** + * Helper function to build domain array + * + * @access private + * @param String $domain + * @return Array + */ + function _dnsName($domain) + { + return array('dNSName' => $domain); + } + + /** + * Get the index of a revoked certificate. + * + * @param array $rclist + * @param String $serial + * @param Boolean $create optional + * @access private + * @return Integer or false + */ + function _revokedCertificate(&$rclist, $serial, $create = false) + { + $serial = new Math_BigInteger($serial); + + foreach ($rclist as $i => $rc) { + if (!($serial->compare($rc['userCertificate']))) { + return $i; + } + } + + if (!$create) { + return false; + } + + $i = count($rclist); + $rclist[] = array('userCertificate' => $serial, + 'revocationDate' => array('generalTime' => @date('D, d M y H:i:s O'))); + return $i; + } + + /** + * Revoke a certificate. + * + * @param String $serial + * @param String $date optional + * @access public + * @return Boolean + */ + function revoke($serial, $date = NULL) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + + if (!empty($date)) { + $rclist[$i]['revocationDate'] = array('generalTime' => $date); + } + + return true; + } + } + } + } + + return false; + } + + /** + * Unrevoke a certificate. + * + * @param String $serial + * @access public + * @return Boolean + */ + function unrevoke($serial) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + unset($rclist[$i]); + $rclist = array_values($rclist); + return true; + } + } + + return false; + } + + /** + * Get a revoked certificate. + * + * @param String $serial + * @access public + * @return Mixed + */ + function getRevoked($serial) + { + if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $rclist[$i]; + } + } + + return false; + } + + /** + * List revoked certificates + * + * @param array $crl optional + * @access public + * @return array + */ + function listRevoked($crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (!isset($crl['tbsCertList'])) { + return false; + } + + $result = array(); + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + foreach ($rclist as $rc) { + $result[] = $rc['userCertificate']->toString(); + } + } + + return $result; + } + + /** + * Remove a Revoked Certificate Extension + * + * @param String $serial + * @param String $id + * @access public + * @return Boolean + */ + function removeRevokedCertificateExtension($serial, $id) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Get a Revoked Certificate Extension + * + * Returns the extension if it exists and false if not + * + * @param String $serial + * @param String $id + * @param Array $crl optional + * @access public + * @return Mixed + */ + function getRevokedCertificateExtension($serial, $id, $crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Returns a list of all extensions in use for a given revoked certificate + * + * @param String $serial + * @param array $crl optional + * @access public + * @return Array + */ + function getRevokedCertificateExtensions($serial, $crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Set a Revoked Certificate Extension + * + * @param String $serial + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @access public + * @return Boolean + */ + function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + } + + return false; + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php new file mode 100644 index 0000000000000000000000000000000000000000..d048cb032c5974e0c4fe9048d9a52de7bd46d3ea --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php @@ -0,0 +1,3633 @@ +> and << cannot be used, nor can the modulo operator %, + * which only supports integers. Although this fact will slow this library down, the fact that such a high + * base is being used should more than compensate. + * + * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again, + * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition / + * subtraction). + * + * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. + * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1) + * + * Useful resources are as follows: + * + * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)} + * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)} + * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip + * + * Here's an example of how to use this library: + * + * add($b); + * + * echo $c->toString(); // outputs 5 + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Math + * @package Math_BigInteger + * @author Jim Wigginton + * @copyright MMVI Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $ + * @link http://pear.php.net/package/Math_BigInteger + */ + +/**#@+ + * Reduction constants + * + * @access private + * @see Math_BigInteger::_reduce() + */ +/** + * @see Math_BigInteger::_montgomery() + * @see Math_BigInteger::_prepMontgomery() + */ +define('MATH_BIGINTEGER_MONTGOMERY', 0); +/** + * @see Math_BigInteger::_barrett() + */ +define('MATH_BIGINTEGER_BARRETT', 1); +/** + * @see Math_BigInteger::_mod2() + */ +define('MATH_BIGINTEGER_POWEROF2', 2); +/** + * @see Math_BigInteger::_remainder() + */ +define('MATH_BIGINTEGER_CLASSIC', 3); +/** + * @see Math_BigInteger::__clone() + */ +define('MATH_BIGINTEGER_NONE', 4); +/**#@-*/ + +/**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. + * + * @access private + */ +/** + * $result[MATH_BIGINTEGER_VALUE] contains the value. + */ +define('MATH_BIGINTEGER_VALUE', 0); +/** + * $result[MATH_BIGINTEGER_SIGN] contains the sign. + */ +define('MATH_BIGINTEGER_SIGN', 1); +/**#@-*/ + +/**#@+ + * @access private + * @see Math_BigInteger::_montgomery() + * @see Math_BigInteger::_barrett() + */ +/** + * Cache constants + * + * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid. + */ +define('MATH_BIGINTEGER_VARIABLE', 0); +/** + * $cache[MATH_BIGINTEGER_DATA] contains the cached data. + */ +define('MATH_BIGINTEGER_DATA', 1); +/**#@-*/ + +/**#@+ + * Mode constants. + * + * @access private + * @see Math_BigInteger::Math_BigInteger() + */ +/** + * To use the pure-PHP implementation + */ +define('MATH_BIGINTEGER_MODE_INTERNAL', 1); +/** + * To use the BCMath library + * + * (if enabled; otherwise, the internal implementation will be used) + */ +define('MATH_BIGINTEGER_MODE_BCMATH', 2); +/** + * To use the GMP library + * + * (if present; otherwise, either the BCMath or the internal implementation will be used) + */ +define('MATH_BIGINTEGER_MODE_GMP', 3); +/**#@-*/ + +/** + * The largest digit that may be used in addition / subtraction + * + * (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations + * will truncate 4503599627370496) + * + * @access private + */ +define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52)); + +/** + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * + * @access private + */ +define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25); + +/** + * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 + * numbers. + * + * @author Jim Wigginton + * @version 1.0.0RC4 + * @access public + * @package Math_BigInteger + */ +class Math_BigInteger { + /** + * Holds the BigInteger's value. + * + * @var Array + * @access private + */ + var $value; + + /** + * Holds the BigInteger's magnitude. + * + * @var Boolean + * @access private + */ + var $is_negative = false; + + /** + * Random number generator function + * + * @see setRandomGenerator() + * @access private + */ + var $generator = 'mt_rand'; + + /** + * Precision + * + * @see setPrecision() + * @access private + */ + var $precision = -1; + + /** + * Precision Bitmask + * + * @see setPrecision() + * @access private + */ + var $bitmask = false; + + /** + * Mode independant value used for serialization. + * + * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for + * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, + * however, $this->hex is only calculated when $this->__sleep() is called. + * + * @see __sleep() + * @see __wakeup() + * @var String + * @access private + */ + var $hex; + + /** + * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers. + * + * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using + * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. + * + * Here's an example: + * + * toString(); // outputs 50 + * ?> + * + * + * @param optional $x base-10 number or base-$base number if $base set. + * @param optional integer $base + * @return Math_BigInteger + * @access public + */ + function Math_BigInteger($x = 0, $base = 10) + { + if ( !defined('MATH_BIGINTEGER_MODE') ) { + switch (true) { + case extension_loaded('gmp'): + define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP); + break; + case extension_loaded('bcmath'): + define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH); + break; + default: + define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL); + } + } + + if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + } + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + if (is_resource($x) && get_resource_type($x) == 'GMP integer') { + $this->value = $x; + return; + } + $this->value = gmp_init(0); + break; + case MATH_BIGINTEGER_MODE_BCMATH: + $this->value = '0'; + break; + default: + $this->value = array(); + } + + // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 + // '0' is the only value like this per http://php.net/empty + if (empty($x) && (abs($base) != 256 || $x !== '0')) { + return; + } + + switch ($base) { + case -256: + if (ord($x[0]) & 0x80) { + $x = ~$x; + $this->is_negative = true; + } + case 256: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $sign = $this->is_negative ? '-' : ''; + $this->value = gmp_init($sign . '0x' . bin2hex($x)); + break; + case MATH_BIGINTEGER_MODE_BCMATH: + // round $len to the nearest 4 (thanks, DavidMJ!) + $len = (strlen($x) + 3) & 0xFFFFFFFC; + + $x = str_pad($x, $len, chr(0), STR_PAD_LEFT); + + for ($i = 0; $i < $len; $i+= 4) { + $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32 + $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0); + } + + if ($this->is_negative) { + $this->value = '-' . $this->value; + } + + break; + // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) + default: + while (strlen($x)) { + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26)); + } + } + + if ($this->is_negative) { + if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) { + $this->is_negative = false; + } + $temp = $this->add(new Math_BigInteger('-1')); + $this->value = $temp->value; + } + break; + case 16: + case -16: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x); + + $is_negative = false; + if ($base < 0 && hexdec($x[0]) >= 8) { + $this->is_negative = $is_negative = true; + $x = bin2hex(~pack('H*', $x)); + } + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; + $this->value = gmp_init($temp); + $this->is_negative = false; + break; + case MATH_BIGINTEGER_MODE_BCMATH: + $x = ( strlen($x) & 1 ) ? '0' . $x : $x; + $temp = new Math_BigInteger(pack('H*', $x), 256); + $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; + $this->is_negative = false; + break; + default: + $x = ( strlen($x) & 1 ) ? '0' . $x : $x; + $temp = new Math_BigInteger(pack('H*', $x), 256); + $this->value = $temp->value; + } + + if ($is_negative) { + $temp = $this->add(new Math_BigInteger('-1')); + $this->value = $temp->value; + } + break; + case 10: + case -10: + $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x); + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $this->value = gmp_init($x); + break; + case MATH_BIGINTEGER_MODE_BCMATH: + // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different + // results then doing it on '-1' does (modInverse does $x[0]) + $this->value = (string) $x; + break; + default: + $temp = new Math_BigInteger(); + + // array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it. + $multiplier = new Math_BigInteger(); + $multiplier->value = array(10000000); + + if ($x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT); + + while (strlen($x)) { + $temp = $temp->multiply($multiplier); + $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256)); + $x = substr($x, 7); + } + + $this->value = $temp->value; + } + break; + case 2: // base-2 support originally implemented by Lluis Pamies - thanks! + case -2: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^([01]*).*#', '$1', $x); + $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT); + + $str = '0x'; + while (strlen($x)) { + $part = substr($x, 0, 4); + $str.= dechex(bindec($part)); + $x = substr($x, 4); + } + + if ($this->is_negative) { + $str = '-' . $str; + } + + $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16 + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + + break; + default: + // base not supported, so we'll let $this == 0 + } + } + + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toBytes(); // outputs chr(65) + * ?> + * + * + * @param Boolean $twos_compliment + * @return String + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toBytes($twos_compliment = false) + { + if ($twos_compliment) { + $comparison = $this->compare(new Math_BigInteger()); + if ($comparison == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy(); + $bytes = $temp->toBytes(); + + if (empty($bytes)) { // eg. if the number we're trying to convert is -1 + $bytes = chr(0); + } + + if (ord($bytes[0]) & 0x80) { + $bytes = chr(0) . $bytes; + } + + return $comparison < 0 ? ~$bytes : $bytes; + } + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + if (gmp_cmp($this->value, gmp_init(0)) == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $temp = gmp_strval(gmp_abs($this->value), 16); + $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); + + return $this->precision > 0 ? + substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($temp, chr(0)); + case MATH_BIGINTEGER_MODE_BCMATH: + if ($this->value === '0') { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $value = ''; + $current = $this->value; + + if ($current[0] == '-') { + $current = substr($current, 1); + } + + while (bccomp($current, '0', 0) > 0) { + $temp = bcmod($current, '16777216'); + $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value; + $current = bcdiv($current, '16777216', 0); + } + + return $this->precision > 0 ? + substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($value, chr(0)); + } + + if (!count($this->value)) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + $result = $this->_int2bytes($this->value[count($this->value) - 1]); + + $temp = $this->copy(); + + for ($i = count($temp->value) - 2; $i >= 0; --$i) { + $temp->_base256_lshift($result, 26); + $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + } + + return $this->precision > 0 ? + str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) : + $result; + } + + /** + * Converts a BigInteger to a hex string (eg. base-16)). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toHex(); // outputs '41' + * ?> + * + * + * @param Boolean $twos_compliment + * @return String + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toHex($twos_compliment = false) + { + return bin2hex($this->toBytes($twos_compliment)); + } + + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toBits(); // outputs '1000001' + * ?> + * + * + * @param Boolean $twos_compliment + * @return String + * @access public + * @internal Converts a base-2**26 number to base-2**2 + */ + function toBits($twos_compliment = false) + { + $hex = $this->toHex($twos_compliment); + $bits = ''; + for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; + } + if ($start) { // hexdec('') == 0 + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; + } + $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); + + if ($twos_compliment && $this->compare(new Math_BigInteger()) > 0 && $this->precision <= 0) { + return '0' . $result; + } + + return $result; + } + + /** + * Converts a BigInteger to a base-10 number. + * + * Here's an example: + * + * toString(); // outputs 50 + * ?> + * + * + * @return String + * @access public + * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) + */ + function toString() + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + return gmp_strval($this->value); + case MATH_BIGINTEGER_MODE_BCMATH: + if ($this->value === '0') { + return '0'; + } + + return ltrim($this->value, '0'); + } + + if (!count($this->value)) { + return '0'; + } + + $temp = $this->copy(); + $temp->is_negative = false; + + $divisor = new Math_BigInteger(); + $divisor->value = array(10000000); // eg. 10**7 + $result = ''; + while (count($temp->value)) { + list($temp, $mod) = $temp->divide($divisor); + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result; + } + $result = ltrim($result, '0'); + if (empty($result)) { + $result = '0'; + } + + if ($this->is_negative) { + $result = '-' . $result; + } + + return $result; + } + + /** + * Copy an object + * + * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee + * that all objects are passed by value, when appropriate. More information can be found here: + * + * {@link http://php.net/language.oop5.basic#51624} + * + * @access public + * @see __clone() + * @return Math_BigInteger + */ + function copy() + { + $temp = new Math_BigInteger(); + $temp->value = $this->value; + $temp->is_negative = $this->is_negative; + $temp->generator = $this->generator; + $temp->precision = $this->precision; + $temp->bitmask = $this->bitmask; + return $temp; + } + + /** + * __toString() magic method + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * toString(). + * + * @access public + * @internal Implemented per a suggestion by Techie-Michael - thanks! + */ + function __toString() + { + return $this->toString(); + } + + /** + * __clone() magic method + * + * Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone() + * directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 + * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5, + * call Math_BigInteger::copy(), instead. + * + * @access public + * @see copy() + * @return Math_BigInteger + */ + function __clone() + { + return $this->copy(); + } + + /** + * __sleep() magic method + * + * Will be called, automatically, when serialize() is called on a Math_BigInteger object. + * + * @see __wakeup() + * @access public + */ + function __sleep() + { + $this->hex = $this->toHex(true); + $vars = array('hex'); + if ($this->generator != 'mt_rand') { + $vars[] = 'generator'; + } + if ($this->precision > 0) { + $vars[] = 'precision'; + } + return $vars; + + } + + /** + * __wakeup() magic method + * + * Will be called, automatically, when unserialize() is called on a Math_BigInteger object. + * + * @see __sleep() + * @access public + */ + function __wakeup() + { + $temp = new Math_BigInteger($this->hex, -16); + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + $this->setRandomGenerator($this->generator); + if ($this->precision > 0) { + // recalculate $this->bitmask + $this->setPrecision($this->precision); + } + } + + /** + * Adds two BigIntegers. + * + * Here's an example: + * + * add($b); + * + * echo $c->toString(); // outputs 30 + * ?> + * + * + * @param Math_BigInteger $y + * @return Math_BigInteger + * @access public + * @internal Performs base-2**52 addition + */ + function add($y) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_add($this->value, $y->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new Math_BigInteger(); + $temp->value = bcadd($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new Math_BigInteger(); + $result->value = $temp[MATH_BIGINTEGER_VALUE]; + $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs addition. + * + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array + * @access private + */ + function _add($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + MATH_BIGINTEGER_VALUE => $y_value, + MATH_BIGINTEGER_SIGN => $y_negative + ); + } else if ($y_size == 0) { + return array( + MATH_BIGINTEGER_VALUE => $x_value, + MATH_BIGINTEGER_SIGN => $x_negative + ); + } + + // subtract, if appropriate + if ( $x_negative != $y_negative ) { + if ( $x_value == $y_value ) { + return array( + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false + ); + } + + $temp = $this->_subtract($x_value, false, $y_value, false); + $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? + $x_negative : $y_negative; + + return $temp; + } + + if ($x_size < $y_size) { + $size = $x_size; + $value = $y_value; + } else { + $size = $y_size; + $value = $x_value; + } + + $value[] = 0; // just in case the carry adds an extra digit + + $carry = 0; + for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { + $sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry; + $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum; + + $temp = (int) ($sum / 0x4000000); + + $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$j] = $temp; + } + + if ($j == $size) { // ie. if $y_size is odd + $sum = $x_value[$i] + $y_value[$i] + $carry; + $carry = $sum >= 0x4000000; + $value[$i] = $carry ? $sum - 0x4000000 : $sum; + ++$i; // ie. let $i = $j since we've just done $value[$i] + } + + if ($carry) { + for (; $value[$i] == 0x3FFFFFF; ++$i) { + $value[$i] = 0; + } + ++$value[$i]; + } + + return array( + MATH_BIGINTEGER_VALUE => $this->_trim($value), + MATH_BIGINTEGER_SIGN => $x_negative + ); + } + + /** + * Subtracts two BigIntegers. + * + * Here's an example: + * + * subtract($b); + * + * echo $c->toString(); // outputs -10 + * ?> + * + * + * @param Math_BigInteger $y + * @return Math_BigInteger + * @access public + * @internal Performs base-2**52 subtraction + */ + function subtract($y) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_sub($this->value, $y->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new Math_BigInteger(); + $temp->value = bcsub($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new Math_BigInteger(); + $result->value = $temp[MATH_BIGINTEGER_VALUE]; + $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs subtraction. + * + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array + * @access private + */ + function _subtract($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + MATH_BIGINTEGER_VALUE => $y_value, + MATH_BIGINTEGER_SIGN => !$y_negative + ); + } else if ($y_size == 0) { + return array( + MATH_BIGINTEGER_VALUE => $x_value, + MATH_BIGINTEGER_SIGN => $x_negative + ); + } + + // add, if appropriate (ie. -$x - +$y or +$x - -$y) + if ( $x_negative != $y_negative ) { + $temp = $this->_add($x_value, false, $y_value, false); + $temp[MATH_BIGINTEGER_SIGN] = $x_negative; + + return $temp; + } + + $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); + + if ( !$diff ) { + return array( + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false + ); + } + + // switch $x and $y around, if appropriate. + if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_negative = !$x_negative; + + $x_size = count($x_value); + $y_size = count($y_value); + } + + // at this point, $x_value should be at least as big as - if not bigger than - $y_value + + $carry = 0; + for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { + $sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry; + $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum; + + $temp = (int) ($sum / 0x4000000); + + $x_value[$i] = (int) ($sum - 0x4000000 * $temp); + $x_value[$j] = $temp; + } + + if ($j == $y_size) { // ie. if $y_size is odd + $sum = $x_value[$i] - $y_value[$i] - $carry; + $carry = $sum < 0; + $x_value[$i] = $carry ? $sum + 0x4000000 : $sum; + ++$i; + } + + if ($carry) { + for (; !$x_value[$i]; ++$i) { + $x_value[$i] = 0x3FFFFFF; + } + --$x_value[$i]; + } + + return array( + MATH_BIGINTEGER_VALUE => $this->_trim($x_value), + MATH_BIGINTEGER_SIGN => $x_negative + ); + } + + /** + * Multiplies two BigIntegers + * + * Here's an example: + * + * multiply($b); + * + * echo $c->toString(); // outputs 200 + * ?> + * + * + * @param Math_BigInteger $x + * @return Math_BigInteger + * @access public + */ + function multiply($x) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_mul($this->value, $x->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new Math_BigInteger(); + $temp->value = bcmul($this->value, $x->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + + $product = new Math_BigInteger(); + $product->value = $temp[MATH_BIGINTEGER_VALUE]; + $product->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + + return $this->_normalize($product); + } + + /** + * Performs multiplication. + * + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array + * @access private + */ + function _multiply($x_value, $x_negative, $y_value, $y_negative) + { + //if ( $x_value == $y_value ) { + // return array( + // MATH_BIGINTEGER_VALUE => $this->_square($x_value), + // MATH_BIGINTEGER_SIGN => $x_sign != $y_value + // ); + //} + + $x_length = count($x_value); + $y_length = count($y_value); + + if ( !$x_length || !$y_length ) { // a 0 is being multiplied + return array( + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false + ); + } + + return array( + MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? + $this->_trim($this->_regularMultiply($x_value, $y_value)) : + $this->_trim($this->_karatsuba($x_value, $y_value)), + MATH_BIGINTEGER_SIGN => $x_negative != $y_negative + ); + } + + /** + * Performs long multiplication on two BigIntegers + * + * Modeled after 'multiply' in MutableBigInteger.java. + * + * @param Array $x_value + * @param Array $y_value + * @return Array + * @access private + */ + function _regularMultiply($x_value, $y_value) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if ( !$x_length || !$y_length ) { // a 0 is being multiplied + return array(); + } + + if ( $x_length < $y_length ) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = (int) ($temp / 0x4000000); + $product_value[$j] = (int) ($temp - 0x4000000 * $carry); + } + + $product_value[$j] = $carry; + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = (int) ($temp / 0x4000000); + $product_value[$k] = (int) ($temp - 0x4000000 * $carry); + } + + $product_value[$k] = $carry; + } + + return $product_value; + } + + /** + * Performs Karatsuba multiplication on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. + * + * @param Array $x_value + * @param Array $y_value + * @return Array + * @access private + */ + function _karatsuba($x_value, $y_value) + { + $m = min(count($x_value) >> 1, count($y_value) >> 1); + + if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { + return $this->_regularMultiply($x_value, $y_value); + } + + $x1 = array_slice($x_value, $m); + $x0 = array_slice($x_value, 0, $m); + $y1 = array_slice($y_value, $m); + $y0 = array_slice($y_value, 0, $m); + + $z2 = $this->_karatsuba($x1, $y1); + $z0 = $this->_karatsuba($x0, $y0); + + $z1 = $this->_add($x1, false, $x0, false); + $temp = $this->_add($y1, false, $y0, false); + $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); + + $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); + $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false); + + return $xy[MATH_BIGINTEGER_VALUE]; + } + + /** + * Performs squaring + * + * @param Array $x + * @return Array + * @access private + */ + function _square($x = false) + { + return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? + $this->_trim($this->_baseSquare($x)) : + $this->_trim($this->_karatsubaSquare($x)); + } + + /** + * Performs traditional squaring on two BigIntegers + * + * Squaring can be done faster than multiplying a number by itself can be. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. + * + * @param Array $value + * @return Array + * @access private + */ + function _baseSquare($value) + { + if ( empty($value) ) { + return array(); + } + $square_value = $this->_array_repeat(0, 2 * count($value)); + + for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { + $i2 = $i << 1; + + $temp = $square_value[$i2] + $value[$i] * $value[$i]; + $carry = (int) ($temp / 0x4000000); + $square_value[$i2] = (int) ($temp - 0x4000000 * $carry); + + // note how we start from $i+1 instead of 0 as we do in multiplication. + for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { + $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; + $carry = (int) ($temp / 0x4000000); + $square_value[$k] = (int) ($temp - 0x4000000 * $carry); + } + + // the following line can yield values larger 2**15. at this point, PHP should switch + // over to floats. + $square_value[$i + $max_index + 1] = $carry; + } + + return $square_value; + } + + /** + * Performs Karatsuba "squaring" on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. + * + * @param Array $value + * @return Array + * @access private + */ + function _karatsubaSquare($value) + { + $m = count($value) >> 1; + + if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { + return $this->_baseSquare($value); + } + + $x1 = array_slice($value, $m); + $x0 = array_slice($value, 0, $m); + + $z2 = $this->_karatsubaSquare($x1); + $z0 = $this->_karatsubaSquare($x0); + + $z1 = $this->_add($x1, false, $x0, false); + $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); + + $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); + $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false); + + return $xx[MATH_BIGINTEGER_VALUE]; + } + + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * Here's an example: + * + * divide($b); + * + * echo $quotient->toString(); // outputs 0 + * echo "\r\n"; + * echo $remainder->toString(); // outputs 10 + * ?> + * + * + * @param Math_BigInteger $y + * @return Array + * @access public + * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. + */ + function divide($y) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $quotient = new Math_BigInteger(); + $remainder = new Math_BigInteger(); + + list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); + + if (gmp_sign($remainder->value) < 0) { + $remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + case MATH_BIGINTEGER_MODE_BCMATH: + $quotient = new Math_BigInteger(); + $remainder = new Math_BigInteger(); + + $quotient->value = bcdiv($this->value, $y->value, 0); + $remainder->value = bcmod($this->value, $y->value); + + if ($remainder->value[0] == '-') { + $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + if (count($y->value) == 1) { + list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); + $quotient = new Math_BigInteger(); + $remainder = new Math_BigInteger(); + $quotient->value = $q; + $remainder->value = array($r); + $quotient->is_negative = $this->is_negative != $y->is_negative; + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + static $zero; + if ( !isset($zero) ) { + $zero = new Math_BigInteger(); + } + + $x = $this->copy(); + $y = $y->copy(); + + $x_sign = $x->is_negative; + $y_sign = $y->is_negative; + + $x->is_negative = $y->is_negative = false; + + $diff = $x->compare($y); + + if ( !$diff ) { + $temp = new Math_BigInteger(); + $temp->value = array(1); + $temp->is_negative = $x_sign != $y_sign; + return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger())); + } + + if ( $diff < 0 ) { + // if $x is negative, "add" $y. + if ( $x_sign ) { + $x = $y->subtract($x); + } + return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x)); + } + + // normalize $x and $y as described in HAC 14.23 / 14.24 + $msb = $y->value[count($y->value) - 1]; + for ($shift = 0; !($msb & 0x2000000); ++$shift) { + $msb <<= 1; + } + $x->_lshift($shift); + $y->_lshift($shift); + $y_value = &$y->value; + + $x_max = count($x->value) - 1; + $y_max = count($y->value) - 1; + + $quotient = new Math_BigInteger(); + $quotient_value = &$quotient->value; + $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); + + static $temp, $lhs, $rhs; + if (!isset($temp)) { + $temp = new Math_BigInteger(); + $lhs = new Math_BigInteger(); + $rhs = new Math_BigInteger(); + } + $temp_value = &$temp->value; + $rhs_value = &$rhs->value; + + // $temp = $y << ($x_max - $y_max-1) in base 2**26 + $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); + + while ( $x->compare($temp) >= 0 ) { + // calculate the "common residue" + ++$quotient_value[$x_max - $y_max]; + $x = $x->subtract($temp); + $x_max = count($x->value) - 1; + } + + for ($i = $x_max; $i >= $y_max + 1; --$i) { + $x_value = &$x->value; + $x_window = array( + isset($x_value[$i]) ? $x_value[$i] : 0, + isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, + isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0 + ); + $y_window = array( + $y_value[$y_max], + ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0 + ); + + $q_index = $i - $y_max - 1; + if ($x_window[0] == $y_window[0]) { + $quotient_value[$q_index] = 0x3FFFFFF; + } else { + $quotient_value[$q_index] = (int) ( + ($x_window[0] * 0x4000000 + $x_window[1]) + / + $y_window[0] + ); + } + + $temp_value = array($y_window[1], $y_window[0]); + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + + $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); + + while ( $lhs->compare($rhs) > 0 ) { + --$quotient_value[$q_index]; + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + } + + $adjust = $this->_array_repeat(0, $q_index); + $temp_value = array($quotient_value[$q_index]); + $temp = $temp->multiply($y); + $temp_value = &$temp->value; + $temp_value = array_merge($adjust, $temp_value); + + $x = $x->subtract($temp); + + if ($x->compare($zero) < 0) { + $temp_value = array_merge($adjust, $y_value); + $x = $x->add($temp); + + --$quotient_value[$q_index]; + } + + $x_max = count($x_value) - 1; + } + + // unnormalize the remainder + $x->_rshift($shift); + + $quotient->is_negative = $x_sign != $y_sign; + + // calculate the "common residue", if appropriate + if ( $x_sign ) { + $y->_rshift($shift); + $x = $y->subtract($x); + } + + return array($this->_normalize($quotient), $this->_normalize($x)); + } + + /** + * Divides a BigInteger by a regular integer + * + * abc / x = a00 / x + b0 / x + c / x + * + * @param Array $dividend + * @param Array $divisor + * @return Array + * @access private + */ + function _divide_digit($dividend, $divisor) + { + $carry = 0; + $result = array(); + + for ($i = count($dividend) - 1; $i >= 0; --$i) { + $temp = 0x4000000 * $carry + $dividend[$i]; + $result[$i] = (int) ($temp / $divisor); + $carry = (int) ($temp - $divisor * $result[$i]); + } + + return array($result, $carry); + } + + /** + * Performs modular exponentiation. + * + * Here's an example: + * + * modPow($b, $c); + * + * echo $c->toString(); // outputs 10 + * ?> + * + * + * @param Math_BigInteger $e + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and + * and although the approach involving repeated squaring does vastly better, it, too, is impractical + * for our purposes. The reason being that division - by far the most complicated and time-consuming + * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. + * + * Modular reductions resolve this issue. Although an individual modular reduction takes more time + * then an individual division, when performed in succession (with the same modulo), they're a lot faster. + * + * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, + * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the + * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because + * the product of two odd numbers is odd), but what about when RSA isn't used? + * + * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a + * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the + * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, + * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and + * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. + * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. + */ + function modPow($e, $n) + { + $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); + + if ($e->compare(new Math_BigInteger()) < 0) { + $e = $e->abs(); + + $temp = $this->modInverse($n); + if ($temp === false) { + return false; + } + + return $this->_normalize($temp->modPow($e, $n)); + } + + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP ) { + $temp = new Math_BigInteger(); + $temp->value = gmp_powm($this->value, $e->value, $n->value); + + return $this->_normalize($temp); + } + + if ($this->compare(new Math_BigInteger()) < 0 || $this->compare($n) > 0) { + list(, $temp) = $this->divide($n); + return $temp->modPow($e, $n); + } + + if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $components = array( + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true) + ); + + $components = array( + 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + ); + + $RSAPublicKey = pack('Ca*a*a*', + 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], $components['publicExponent'] + ); + + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + + $encapsulated = pack('Ca*a*', + 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($encapsulated)) . + '-----END PUBLIC KEY-----'; + + $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); + + if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { + return new Math_BigInteger($result, 256); + } + } + + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $temp = new Math_BigInteger(); + $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); + + return $this->_normalize($temp); + } + + if ( empty($e->value) ) { + $temp = new Math_BigInteger(); + $temp->value = array(1); + return $this->_normalize($temp); + } + + if ( $e->value == array(1) ) { + list(, $temp) = $this->divide($n); + return $this->_normalize($temp); + } + + if ( $e->value == array(2) ) { + $temp = new Math_BigInteger(); + $temp->value = $this->_square($this->value); + list(, $temp) = $temp->divide($n); + return $this->_normalize($temp); + } + + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT)); + + // is the modulo odd? + if ( $n->value[0] & 1 ) { + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY)); + } + // if it's not, it's even + + // find the lowest set bit (eg. the max pow of 2 that divides $n) + for ($i = 0; $i < count($n->value); ++$i) { + if ( $n->value[$i] ) { + $temp = decbin($n->value[$i]); + $j = strlen($temp) - strrpos($temp, '1') - 1; + $j+= 26 * $i; + break; + } + } + // at this point, 2^$j * $n/(2^$j) == $n + + $mod1 = $n->copy(); + $mod1->_rshift($j); + $mod2 = new Math_BigInteger(); + $mod2->value = array(1); + $mod2->_lshift($j); + + $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger(); + $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2); + + $y1 = $mod2->modInverse($mod1); + $y2 = $mod1->modInverse($mod2); + + $result = $part1->multiply($mod2); + $result = $result->multiply($y1); + + $temp = $part2->multiply($mod1); + $temp = $temp->multiply($y2); + + $result = $result->add($temp); + list(, $result) = $result->divide($n); + + return $this->_normalize($result); + } + + /** + * Performs modular exponentiation. + * + * Alias for Math_BigInteger::modPow() + * + * @param Math_BigInteger $e + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + */ + function powMod($e, $n) + { + return $this->modPow($e, $n); + } + + /** + * Sliding Window k-ary Modular Exponentiation + * + * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, + * however, this function performs a modular reduction after every multiplication and squaring operation. + * As such, this function has the same preconditions that the reductions being used do. + * + * @param Math_BigInteger $e + * @param Math_BigInteger $n + * @param Integer $mode + * @return Math_BigInteger + * @access private + */ + function _slidingWindow($e, $n, $mode) + { + static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function + //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1 + + $e_value = $e->value; + $e_length = count($e_value) - 1; + $e_bits = decbin($e_value[$e_length]); + for ($i = $e_length - 1; $i >= 0; --$i) { + $e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT); + } + + $e_length = strlen($e_bits); + + // calculate the appropriate window size. + // $window_size == 3 if $window_ranges is between 25 and 81, for example. + for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i); + + $n_value = $n->value; + + // precompute $this^0 through $this^$window_size + $powers = array(); + $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); + $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); + + // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end + // in a 1. ie. it's supposed to be odd. + $temp = 1 << ($window_size - 1); + for ($i = 1; $i < $temp; ++$i) { + $i2 = $i << 1; + $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + } + + $result = array(1); + $result = $this->_prepareReduce($result, $n_value, $mode); + + for ($i = 0; $i < $e_length; ) { + if ( !$e_bits[$i] ) { + $result = $this->_squareReduce($result, $n_value, $mode); + ++$i; + } else { + for ($j = $window_size - 1; $j > 0; --$j) { + if ( !empty($e_bits[$i + $j]) ) { + break; + } + } + + for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1) + $result = $this->_squareReduce($result, $n_value, $mode); + } + + $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + + $i+=$j + 1; + } + } + + $temp = new Math_BigInteger(); + $temp->value = $this->_reduce($result, $n_value, $mode); + + return $temp; + } + + /** + * Modular reduction + * + * For most $modes this will return the remainder. + * + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array + */ + function _reduce($x, $n, $mode) + { + switch ($mode) { + case MATH_BIGINTEGER_MONTGOMERY: + return $this->_montgomery($x, $n); + case MATH_BIGINTEGER_BARRETT: + return $this->_barrett($x, $n); + case MATH_BIGINTEGER_POWEROF2: + $lhs = new Math_BigInteger(); + $lhs->value = $x; + $rhs = new Math_BigInteger(); + $rhs->value = $n; + return $x->_mod2($n); + case MATH_BIGINTEGER_CLASSIC: + $lhs = new Math_BigInteger(); + $lhs->value = $x; + $rhs = new Math_BigInteger(); + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + case MATH_BIGINTEGER_NONE: + return $x; + default: + // an invalid $mode was provided + } + } + + /** + * Modular reduction preperation + * + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array + */ + function _prepareReduce($x, $n, $mode) + { + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_prepMontgomery($x, $n); + } + return $this->_reduce($x, $n, $mode); + } + + /** + * Modular multiply + * + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $y + * @param Array $n + * @param Integer $mode + * @return Array + */ + function _multiplyReduce($x, $y, $n, $mode) + { + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_montgomeryMultiply($x, $y, $n); + } + $temp = $this->_multiply($x, false, $y, false); + return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode); + } + + /** + * Modular square + * + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array + */ + function _squareReduce($x, $n, $mode) + { + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_montgomeryMultiply($x, $x, $n); + } + return $this->_reduce($this->_square($x), $n, $mode); + } + + /** + * Modulos for Powers of Two + * + * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), + * we'll just use this function as a wrapper for doing that. + * + * @see _slidingWindow() + * @access private + * @param Math_BigInteger + * @return Math_BigInteger + */ + function _mod2($n) + { + $temp = new Math_BigInteger(); + $temp->value = array(1); + return $this->bitwise_and($n->subtract($temp)); + } + + /** + * Barrett Modular Reduction + * + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, + * so as not to require negative numbers (initially, this script didn't support negative numbers). + * + * Employs "folding", as described at + * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from + * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." + * + * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that + * usable on account of (1) its not using reasonable radix points as discussed in + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable + * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that + * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line + * comments for details. + * + * @see _slidingWindow() + * @access private + * @param Array $n + * @param Array $m + * @return Array + */ + function _barrett($n, $m) + { + static $cache = array( + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() + ); + + $m_length = count($m); + + // if ($this->_compare($n, $this->_square($m)) >= 0) { + if (count($n) > 2 * $m_length) { + $lhs = new Math_BigInteger(); + $rhs = new Math_BigInteger(); + $lhs->value = $n; + $rhs->value = $m; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced + if ($m_length < 5) { + return $this->_regularBarrett($n, $m); + } + + // n = 2 * m.length + + if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $m; + + $lhs = new Math_BigInteger(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value[] = 1; + $rhs = new Math_BigInteger(); + $rhs->value = $m; + + list($u, $m1) = $lhs->divide($rhs); + $u = $u->value; + $m1 = $m1->value; + + $cache[MATH_BIGINTEGER_DATA][] = array( + 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) + 'm1'=> $m1 // m.length + ); + } else { + extract($cache[MATH_BIGINTEGER_DATA][$key]); + } + + $cutoff = $m_length + ($m_length >> 1); + $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) + $msd = array_slice($n, $cutoff); // m.length >> 1 + $lsd = $this->_trim($lsd); + $temp = $this->_multiply($msd, false, $m1, false); + $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1 + + if ($m_length & 1) { + return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m); + } + + // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 + $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1); + // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 + // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 + $temp = $this->_multiply($temp, false, $u, false); + // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 + // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1); + // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 + // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) + $temp = $this->_multiply($temp, false, $m, false); + + // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit + // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop + // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). + + $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); + + while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false); + } + + return $result[MATH_BIGINTEGER_VALUE]; + } + + /** + * (Regular) Barrett Modular Reduction + * + * For numbers with more than four digits Math_BigInteger::_barrett() is faster. The difference between that and this + * is that this function does not fold the denominator into a smaller form. + * + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @return Array + */ + function _regularBarrett($x, $n) + { + static $cache = array( + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() + ); + + $n_length = count($n); + + if (count($x) > 2 * $n_length) { + $lhs = new Math_BigInteger(); + $rhs = new Math_BigInteger(); + $lhs->value = $x; + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $n; + $lhs = new Math_BigInteger(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, 2 * $n_length); + $lhs_value[] = 1; + $rhs = new Math_BigInteger(); + $rhs->value = $n; + list($temp, ) = $lhs->divide($rhs); // m.length + $cache[MATH_BIGINTEGER_DATA][] = $temp->value; + } + + // 2 * m.length - (m.length - 1) = m.length + 1 + $temp = array_slice($x, $n_length - 1); + // (m.length + 1) + m.length = 2 * m.length + 1 + $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false); + // (2 * m.length + 1) - (m.length - 1) = m.length + 2 + $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1); + + // m.length + 1 + $result = array_slice($x, 0, $n_length + 1); + // m.length + 1 + $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + + if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) { + $corrector_value = $this->_array_repeat(0, $n_length + 1); + $corrector_value[] = 1; + $result = $this->_add($result, false, $corrector, false); + $result = $result[MATH_BIGINTEGER_VALUE]; + } + + // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits + $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]); + while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false); + } + + return $result[MATH_BIGINTEGER_VALUE]; + } + + /** + * Performs long multiplication up to $stop digits + * + * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. + * + * @see _regularBarrett() + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array + * @access private + */ + function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if ( !$x_length || !$y_length ) { // a 0 is being multiplied + return array( + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false + ); + } + + if ( $x_length < $y_length ) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = (int) ($temp / 0x4000000); + $product_value[$j] = (int) ($temp - 0x4000000 * $carry); + } + + if ($j < $stop) { + $product_value[$j] = $carry; + } + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = (int) ($temp / 0x4000000); + $product_value[$k] = (int) ($temp - 0x4000000 * $carry); + } + + if ($k < $stop) { + $product_value[$k] = $carry; + } + } + + return array( + MATH_BIGINTEGER_VALUE => $this->_trim($product_value), + MATH_BIGINTEGER_SIGN => $x_negative != $y_negative + ); + } + + /** + * Montgomery Modular Reduction + * + * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n. + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be + * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function + * to work correctly. + * + * @see _prepMontgomery() + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @return Array + */ + function _montgomery($x, $n) + { + static $cache = array( + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() + ); + + if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $x; + $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n); + } + + $k = count($n); + + $result = array(MATH_BIGINTEGER_VALUE => $x); + + for ($i = 0; $i < $k; ++$i) { + $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key]; + $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = $this->_regularMultiply(array($temp), $n); + $temp = array_merge($this->_array_repeat(0, $i), $temp); + $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false); + } + + $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k); + + if ($this->_compare($result, false, $n, false) >= 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false); + } + + return $result[MATH_BIGINTEGER_VALUE]; + } + + /** + * Montgomery Multiply + * + * Interleaves the montgomery reduction and long multiplication algorithms together as described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} + * + * @see _prepMontgomery() + * @see _montgomery() + * @access private + * @param Array $x + * @param Array $y + * @param Array $m + * @return Array + */ + function _montgomeryMultiply($x, $y, $m) + { + $temp = $this->_multiply($x, false, $y, false); + return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m); + + static $cache = array( + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() + ); + + if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $m; + $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m); + } + + $n = max(count($x), count($y), count($m)); + $x = array_pad($x, $n, 0); + $y = array_pad($y, $n, 0); + $m = array_pad($m, $n, 0); + $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1)); + for ($i = 0; $i < $n; ++$i) { + $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0]; + $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; + $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); + $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); + $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1); + } + if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) { + $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false); + } + return $a[MATH_BIGINTEGER_VALUE]; + } + + /** + * Prepare a number for use in Montgomery Modular Reductions + * + * @see _montgomery() + * @see _slidingWindow() + * @access private + * @param Array $x + * @param Array $n + * @return Array + */ + function _prepMontgomery($x, $n) + { + $lhs = new Math_BigInteger(); + $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); + $rhs = new Math_BigInteger(); + $rhs->value = $n; + + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + /** + * Modular Inverse of a number mod 2**26 (eg. 67108864) + * + * Based off of the bnpInvDigit function implemented and justified in the following URL: + * + * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} + * + * The following URL provides more info: + * + * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} + * + * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For + * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields + * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't + * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that + * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the + * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to + * 40 bits, which only 64-bit floating points will support. + * + * Thanks to Pedro Gimeno Fortea for input! + * + * @see _montgomery() + * @access private + * @param Array $x + * @return Integer + */ + function _modInverse67108864($x) // 2**26 == 67108864 + { + $x = -$x[0]; + $result = $x & 0x3; // x**-1 mod 2**2 + $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 + $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 + $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 + $result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26 + return $result & 0x3FFFFFF; + } + + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * Here's an example: + * + * modInverse($b); + * echo $c->toString(); // outputs 4 + * + * echo "\r\n"; + * + * $d = $a->multiply($c); + * list(, $d) = $d->divide($b); + * echo $d; // outputs 1 (as per the definition of modular inverse) + * ?> + * + * + * @param Math_BigInteger $n + * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise. + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. + */ + function modInverse($n) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_invert($this->value, $n->value); + + return ( $temp->value === false ) ? false : $this->_normalize($temp); + } + + static $zero, $one; + if (!isset($zero)) { + $zero = new Math_BigInteger(); + $one = new Math_BigInteger(1); + } + + // $x mod -$n == $x mod $n. + $n = $n->abs(); + + if ($this->compare($zero) < 0) { + $temp = $this->abs(); + $temp = $temp->modInverse($n); + return $this->_normalize($n->subtract($temp)); + } + + extract($this->extendedGCD($n)); + + if (!$gcd->equals($one)) { + return false; + } + + $x = $x->compare($zero) < 0 ? $x->add($n) : $x; + + return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); + } + + /** + * Calculates the greatest common divisor and Bzout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bzout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependant upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bzout's identity - Wikipedia} for more information. + * + * Here's an example: + * + * extendedGCD($b)); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 + * ?> + * + * + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + * @internal Calculates the GCD using the binary xGCD algorithim described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, + * the more traditional algorithim requires "relatively costly multiple-precision divisions". + */ + function extendedGCD($n) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + extract(gmp_gcdext($this->value, $n->value)); + + return array( + 'gcd' => $this->_normalize(new Math_BigInteger($g)), + 'x' => $this->_normalize(new Math_BigInteger($s)), + 'y' => $this->_normalize(new Math_BigInteger($t)) + ); + case MATH_BIGINTEGER_MODE_BCMATH: + // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works + // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, + // the basic extended euclidean algorithim is what we're using. + + $u = $this->value; + $v = $n->value; + + $a = '1'; + $b = '0'; + $c = '0'; + $d = '1'; + + while (bccomp($v, '0', 0) != 0) { + $q = bcdiv($u, $v, 0); + + $temp = $u; + $u = $v; + $v = bcsub($temp, bcmul($v, $q, 0), 0); + + $temp = $a; + $a = $c; + $c = bcsub($temp, bcmul($a, $q, 0), 0); + + $temp = $b; + $b = $d; + $d = bcsub($temp, bcmul($b, $q, 0), 0); + } + + return array( + 'gcd' => $this->_normalize(new Math_BigInteger($u)), + 'x' => $this->_normalize(new Math_BigInteger($a)), + 'y' => $this->_normalize(new Math_BigInteger($b)) + ); + } + + $y = $n->copy(); + $x = $this->copy(); + $g = new Math_BigInteger(); + $g->value = array(1); + + while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) { + $x->_rshift(1); + $y->_rshift(1); + $g->_lshift(1); + } + + $u = $x->copy(); + $v = $y->copy(); + + $a = new Math_BigInteger(); + $b = new Math_BigInteger(); + $c = new Math_BigInteger(); + $d = new Math_BigInteger(); + + $a->value = $d->value = $g->value = array(1); + $b->value = $c->value = array(); + + while ( !empty($u->value) ) { + while ( !($u->value[0] & 1) ) { + $u->_rshift(1); + if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) { + $a = $a->add($y); + $b = $b->subtract($x); + } + $a->_rshift(1); + $b->_rshift(1); + } + + while ( !($v->value[0] & 1) ) { + $v->_rshift(1); + if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) { + $c = $c->add($y); + $d = $d->subtract($x); + } + $c->_rshift(1); + $d->_rshift(1); + } + + if ($u->compare($v) >= 0) { + $u = $u->subtract($v); + $a = $a->subtract($c); + $b = $b->subtract($d); + } else { + $v = $v->subtract($u); + $c = $c->subtract($a); + $d = $d->subtract($b); + } + } + + return array( + 'gcd' => $this->_normalize($g->multiply($v)), + 'x' => $this->_normalize($c), + 'y' => $this->_normalize($d) + ); + } + + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * Here's an example: + * + * extendedGCD($b); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * ?> + * + * + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + */ + function gcd($n) + { + extract($this->extendedGCD($n)); + return $gcd; + } + + /** + * Absolute value. + * + * @return Math_BigInteger + * @access public + */ + function abs() + { + $temp = new Math_BigInteger(); + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp->value = gmp_abs($this->value); + break; + case MATH_BIGINTEGER_MODE_BCMATH: + $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; + break; + default: + $temp->value = $this->value; + } + + return $temp; + } + + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is + * demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * @param Math_BigInteger $x + * @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal. + * @access public + * @see equals() + * @internal Could return $this->subtract($x), but that's not as fast as what we do do. + */ + function compare($y) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + return gmp_cmp($this->value, $y->value); + case MATH_BIGINTEGER_MODE_BCMATH: + return bccomp($this->value, $y->value, 0); + } + + return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + } + + /** + * Compares two numbers. + * + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Integer + * @see compare() + * @access private + */ + function _compare($x_value, $x_negative, $y_value, $y_negative) + { + if ( $x_negative != $y_negative ) { + return ( !$x_negative && $y_negative ) ? 1 : -1; + } + + $result = $x_negative ? -1 : 1; + + if ( count($x_value) != count($y_value) ) { + return ( count($x_value) > count($y_value) ) ? $result : -$result; + } + $size = max(count($x_value), count($y_value)); + + $x_value = array_pad($x_value, $size, 0); + $y_value = array_pad($y_value, $size, 0); + + for ($i = count($x_value) - 1; $i >= 0; --$i) { + if ($x_value[$i] != $y_value[$i]) { + return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result; + } + } + + return 0; + } + + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare() + * + * @param Math_BigInteger $x + * @return Boolean + * @access public + * @see compare() + */ + function equals($x) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + return gmp_cmp($this->value, $x->value) == 0; + default: + return $this->value === $x->value && $this->is_negative == $x->is_negative; + } + } + + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param Math_BigInteger $x + * @access public + * @return Math_BigInteger + */ + function setPrecision($bits) + { + $this->precision = $bits; + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) { + $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + } else { + $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0)); + } + + $temp = $this->_normalize($this); + $this->value = $temp->value; + } + + /** + * Logical And + * + * @param Math_BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return Math_BigInteger + */ + function bitwise_and($x) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_and($this->value, $x->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left & $right, 256)); + } + + $result = $this->copy(); + + $length = min(count($x->value), count($this->value)); + + $result->value = array_slice($result->value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i] = $result->value[$i] & $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Or + * + * @param Math_BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return Math_BigInteger + */ + function bitwise_or($x) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_or($this->value, $x->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left | $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->value = array_pad($result->value, 0, $length); + $x->value = array_pad($x->value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i] = $this->value[$i] | $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Exclusive-Or + * + * @param Math_BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return Math_BigInteger + */ + function bitwise_xor($x) + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new Math_BigInteger(); + $temp->value = gmp_xor($this->value, $x->value); + + return $this->_normalize($temp); + case MATH_BIGINTEGER_MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left ^ $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->value = array_pad($result->value, 0, $length); + $x->value = array_pad($x->value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i] = $this->value[$i] ^ $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Not + * + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return Math_BigInteger + */ + function bitwise_not() + { + // calculuate "not" without regard to $this->precision + // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) + $temp = $this->toBytes(); + $pre_msb = decbin(ord($temp[0])); + $temp = ~$temp; + $msb = decbin(ord($temp[0])); + if (strlen($msb) == 8) { + $msb = substr($msb, strpos($msb, '0')); + } + $temp[0] = chr(bindec($msb)); + + // see if we need to add extra leading 1's + $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; + $new_bits = $this->precision - $current_bits; + if ($new_bits <= 0) { + return $this->_normalize(new Math_BigInteger($temp, 256)); + } + + // generate as many leading 1's as we need to. + $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); + $this->_base256_lshift($leading_ones, $current_bits); + + $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256)); + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_rightShift($shift) + { + $temp = new Math_BigInteger(); + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); + + break; + case MATH_BIGINTEGER_MODE_BCMATH: + $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_rshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_leftShift($shift) + { + $temp = new Math_BigInteger(); + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); + + break; + case MATH_BIGINTEGER_MODE_BCMATH: + $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_lshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + */ + function bitwise_leftRotate($shift) + { + $bits = $this->toBytes(); + + if ($this->precision > 0) { + $precision = $this->precision; + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $mask = $this->bitmask->subtract(new Math_BigInteger(1)); + $mask = $mask->toBytes(); + } else { + $mask = $this->bitmask->toBytes(); + } + } else { + $temp = ord($bits[0]); + for ($i = 0; $temp >> $i; ++$i); + $precision = 8 * strlen($bits) - 8 + $i; + $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); + } + + if ($shift < 0) { + $shift+= $precision; + } + $shift%= $precision; + + if (!$shift) { + return $this->copy(); + } + + $left = $this->bitwise_leftShift($shift); + $left = $left->bitwise_and(new Math_BigInteger($mask, 256)); + $right = $this->bitwise_rightShift($precision - $shift); + $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + return $this->_normalize($result); + } + + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + */ + function bitwise_rightRotate($shift) + { + return $this->bitwise_leftRotate(-$shift); + } + + /** + * Set random number generator function + * + * This function is deprecated. + * + * @param String $generator + * @access public + */ + function setRandomGenerator($generator) + { + } + + /** + * Generate a random number + * + * @param optional Integer $min + * @param optional Integer $max + * @return Math_BigInteger + * @access public + */ + function random($min = false, $max = false) + { + if ($min === false) { + $min = new Math_BigInteger(0); + } + + if ($max === false) { + $max = new Math_BigInteger(0x7FFFFFFF); + } + + $compare = $max->compare($min); + + if (!$compare) { + return $this->_normalize($min); + } else if ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + $generator = $this->generator; + + $max = $max->subtract($min); + $max = ltrim($max->toBytes(), chr(0)); + $size = strlen($max) - 1; + + $crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string')); + if ($crypt_random) { + $random = crypt_random_string($size); + } else { + $random = ''; + + if ($size & 1) { + $random.= chr(mt_rand(0, 255)); + } + + $blocks = $size >> 1; + for ($i = 0; $i < $blocks; ++$i) { + // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems + $random.= pack('n', mt_rand(0, 0xFFFF)); + } + } + + $fragment = new Math_BigInteger($random, 256); + $leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ? + ord($max[0]) - 1 : ord($max[0]); + + if (!$crypt_random) { + $msb = chr(mt_rand(0, $leading)); + } else { + $cutoff = floor(0xFF / $leading) * $leading; + while (true) { + $msb = ord(crypt_random_string(1)); + if ($msb <= $cutoff) { + $msb%= $leading; + break; + } + } + $msb = chr($msb); + } + + $random = new Math_BigInteger($msb . $random, 256); + + return $this->_normalize($random->add($min)); + } + + /** + * Generate a random prime number. + * + * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed, + * give up and return false. + * + * @param optional Integer $min + * @param optional Integer $max + * @param optional Integer $timeout + * @return Math_BigInteger + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. + */ + function randomPrime($min = false, $max = false, $timeout = false) + { + $compare = $max->compare($min); + + if (!$compare) { + return $min; + } else if ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per . + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { + // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function + // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however, + // the same $max / $min checks are not performed. + if ($min === false) { + $min = new Math_BigInteger(0); + } + + if ($max === false) { + $max = new Math_BigInteger(0x7FFFFFFF); + } + + $x = $this->random($min, $max); + + $x->value = gmp_nextprime($x->value); + + if ($x->compare($max) <= 0) { + return $x; + } + + $x->value = gmp_nextprime($min->value); + + if ($x->compare($max) <= 0) { + return $x; + } + + return false; + } + + static $one, $two; + if (!isset($one)) { + $one = new Math_BigInteger(1); + $two = new Math_BigInteger(2); + } + + $start = time(); + + $x = $this->random($min, $max); + if ($x->equals($two)) { + return $x; + } + + $x->_make_odd(); + if ($x->compare($max) > 0) { + // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range + if ($min->equals($max)) { + return false; + } + $x = $min->copy(); + $x->_make_odd(); + } + + $initial_x = $x->copy(); + + while (true) { + if ($timeout !== false && time() - $start > $timeout) { + return false; + } + + if ($x->isPrime()) { + return $x; + } + + $x = $x->add($two); + + if ($x->compare($max) > 0) { + $x = $min->copy(); + if ($x->equals($two)) { + return $x; + } + $x->_make_odd(); + } + + if ($x->equals($initial_x)) { + return false; + } + } + } + + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see randomPrime() + * @access private + */ + function _make_odd() + { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + gmp_setbit($this->value, 0); + break; + case MATH_BIGINTEGER_MODE_BCMATH: + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + $this->value = bcadd($this->value, '1'); + } + break; + default: + $this->value[0] |= 1; + } + } + + /** + * Checks a numer to see if it's prime + * + * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the + * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads + * on a website instead of just one. + * + * @param optional Integer $t + * @return Boolean + * @access public + * @internal Uses the + * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}. + */ + function isPrime($t = false) + { + $length = strlen($this->toBytes()); + + if (!$t) { + // see HAC 4.49 "Note (controlling the error probability)" + if ($length >= 163) { $t = 2; } // floor(1300 / 8) + else if ($length >= 106) { $t = 3; } // floor( 850 / 8) + else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8) + else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8) + else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8) + else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8) + else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8) + else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8) + else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8) + else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8) + else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8) + else { $t = 27; } + } + + // ie. gmp_testbit($this, 0) + // ie. isEven() or !isOdd() + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + return gmp_prob_prime($this->value, $t) != 0; + case MATH_BIGINTEGER_MODE_BCMATH: + if ($this->value === '2') { + return true; + } + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + return false; + } + break; + default: + if ($this->value == array(2)) { + return true; + } + if (~$this->value[0] & 1) { + return false; + } + } + + static $primes, $zero, $one, $two; + + if (!isset($primes)) { + $primes = array( + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997 + ); + + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { + for ($i = 0; $i < count($primes); ++$i) { + $primes[$i] = new Math_BigInteger($primes[$i]); + } + } + + $zero = new Math_BigInteger(); + $one = new Math_BigInteger(1); + $two = new Math_BigInteger(2); + } + + if ($this->equals($one)) { + return false; + } + + // see HAC 4.4.1 "Random search for probable primes" + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { + foreach ($primes as $prime) { + list(, $r) = $this->divide($prime); + if ($r->equals($zero)) { + return $this->equals($prime); + } + } + } else { + $value = $this->value; + foreach ($primes as $prime) { + list(, $r) = $this->_divide_digit($value, $prime); + if (!$r) { + return count($value) == 1 && $value[0] == $prime; + } + } + } + + $n = $this->copy(); + $n_1 = $n->subtract($one); + $n_2 = $n->subtract($two); + + $r = $n_1->copy(); + $r_value = $r->value; + // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $s = 0; + // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier + while ($r->value[strlen($r->value) - 1] % 2 == 0) { + $r->value = bcdiv($r->value, '2', 0); + ++$s; + } + } else { + for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { + $temp = ~$r_value[$i] & 0xFFFFFF; + for ($j = 1; ($temp >> $j) & 1; ++$j); + if ($j != 25) { + break; + } + } + $s = 26 * $i + $j - 1; + $r->_rshift($s); + } + + for ($i = 0; $i < $t; ++$i) { + $a = $this->random($two, $n_2); + $y = $a->modPow($r, $n); + + if (!$y->equals($one) && !$y->equals($n_1)) { + for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { + $y = $y->modPow($two, $n); + if ($y->equals($one)) { + return false; + } + } + + if (!$y->equals($n_1)) { + return false; + } + } + } + return true; + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param Integer $shift + * @access private + */ + function _lshift($shift) + { + if ( $shift == 0 ) { + return; + } + + $num_digits = (int) ($shift / 26); + $shift %= 26; + $shift = 1 << $shift; + + $carry = 0; + + for ($i = 0; $i < count($this->value); ++$i) { + $temp = $this->value[$i] * $shift + $carry; + $carry = (int) ($temp / 0x4000000); + $this->value[$i] = (int) ($temp - $carry * 0x4000000); + } + + if ( $carry ) { + $this->value[] = $carry; + } + + while ($num_digits--) { + array_unshift($this->value, 0); + } + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param Integer $shift + * @access private + */ + function _rshift($shift) + { + if ($shift == 0) { + return; + } + + $num_digits = (int) ($shift / 26); + $shift %= 26; + $carry_shift = 26 - $shift; + $carry_mask = (1 << $shift) - 1; + + if ( $num_digits ) { + $this->value = array_slice($this->value, $num_digits); + } + + $carry = 0; + + for ($i = count($this->value) - 1; $i >= 0; --$i) { + $temp = $this->value[$i] >> $shift | $carry; + $carry = ($this->value[$i] & $carry_mask) << $carry_shift; + $this->value[$i] = $temp; + } + + $this->value = $this->_trim($this->value); + } + + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param Math_BigInteger + * @return Math_BigInteger + * @see _trim() + * @access private + */ + function _normalize($result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + if (!empty($result->bitmask->value)) { + $result->value = gmp_and($result->value, $result->bitmask->value); + } + + return $result; + case MATH_BIGINTEGER_MODE_BCMATH: + if (!empty($result->bitmask->value)) { + $result->value = bcmod($result->value, $result->bitmask->value); + } + + return $result; + } + + $value = &$result->value; + + if ( !count($value) ) { + return $result; + } + + $value = $this->_trim($value); + + if (!empty($result->bitmask->value)) { + $length = min(count($value), count($this->bitmask->value)); + $value = array_slice($value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $value[$i] = $value[$i] & $this->bitmask->value[$i]; + } + } + + return $result; + } + + /** + * Trim + * + * Removes leading zeros + * + * @return Math_BigInteger + * @access private + */ + function _trim($value) + { + for ($i = count($value) - 1; $i >= 0; --$i) { + if ( $value[$i] ) { + break; + } + unset($value[$i]); + } + + return $value; + } + + /** + * Array Repeat + * + * @param $input Array + * @param $multiplier mixed + * @return Array + * @access private + */ + function _array_repeat($input, $multiplier) + { + return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); + } + + /** + * Logical Left Shift + * + * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. + * + * @param $x String + * @param $shift Integer + * @return String + * @access private + */ + function _base256_lshift(&$x, $shift) + { + if ($shift == 0) { + return; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $carry = 0; + for ($i = strlen($x) - 1; $i >= 0; --$i) { + $temp = ord($x[$i]) << $shift | $carry; + $x[$i] = chr($temp); + $carry = $temp >> 8; + } + $carry = ($carry != 0) ? chr($carry) : ''; + $x = $carry . $x . str_repeat(chr(0), $num_bytes); + } + + /** + * Logical Right Shift + * + * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. + * + * @param $x String + * @param $shift Integer + * @return String + * @access private + */ + function _base256_rshift(&$x, $shift) + { + if ($shift == 0) { + $x = ltrim($x, chr(0)); + return ''; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $remainder = ''; + if ($num_bytes) { + $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes; + $remainder = substr($x, $start); + $x = substr($x, 0, -$num_bytes); + } + + $carry = 0; + $carry_shift = 8 - $shift; + for ($i = 0; $i < strlen($x); ++$i) { + $temp = (ord($x[$i]) >> $shift) | $carry; + $carry = (ord($x[$i]) << $carry_shift) & 0xFF; + $x[$i] = chr($temp); + } + $x = ltrim($x, chr(0)); + + $remainder = chr($carry >> $carry_shift) . $remainder; + + return ltrim($remainder, chr(0)); + } + + // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long + // at 32-bits, while java's longs are 64-bits. + + /** + * Converts 32-bit integers to bytes. + * + * @param Integer $x + * @return String + * @access private + */ + function _int2bytes($x) + { + return ltrim(pack('N', $x), chr(0)); + } + + /** + * Converts bytes to 32-bit integers + * + * @param String $x + * @return Integer + * @access private + */ + function _bytes2int($x) + { + $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); + return $temp['int']; + } + + /** + * DER-encode an integer + * + * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL + * + * @see modPow() + * @access private + * @param Integer $length + * @return String + */ + function _encodeASN1Length($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } +} \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php new file mode 100644 index 0000000000000000000000000000000000000000..8db087d3d99c695fe6b701ce720d6e9c3f9431a7 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php @@ -0,0 +1,2029 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $sftp->pwd() . "\r\n"; + * $sftp->put('filename.ext', 'hello, world!'); + * print_r($sftp->nlist()); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Net + * @package Net_SFTP + * @author Jim Wigginton + * @copyright MMIX Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Net_SSH2 + */ +if (!class_exists('Net_SSH2')) { + require_once('Net/SSH2.php'); +} + +/**#@+ + * @access public + * @see Net_SFTP::getLog() + */ +/** + * Returns the message numbers + */ +define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE); +/** + * Returns the message content + */ +define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX); +/** + * Outputs the message content in real-time. + */ +define('NET_SFTP_LOG_REALTIME', 3); +/**#@-*/ + +/** + * SFTP channel constant + * + * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1. + * + * @see Net_SSH2::_send_channel_packet() + * @see Net_SSH2::_get_channel_packet() + * @access private + */ +define('NET_SFTP_CHANNEL', 2); + +/**#@+ + * @access public + * @see Net_SFTP::put() + */ +/** + * Reads data from a local file. + */ +define('NET_SFTP_LOCAL_FILE', 1); +/** + * Reads data from a string. + */ +// this value isn't really used anymore but i'm keeping it reserved for historical reasons +define('NET_SFTP_STRING', 2); +/** + * Resumes an upload + */ +define('NET_SFTP_RESUME', 4); +/**#@-*/ + +/** + * Pure-PHP implementations of SFTP. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Net_SFTP + */ +class Net_SFTP extends Net_SSH2 { + /** + * Packet Types + * + * @see Net_SFTP::Net_SFTP() + * @var Array + * @access private + */ + var $packet_types = array(); + + /** + * Status Codes + * + * @see Net_SFTP::Net_SFTP() + * @var Array + * @access private + */ + var $status_codes = array(); + + /** + * The Request ID + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var Integer + * @see Net_SFTP::_send_sftp_packet() + * @access private + */ + var $request_id = false; + + /** + * The Packet Type + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var Integer + * @see Net_SFTP::_get_sftp_packet() + * @access private + */ + var $packet_type = -1; + + /** + * Packet Buffer + * + * @var String + * @see Net_SFTP::_get_sftp_packet() + * @access private + */ + var $packet_buffer = ''; + + /** + * Extensions supported by the server + * + * @var Array + * @see Net_SFTP::_initChannel() + * @access private + */ + var $extensions = array(); + + /** + * Server SFTP version + * + * @var Integer + * @see Net_SFTP::_initChannel() + * @access private + */ + var $version; + + /** + * Current working directory + * + * @var String + * @see Net_SFTP::_realpath() + * @see Net_SFTP::chdir() + * @access private + */ + var $pwd = false; + + /** + * Packet Type Log + * + * @see Net_SFTP::getLog() + * @var Array + * @access private + */ + var $packet_type_log = array(); + + /** + * Packet Log + * + * @see Net_SFTP::getLog() + * @var Array + * @access private + */ + var $packet_log = array(); + + /** + * Error information + * + * @see Net_SFTP::getSFTPErrors() + * @see Net_SFTP::getLastSFTPError() + * @var String + * @access private + */ + var $sftp_errors = array(); + + /** + * File Type + * + * @see Net_SFTP::_parseLongname() + * @var Integer + * @access private + */ + var $fileType = 0; + + /** + * Directory Cache + * + * Rather than always having to open a directory and close it immediately there after to see if a file is a directory or + * rather than always + * + * @see Net_SFTP::_save_dir() + * @see Net_SFTP::_remove_dir() + * @see Net_SFTP::_is_dir() + * @var Array + * @access private + */ + var $dirs = array(); + + /** + * Default Constructor. + * + * Connects to an SFTP server + * + * @param String $host + * @param optional Integer $port + * @param optional Integer $timeout + * @return Net_SFTP + * @access public + */ + function Net_SFTP($host, $port = 22, $timeout = 10) + { + parent::Net_SSH2($host, $port, $timeout); + $this->packet_types = array( + 1 => 'NET_SFTP_INIT', + 2 => 'NET_SFTP_VERSION', + /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+: + SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1 + pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */ + 3 => 'NET_SFTP_OPEN', + 4 => 'NET_SFTP_CLOSE', + 5 => 'NET_SFTP_READ', + 6 => 'NET_SFTP_WRITE', + 7 => 'NET_SFTP_LSTAT', + 9 => 'NET_SFTP_SETSTAT', + 11 => 'NET_SFTP_OPENDIR', + 12 => 'NET_SFTP_READDIR', + 13 => 'NET_SFTP_REMOVE', + 14 => 'NET_SFTP_MKDIR', + 15 => 'NET_SFTP_RMDIR', + 16 => 'NET_SFTP_REALPATH', + 17 => 'NET_SFTP_STAT', + /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+: + SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */ + 18 => 'NET_SFTP_RENAME', + + 101=> 'NET_SFTP_STATUS', + 102=> 'NET_SFTP_HANDLE', + /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+: + SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4 + pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */ + 103=> 'NET_SFTP_DATA', + 104=> 'NET_SFTP_NAME', + 105=> 'NET_SFTP_ATTRS', + + 200=> 'NET_SFTP_EXTENDED' + ); + $this->status_codes = array( + 0 => 'NET_SFTP_STATUS_OK', + 1 => 'NET_SFTP_STATUS_EOF', + 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', + 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', + 4 => 'NET_SFTP_STATUS_FAILURE', + 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', + 6 => 'NET_SFTP_STATUS_NO_CONNECTION', + 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', + 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 + // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why + $this->attributes = array( + 0x00000001 => 'NET_SFTP_ATTR_SIZE', + 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ + 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', + 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', + // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers + // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in + // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. + // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. + -1 << 31 => 'NET_SFTP_ATTR_EXTENDED' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 + // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name + // the array for that $this->open5_flags and similarily alter the constant names. + $this->open_flags = array( + 0x00000001 => 'NET_SFTP_OPEN_READ', + 0x00000002 => 'NET_SFTP_OPEN_WRITE', + 0x00000004 => 'NET_SFTP_OPEN_APPEND', + 0x00000008 => 'NET_SFTP_OPEN_CREATE', + 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 + // see Net_SFTP::_parseLongname() for an explanation + $this->file_types = array( + 1 => 'NET_SFTP_TYPE_REGULAR', + 2 => 'NET_SFTP_TYPE_DIRECTORY', + 3 => 'NET_SFTP_TYPE_SYMLINK', + 4 => 'NET_SFTP_TYPE_SPECIAL' + ); + $this->_define_array( + $this->packet_types, + $this->status_codes, + $this->attributes, + $this->open_flags, + $this->file_types + ); + } + + /** + * Login + * + * @param String $username + * @param optional String $password + * @return Boolean + * @access public + */ + function login($username, $password = '') + { + if (!parent::login($username, $password)) { + return false; + } + + $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size; + + $packet = pack('CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); + if ($response === false) { + return false; + } + + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp'); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); + if ($response === false) { + return false; + } + + $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; + + if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_VERSION) { + user_error('Expected SSH_FXP_VERSION'); + return false; + } + + extract(unpack('Nversion', $this->_string_shift($response, 4))); + $this->version = $version; + while (!empty($response)) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + $this->extensions[$key] = $value; + } + + /* + SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', + however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's + not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for + one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that + 'newline@vandyke.com' would. + */ + /* + if (isset($this->extensions['newline@vandyke.com'])) { + $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; + unset($this->extensions['newline@vandyke.com']); + } + */ + + $this->request_id = 1; + + /* + A Note on SFTPv4/5/6 support: + states the following: + + "If the client wishes to interoperate with servers that support noncontiguous version + numbers it SHOULD send '3'" + + Given that the server only sends its version number after the client has already done so, the above + seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the + most popular. + + states the following; + + "If the server did not send the "versions" extension, or the version-from-list was not included, the + server MAY send a status response describing the failure, but MUST then close the channel without + processing any further requests." + + So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and + a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements + v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed + in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the + channel and reopen it with a new and updated SSH_FXP_INIT packet. + */ + switch ($this->version) { + case 2: + case 3: + break; + default: + return false; + } + + $this->pwd = $this->_realpath('.', false); + + $this->_save_dir($this->pwd); + + return true; + } + + /** + * Returns the current directory name + * + * @return Mixed + * @access public + */ + function pwd() + { + return $this->pwd; + } + + /** + * Logs errors + * + * @param String $response + * @param optional Integer $status + * @access public + */ + function _logError($response, $status = -1) { + if ($status == -1) { + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + } + + $error = $this->status_codes[$status]; + + if ($this->version > 2) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); + } else { + $this->sftp_errors[] = $error; + } + } + + /** + * Canonicalize the Server-Side Path Name + * + * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns + * the absolute (canonicalized) path. + * + * @see Net_SFTP::chdir() + * @param String $dir + * @return Mixed + * @access private + */ + function _realpath($dir, $check_dir = true) + { + if ($check_dir && $this->_is_dir($dir)) { + return true; + } + + /* + "This protocol represents file names as strings. File names are + assumed to use the slash ('/') character as a directory separator. + + File names starting with a slash are "absolute", and are relative to + the root of the file system. Names starting with any other character + are relative to the user's default directory (home directory). Note + that identifying the user is assumed to take place outside of this + protocol." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6 + */ + $file = ''; + if ($this->pwd !== false) { + // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary + // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on + // the subject, we'll go ahead and work around it with the following. + if (empty($dir) || $dir[strlen($dir) - 1] != '/') { + $file = basename($dir); + $dir = dirname($dir); + } + + $dir = $dir[0] == '/' ? '/' . rtrim(substr($dir, 1), '/') : rtrim($dir, '/'); + + if ($dir == '.' || $dir == $this->pwd) { + $temp = $this->pwd; + if (!empty($file)) { + $temp.= '/' . $file; + } + return $temp; + } + + if ($dir[0] != '/') { + $dir = $this->pwd . '/' . $dir; + } + // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths + // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let + // the server do it. we'll do the latter. + } + + /* + that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the + specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet + regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename + field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely + not be the case, but for this one, it is. + */ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following + // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks + // at is the first part and that part is defined the same in SFTP versions 3 through 6. + $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $realpath = $this->_string_shift($response, $length); + // the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information. + // per the above comment, this is a shot in the dark that, on most servers, won't help us in determining + // the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot. + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->fileType = $this->_parseLongname($this->_string_shift($response, $length)); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + return false; + } + + // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to + // be a bonafide directory + if (!empty($file)) { + $realpath.= '/' . $file; + } + + return $realpath; + } + + /** + * Changes the current directory + * + * @param String $dir + * @return Boolean + * @access public + */ + function chdir($dir) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + if ($dir[strlen($dir) - 1] != '/') { + $dir.= '/'; + } + + // confirm that $dir is, in fact, a valid directory + if ($this->_is_dir($dir)) { + $this->pwd = $dir; + return true; + } + + $dir = $this->_realpath($dir, false); + + if ($this->_is_dir($dir)) { + $this->pwd = $dir; + return true; + } + + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + // see Net_SFTP::nlist() for a more thorough explanation of the following + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + $this->_save_dir($dir); + + $this->pwd = $dir; + return true; + } + + /** + * Returns a list of files in the given directory + * + * @param optional String $dir + * @return Mixed + * @access public + */ + function nlist($dir = '.') + { + return $this->_list($dir, false); + } + + /** + * Returns a detailed list of files in the given directory + * + * @param optional String $dir + * @return Mixed + * @access public + */ + function rawlist($dir = '.') + { + return $this->_list($dir, true); + } + + /** + * Reads a list, be it detailed or not, of files in the given directory + * + * $realpath exists because, in the case of the recursive deletes and recursive chmod's $realpath has already + * been calculated. + * + * @param String $dir + * @param optional Boolean $raw + * @param optional Boolean $realpath + * @return Mixed + * @access private + */ + function _list($dir, $raw = true, $realpath = true) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $dir = $this->_realpath($dir . '/'); + if ($dir === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 + // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that + // represent the length of the string and leave it at that + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + $this->_save_dir($dir); + + $contents = array(); + while (true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 + // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many + // SSH_MSG_CHANNEL_DATA messages is not known to me. + if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $shortname = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $longname = $this->_string_shift($response, $length); + $attributes = $this->_parseAttributes($response); // we also don't care about the attributes + if (!$raw) { + $contents[] = $shortname; + } else { + $contents[$shortname] = $attributes; + $fileType = $this->_parseLongname($longname); + if ($fileType) { + if ($fileType == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->_save_dir($dir . '/' . $shortname); + } + $contents[$shortname]['type'] = $fileType; + } + } + // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the + // final SSH_FXP_STATUS packet should tell us that, already. + } + break; + case NET_SFTP_STATUS: + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_EOF) { + $this->_logError($response, $status); + return false; + } + break 2; + default: + user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + return false; + } + } + + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return $contents; + } + + /** + * Returns the file size, in bytes, or false, on failure + * + * Files larger than 4GB will show up as being exactly 4GB. + * + * @param String $filename + * @return Mixed + * @access public + */ + function size($filename) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + return $this->_size($filename); + } + + /** + * Save directories to cache + * + * @param String $dir + * @access private + */ + function _save_dir($dir) + { + // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($dir, '/')) + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); + + $temp = &$this->dirs; + foreach ($dirs as $dir) { + if (!isset($temp[$dir])) { + $temp[$dir] = array(); + } + $temp = &$temp[$dir]; + } + } + + /** + * Remove directories from cache + * + * @param String $dir + * @access private + */ + function _remove_dir($dir) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); + + $temp = &$this->dirs; + foreach ($dirs as $dir) { + if ($dir == end($dirs)) { + unset($temp[$dir]); + return true; + } + if (!isset($temp[$dir])) { + return false; + } + $temp = &$temp[$dir]; + } + } + + /** + * Checks cache for directory + * + * @param String $dir + * @access private + */ + function _is_dir($dir) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); + + $temp = &$this->dirs; + foreach ($dirs as $dir) { + if (!isset($temp[$dir])) { + return false; + } + $temp = &$temp[$dir]; + } + } + + /** + * Returns general information about a file. + * + * Returns an array on success and false otherwise. + * + * @param String $filename + * @return Mixed + * @access public + */ + function stat($filename) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); + if ($stat === false) { + return false; + } + + $pwd = $this->pwd; + $stat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + return $stat; + } + + /** + * Returns general information about a file or symbolic link. + * + * Returns an array on success and false otherwise. + * + * @param String $filename + * @return Mixed + * @access public + */ + function lstat($filename) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + $lstat = $this->_stat($filename, NET_SFTP_LSTAT); + $stat = $this->_stat($filename, NET_SFTP_STAT); + if ($stat === false) { + return false; + } + + if ($lstat != $stat) { + return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); + } + + $pwd = $this->pwd; + $lstat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + return $lstat; + } + + /** + * Returns general information about a file or symbolic link + * + * Determines information without calling Net_SFTP::_realpath(). + * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. + * + * @param String $filename + * @param Integer $type + * @return Mixed + * @access private + */ + function _stat($filename, $type) + { + // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet($type, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + $attributes = $this->_parseAttributes($response); + if ($this->fileType) { + $attributes['type'] = $this->fileType; + } + return $attributes; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + return false; + } + + /** + * Attempt to identify the file type + * + * @param String $path + * @param Array $stat + * @param Array $lstat + * @return Integer + * @access private + */ + function _identify_type($path, $stat1, $stat2) + { + $stat1 = $this->_stat($path, $stat1); + $stat2 = $this->_stat($path, $stat2); + + if ($stat1 != $stat2) { + return array_merge($stat1, array('type' => NET_SFTP_TYPE_SYMLINK)); + } + + $pwd = $this->pwd; + $stat1['type'] = $this->chdir($path) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + return $stat1; + } + + /** + * Returns the file size, in bytes, or false, on failure + * + * Determines the size without calling Net_SFTP::_realpath() + * + * @param String $filename + * @return Mixed + * @access private + */ + function _size($filename) + { + $result = $this->_stat($filename, NET_SFTP_LSTAT); + if ($result === false) { + return false; + } + return isset($result['size']) ? $result['size'] : -1; + } + + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or FALSE on error. + * + * @param Integer $mode + * @param String $filename + * @return Mixed + * @access public + */ + function chmod($mode, $filename, $recursive = false) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($recursive) { + $i = 0; + $result = $this->_chmod_recursive($mode, $filename, $i); + $this->_read_put_responses($i); + return $result; + } + + // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to + // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { + return false; + } + + /* + "Because some systems must use separate system calls to set various attributes, it is possible that a failure + response will be returned, but yet some of the attributes may be have been successfully modified. If possible, + servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 + */ + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + } + + // rather than return what the permissions *should* be, we'll return what they actually are. this will also + // tell us if the file actually exists. + // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + $attrs = $this->_parseAttributes($response); + return $attrs['permissions']; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + return false; + } + + /** + * Recursively chmods directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param Integer $mode + * @param String $filename + * @return Boolean + * @access private + */ + function _chmod_recursive($mode, $path, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true, false); + + if ($entries === false) { + return $this->chmod($mode, $path); + } + + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return false; + } + + foreach ($entries as $filename=>$props) { + if ($filename == '.' || $filename == '..') { + continue; + } + + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_chmod_recursive($mode, $temp, $i)) { + return false; + } + } else { + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { + return false; + } + + $i++; + + if ($i >= 50) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { + return false; + } + + $i++; + + if ($i >= 50) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Creates a directory. + * + * @param String $dir + * @return Boolean + * @access public + */ + function mkdir($dir) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + if ($dir[0] != '/') { + $dir = $this->_realpath(rtrim($dir, '/')); + if ($dir === false) { + return false; + } + if (!$this->_mkdir_helper($dir)) { + return false; + } + } else { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); + $temp = ''; + foreach ($dirs as $dir) { + $temp.= '/' . $dir; + $result = $this->_mkdir_helper($temp); + } + if (!$result) { + return false; + } + } + + return true; + } + + /** + * Helper function for directory creation + * + * @param String $dir + * @return Boolean + * @access private + */ + function _mkdir_helper($dir) + { + // by not providing any permissions, hopefully the server will use the logged in users umask - their + // default permissions. + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + $this->_save_dir($dir); + + return true; + } + + /** + * Removes a directory. + * + * @param String $dir + * @return Boolean + * @access public + */ + function rmdir($dir) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $dir = $this->_realpath($dir); + if ($dir === false) { + return false; + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? + $this->_logError($response, $status); + return false; + } + + $this->_remove_dir($dir); + + return true; + } + + /** + * Uploads a file to the SFTP server. + * + * By default, Net_SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * @param String $remote_file + * @param String $data + * @param optional Integer $mode + * @return Boolean + * @access public + * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode(). + */ + function put($remote_file, $data, $mode = NET_SFTP_STRING) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; + // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." + // in practice, it doesn't seem to do that. + //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + + // if NET_SFTP_OPEN_APPEND worked as it should the following (up until the -----------) wouldn't be necessary + $offset = 0; + if ($mode & NET_SFTP_RESUME) { + $size = $this->_size($remote_file); + $offset = $size !== false ? $size : 0; + } else { + $flags|= NET_SFTP_OPEN_TRUNCATE; + } + // -------------- + + $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + $initialize = true; + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 + if ($mode & NET_SFTP_LOCAL_FILE) { + if (!is_file($data)) { + user_error("$data is not a valid file"); + return false; + } + $fp = @fopen($data, 'rb'); + if (!$fp) { + return false; + } + $size = filesize($data); + } else { + $size = strlen($data); + } + + $sent = 0; + $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; + + $sftp_packet_size = 4096; // PuTTY uses 4096 + $i = 0; + while ($sent < $size) { + $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size); + $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $offset + $sent, strlen($temp), $temp); + if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { + fclose($fp); + return false; + } + $sent+= strlen($temp); + + $i++; + + if ($i == 50) { + if (!$this->_read_put_responses($i)) { + $i = 0; + break; + } + $i = 0; + } + } + + if (!$this->_read_put_responses($i)) { + return false; + } + + if ($mode & NET_SFTP_LOCAL_FILE) { + fclose($fp); + } + + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Reads multiple successive SSH_FXP_WRITE responses + * + * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i + * SSH_FXP_WRITEs, in succession, and then reading $i responses. + * + * @param Integer $i + * @return Boolean + * @access private + */ + function _read_put_responses($i) + { + while ($i--) { + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + break; + } + } + + return $i < 0; + } + + /** + * Downloads a file from the SFTP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation + * + * @param String $remote_file + * @param optional String $local_file + * @return Mixed + * @access public + */ + function get($remote_file, $local_file = false, $offset = 0, $length = -1) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + if ($local_file !== false) { + $fp = fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } else { + $content = ''; + } + + $size = (1 << 20) < $length || $length < 0 ? 1 << 20 : $length; + $start = $offset; + while (true) { + $packet = pack('Na*N3', strlen($handle), $handle, 0, $offset, $size); + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { + if ($local_file !== false) { + fclose($fp); + } + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_DATA: + $temp = substr($response, 4); + $offset+= strlen($temp); + if ($local_file === false) { + $content.= $temp; + } else { + fputs($fp, $temp); + } + break; + case NET_SFTP_STATUS: + $this->_logError($response); + break 2; + default: + user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); + if ($local_file !== false) { + fclose($fp); + } + return false; + } + + if ($length > 0 && $length <= $offset - $size) { + if ($local_file === false) { + $content = substr($content, 0, $length); + } else { + ftruncate($fp, $length); + } + break; + } + } + + if ($local_file !== false) { + fclose($fp); + } + + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + if (isset($content)) { + return $content; + } + + return true; + } + + /** + * Deletes a file on the SFTP server. + * + * @param String $path + * @param Boolean $recursive + * @return Boolean + * @access public + */ + function delete($path, $recursive = true) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $path = $this->_realpath($path); + if ($path === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + if (!$recursive) { + return false; + } + $i = 0; + $result = $this->_delete_recursive($path, $i); + $this->_read_put_responses($i); + return $result; + } + + return true; + } + + /** + * Recursively deletes directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param String $path + * @param Integer $i + * @return Boolean + * @access private + */ + function _delete_recursive($path, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true, false); + + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return false; + } + + foreach ($entries as $filename=>$props) { + if ($filename == '.' || $filename == '..') { + continue; + } + + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_delete_recursive($temp, $i)) { + return false; + } + } else { + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { + return false; + } + + $i++; + + if ($i >= 50) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { + return false; + } + $this->_remove_dir($path); + + $i++; + + if ($i >= 50) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Renames a file or a directory on the SFTP server + * + * @param String $oldname + * @param String $newname + * @return Boolean + * @access public + */ + function rename($oldname, $newname) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $oldname = $this->_realpath($oldname); + $newname = $this->_realpath($newname); + if ($oldname === false || $newname === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); + if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Parse Attributes + * + * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param String $response + * @return Array + * @access private + */ + function _parseAttributes(&$response) + { + $attr = array(); + extract(unpack('Nflags', $this->_string_shift($response, 4))); + // SFTPv4+ have a type field (a byte) that follows the above flag field + foreach ($this->attributes as $key => $value) { + switch ($flags & $key) { + case NET_SFTP_ATTR_SIZE: // 0x00000001 + // size is represented by a 64-bit integer, so we perhaps ought to be doing the following: + // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256); + // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB + // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than + // 4GB as being 4GB. + extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8))); + if ($upper) { + $attr['size'] = 0xFFFFFFFF; + } else { + $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; + } + break; + case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) + $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 + $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); + break; + case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 + $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_EXTENDED: // 0x80000000 + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $attr[$key] = $this->_string_shift($response, $length); + } + } + } + return $attr; + } + + /** + * Parse Longname + * + * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open + * a file as a directory and see if an error is returned or you could try to parse the + * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. + * The result is returned using the + * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. + * + * If the longname is in an unrecognized format bool(false) is returned. + * + * @param String $longname + * @return Mixed + * @access private + */ + function _parseLongname($longname) + { + // http://en.wikipedia.org/wiki/Unix_file_types + // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions + if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { + switch ($longname[0]) { + case '-': + return NET_SFTP_TYPE_REGULAR; + case 'd': + return NET_SFTP_TYPE_DIRECTORY; + case 'l': + return NET_SFTP_TYPE_SYMLINK; + default: + return NET_SFTP_TYPE_SPECIAL; + } + } + + return false; + } + + /** + * Sends SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param Integer $type + * @param String $data + * @see Net_SFTP::_get_sftp_packet() + * @see Net_SSH2::_send_channel_packet() + * @return Boolean + * @access private + */ + function _send_sftp_packet($type, $data) + { + $packet = $this->request_id !== false ? + pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : + pack('NCa*', strlen($data) + 1, $type, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '-> ' . $this->packet_types[$type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { + echo "
\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n
\r\n"; + flush(); + ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { + $this->packet_log[] = $data; + } + } + } + + return $result; + } + + /** + * Receives SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. + * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA + * messages containing one SFTP packet. + * + * @see Net_SFTP::_send_sftp_packet() + * @return String + * @access private + */ + function _get_sftp_packet() + { + $this->curTimeout = false; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + // SFTP packet length + while (strlen($this->packet_buffer) < 4) { + $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL); + if (is_bool($temp)) { + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + $this->packet_buffer.= $temp; + } + extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); + $tempLength = $length; + $tempLength-= strlen($this->packet_buffer); + + // SFTP packet type and data payload + while ($tempLength > 0) { + $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL); + if (is_bool($temp)) { + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + $this->packet_buffer.= $temp; + $tempLength-= strlen($temp); + } + + $stop = strtok(microtime(), ' ') + strtok(''); + + $this->packet_type = ord($this->_string_shift($this->packet_buffer)); + + if ($this->request_id !== false) { + $this->_string_shift($this->packet_buffer, 4); // remove the request id + $length-= 5; // account for the request id and the packet type + } else { + $length-= 1; // account for the packet type + } + + $packet = $this->_string_shift($this->packet_buffer, $length); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '<- ' . $this->packet_types[$this->packet_type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { + echo "
\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n
\r\n"; + flush(); + ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { + $this->packet_log[] = $packet; + } + } + } + + return $packet; + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') + * + * @access public + * @return String or Array + */ + function getSFTPLog() + { + if (!defined('NET_SFTP_LOGGING')) { + return false; + } + + switch (NET_SFTP_LOGGING) { + case NET_SFTP_LOG_COMPLEX: + return $this->_format_log($this->packet_log, $this->packet_type_log); + break; + //case NET_SFTP_LOG_SIMPLE: + default: + return $this->packet_type_log; + } + } + + /** + * Returns all errors + * + * @return String + * @access public + */ + function getSFTPErrors() + { + return $this->sftp_errors; + } + + /** + * Returns the last error + * + * @return String + * @access public + */ + function getLastSFTPError() + { + return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; + } + + /** + * Get supported SFTP versions + * + * @return Array + * @access public + */ + function getSupportedVersions() + { + $temp = array('version' => $this->version); + if (isset($this->extensions['versions'])) { + $temp['extensions'] = $this->extensions['versions']; + } + return $temp; + } + + /** + * Disconnect + * + * @param Integer $reason + * @return Boolean + * @access private + */ + function _disconnect($reason) + { + $this->pwd = false; + parent::_disconnect($reason); + } +} \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php new file mode 100644 index 0000000000000000000000000000000000000000..8f5c79938e49f21d223fae387b72911107a3489f --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php @@ -0,0 +1,1577 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('ls -la'); + * ?> + * + * + * Here's another short example: + * + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * More information on the SSHv1 specification can be found by reading + * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}. + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Net + * @package Net_SSH1 + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Math_BigInteger + * + * Used to do RSA encryption. + */ +if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); +} + +/** + * Include Crypt_Null + */ +//require_once('Crypt/Null.php'); + +/** + * Include Crypt_DES + */ +if (!class_exists('Crypt_DES')) { + require_once('Crypt/DES.php'); +} + +/** + * Include Crypt_TripleDES + */ +if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); +} + +/** + * Include Crypt_RC4 + */ +if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); +} + +/** + * Include Crypt_Random + */ +// the class_exists() will only be called if the crypt_random_string function hasn't been defined and +// will trigger a call to __autoload() if you're wanting to auto-load classes +// call function_exists() a second time to stop the require_once from being called outside +// of the auto loader +if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); +} + +/**#@+ + * Encryption Methods + * + * @see Net_SSH1::getSupportedCiphers() + * @access public + */ +/** + * No encryption + * + * Not supported. + */ +define('NET_SSH1_CIPHER_NONE', 0); +/** + * IDEA in CFB mode + * + * Not supported. + */ +define('NET_SSH1_CIPHER_IDEA', 1); +/** + * DES in CBC mode + */ +define('NET_SSH1_CIPHER_DES', 2); +/** + * Triple-DES in CBC mode + * + * All implementations are required to support this + */ +define('NET_SSH1_CIPHER_3DES', 3); +/** + * TRI's Simple Stream encryption CBC + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h), + * although it doesn't use it (see cipher.c) + */ +define('NET_SSH1_CIPHER_BROKEN_TSS', 4); +/** + * RC4 + * + * Not supported. + * + * @internal According to the SSH1 specs: + * + * "The first 16 bytes of the session key are used as the key for + * the server to client direction. The remaining 16 bytes are used + * as the key for the client to server direction. This gives + * independent 128-bit keys for each direction." + * + * This library currently only supports encryption when the same key is being used for both directions. This is + * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps). + */ +define('NET_SSH1_CIPHER_RC4', 5); +/** + * Blowfish + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and + * uses it (see cipher.c) + */ +define('NET_SSH1_CIPHER_BLOWFISH', 6); +/**#@-*/ + +/**#@+ + * Authentication Methods + * + * @see Net_SSH1::getSupportedAuthentications() + * @access public + */ +/** + * .rhosts or /etc/hosts.equiv + */ +define('NET_SSH1_AUTH_RHOSTS', 1); +/** + * pure RSA authentication + */ +define('NET_SSH1_AUTH_RSA', 2); +/** + * password authentication + * + * This is the only method that is supported by this library. + */ +define('NET_SSH1_AUTH_PASSWORD', 3); +/** + * .rhosts with RSA host authentication + */ +define('NET_SSH1_AUTH_RHOSTS_RSA', 4); +/**#@-*/ + +/**#@+ + * Terminal Modes + * + * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html + * @access private + */ +define('NET_SSH1_TTY_OP_END', 0); +/**#@-*/ + +/** + * The Response Type + * + * @see Net_SSH1::_get_binary_packet() + * @access private + */ +define('NET_SSH1_RESPONSE_TYPE', 1); + +/** + * The Response Data + * + * @see Net_SSH1::_get_binary_packet() + * @access private + */ +define('NET_SSH1_RESPONSE_DATA', 2); + +/**#@+ + * Execution Bitmap Masks + * + * @see Net_SSH1::bitmap + * @access private + */ +define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001); +define('NET_SSH1_MASK_LOGIN', 0x00000002); +define('NET_SSH1_MASK_SHELL', 0x00000004); +/**#@-*/ + +/**#@+ + * @access public + * @see Net_SSH1::getLog() + */ +/** + * Returns the message numbers + */ +define('NET_SSH1_LOG_SIMPLE', 1); +/** + * Returns the message content + */ +define('NET_SSH1_LOG_COMPLEX', 2); +/** + * Outputs the content real-time + */ +define('NET_SSH2_LOG_REALTIME', 3); +/** + * Dumps the content real-time to a file + */ +define('NET_SSH2_LOG_REALTIME_FILE', 4); +/**#@-*/ + +/**#@+ + * @access public + * @see Net_SSH1::read() + */ +/** + * Returns when a string matching $expect exactly is found + */ +define('NET_SSH1_READ_SIMPLE', 1); +/** + * Returns when a string matching the regular expression $expect is found + */ +define('NET_SSH1_READ_REGEX', 2); +/**#@-*/ + +/** + * Pure-PHP implementation of SSHv1. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Net_SSH1 + */ +class Net_SSH1 { + /** + * The SSH identifier + * + * @var String + * @access private + */ + var $identifier = 'SSH-1.5-phpseclib'; + + /** + * The Socket Object + * + * @var Object + * @access private + */ + var $fsock; + + /** + * The cryptography object + * + * @var Object + * @access private + */ + var $crypto = false; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var Integer + * @access private + */ + var $bitmap = 0; + + /** + * The Server Key Public Exponent + * + * Logged for debug purposes + * + * @see Net_SSH1::getServerKeyPublicExponent() + * @var String + * @access private + */ + var $server_key_public_exponent; + + /** + * The Server Key Public Modulus + * + * Logged for debug purposes + * + * @see Net_SSH1::getServerKeyPublicModulus() + * @var String + * @access private + */ + var $server_key_public_modulus; + + /** + * The Host Key Public Exponent + * + * Logged for debug purposes + * + * @see Net_SSH1::getHostKeyPublicExponent() + * @var String + * @access private + */ + var $host_key_public_exponent; + + /** + * The Host Key Public Modulus + * + * Logged for debug purposes + * + * @see Net_SSH1::getHostKeyPublicModulus() + * @var String + * @access private + */ + var $host_key_public_modulus; + + /** + * Supported Ciphers + * + * Logged for debug purposes + * + * @see Net_SSH1::getSupportedCiphers() + * @var Array + * @access private + */ + var $supported_ciphers = array( + NET_SSH1_CIPHER_NONE => 'No encryption', + NET_SSH1_CIPHER_IDEA => 'IDEA in CFB mode', + NET_SSH1_CIPHER_DES => 'DES in CBC mode', + NET_SSH1_CIPHER_3DES => 'Triple-DES in CBC mode', + NET_SSH1_CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC', + NET_SSH1_CIPHER_RC4 => 'RC4', + NET_SSH1_CIPHER_BLOWFISH => 'Blowfish' + ); + + /** + * Supported Authentications + * + * Logged for debug purposes + * + * @see Net_SSH1::getSupportedAuthentications() + * @var Array + * @access private + */ + var $supported_authentications = array( + NET_SSH1_AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv', + NET_SSH1_AUTH_RSA => 'pure RSA authentication', + NET_SSH1_AUTH_PASSWORD => 'password authentication', + NET_SSH1_AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication' + ); + + /** + * Server Identification + * + * @see Net_SSH1::getServerIdentification() + * @var String + * @access private + */ + var $server_identification = ''; + + /** + * Protocol Flags + * + * @see Net_SSH1::Net_SSH1() + * @var Array + * @access private + */ + var $protocol_flags = array(); + + /** + * Protocol Flag Log + * + * @see Net_SSH1::getLog() + * @var Array + * @access private + */ + var $protocol_flag_log = array(); + + /** + * Message Log + * + * @see Net_SSH1::getLog() + * @var Array + * @access private + */ + var $message_log = array(); + + /** + * Real-time log file pointer + * + * @see Net_SSH1::_append_log() + * @var Resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see Net_SSH1::_append_log() + * @var Integer + * @access private + */ + var $realtime_log_size; + + /** + * Real-time log file wrap boolean + * + * @see Net_SSH1::_append_log() + * @var Boolean + * @access private + */ + var $realtime_log_wrap; + + /** + * Interactive Buffer + * + * @see Net_SSH1::read() + * @var Array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Timeout + * + * @see Net_SSH1::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see Net_SSH2::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Default Constructor. + * + * Connects to an SSHv1 server + * + * @param String $host + * @param optional Integer $port + * @param optional Integer $timeout + * @param optional Integer $cipher + * @return Net_SSH1 + * @access public + */ + function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES) + { + $this->protocol_flags = array( + 1 => 'NET_SSH1_MSG_DISCONNECT', + 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', + 3 => 'NET_SSH1_CMSG_SESSION_KEY', + 4 => 'NET_SSH1_CMSG_USER', + 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD', + 10 => 'NET_SSH1_CMSG_REQUEST_PTY', + 12 => 'NET_SSH1_CMSG_EXEC_SHELL', + 13 => 'NET_SSH1_CMSG_EXEC_CMD', + 14 => 'NET_SSH1_SMSG_SUCCESS', + 15 => 'NET_SSH1_SMSG_FAILURE', + 16 => 'NET_SSH1_CMSG_STDIN_DATA', + 17 => 'NET_SSH1_SMSG_STDOUT_DATA', + 18 => 'NET_SSH1_SMSG_STDERR_DATA', + 19 => 'NET_SSH1_CMSG_EOF', + 20 => 'NET_SSH1_SMSG_EXITSTATUS', + 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION' + ); + + $this->_define_array($this->protocol_flags); + + $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout); + if (!$this->fsock) { + user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); + return; + } + + $this->server_identification = $init_line = fgets($this->fsock, 255); + + if (defined('NET_SSH1_LOGGING')) { + $this->_append_log('<-', $this->server_identification); + $this->_append_log('->', $this->identifier . "\r\n"); + } + + if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { + user_error('Can only connect to SSH servers'); + return; + } + if ($parts[1][0] != 1) { + user_error("Cannot connect to SSH $parts[1] servers"); + return; + } + + fputs($this->fsock, $this->identifier."\r\n"); + + $response = $this->_get_binary_packet(); + if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { + user_error('Expected SSH_SMSG_PUBLIC_KEY'); + return; + } + + $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8); + + $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4); + + $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2)); + $server_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->server_key_public_exponent = $server_key_public_exponent; + + $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2)); + $server_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->server_key_public_modulus = $server_key_public_modulus; + + $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4); + + $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2)); + $host_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->host_key_public_exponent = $host_key_public_exponent; + + $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2)); + $host_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->host_key_public_modulus = $host_key_public_modulus; + + $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4); + + // get a list of the supported ciphers + extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4))); + foreach ($this->supported_ciphers as $mask=>$name) { + if (($supported_ciphers_mask & (1 << $mask)) == 0) { + unset($this->supported_ciphers[$mask]); + } + } + + // get a list of the supported authentications + extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4))); + foreach ($this->supported_authentications as $mask=>$name) { + if (($supported_authentications_mask & (1 << $mask)) == 0) { + unset($this->supported_authentications[$mask]); + } + } + + $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie)); + + $session_key = crypt_random_string(32); + $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); + + if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) { + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $server_key_public_exponent, + $server_key_public_modulus + ) + ); + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $host_key_public_exponent, + $host_key_public_modulus + ) + ); + } else { + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $host_key_public_exponent, + $host_key_public_modulus + ) + ); + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $server_key_public_exponent, + $server_key_public_modulus + ) + ); + } + + $cipher = isset($this->supported_ciphers[$cipher]) ? $cipher : NET_SSH1_CIPHER_3DES; + $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_SESSION_KEY'); + return; + } + + switch ($cipher) { + //case NET_SSH1_CIPHER_NONE: + // $this->crypto = new Crypt_Null(); + // break; + case NET_SSH1_CIPHER_DES: + $this->crypto = new Crypt_DES(); + $this->crypto->disablePadding(); + $this->crypto->enableContinuousBuffer(); + $this->crypto->setKey(substr($session_key, 0, 8)); + break; + case NET_SSH1_CIPHER_3DES: + $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC); + $this->crypto->disablePadding(); + $this->crypto->enableContinuousBuffer(); + $this->crypto->setKey(substr($session_key, 0, 24)); + break; + //case NET_SSH1_CIPHER_RC4: + // $this->crypto = new Crypt_RC4(); + // $this->crypto->enableContinuousBuffer(); + // $this->crypto->setKey(substr($session_key, 0, 16)); + // break; + } + + $response = $this->_get_binary_packet(); + + if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { + user_error('Expected SSH_SMSG_SUCCESS'); + return; + } + + $this->bitmap = NET_SSH1_MASK_CONSTRUCTOR; + } + + /** + * Login + * + * @param String $username + * @param optional String $password + * @return Boolean + * @access public + */ + function login($username, $password = '') + { + if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) { + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_USER'); + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= NET_SSH1_MASK_LOGIN; + return true; + } else if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { + user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); + return false; + } + + // remove the username and password from the last logged packet + if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) { + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password'); + $this->message_log[count($this->message_log) - 1] = $data; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= NET_SSH1_MASK_LOGIN; + return true; + } else if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { + return false; + } else { + user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + return false; + } + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param Mixed $timeout + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Executes a command on a non-interactive shell, returns the output, and quits. + * + * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2 + * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a + * shell with the -s option, as discussed in the following links: + * + * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html} + * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html} + * + * To execute further commands, a new Net_SSH1 object will need to be created. + * + * Returns false on failure and the output, otherwise. + * + * @see Net_SSH1::interactiveRead() + * @see Net_SSH1::interactiveWrite() + * @param String $cmd + * @return mixed + * @access public + */ + function exec($cmd, $block = true) + { + if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_EXEC_CMD'); + return false; + } + + if (!$block) { + return true; + } + + $output = ''; + $response = $this->_get_binary_packet(); + + if ($response !== false) { + do { + $output.= substr($response[NET_SSH1_RESPONSE_DATA], 4); + $response = $this->_get_binary_packet(); + } while (is_array($response) && $response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS); + } + + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + + // i don't think it's really all that important if this packet gets sent or not. + $this->_send_binary_packet($data); + + fclose($this->fsock); + + // reset the execution bitmap - a new Net_SSH1 object needs to be created. + $this->bitmap = 0; + + return $output; + } + + /** + * Creates an interactive shell + * + * @see Net_SSH1::interactiveRead() + * @see Net_SSH1::interactiveWrite() + * @return Boolean + * @access private + */ + function _initShell() + { + // connect using the sample parameters in protocol-1.5.txt. + // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text + // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell. + $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_REQUEST_PTY'); + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { + user_error('Expected SSH_SMSG_SUCCESS'); + return false; + } + + $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_EXEC_SHELL'); + return false; + } + + $this->bitmap |= NET_SSH1_MASK_SHELL; + + //stream_set_blocking($this->fsock, 0); + + return true; + } + + /** + * Inputs a command into an interactive shell. + * + * @see Net_SSH1::interactiveWrite() + * @param String $cmd + * @return Boolean + * @access public + */ + function write($cmd) + { + return $this->interactiveWrite($cmd); + } + + /** + * Returns the output of an interactive shell when there's a match for $expect + * + * $expect can take the form of a string literal or, if $mode == NET_SSH1_READ_REGEX, + * a regular expression. + * + * @see Net_SSH1::write() + * @param String $expect + * @param Integer $mode + * @return Boolean + * @access public + */ + function read($expect, $mode = NET_SSH1_READ_SIMPLE) + { + if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $match = $expect; + while (true) { + if ($mode == NET_SSH1_READ_REGEX) { + preg_match($expect, $this->interactiveBuffer, $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_binary_packet(); + + if ($response === true) { + return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); + } + $this->interactiveBuffer.= substr($response[NET_SSH1_RESPONSE_DATA], 4); + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see Net_SSH1::interactiveRead() + * @param String $cmd + * @return Boolean + * @access public + */ + function interactiveWrite($cmd) + { + if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_STDIN'); + return false; + } + + return true; + } + + /** + * Returns the output of an interactive shell when no more output is available. + * + * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like + * "", you're seeing ANSI escape codes. According to + * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT + * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user, + * there's not going to be much recourse. + * + * @see Net_SSH1::interactiveRead() + * @return String + * @access public + */ + function interactiveRead() + { + if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $read = array($this->fsock); + $write = $except = null; + if (stream_select($read, $write, $except, 0)) { + $response = $this->_get_binary_packet(); + return substr($response[NET_SSH1_RESPONSE_DATA], 4); + } else { + return ''; + } + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(); + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->_disconnect(); + } + + /** + * Disconnect + * + * @param String $msg + * @access private + */ + function _disconnect($msg = 'Client Quit') + { + if ($this->bitmap) { + $data = pack('C', NET_SSH1_CMSG_EOF); + $this->_send_binary_packet($data); + /* + $response = $this->_get_binary_packet(); + if ($response === true) { + $response = array(NET_SSH1_RESPONSE_TYPE => -1); + } + switch ($response[NET_SSH1_RESPONSE_TYPE]) { + case NET_SSH1_SMSG_EXITSTATUS: + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + break; + default: + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + } + */ + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + + $this->_send_binary_packet($data); + fclose($this->fsock); + $this->bitmap = 0; + } + } + + /** + * Gets Binary Packets + * + * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info. + * + * Also, this function could be improved upon by adding detection for the following exploit: + * http://www.securiteam.com/securitynews/5LP042K3FY.html + * + * @see Net_SSH1::_send_binary_packet() + * @return Array + * @access private + */ + function _get_binary_packet() + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + if ($this->curTimeout) { + $read = array($this->fsock); + $write = $except = NULL; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + //$this->_disconnect('Timeout'); + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $temp = unpack('Nlength', fread($this->fsock, 4)); + + $padding_length = 8 - ($temp['length'] & 7); + $length = $temp['length'] + $padding_length; + + while ($length > 0) { + $temp = fread($this->fsock, $length); + $raw.= $temp; + $length-= strlen($temp); + } + $stop = strtok(microtime(), ' ') + strtok(''); + + if (strlen($raw) && $this->crypto !== false) { + $raw = $this->crypto->decrypt($raw); + } + + $padding = substr($raw, 0, $padding_length); + $type = $raw[$padding_length]; + $data = substr($raw, $padding_length + 1, -4); + + $temp = unpack('Ncrc', substr($raw, -4)); + + //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { + // user_error('Bad CRC in packet from server'); + // return false; + //} + + $type = ord($type); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN'; + $temp = '<- ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $data); + } + + return array( + NET_SSH1_RESPONSE_TYPE => $type, + NET_SSH1_RESPONSE_DATA => $data + ); + } + + /** + * Sends Binary Packets + * + * Returns true on success, false on failure. + * + * @see Net_SSH1::_get_binary_packet() + * @param String $data + * @return Boolean + * @access private + */ + function _send_binary_packet($data) + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + $length = strlen($data) + 4; + + $padding = crypt_random_string(8 - ($length & 7)); + + $orig = $data; + $data = $padding . $data; + $data.= pack('N', $this->_crc($data)); + + if ($this->crypto !== false) { + $data = $this->crypto->encrypt($data); + } + + $packet = pack('Na*', $length, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = strlen($packet) == fputs($this->fsock, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN'; + $temp = '-> ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $orig); + } + + return $result; + } + + /** + * Cyclic Redundancy Check (CRC) + * + * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so + * we've reimplemented it. A more detailed discussion of the differences can be found after + * $crc_lookup_table's initialization. + * + * @see Net_SSH1::_get_binary_packet() + * @see Net_SSH1::_send_binary_packet() + * @param String $data + * @return Integer + * @access private + */ + function _crc($data) + { + static $crc_lookup_table = array( + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + ); + + // For this function to yield the same output as PHP's crc32 function, $crc would have to be + // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is. + $crc = 0x00000000; + $length = strlen($data); + + for ($i=0;$i<$length;$i++) { + // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all + // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example, + // yields 0xFF800000 - not 0x00800000. The following link elaborates: + // http://www.php.net/manual/en/language.operators.bitwise.php#57281 + $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])]; + } + + // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with + // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would. + return $crc; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * RSA Encrypt + * + * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e + * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that + * calls this call modexp, instead, but I think this makes things clearer, maybe... + * + * @see Net_SSH1::Net_SSH1() + * @param Math_BigInteger $m + * @param Array $key + * @return Math_BigInteger + * @access private + */ + function _rsa_crypt($m, $key) + { + /* + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + + $rsa = new Crypt_RSA(); + $rsa->loadKey($key, CRYPT_RSA_PUBLIC_FORMAT_RAW); + $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); + return $rsa->encrypt($m); + */ + + // To quote from protocol-1.5.txt: + // The most significant byte (which is only partial as the value must be + // less than the public modulus, which is never a power of two) is zero. + // + // The next byte contains the value 2 (which stands for public-key + // encrypted data in the PKCS standard [PKCS#1]). Then, there are non- + // zero random bytes to fill any unused space, a zero byte, and the data + // to be encrypted in the least significant bytes, the last byte of the + // data in the least significant byte. + + // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation", + // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL: + // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf + $modulus = $key[1]->toBytes(); + $length = strlen($modulus) - strlen($m) - 3; + $random = ''; + while (strlen($random) != $length) { + $block = crypt_random_string($length - strlen($random)); + $block = str_replace("\x00", '', $block); + $random.= $block; + } + $temp = chr(0) . chr(2) . $random . chr(0) . $m; + + $m = new Math_BigInteger($temp, 256); + $m = $m->modPow($key[0], $key[1]); + + return $m->toBytes(); + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @param Array $array + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key=>$value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') + * + * @access public + * @return String or Array + */ + function getLog() + { + if (!defined('NET_SSH1_LOGGING')) { + return false; + } + + switch (NET_SSH1_LOGGING) { + case NET_SSH1_LOG_SIMPLE: + return $this->message_number_log; + break; + case NET_SSH1_LOG_COMPLEX: + return $this->_format_log($this->message_log, $this->protocol_flags_log); + break; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param Array $message_log + * @param Array $message_number_log + * @access private + * @return String + */ + function _format_log($message_log, $message_number_log) + { + static $boundary = ':', $long_width = 65, $short_width = 16; + + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $short_width); + $hex = substr( + preg_replace( + '#(.)#es', + '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)', + $fragment), + strlen($boundary) + ); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Return the server key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param optional Boolean $raw_output + * @return String + * @access public + */ + function getServerKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString(); + } + + /** + * Return the server key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param optional Boolean $raw_output + * @return String + * @access public + */ + function getServerKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString(); + } + + /** + * Return the host key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param optional Boolean $raw_output + * @return String + * @access public + */ + function getHostKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString(); + } + + /** + * Return the host key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param optional Boolean $raw_output + * @return String + * @access public + */ + function getHostKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString(); + } + + /** + * Return a list of ciphers supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll + * get array(NET_SSH1_CIPHER_3DES). + * + * @param optional Boolean $raw_output + * @return Array + * @access public + */ + function getSupportedCiphers($raw_output = false) + { + return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers); + } + + /** + * Return a list of authentications supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll + * get array(NET_SSH1_AUTH_PASSWORD). + * + * @param optional Boolean $raw_output + * @return Array + * @access public + */ + function getSupportedAuthentications($raw_output = false) + { + return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications); + } + + /** + * Return the server identification. + * + * @return String + * @access public + */ + function getServerIdentification() + { + return rtrim($this->server_identification); + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param String $data + * @access private + */ + function _append_log($protocol_flags, $message) + { + switch (NET_SSH1_LOGGING) { + // useful for benchmarks + case NET_SSH1_LOG_SIMPLE: + $this->protocol_flags_log[] = $protocol_flags; + break; + // the most useful log for SSH1 + case NET_SSH1_LOG_COMPLEX: + $this->protocol_flags_log[] = $protocol_flags; + $this->_string_shift($message); + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->protocol_flags_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case NET_SSH1_LOG_REALTIME: + echo "
\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n
\r\n"; + @flush(); + @ob_flush(); + break; + // basically the same thing as NET_SSH1_LOG_REALTIME with the caveat that NET_SSH1_LOG_REALTIME_FILE + // needs to be defined and that the resultant log file will be capped out at NET_SSH1_LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case NET_SSH1_LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = NET_SSH2_LOG_REALTIME_FILE; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($protocol_flags)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > NET_SSH1_LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } +} \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php new file mode 100644 index 0000000000000000000000000000000000000000..43bfbca2dbe17abbd111da70af3a6b61399acb71 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php @@ -0,0 +1,3009 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * + * setPassword('whatever'); + * $key->loadKey(file_get_contents('privatekey')); + * + * $ssh = new Net_SSH2('www.domain.tld'); + * if (!$ssh->login('username', $key)) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Net + * @package Net_SSH2 + * @author Jim Wigginton + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Math_BigInteger + * + * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. + */ +if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); +} + +/** + * Include Crypt_Random + */ +// the class_exists() will only be called if the crypt_random_string function hasn't been defined and +// will trigger a call to __autoload() if you're wanting to auto-load classes +// call function_exists() a second time to stop the require_once from being called outside +// of the auto loader +if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); +} + +/** + * Include Crypt_Hash + */ +if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); +} + +/** + * Include Crypt_TripleDES + */ +if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); +} + +/** + * Include Crypt_RC4 + */ +if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); +} + +/** + * Include Crypt_AES + */ +if (!class_exists('Crypt_AES')) { + require_once('Crypt/AES.php'); +} + +/**#@+ + * Execution Bitmap Masks + * + * @see Net_SSH2::bitmap + * @access private + */ +define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); +define('NET_SSH2_MASK_LOGIN', 0x00000002); +define('NET_SSH2_MASK_SHELL', 0x00000004); +/**#@-*/ + +/**#@+ + * Channel constants + * + * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer + * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with + * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a + * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel + * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet: + * The 'recipient channel' is the channel number given in the original + * open request, and 'sender channel' is the channel number allocated by + * the other side. + * + * @see Net_SSH2::_send_channel_packet() + * @see Net_SSH2::_get_channel_packet() + * @access private + */ +define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 +define('NET_SSH2_CHANNEL_SHELL',1); +/**#@-*/ + +/**#@+ + * @access public + * @see Net_SSH2::getLog() + */ +/** + * Returns the message numbers + */ +define('NET_SSH2_LOG_SIMPLE', 1); +/** + * Returns the message content + */ +define('NET_SSH2_LOG_COMPLEX', 2); +/** + * Outputs the content real-time + */ +define('NET_SSH2_LOG_REALTIME', 3); +/** + * Dumps the content real-time to a file + */ +define('NET_SSH2_LOG_REALTIME_FILE', 4); +/**#@-*/ + +/**#@+ + * @access public + * @see Net_SSH2::read() + */ +/** + * Returns when a string matching $expect exactly is found + */ +define('NET_SSH2_READ_SIMPLE', 1); +/** + * Returns when a string matching the regular expression $expect is found + */ +define('NET_SSH2_READ_REGEX', 2); +/** + * Make sure that the log never gets larger than this + */ +define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024); +/**#@-*/ + +/** + * Pure-PHP implementation of SSHv2. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Net_SSH2 + */ +class Net_SSH2 { + /** + * The SSH identifier + * + * @var String + * @access private + */ + var $identifier = 'SSH-2.0-phpseclib_0.3'; + + /** + * The Socket Object + * + * @var Object + * @access private + */ + var $fsock; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var Integer + * @access private + */ + var $bitmap = 0; + + /** + * Error information + * + * @see Net_SSH2::getErrors() + * @see Net_SSH2::getLastError() + * @var String + * @access private + */ + var $errors = array(); + + /** + * Server Identifier + * + * @see Net_SSH2::getServerIdentification() + * @var String + * @access private + */ + var $server_identifier = ''; + + /** + * Key Exchange Algorithms + * + * @see Net_SSH2::getKexAlgorithims() + * @var Array + * @access private + */ + var $kex_algorithms; + + /** + * Server Host Key Algorithms + * + * @see Net_SSH2::getServerHostKeyAlgorithms() + * @var Array + * @access private + */ + var $server_host_key_algorithms; + + /** + * Encryption Algorithms: Client to Server + * + * @see Net_SSH2::getEncryptionAlgorithmsClient2Server() + * @var Array + * @access private + */ + var $encryption_algorithms_client_to_server; + + /** + * Encryption Algorithms: Server to Client + * + * @see Net_SSH2::getEncryptionAlgorithmsServer2Client() + * @var Array + * @access private + */ + var $encryption_algorithms_server_to_client; + + /** + * MAC Algorithms: Client to Server + * + * @see Net_SSH2::getMACAlgorithmsClient2Server() + * @var Array + * @access private + */ + var $mac_algorithms_client_to_server; + + /** + * MAC Algorithms: Server to Client + * + * @see Net_SSH2::getMACAlgorithmsServer2Client() + * @var Array + * @access private + */ + var $mac_algorithms_server_to_client; + + /** + * Compression Algorithms: Client to Server + * + * @see Net_SSH2::getCompressionAlgorithmsClient2Server() + * @var Array + * @access private + */ + var $compression_algorithms_client_to_server; + + /** + * Compression Algorithms: Server to Client + * + * @see Net_SSH2::getCompressionAlgorithmsServer2Client() + * @var Array + * @access private + */ + var $compression_algorithms_server_to_client; + + /** + * Languages: Server to Client + * + * @see Net_SSH2::getLanguagesServer2Client() + * @var Array + * @access private + */ + var $languages_server_to_client; + + /** + * Languages: Client to Server + * + * @see Net_SSH2::getLanguagesClient2Server() + * @var Array + * @access private + */ + var $languages_client_to_server; + + /** + * Block Size for Server to Client Encryption + * + * "Note that the length of the concatenation of 'packet_length', + * 'padding_length', 'payload', and 'random padding' MUST be a multiple + * of the cipher block size or 8, whichever is larger. This constraint + * MUST be enforced, even when using stream ciphers." + * + * -- http://tools.ietf.org/html/rfc4253#section-6 + * + * @see Net_SSH2::Net_SSH2() + * @see Net_SSH2::_send_binary_packet() + * @var Integer + * @access private + */ + var $encrypt_block_size = 8; + + /** + * Block Size for Client to Server Encryption + * + * @see Net_SSH2::Net_SSH2() + * @see Net_SSH2::_get_binary_packet() + * @var Integer + * @access private + */ + var $decrypt_block_size = 8; + + /** + * Server to Client Encryption Object + * + * @see Net_SSH2::_get_binary_packet() + * @var Object + * @access private + */ + var $decrypt = false; + + /** + * Client to Server Encryption Object + * + * @see Net_SSH2::_send_binary_packet() + * @var Object + * @access private + */ + var $encrypt = false; + + /** + * Client to Server HMAC Object + * + * @see Net_SSH2::_send_binary_packet() + * @var Object + * @access private + */ + var $hmac_create = false; + + /** + * Server to Client HMAC Object + * + * @see Net_SSH2::_get_binary_packet() + * @var Object + * @access private + */ + var $hmac_check = false; + + /** + * Size of server to client HMAC + * + * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. + * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is + * append it. + * + * @see Net_SSH2::_get_binary_packet() + * @var Integer + * @access private + */ + var $hmac_size = false; + + /** + * Server Public Host Key + * + * @see Net_SSH2::getServerPublicHostKey() + * @var String + * @access private + */ + var $server_public_host_key; + + /** + * Session identifer + * + * "The exchange hash H from the first key exchange is additionally + * used as the session identifier, which is a unique identifier for + * this connection." + * + * -- http://tools.ietf.org/html/rfc4253#section-7.2 + * + * @see Net_SSH2::_key_exchange() + * @var String + * @access private + */ + var $session_id = false; + + /** + * Exchange hash + * + * The current exchange hash + * + * @see Net_SSH2::_key_exchange() + * @var String + * @access private + */ + var $exchange_hash = false; + + /** + * Message Numbers + * + * @see Net_SSH2::Net_SSH2() + * @var Array + * @access private + */ + var $message_numbers = array(); + + /** + * Disconnection Message 'reason codes' defined in RFC4253 + * + * @see Net_SSH2::Net_SSH2() + * @var Array + * @access private + */ + var $disconnect_reasons = array(); + + /** + * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 + * + * @see Net_SSH2::Net_SSH2() + * @var Array + * @access private + */ + var $channel_open_failure_reasons = array(); + + /** + * Terminal Modes + * + * @link http://tools.ietf.org/html/rfc4254#section-8 + * @see Net_SSH2::Net_SSH2() + * @var Array + * @access private + */ + var $terminal_modes = array(); + + /** + * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes + * + * @link http://tools.ietf.org/html/rfc4254#section-5.2 + * @see Net_SSH2::Net_SSH2() + * @var Array + * @access private + */ + var $channel_extended_data_type_codes = array(); + + /** + * Send Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see Net_SSH2::_send_binary_packet() + * @var Integer + * @access private + */ + var $send_seq_no = 0; + + /** + * Get Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see Net_SSH2::_get_binary_packet() + * @var Integer + * @access private + */ + var $get_seq_no = 0; + + /** + * Server Channels + * + * Maps client channels to server channels + * + * @see Net_SSH2::_get_channel_packet() + * @see Net_SSH2::exec() + * @var Array + * @access private + */ + var $server_channels = array(); + + /** + * Channel Buffers + * + * If a client requests a packet from one channel but receives two packets from another those packets should + * be placed in a buffer + * + * @see Net_SSH2::_get_channel_packet() + * @see Net_SSH2::exec() + * @var Array + * @access private + */ + var $channel_buffers = array(); + + /** + * Channel Status + * + * Contains the type of the last sent message + * + * @see Net_SSH2::_get_channel_packet() + * @var Array + * @access private + */ + var $channel_status = array(); + + /** + * Packet Size + * + * Maximum packet size indexed by channel + * + * @see Net_SSH2::_send_channel_packet() + * @var Array + * @access private + */ + var $packet_size_client_to_server = array(); + + /** + * Message Number Log + * + * @see Net_SSH2::getLog() + * @var Array + * @access private + */ + var $message_number_log = array(); + + /** + * Message Log + * + * @see Net_SSH2::getLog() + * @var Array + * @access private + */ + var $message_log = array(); + + /** + * The Window Size + * + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB) + * + * @var Integer + * @see Net_SSH2::_send_channel_packet() + * @see Net_SSH2::exec() + * @access private + */ + var $window_size = 0x7FFFFFFF; + + /** + * Window size + * + * Window size indexed by channel + * + * @see Net_SSH2::_send_channel_packet() + * @var Array + * @access private + */ + var $window_size_client_to_server = array(); + + /** + * Server signature + * + * Verified against $this->session_id + * + * @see Net_SSH2::getServerPublicHostKey() + * @var String + * @access private + */ + var $signature = ''; + + /** + * Server signature format + * + * ssh-rsa or ssh-dss. + * + * @see Net_SSH2::getServerPublicHostKey() + * @var String + * @access private + */ + var $signature_format = ''; + + /** + * Interactive Buffer + * + * @see Net_SSH2::read() + * @var Array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Current log size + * + * Should never exceed NET_SSH2_LOG_MAX_SIZE + * + * @see Net_SSH2::_send_binary_packet() + * @see Net_SSH2::_get_binary_packet() + * @var Integer + * @access private + */ + var $log_size; + + /** + * Timeout + * + * @see Net_SSH2::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see Net_SSH2::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Real-time log file pointer + * + * @see Net_SSH2::_append_log() + * @var Resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see Net_SSH2::_append_log() + * @var Integer + * @access private + */ + var $realtime_log_size; + + /** + * Has the signature been validated? + * + * @see Net_SSH2::getServerPublicHostKey() + * @var Boolean + * @access private + */ + var $signature_validated = false; + + /** + * Real-time log file wrap boolean + * + * @see Net_SSH2::_append_log() + * @access private + */ + var $realtime_log_wrap; + + /** + * Flag to suppress stderr from output + * + * @see Net_SSH2::enableQuietMode() + * @access private + */ + var $quiet_mode = false; + + /** + * Time of first network activity + * + * @access private + */ + var $last_packet; + + /** + * Exit status returned from ssh if any + * + * @var Integer + * @access private + */ + var $exit_status; + + /** + * Default Constructor. + * + * Connects to an SSHv2 server + * + * @param String $host + * @param optional Integer $port + * @param optional Integer $timeout + * @return Net_SSH2 + * @access public + */ + function Net_SSH2($host, $port = 22, $timeout = 10) + { + $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5 + $this->message_numbers = array( + 1 => 'NET_SSH2_MSG_DISCONNECT', + 2 => 'NET_SSH2_MSG_IGNORE', + 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', + 4 => 'NET_SSH2_MSG_DEBUG', + 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', + 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', + 20 => 'NET_SSH2_MSG_KEXINIT', + 21 => 'NET_SSH2_MSG_NEWKEYS', + 30 => 'NET_SSH2_MSG_KEXDH_INIT', + 31 => 'NET_SSH2_MSG_KEXDH_REPLY', + 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', + 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', + 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', + 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', + + 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', + 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', + 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', + 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', + 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', + 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', + 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', + 94 => 'NET_SSH2_MSG_CHANNEL_DATA', + 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', + 96 => 'NET_SSH2_MSG_CHANNEL_EOF', + 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', + 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', + 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', + 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' + ); + $this->disconnect_reasons = array( + 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', + 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', + 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', + 4 => 'NET_SSH2_DISCONNECT_RESERVED', + 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', + 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', + 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', + 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', + 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', + 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', + 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', + 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', + 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', + 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', + 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' + ); + $this->channel_open_failure_reasons = array( + 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' + ); + $this->terminal_modes = array( + 0 => 'NET_SSH2_TTY_OP_END' + ); + $this->channel_extended_data_type_codes = array( + 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' + ); + + $this->_define_array( + $this->message_numbers, + $this->disconnect_reasons, + $this->channel_open_failure_reasons, + $this->terminal_modes, + $this->channel_extended_data_type_codes, + array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), + array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'), + array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE') + ); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout); + if (!$this->fsock) { + user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); + return; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + + $timeout-= $elapsed; + + if ($timeout <= 0) { + user_error(rtrim("Cannot connect to $host. Timeout error")); + return; + } + + $read = array($this->fsock); + $write = $except = NULL; + + $sec = floor($timeout); + $usec = 1000000 * ($timeout - $sec); + + // on windows this returns a "Warning: Invalid CRT parameters detected" error + // the !count() is done as a workaround for + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + user_error(rtrim("Cannot connect to $host. Banner timeout")); + return; + } + + /* According to the SSH2 specs, + + "The server MAY send other lines of data before sending the version + string. Each line SHOULD be terminated by a Carriage Return and Line + Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded + in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients + MUST be able to process such lines." */ + $temp = ''; + $extra = ''; + while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) { + if (substr($temp, -2) == "\r\n") { + $extra.= $temp; + $temp = ''; + } + $temp.= fgets($this->fsock, 255); + } + + if (feof($this->fsock)) { + user_error('Connection closed by server'); + return false; + } + + $ext = array(); + if (extension_loaded('mcrypt')) { + $ext[] = 'mcrypt'; + } + if (extension_loaded('gmp')) { + $ext[] = 'gmp'; + } else if (extension_loaded('bcmath')) { + $ext[] = 'bcmath'; + } + + if (!empty($ext)) { + $this->identifier.= ' (' . implode(', ', $ext) . ')'; + } + + if (defined('NET_SSH2_LOGGING')) { + $this->_append_log('<-', $extra . $temp); + $this->_append_log('->', $this->identifier . "\r\n"); + } + + $this->server_identifier = trim($temp, "\r\n"); + if (strlen($extra)) { + $this->errors[] = utf8_decode($extra); + } + + if ($matches[1] != '1.99' && $matches[1] != '2.0') { + user_error("Cannot connect to SSH $matches[1] servers"); + return; + } + + fputs($this->fsock, $this->identifier . "\r\n"); + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return; + } + + if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + user_error('Expected SSH_MSG_KEXINIT'); + return; + } + + if (!$this->_key_exchange($response)) { + return; + } + + $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR; + } + + /** + * Key Exchange + * + * @param String $kexinit_payload_server + * @access private + */ + function _key_exchange($kexinit_payload_server) + { + static $kex_algorithms = array( + 'diffie-hellman-group1-sha1', // REQUIRED + 'diffie-hellman-group14-sha1' // REQUIRED + ); + + static $server_host_key_algorithms = array( + 'ssh-rsa', // RECOMMENDED sign Raw RSA Key + 'ssh-dss' // REQUIRED sign Raw DSS Key + ); + + static $encryption_algorithms = array( + // from : + 'arcfour256', + 'arcfour128', + + 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + + 'aes128-cbc', // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', // OPTIONAL AES with a 192-bit key + 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + // from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + 'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + static $mac_algorithms = array( + 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) + 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) + 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) + 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) + 'none' // OPTIONAL no MAC; NOT RECOMMENDED + ); + + static $compression_algorithms = array( + 'none' // REQUIRED no compression + //'zlib' // OPTIONAL ZLIB (LZ77) compression + ); + + // some SSH servers have buggy implementations of some of the above algorithms + switch ($this->server_identifier) { + case 'SSH-2.0-SSHD': + $mac_algorithms = array_values(array_diff( + $mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } + + static $str_kex_algorithms, $str_server_host_key_algorithms, + $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client, + $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server; + + if (empty($str_kex_algorithms)) { + $str_kex_algorithms = implode(',', $kex_algorithms); + $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); + $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms); + $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms); + $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms); + } + + $client_cookie = crypt_random_string(16); + + $response = $kexinit_payload_server; + $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = $this->_string_shift($response, 16); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); + $first_kex_packet_follows = $first_kex_packet_follows != 0; + + // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. + $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', + NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms, + strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server), + $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client, + strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client), + $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server, + strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '', + 0, 0 + ); + + if (!$this->_send_binary_packet($kexinit_payload_client)) { + return false; + } + // here ends the second place. + + // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++); + if ($i == count($encryption_algorithms)) { + user_error('No compatible server to client encryption algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the + // diffie-hellman key exchange as fast as possible + $decrypt = $encryption_algorithms[$i]; + switch ($decrypt) { + case '3des-cbc': + case '3des-ctr': + $decryptKeyLength = 24; // eg. 192 / 8 + break; + case 'aes256-cbc': + case 'aes256-ctr': + $decryptKeyLength = 32; // eg. 256 / 8 + break; + case 'aes192-cbc': + case 'aes192-ctr': + $decryptKeyLength = 24; // eg. 192 / 8 + break; + case 'aes128-cbc': + case 'aes128-ctr': + $decryptKeyLength = 16; // eg. 128 / 8 + break; + case 'arcfour': + case 'arcfour128': + $decryptKeyLength = 16; // eg. 128 / 8 + break; + case 'arcfour256': + $decryptKeyLength = 32; // eg. 128 / 8 + break; + case 'none'; + $decryptKeyLength = 0; + } + + for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++); + if ($i == count($encryption_algorithms)) { + user_error('No compatible client to server encryption algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $encrypt = $encryption_algorithms[$i]; + switch ($encrypt) { + case '3des-cbc': + case '3des-ctr': + $encryptKeyLength = 24; + break; + case 'aes256-cbc': + case 'aes256-ctr': + $encryptKeyLength = 32; + break; + case 'aes192-cbc': + case 'aes192-ctr': + $encryptKeyLength = 24; + break; + case 'aes128-cbc': + case 'aes128-ctr': + $encryptKeyLength = 16; + break; + case 'arcfour': + case 'arcfour128': + $encryptKeyLength = 16; + break; + case 'arcfour256': + $encryptKeyLength = 32; + break; + case 'none'; + $encryptKeyLength = 0; + } + + $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength; + + // through diffie-hellman key exchange a symmetric key is obtained + for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++); + if ($i == count($kex_algorithms)) { + user_error('No compatible key exchange algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + switch ($kex_algorithms[$i]) { + // see http://tools.ietf.org/html/rfc2409#section-6.2 and + // http://tools.ietf.org/html/rfc2412, appendex E + case 'diffie-hellman-group1-sha1': + $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'); + $keyLength = $keyLength < 160 ? $keyLength : 160; + $hash = 'sha1'; + break; + // see http://tools.ietf.org/html/rfc3526#section-3 + case 'diffie-hellman-group14-sha1': + $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'); + $keyLength = $keyLength < 160 ? $keyLength : 160; + $hash = 'sha1'; + } + + $p = new Math_BigInteger($p, 256); + //$q = $p->bitwise_rightShift(1); + + /* To increase the speed of the key exchange, both client and server may + reduce the size of their private exponents. It should be at least + twice as long as the key material that is generated from the shared + secret. For more details, see the paper by van Oorschot and Wiener + [VAN-OORSCHOT]. + + -- http://tools.ietf.org/html/rfc4419#section-6.2 */ + $q = new Math_BigInteger(1); + $q = $q->bitwise_leftShift(2 * $keyLength); + $q = $q->subtract(new Math_BigInteger(1)); + + $g = new Math_BigInteger(2); + $x = new Math_BigInteger(); + $x = $x->random(new Math_BigInteger(1), $q); + $e = $g->modPow($x, $p); + + $eBytes = $e->toBytes(true); + $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes); + + if (!$this->_send_binary_packet($data)) { + user_error('Connection closed by server'); + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_KEXDH_REPLY) { + user_error('Expected SSH_MSG_KEXDH_REPLY'); + return false; + } + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $fBytes = $this->_string_shift($response, $temp['length']); + $f = new Math_BigInteger($fBytes, -256); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->signature = $this->_string_shift($response, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); + $this->signature_format = $this->_string_shift($this->signature, $temp['length']); + + $key = $f->modPow($x, $p); + $keyBytes = $key->toBytes(true); + + $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*', + strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier, + strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server), + $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes), + $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes + ); + + $this->exchange_hash = pack('H*', $hash($this->exchange_hash)); + + if ($this->session_id === false) { + $this->session_id = $this->exchange_hash; + } + + for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++); + if ($i == count($server_host_key_algorithms)) { + user_error('No compatible server host key algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) { + user_error('Sever Host Key Algorithm Mismatch'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $packet = pack('C', + NET_SSH2_MSG_NEWKEYS + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_NEWKEYS) { + user_error('Expected SSH_MSG_NEWKEYS'); + return false; + } + + switch ($encrypt) { + case '3des-cbc': + $this->encrypt = new Crypt_TripleDES(); + // $this->encrypt_block_size = 64 / 8 == the default + break; + case '3des-ctr': + $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); + // $this->encrypt_block_size = 64 / 8 == the default + break; + case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': + $this->encrypt = new Crypt_AES(); + $this->encrypt_block_size = 16; // eg. 128 / 8 + break; + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + $this->encrypt_block_size = 16; // eg. 128 / 8 + break; + case 'arcfour': + case 'arcfour128': + case 'arcfour256': + $this->encrypt = new Crypt_RC4(); + break; + case 'none'; + //$this->encrypt = new Crypt_Null(); + } + + switch ($decrypt) { + case '3des-cbc': + $this->decrypt = new Crypt_TripleDES(); + break; + case '3des-ctr': + $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); + break; + case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': + $this->decrypt = new Crypt_AES(); + $this->decrypt_block_size = 16; + break; + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + $this->decrypt_block_size = 16; + break; + case 'arcfour': + case 'arcfour128': + case 'arcfour256': + $this->decrypt = new Crypt_RC4(); + break; + case 'none'; + //$this->decrypt = new Crypt_Null(); + } + + $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); + + if ($this->encrypt) { + $this->encrypt->enableContinuousBuffer(); + $this->encrypt->disablePadding(); + + $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id)); + while ($this->encrypt_block_size > strlen($iv)) { + $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + } + $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); + + $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id)); + while ($encryptKeyLength > strlen($key)) { + $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + } + $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); + } + + if ($this->decrypt) { + $this->decrypt->enableContinuousBuffer(); + $this->decrypt->disablePadding(); + + $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id)); + while ($this->decrypt_block_size > strlen($iv)) { + $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + } + $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); + + $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id)); + while ($decryptKeyLength > strlen($key)) { + $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + } + $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); + } + + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { + $this->encrypt->encrypt(str_repeat("\0", 1536)); + } + if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { + $this->decrypt->decrypt(str_repeat("\0", 1536)); + } + + for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++); + if ($i == count($mac_algorithms)) { + user_error('No compatible client to server message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none' + switch ($mac_algorithms[$i]) { + case 'hmac-sha1': + $this->hmac_create = new Crypt_Hash('sha1'); + $createKeyLength = 20; + break; + case 'hmac-sha1-96': + $this->hmac_create = new Crypt_Hash('sha1-96'); + $createKeyLength = 20; + break; + case 'hmac-md5': + $this->hmac_create = new Crypt_Hash('md5'); + $createKeyLength = 16; + break; + case 'hmac-md5-96': + $this->hmac_create = new Crypt_Hash('md5-96'); + $createKeyLength = 16; + } + + for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++); + if ($i == count($mac_algorithms)) { + user_error('No compatible server to client message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $checkKeyLength = 0; + $this->hmac_size = 0; + switch ($mac_algorithms[$i]) { + case 'hmac-sha1': + $this->hmac_check = new Crypt_Hash('sha1'); + $checkKeyLength = 20; + $this->hmac_size = 20; + break; + case 'hmac-sha1-96': + $this->hmac_check = new Crypt_Hash('sha1-96'); + $checkKeyLength = 20; + $this->hmac_size = 12; + break; + case 'hmac-md5': + $this->hmac_check = new Crypt_Hash('md5'); + $checkKeyLength = 16; + $this->hmac_size = 16; + break; + case 'hmac-md5-96': + $this->hmac_check = new Crypt_Hash('md5-96'); + $checkKeyLength = 16; + $this->hmac_size = 12; + } + + $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id)); + while ($createKeyLength > strlen($key)) { + $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + } + $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); + + $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id)); + while ($checkKeyLength > strlen($key)) { + $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + } + $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); + + for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++); + if ($i == count($compression_algorithms)) { + user_error('No compatible server to client compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->decompress = $compression_algorithms[$i] == 'zlib'; + + for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++); + if ($i == count($compression_algorithms)) { + user_error('No compatible client to server compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->compress = $compression_algorithms[$i] == 'zlib'; + + return true; + } + + /** + * Login + * + * The $password parameter can be a plaintext password or a Crypt_RSA object. + * + * @param String $username + * @param optional String $password + * @return Boolean + * @access public + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function login($username, $password = null) + { + if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) { + return false; + } + + $packet = pack('CNa*', + NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { + user_error('Expected SSH_MSG_SERVICE_ACCEPT'); + return false; + } + + // although PHP5's get_class() preserves the case, PHP4's does not + if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') { + return $this->_privatekey_login($username, $password); + } + + if (!isset($password)) { + $packet = pack('CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', + strlen('none'), 'none' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + //case NET_SSH2_MSG_USERAUTH_FAILURE: + default: + return false; + } + } + + $packet = pack('CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', + strlen('password'), 'password', 0, strlen($password), $password + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + // remove the username and password from the last logged packet + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { + $packet = pack('CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection', + strlen('password'), 'password', 0, strlen('password'), 'password' + ); + $this->message_log[count($this->message_log) - 1] = $packet; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed + if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); + return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + case NET_SSH2_MSG_USERAUTH_FAILURE: + // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees + // multi-factor authentication + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $auth_methods = explode(',', $this->_string_shift($response, $length)); + if (in_array('keyboard-interactive', $auth_methods)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + return false; + } + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + + return false; + } + + /** + * Login via keyboard-interactive authentication + * + * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. + * + * @param String $username + * @param String $password + * @return Boolean + * @access private + */ + function _keyboard_interactive_login($username, $password) + { + $packet = pack('CNa*Na*Na*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', + strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, '' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + return $this->_keyboard_interactive_process($password); + } + + /** + * Handle the keyboard-interactive requests / responses. + * + * @param String $responses... + * @return Boolean + * @access private + */ + function _keyboard_interactive_process() + { + $responses = func_get_args(); + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // name; may be empty + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // instruction; may be empty + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // language tag; may be empty + extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); + /* + for ($i = 0; $i < $num_prompts; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $this->_string_shift($response, $length); + $echo = $this->_string_shift($response) != chr(0); + } + */ + + /* + After obtaining the requested information from the user, the client + MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. + */ + // see http://tools.ietf.org/html/rfc4256#section-3.4 + $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); + for ($i = 0; $i < count($responses); $i++) { + $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]); + $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); + } + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE', + $this->message_number_log[count($this->message_number_log) - 1] + ); + $this->message_log[count($this->message_log) - 1] = $logged; + } + + /* + After receiving the response, the server MUST send either an + SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another + SSH_MSG_USERAUTH_INFO_REQUEST message. + */ + // maybe phpseclib should force close the connection after x request / responses? unless something like that is done + // there could be an infinite loop of request / responses. + return $this->_keyboard_interactive_process(); + case NET_SSH2_MSG_USERAUTH_SUCCESS: + return true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + return false; + } + + return false; + } + + /** + * Login with an RSA private key + * + * @param String $username + * @param Crypt_RSA $password + * @return Boolean + * @access private + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function _privatekey_login($username, $privatekey) + { + // see http://tools.ietf.org/html/rfc4253#page-15 + $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW); + if ($publickey === false) { + return false; + } + + $publickey = array( + 'e' => $publickey['e']->toBytes(true), + 'n' => $publickey['n']->toBytes(true) + ); + $publickey = pack('Na*Na*Na*', + strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n'] + ); + + $part1 = pack('CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', + strlen('publickey'), 'publickey' + ); + $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); + + $packet = $part1 . chr(0) . $part2; + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); + return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + case NET_SSH2_MSG_USERAUTH_PK_OK: + // we'll just take it on faith that the public key blob and the public key algorithm name are as + // they should be + if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_PK_OK', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + } + + $packet = $part1 . chr(1) . $part2; + $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); + $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet)); + $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); + $packet.= pack('Na*', strlen($signature), $signature); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + // either the login is bad or the server employs multi-factor authentication + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + + return false; + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param Mixed $timeout + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Execute Command + * + * If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually. + * In all likelihood, this is not a feature you want to be taking advantage of. + * + * @param String $command + * @param optional Boolean $block + * @return String + * @access public + */ + function exec($command, $block = true) + { + $this->curTimeout = $this->timeout; + + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to + // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway. + // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info + $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF; + // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy + // uses 0x4000, that's what will be used here, as well. + $packet_size = 0x4000; + + $packet = pack('CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); + if ($response === false) { + return false; + } + + // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things + // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &'). + // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then + // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but + // neither will your script. + + // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by + // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the + // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); + if ($response === false) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; + + if (!$block) { + return true; + } + + $output = ''; + while (true) { + $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); + switch (true) { + case $temp === true: + return $output; + case $temp === false: + return false; + default: + $output.= $temp; + } + } + } + + /** + * Creates an interactive shell + * + * @see Net_SSH2::read() + * @see Net_SSH2::write() + * @return Boolean + * @access private + */ + function _initShell() + { + $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF; + $packet_size = 0x4000; + + $packet = pack('CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack('CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100', + 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + list(, $type) = unpack('C', $this->_string_shift($response, 1)); + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + break; + case NET_SSH2_MSG_CHANNEL_FAILURE: + default: + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $packet = pack('CNNa*C', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= NET_SSH2_MASK_SHELL; + + return true; + } + + /** + * Returns the output of an interactive shell + * + * Returns when there's a match for $expect, which can take the form of a string literal or, + * if $mode == NET_SSH2_READ_REGEX, a regular expression. + * + * @see Net_SSH2::read() + * @param String $expect + * @param Integer $mode + * @return String + * @access public + */ + function read($expect = '', $mode = NET_SSH2_READ_SIMPLE) + { + $this->curTimeout = $this->timeout; + + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $match = $expect; + while (true) { + if ($mode == NET_SSH2_READ_REGEX) { + preg_match($expect, $this->interactiveBuffer, $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + if (is_bool($response)) { + return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; + } + + $this->interactiveBuffer.= $response; + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see Net_SSH1::interactiveWrite() + * @param String $cmd + * @return Boolean + * @access public + */ + function write($cmd) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd); + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { + fclose($this->realtime_log_file); + } + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->disconnect(); + } + + /** + * Gets Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @see Net_SSH2::_send_binary_packet() + * @return String + * @access private + */ + function _get_binary_packet() + { + if (!is_resource($this->fsock) || feof($this->fsock)) { + user_error('Connection closed prematurely'); + $this->bitmask = 0; + return false; + } + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $raw = fread($this->fsock, $this->decrypt_block_size); + + if (!strlen($raw)) { + return ''; + } + + if ($this->decrypt !== false) { + $raw = $this->decrypt->decrypt($raw); + } + if ($raw === false) { + user_error('Unable to decrypt content'); + return false; + } + + extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); + + $remaining_length = $packet_length + 4 - $this->decrypt_block_size; + + // quoting , + // "implementations SHOULD check that the packet length is reasonable" + // PuTTY uses 0x9000 as the actual max packet size and so to shall we + if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { + user_error('Invalid size'); + return false; + } + + $buffer = ''; + while ($remaining_length > 0) { + $temp = fread($this->fsock, $remaining_length); + $buffer.= $temp; + $remaining_length-= strlen($temp); + } + $stop = strtok(microtime(), ' ') + strtok(''); + if (strlen($buffer)) { + $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; + } + + $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); + $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty + + if ($this->hmac_check !== false) { + $hmac = fread($this->fsock, $this->hmac_size); + if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { + user_error('Invalid HMAC'); + return false; + } + } + + //if ($this->decompress) { + // $payload = gzinflate(substr($payload, 2)); + //} + + $this->get_seq_no++; + + if (defined('NET_SSH2_LOGGING')) { + $current = strtok(microtime(), ' ') + strtok(''); + $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; + $message_number = '<- ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, $payload); + $this->last_packet = $current; + } + + return $this->_filter($payload); + } + + /** + * Filter Binary Packets + * + * Because some binary packets need to be ignored... + * + * @see Net_SSH2::_get_binary_packet() + * @return String + * @access private + */ + function _filter($payload) + { + switch (ord($payload[0])) { + case NET_SSH2_MSG_DISCONNECT: + $this->_string_shift($payload, 1); + extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); + $this->bitmask = 0; + return false; + case NET_SSH2_MSG_IGNORE: + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_DEBUG: + $this->_string_shift($payload, 2); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_UNIMPLEMENTED: + return false; + case NET_SSH2_MSG_KEXINIT: + if ($this->session_id !== false) { + if (!$this->_key_exchange($payload)) { + $this->bitmask = 0; + return false; + } + $payload = $this->_get_binary_packet(); + } + } + + // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in + if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + $this->_string_shift($payload, 1); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length)); + $payload = $this->_get_binary_packet(); + } + + // only called when we've already logged in + if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) { + switch (ord($payload[0])) { + case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 + $this->_string_shift($payload, 1); + extract(unpack('Nlength', $this->_string_shift($payload))); + $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length)); + + if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 + $this->_string_shift($payload, 1); + extract(unpack('N', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length)); + + $this->_string_shift($payload, 4); // skip over client channel + extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); + + $packet = pack('CN3a*Na*', + NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, ''); + + if (!$this->_send_binary_packet($packet)) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: + $payload = $this->_get_binary_packet(); + } + } + + return $payload; + } + + /** + * Enable Quiet Mode + * + * Suppress stderr from output + * + * @access public + */ + function enableQuietMode() + { + $this->quiet_mode = true; + } + + /** + * Disable Quiet Mode + * + * Show stderr in output + * + * @access public + */ + function disableQuietMode() + { + $this->quiet_mode = false; + } + + /** + * Gets channel data + * + * Returns the data as a string if it's available and false if not. + * + * @param $client_channel + * @return Mixed + * @access private + */ + function _get_channel_packet($client_channel, $skip_extended = false) + { + if (!empty($this->channel_buffers[$client_channel])) { + return array_shift($this->channel_buffers[$client_channel]); + } + + while (true) { + if ($this->curTimeout) { + $read = array($this->fsock); + $write = $except = NULL; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->_close_channel($client_channel); + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return ''; + } + + extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); + + switch ($this->channel_status[$channel]) { + case NET_SSH2_MSG_CHANNEL_OPEN: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); + $this->server_channels[$channel] = $server_channel; + $this->_string_shift($response, 4); // skip over (server) window size + $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); + $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; + return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); + //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: + default: + user_error('Unable to open channel'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + return true; + //case NET_SSH2_MSG_CHANNEL_FAILURE: + default: + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + case NET_SSH2_MSG_CHANNEL_CLOSE: + return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); + } + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_DATA: + /* + if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server + // this actually seems to make things twice as fast. more to the point, the message right after + // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. + // in OpenSSH it slows things down but only by a couple thousandths of a second. + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + if ($client_channel == $channel) { + return $data; + } + if (!isset($this->channel_buffers[$client_channel])) { + $this->channel_buffers[$client_channel] = array(); + } + $this->channel_buffers[$client_channel][] = $data; + break; + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + if ($skip_extended || $this->quiet_mode) { + break; + } + /* + if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); + $data = $this->_string_shift($response, $length); + if ($client_channel == $channel) { + return $data; + } + if (!isset($this->channel_buffers[$client_channel])) { + $this->channel_buffers[$client_channel] = array(); + } + $this->channel_buffers[$client_channel][] = $data; + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + switch ($value) { + case 'exit-signal': + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if ($length) { + $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); + } + case 'exit-status': + extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); + $this->exit_status = $exit_status; + // "The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + break; + } + break; + case NET_SSH2_MSG_CHANNEL_CLOSE: + $this->curTimeout = 0; + + if ($this->bitmap & NET_SSH2_MASK_SHELL) { + $this->bitmap&= ~NET_SSH2_MASK_SHELL; + } + if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + } + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + return true; + case NET_SSH2_MSG_CHANNEL_EOF: + break; + default: + user_error('Error reading channel data'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + } + } + + /** + * Sends Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @param String $data + * @see Net_SSH2::_get_binary_packet() + * @return Boolean + * @access private + */ + function _send_binary_packet($data) + { + if (!is_resource($this->fsock) || feof($this->fsock)) { + user_error('Connection closed prematurely'); + $this->bitmask = 0; + return false; + } + + //if ($this->compress) { + // // the -4 removes the checksum: + // // http://php.net/function.gzcompress#57710 + // $data = substr(gzcompress($data), 0, -4); + //} + + // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 + $packet_length = strlen($data) + 9; + // round up to the nearest $this->encrypt_block_size + $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; + // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length + $padding_length = $packet_length - strlen($data) - 5; + $padding = crypt_random_string($padding_length); + + // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself + $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); + + $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : ''; + $this->send_seq_no++; + + if ($this->encrypt !== false) { + $packet = $this->encrypt->encrypt($packet); + } + + $packet.= $hmac; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = strlen($packet) == fputs($this->fsock, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SSH2_LOGGING')) { + $current = strtok(microtime(), ' ') + strtok(''); + $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; + $message_number = '-> ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, $data); + $this->last_packet = $current; + } + + return $result; + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param String $data + * @access private + */ + function _append_log($message_number, $message) + { + switch (NET_SSH2_LOGGING) { + // useful for benchmarks + case NET_SSH2_LOG_SIMPLE: + $this->message_number_log[] = $message_number; + break; + // the most useful log for SSH2 + case NET_SSH2_LOG_COMPLEX: + $this->message_number_log[] = $message_number; + $this->_string_shift($message); + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->message_number_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case NET_SSH2_LOG_REALTIME: + echo "
\r\n" . $this->_format_log(array($message), array($message_number)) . "\r\n
\r\n"; + @flush(); + @ob_flush(); + break; + // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE + // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case NET_SSH2_LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = NET_SSH2_LOG_REALTIME_FILE; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($message_number)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } + + /** + * Sends channel data + * + * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate + * + * @param Integer $client_channel + * @param String $data + * @return Boolean + * @access private + */ + function _send_channel_packet($client_channel, $data) + { + while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) { + // resize the window, if appropriate + $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel]; + if ($this->window_size_client_to_server[$client_channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_client_to_server[$client_channel]+= $this->window_size; + } + + $packet = pack('CN2a*', + NET_SSH2_MSG_CHANNEL_DATA, + $this->server_channels[$client_channel], + $this->packet_size_client_to_server[$client_channel], + $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel]) + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + } + + // resize the window, if appropriate + $this->window_size_client_to_server[$client_channel]-= strlen($data); + if ($this->window_size_client_to_server[$client_channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_client_to_server[$client_channel]+= $this->window_size; + } + + return $this->_send_binary_packet(pack('CN2a*', + NET_SSH2_MSG_CHANNEL_DATA, + $this->server_channels[$client_channel], + strlen($data), + $data)); + } + + /** + * Closes and flushes a channel + * + * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server + * and for SFTP channels are presumably closed when the client disconnects. This functions is intended + * for SCP more than anything. + * + * @param Integer $client_channel + * @return Boolean + * @access private + */ + function _close_channel($client_channel) + { + // see http://tools.ietf.org/html/rfc4254#section-5.3 + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + + $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + + $this->curTimeout = 0; + + while (!is_bool($this->_get_channel_packet($client_channel))); + + if ($this->bitmap & NET_SSH2_MASK_SHELL) { + $this->bitmap&= ~NET_SSH2_MASK_SHELL; + } + } + + /** + * Disconnect + * + * @param Integer $reason + * @return Boolean + * @access private + */ + function _disconnect($reason) + { + if ($this->bitmap) { + $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); + $this->_send_binary_packet($data); + $this->bitmap = 0; + fclose($this->fsock); + return false; + } + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @param Array $array + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key=>$value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') + * + * @access public + * @return String or Array + */ + function getLog() + { + if (!defined('NET_SSH2_LOGGING')) { + return false; + } + + switch (NET_SSH2_LOGGING) { + case NET_SSH2_LOG_SIMPLE: + return $this->message_number_log; + break; + case NET_SSH2_LOG_COMPLEX: + return $this->_format_log($this->message_log, $this->message_number_log); + break; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param Array $message_log + * @param Array $message_number_log + * @access private + * @return String + */ + function _format_log($message_log, $message_number_log) + { + static $boundary = ':', $long_width = 65, $short_width = 16; + + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $short_width); + $hex = substr( + preg_replace( + '#(.)#es', + '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)', + $fragment), + strlen($boundary) + ); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Returns all errors + * + * @return String + * @access public + */ + function getErrors() + { + return $this->errors; + } + + /** + * Returns the last error + * + * @return String + * @access public + */ + function getLastError() + { + return $this->errors[count($this->errors) - 1]; + } + + /** + * Return the server identification. + * + * @return String + * @access public + */ + function getServerIdentification() + { + return $this->server_identifier; + } + + /** + * Return a list of the key exchange algorithms the server supports. + * + * @return Array + * @access public + */ + function getKexAlgorithms() + { + return $this->kex_algorithms; + } + + /** + * Return a list of the host key (public key) algorithms the server supports. + * + * @return Array + * @access public + */ + function getServerHostKeyAlgorithms() + { + return $this->server_host_key_algorithms; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client. + * + * @return Array + * @access public + */ + function getEncryptionAlgorithmsClient2Server() + { + return $this->encryption_algorithms_client_to_server; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client. + * + * @return Array + * @access public + */ + function getEncryptionAlgorithmsServer2Client() + { + return $this->encryption_algorithms_server_to_client; + } + + /** + * Return a list of the MAC algorithms the server supports, when receiving stuff from the client. + * + * @return Array + * @access public + */ + function getMACAlgorithmsClient2Server() + { + return $this->mac_algorithms_client_to_server; + } + + /** + * Return a list of the MAC algorithms the server supports, when sending stuff to the client. + * + * @return Array + * @access public + */ + function getMACAlgorithmsServer2Client() + { + return $this->mac_algorithms_server_to_client; + } + + /** + * Return a list of the compression algorithms the server supports, when receiving stuff from the client. + * + * @return Array + * @access public + */ + function getCompressionAlgorithmsClient2Server() + { + return $this->compression_algorithms_client_to_server; + } + + /** + * Return a list of the compression algorithms the server supports, when sending stuff to the client. + * + * @return Array + * @access public + */ + function getCompressionAlgorithmsServer2Client() + { + return $this->compression_algorithms_server_to_client; + } + + /** + * Return a list of the languages the server supports, when sending stuff to the client. + * + * @return Array + * @access public + */ + function getLanguagesServer2Client() + { + return $this->languages_server_to_client; + } + + /** + * Return a list of the languages the server supports, when receiving stuff from the client. + * + * @return Array + * @access public + */ + function getLanguagesClient2Server() + { + return $this->languages_client_to_server; + } + + /** + * Returns the server public host key. + * + * Caching this the first time you connect to a server and checking the result on subsequent connections + * is recommended. Returns false if the server signature is not signed correctly with the public host key. + * + * @return Mixed + * @access public + */ + function getServerPublicHostKey() + { + $signature = $this->signature; + $server_public_host_key = $this->server_public_host_key; + + extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); + $this->_string_shift($server_public_host_key, $length); + + if ($this->signature_validated) { + return $this->bitmap ? + $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : + false; + } + + $this->signature_validated = true; + + switch ($this->signature_format) { + case 'ssh-dss': + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + /* The value for 'dss_signature_blob' is encoded as a string containing + r, followed by s (which are 160-bit integers, without lengths or + padding, unsigned, and in network byte order). */ + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + if ($temp['length'] != 40) { + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $r = new Math_BigInteger($this->_string_shift($signature, 20), 256); + $s = new Math_BigInteger($this->_string_shift($signature, 20), 256); + + if ($r->compare($q) >= 0 || $s->compare($q) >= 0) { + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $w = $s->modInverse($q); + + $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16)); + list(, $u1) = $u1->divide($q); + + $u2 = $w->multiply($r); + list(, $u2) = $u2->divide($q); + + $g = $g->modPow($u1, $p); + $y = $y->modPow($u2, $p); + + $v = $g->multiply($y); + list(, $v) = $v->divide($p); + list(, $v) = $v->divide($q); + + if (!$v->equals($r)) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + + break; + case 'ssh-rsa': + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + $nLength = $temp['length']; + + /* + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + $signature = $this->_string_shift($signature, $temp['length']); + + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + + $rsa = new Crypt_RSA(); + $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); + $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW); + if (!$rsa->verify($this->exchange_hash, $signature)) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + */ + + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256); + + // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the + // following URL: + // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf + + // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. + + if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) { + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $s = $s->modPow($e, $n); + $s = $s->toBytes(); + + $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); + $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h; + + if ($s != $h) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + break; + default: + user_error('Unsupported signature format'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + + return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); + } + + /** + * Returns the exit status of an SSH command or false. + * + * @return Integer or false + * @access public + */ + function getExitStatus() + { + if (is_null($this->exit_status)) { + return false; + } + return $this->exit_status; + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf b/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf new file mode 100644 index 0000000000000000000000000000000000000000..6baa566102c71629a2cb669f6d96d414e6f0e37d --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/openssl.cnf @@ -0,0 +1,6 @@ +# minimalist openssl.cnf file for use with phpseclib + +HOME = . +RANDFILE = $ENV::HOME/.rnd + +[ v3_ca ] \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist b/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..f579ab4fd27b31b74aa0e001d0542d3e1d781b14 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + ./tests/ + + + + + + + ./phpseclib/ + + + diff --git a/apps/files_external/ajax/addMountPoint.php b/apps/files_external/ajax/addMountPoint.php index 4cd8871b310c34384f979b0390a4274919cc23b2..fed2ddfcf3d95e3597b6426b0eac3cdf0cedabfa 100644 --- a/apps/files_external/ajax/addMountPoint.php +++ b/apps/files_external/ajax/addMountPoint.php @@ -10,9 +10,10 @@ if ($_POST['isPersonal'] == 'true') { OCP\JSON::checkAdminUser(); $isPersonal = false; } -OC_Mount_Config::addMountPoint($_POST['mountPoint'], +$status = OC_Mount_Config::addMountPoint($_POST['mountPoint'], $_POST['class'], $_POST['classOptions'], $_POST['mountType'], $_POST['applicable'], - $isPersonal); \ No newline at end of file + $isPersonal); +OCP\JSON::success(array('data' => array('message' => $status))); \ No newline at end of file diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php index 2f67e801b2c924434b50ef7b1e1d514a22abc3ca..43fd6752c4ae9a1d85d4a95f4362a47cd05fcb1f 100644 --- a/apps/files_external/ajax/addRootCertificate.php +++ b/apps/files_external/ajax/addRootCertificate.php @@ -1,6 +1,7 @@ file_exists('')){ +if (!$view->file_exists('')) { $view->mkdir(''); } @@ -36,5 +37,5 @@ if ( $isValid ) { OCP\Util::WARN); } -header('Location: settings/personal.php'); +header('Location:' . OCP\Util::linkToRoute( "settings_personal" )); exit; diff --git a/apps/files_external/ajax/dropbox.php b/apps/files_external/ajax/dropbox.php index 58c41d6906263994e36a8c2fc0f250574ba53709..bc9821c62ec4bddab49031ae0c0ca9a560d1cf2f 100644 --- a/apps/files_external/ajax/dropbox.php +++ b/apps/files_external/ajax/dropbox.php @@ -4,6 +4,8 @@ require_once 'Dropbox/autoload.php'; OCP\JSON::checkAppEnabled('files_external'); OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + if (isset($_POST['app_key']) && isset($_POST['app_secret'])) { $oauth = new Dropbox_OAuth_Curl($_POST['app_key'], $_POST['app_secret']); if (isset($_POST['step'])) { diff --git a/apps/files_external/ajax/google.php b/apps/files_external/ajax/google.php index c76c7618e4d056f711a5bc77196c03f9b9037749..70adcb2c2ad4f7a8d10bab181544bef73deb604c 100644 --- a/apps/files_external/ajax/google.php +++ b/apps/files_external/ajax/google.php @@ -4,6 +4,8 @@ require_once 'Google/common.inc.php'; OCP\JSON::checkAppEnabled('files_external'); OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + $consumer = new OAuthConsumer('anonymous', 'anonymous'); $sigMethod = new OAuthSignatureMethod_HMAC_SHA1(); if (isset($_POST['step'])) { diff --git a/apps/files_external/ajax/removeMountPoint.php b/apps/files_external/ajax/removeMountPoint.php index aa44642620267a39b2538eba3773bee1c8759224..2f5dbcfdbacf833994828a4ad0d193309db19396 100644 --- a/apps/files_external/ajax/removeMountPoint.php +++ b/apps/files_external/ajax/removeMountPoint.php @@ -3,6 +3,15 @@ OCP\JSON::checkAppEnabled('files_external'); OCP\JSON::callCheck(); +if (!isset($_POST['isPersonal'])) + return; +if (!isset($_POST['mountPoint'])) + return; +if (!isset($_POST['mountType'])) + return; +if (!isset($_POST['applicable'])) + return; + if ($_POST['isPersonal'] == 'true') { OCP\JSON::checkLoggedIn(); $isPersonal = true; @@ -10,4 +19,5 @@ if ($_POST['isPersonal'] == 'true') { OCP\JSON::checkAdminUser(); $isPersonal = false; } + OC_Mount_Config::removeMountPoint($_POST['mountPoint'], $_POST['mountType'], $_POST['applicable'], $isPersonal); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index c58cfcd0f5e65927e3b5c4f919f5a4a6f993d199..d976c0175232bfe3364f84cca636a40a708765d1 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -14,6 +14,7 @@ OC::$CLASSPATH['OC\Files\Storage\SWIFT']='apps/files_external/lib/swift.php'; OC::$CLASSPATH['OC\Files\Storage\SMB']='apps/files_external/lib/smb.php'; OC::$CLASSPATH['OC\Files\Storage\AmazonS3']='apps/files_external/lib/amazons3.php'; OC::$CLASSPATH['OC\Files\Storage\Dropbox']='apps/files_external/lib/dropbox.php'; +OC::$CLASSPATH['OC\Files\Storage\SFTP']='apps/files_external/lib/sftp.php'; OC::$CLASSPATH['OC_Mount_Config']='apps/files_external/lib/config.php'; OCP\App::registerAdmin('files_external', 'settings'); diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index 2c04216a9fb00f17cbf31ab6dc569970b6a4a928..0542b7b10a7afe831d0b917b9daf284949ab08f5 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -5,7 +5,7 @@ Mount external storage sources AGPL Robin Appelman, Michael Gapczynski - 4.91 + 4.93 true diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css index ca4b1c3ba8937d6affb071e89621cf047fa227ad..94b453793b1badda59574975a89fb9da90893b4e 100644 --- a/apps/files_external/css/settings.css +++ b/apps/files_external/css/settings.css @@ -1,4 +1,7 @@ -.error { color: #FF3B3B; } +td.status>span { display:inline-block; height:16px; width:16px; } +span.success { background-image: url('../img/success.png'); background-repeat:no-repeat; } +span.error { background-image: url('../img/error.png'); background-repeat:no-repeat; } +span.waiting { background-image: url('../img/waiting.png'); background-repeat:no-repeat; } td.mountPoint, td.backend { width:10em; } td.remove>img { visibility:hidden; padding-top:0.8em; } tr:hover>td.remove>img { visibility:visible; cursor:pointer; } diff --git a/apps/files_external/img/error.png b/apps/files_external/img/error.png new file mode 100644 index 0000000000000000000000000000000000000000..e8cf45e7a41e358da5d573dc48edf966b9d8d3cb Binary files /dev/null and b/apps/files_external/img/error.png differ diff --git a/apps/files_external/img/success.png b/apps/files_external/img/success.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7022ee7f5672b43bdf72f848a7358e4473eb60 Binary files /dev/null and b/apps/files_external/img/success.png differ diff --git a/apps/files_external/img/waiting.png b/apps/files_external/img/waiting.png new file mode 100644 index 0000000000000000000000000000000000000000..02a8cbff0da63ad584ae7a5c11ddefdf0bd9e24b Binary files /dev/null and b/apps/files_external/img/waiting.png differ diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js index cd3c957e0a8470a0631ee423b7fbac2904f58f61..957daeb4d1f9152af29b06c85273b39f4cc760da 100644 --- a/apps/files_external/js/dropbox.js +++ b/apps/files_external/js/dropbox.js @@ -15,6 +15,9 @@ $(document).ready(function() { if (pos != -1 && window.location.search.substr(pos, $(token).val().length) == $(token).val()) { var token_secret = $(this).find('.configuration [data-parameter="token_secret"]'); var tr = $(this); + var statusSpan = $(tr).find('.status span'); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 2, app_key: app_key, app_secret: app_secret, request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { if (result && result.status == 'success') { $(token).val(result.access_token); @@ -24,23 +27,40 @@ $(document).ready(function() { $(tr).find('.configuration input').attr('disabled', 'disabled'); $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Dropbox storage') - ); + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); } }); } - } else if ($(this).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '' && $(this).find('.dropbox').length == 0) { - $(this).find('.configuration').append(''+t('files_external', 'Grant access')+''); + } else { + onDropboxInputsChange($(this)); } } }); - $('#externalStorage tbody').on('keyup', 'tr input', function() { - var tr = $(this).parent().parent(); - if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { + $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() { + var tr = $(this).parent(); + setTimeout(function() { + onDropboxInputsChange(tr); + }, 20); + }); + + $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox td', function() { + onDropboxInputsChange($(this).parent()); + }); + + $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox .chzn-select', function() { + onDropboxInputsChange($(this).parent().parent()); + }); + + function onDropboxInputsChange(tr) { + if ($(tr).find('[data-parameter="configured"]').val() != 'true') { var config = $(tr).find('.configuration'); - if ($(tr).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '') { + if ($(tr).find('.mountPoint input').val() != '' + && $(config).find('[data-parameter="app_key"]').val() != '' + && $(config).find('[data-parameter="app_secret"]').val() != '' + && ($(tr).find('.chzn-select').length == 0 + || $(tr).find('.chzn-select').val() != null)) + { if ($(tr).find('.dropbox').length == 0) { $(config).append(''+t('files_external', 'Grant access')+''); } else { @@ -50,41 +70,37 @@ $(document).ready(function() { $(tr).find('.dropbox').hide(); } } - }); + } - $('.dropbox').on('click', function(event) { + $('#externalStorage').on('click', '.dropbox', function(event) { event.preventDefault(); + var tr = $(this).parent().parent(); var app_key = $(this).parent().find('[data-parameter="app_key"]').val(); var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val(); + var statusSpan = $(tr).find('.status span'); if (app_key != '' && app_secret != '') { var tr = $(this).parent().parent(); var configured = $(this).parent().find('[data-parameter="configured"]'); var token = $(this).parent().find('[data-parameter="token"]'); var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); - $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: window.location.href }, function(result) { + $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: location.protocol + '//' + location.host + location.pathname }, function(result) { if (result && result.status == 'success') { $(configured).val('false'); $(token).val(result.data.request_token); $(token_secret).val(result.data.request_token_secret); - if (OC.MountConfig.saveStorage(tr)) { - window.location = result.data.url; - } else { - OC.dialogs.alert( - t('files_external', 'Fill out all required fields'), - t('files_external', 'Error configuring Dropbox storage') - ); - } + OC.MountConfig.saveStorage(tr); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); + window.location = result.data.url; } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Dropbox storage') - ); + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage')); } }); } else { OC.dialogs.alert( - t('files_external', 'Please provide a valid Dropbox app key and secret.'), - t('files_external', 'Error configuring Dropbox storage') - ); + t('files_external', 'Please provide a valid Dropbox app key and secret.'), + t('files_external', 'Error configuring Dropbox storage') + ); } }); diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js index 9b7f9514f12d38b3c9d885bcf4ec269b6503efd7..7be1b338e904fb1cba80cea821d28882a465d18b 100644 --- a/apps/files_external/js/google.js +++ b/apps/files_external/js/google.js @@ -1,19 +1,30 @@ $(document).ready(function() { - $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function() { - var configured = $(this).find('[data-parameter="configured"]'); + $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function(index, tr) { + setupGoogleRow(tr); + }); + + $('#externalStorage').on('change', '#selectBackend', function() { + if ($(this).val() == '\\OC\\Files\\Storage\\Google') { + setupGoogleRow($('#externalStorage tbody>tr:last').prev('tr')); + } + }); + + function setupGoogleRow(tr) { + var configured = $(tr).find('[data-parameter="configured"]'); if ($(configured).val() == 'true') { - $(this).find('.configuration') - .append(''+t('files_external', 'Access granted')+''); + $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); } else { - var token = $(this).find('[data-parameter="token"]'); - var token_secret = $(this).find('[data-parameter="token_secret"]'); + var token = $(tr).find('[data-parameter="token"]'); + var token_secret = $(tr).find('[data-parameter="token_secret"]'); var params = {}; window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) { params[key] = value; }); if (params['oauth_token'] !== undefined && params['oauth_verifier'] !== undefined && decodeURIComponent(params['oauth_token']) == $(token).val()) { - var tr = $(this); + var statusSpan = $(tr).find('.status span'); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 2, oauth_verifier: params['oauth_verifier'], request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { if (result && result.status == 'success') { $(token).val(result.access_token); @@ -22,61 +33,64 @@ $(document).ready(function() { OC.MountConfig.saveStorage(tr); $(tr).find('.configuration').append(''+t('files_external', 'Access granted')+''); } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Google Drive storage') - ); + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Google Drive storage')); + onGoogleInputsChange(tr); } }); - } else if ($(this).find('.google').length == 0) { - $(this).find('.configuration').append(''+t('files_external', 'Grant access')+''); + } else { + onGoogleInputsChange(tr); } } + } + + $('#externalStorage').on('paste', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', function() { + var tr = $(this).parent(); + setTimeout(function() { + onGoogleInputsChange(tr); + }, 20); }); - $('#externalStorage tbody').on('change', 'tr', function() { - if ($(this).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { - if ($(this).find('.mountPoint input').val() != '') { - if ($(this).find('.google').length == 0) { - $(this).find('.configuration').append(''+t('files_external', 'Grant access')+''); - } - } - } + $('#externalStorage').on('keyup', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google td', function() { + onGoogleInputsChange($(this).parent()); }); - $('#externalStorage tbody').on('keyup', 'tr .mountPoint input', function() { - var tr = $(this).parent().parent(); - if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { - if ($(this).val() != '') { - $(tr).find('.google').show(); - } else { + $('#externalStorage').on('change', 'tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google .chzn-select', function() { + onGoogleInputsChange($(this).parent().parent()); + }); + + function onGoogleInputsChange(tr) { + if ($(tr).find('[data-parameter="configured"]').val() != 'true') { + var config = $(tr).find('.configuration'); + if ($(tr).find('.mountPoint input').val() != '' && ($(tr).find('.chzn-select').length == 0 || $(tr).find('.chzn-select').val() != null)) { + if ($(tr).find('.google').length == 0) { + $(config).append(''+t('files_external', 'Grant access')+''); + } else { + $(tr).find('.google').show(); + } + } else if ($(tr).find('.google').length > 0) { $(tr).find('.google').hide(); } } - }); + } - $('.google').on('click', function(event) { + $('#externalStorage').on('click', '.google', function(event) { event.preventDefault(); var tr = $(this).parent().parent(); var configured = $(this).parent().find('[data-parameter="configured"]'); var token = $(this).parent().find('[data-parameter="token"]'); var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); - $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: window.location.href }, function(result) { + var statusSpan = $(tr).find('.status span'); + $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: location.protocol + '//' + location.host + location.pathname }, function(result) { if (result && result.status == 'success') { $(configured).val('false'); $(token).val(result.data.request_token); $(token_secret).val(result.data.request_token_secret); - if (OC.MountConfig.saveStorage(tr)) { - window.location = result.data.url; - } else { - OC.dialogs.alert( - t('files_external', 'Fill out all required fields'), - t('files_external', 'Error configuring Google Drive storage') - ); - } + OC.MountConfig.saveStorage(tr); + statusSpan.removeClass(); + statusSpan.addClass('waiting'); + window.location = result.data.url; } else { - OC.dialogs.alert(result.data.message, - t('files_external', 'Error configuring Google Drive storage') - ); + OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Google Drive storage')); } }); }); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 172ef097fbfb00e6dd145dd8d0ad6930780355af..ac408786ff6271a4088530927f2141075e495e63 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -4,6 +4,7 @@ OC.MountConfig={ if (mountPoint == '') { return false; } + var statusSpan = $(tr).find('.status span'); var backendClass = $(tr).find('.backend').data('class'); var configuration = $(tr).find('.configuration input'); var addMountPoint = true; @@ -26,12 +27,20 @@ OC.MountConfig={ classOptions[$(input).data('parameter')] = $(input).val(); } }); + if ($('#externalStorage').data('admin') === true) { + var multiselect = $(tr).find('.chzn-select').val(); + if (multiselect == null) { + return false; + } + } if (addMountPoint) { + var status = false; if ($('#externalStorage').data('admin') === true) { var isPersonal = false; - var multiselect = $(tr).find('.chzn-select').val(); var oldGroups = $(tr).find('.applicable').data('applicable-groups'); var oldUsers = $(tr).find('.applicable').data('applicable-users'); + var groups = []; + var users = []; $.each(multiselect, function(index, value) { var pos = value.indexOf('(group)'); if (pos != -1) { @@ -40,30 +49,96 @@ OC.MountConfig={ if ($.inArray(applicable, oldGroups) != -1) { oldGroups.splice($.inArray(applicable, oldGroups), 1); } + groups.push(applicable); } else { var mountType = 'user'; var applicable = value; if ($.inArray(applicable, oldUsers) != -1) { oldUsers.splice($.inArray(applicable, oldUsers), 1); } + users.push(applicable); } - $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + $.ajax({type: 'POST', + url: OC.filePath('files_external', 'ajax', 'addMountPoint.php'), + data: { + mountPoint: mountPoint, + 'class': backendClass, + classOptions: classOptions, + mountType: mountType, + applicable: applicable, + isPersonal: isPersonal + }, + async: false, + success: function(result) { + statusSpan.removeClass(); + if (result && result.status == 'success' && result.data.message) { + status = true; + statusSpan.addClass('success'); + } else { + statusSpan.addClass('error'); + } + } + }); }); + $(tr).find('.applicable').data('applicable-groups', groups); + $(tr).find('.applicable').data('applicable-users', users); var mountType = 'group'; $.each(oldGroups, function(index, applicable) { - $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + $.ajax({type: 'POST', + url: OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), + data: { + mountPoint: mountPoint, + class: backendClass, + classOptions: classOptions, + mountType: mountType, + applicable: applicable, + isPersonal: isPersonal + }, + async: false + }); }); var mountType = 'user'; $.each(oldUsers, function(index, applicable) { - $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + $.ajax({type: 'POST', + url: OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), + data: { + mountPoint: mountPoint, + class: backendClass, + classOptions: classOptions, + mountType: mountType, + applicable: applicable, + isPersonal: isPersonal + }, + async: false + }); }); } else { var isPersonal = true; var mountType = 'user'; var applicable = OC.currentUser; - $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + $.ajax({type: 'POST', + url: OC.filePath('files_external', 'ajax', 'addMountPoint.php'), + data: { + mountPoint: mountPoint, + 'class': backendClass, + classOptions: classOptions, + mountType: mountType, + applicable: applicable, + isPersonal: isPersonal + }, + async: false, + success: function(result) { + statusSpan.removeClass(); + if (result && result.status == 'success' && result.data.message) { + status = true; + statusSpan.addClass('success'); + } else { + statusSpan.addClass('error'); + } + } + }); } - return true; + return status; } } }; @@ -71,7 +146,7 @@ OC.MountConfig={ $(document).ready(function() { $('.chzn-select').chosen(); - $('#selectBackend').on('change', function() { + $('#externalStorage').on('change', '#selectBackend', function() { var tr = $(this).parent().parent(); $('#externalStorage tbody').append($(tr).clone()); $('#externalStorage tbody tr').last().find('.mountPoint input').val(''); @@ -79,9 +154,10 @@ $(document).ready(function() { var backendClass = $(this).val(); $(this).parent().text(selected); if ($(tr).find('.mountPoint input').val() == '') { - $(tr).find('.mountPoint input').val(suggestMountPoint(selected.replace(/\s+/g, ''))); + $(tr).find('.mountPoint input').val(suggestMountPoint(selected)); } $(tr).addClass(backendClass); + $(tr).find('.status').append(''); $(tr).find('.backend').data('class', backendClass); var configurations = $(this).data('configurations'); var td = $(tr).find('td.configuration'); @@ -106,7 +182,11 @@ $(document).ready(function() { return false; } }); - $('.chz-select').chosen(); + // Reset chosen + var chosen = $(tr).find('.applicable select'); + chosen.parent().find('div').remove(); + chosen.removeAttr('id').removeClass('chzn-done').css({display:'inline-block'}); + chosen.chosen(); $(tr).find('td').last().attr('class', 'remove'); $(tr).find('td').last().removeAttr('style'); $(tr).removeAttr('id'); @@ -114,6 +194,11 @@ $(document).ready(function() { }); function suggestMountPoint(defaultMountPoint) { + var pos = defaultMountPoint.indexOf('/'); + if (pos !== -1) { + defaultMountPoint = defaultMountPoint.substring(0, pos); + } + defaultMountPoint = defaultMountPoint.replace(/\s+/g, ''); var i = 1; var append = ''; var match = true; @@ -135,11 +220,34 @@ $(document).ready(function() { return defaultMountPoint+append; } - $('#externalStorage').on('change', 'td', function() { - OC.MountConfig.saveStorage($(this).parent()); + $('#externalStorage').on('paste', 'td', function() { + var tr = $(this).parent(); + setTimeout(function() { + OC.MountConfig.saveStorage(tr); + }, 20); }); - $('td.remove>img').on('click', function() { + var timer; + + $('#externalStorage').on('keyup', 'td input', function() { + clearTimeout(timer); + var tr = $(this).parent().parent(); + if ($(this).val) { + timer = setTimeout(function() { + OC.MountConfig.saveStorage(tr); + }, 2000); + } + }); + + $('#externalStorage').on('change', 'td input:checkbox', function() { + OC.MountConfig.saveStorage($(this).parent().parent().parent()); + }); + + $('#externalStorage').on('change', '.applicable .chzn-select', function() { + OC.MountConfig.saveStorage($(this).parent().parent()); + }); + + $('#externalStorage').on('click', 'td.remove>img', function() { var tr = $(this).parent().parent(); var mountPoint = $(tr).find('.mountPoint input').val(); if ( ! mountPoint) { @@ -151,23 +259,25 @@ $(document).ready(function() { if ($('#externalStorage').data('admin') === true) { var isPersonal = false; var multiselect = $(tr).find('.chzn-select').val(); - $.each(multiselect, function(index, value) { - var pos = value.indexOf('(group)'); - if (pos != -1) { - var mountType = 'group'; - var applicable = value.substr(0, pos); - } else { - var mountType = 'user'; - var applicable = value; - } - $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); - }); + if (multiselect != null) { + $.each(multiselect, function(index, value) { + var pos = value.indexOf('(group)'); + if (pos != -1) { + var mountType = 'group'; + var applicable = value.substr(0, pos); + } else { + var mountType = 'user'; + var applicable = value; + } + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + }); + } } else { var mountType = 'user'; var applicable = OC.currentUser; var isPersonal = true; + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); } - $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); $(tr).remove(); }); diff --git a/apps/files_external/l10n/bg_BG.php b/apps/files_external/l10n/bg_BG.php index 1f2c29d54c5596ccbd7bf6d5f6b839b3c7c0fff7..66ad4a879d441a038c6c5f79359fccc22a613024 100644 --- a/apps/files_external/l10n/bg_BG.php +++ b/apps/files_external/l10n/bg_BG.php @@ -1,11 +1,10 @@ "Достъпът е даден", "Grant access" => "Даване на достъп", -"Fill out all required fields" => "Попълнете всички задължителни полета", "External Storage" => "Външно хранилище", -"Backend" => "Администрация", "Configuration" => "Конфигурация", "Options" => "Опции", +"Applicable" => "Приложимо", "None set" => "Няма избрано", "All Users" => "Всички потребители", "Groups" => "Групи", diff --git a/apps/files_external/l10n/bn_BD.php b/apps/files_external/l10n/bn_BD.php index a4a2b23030b9aefe8b0ac1c0069ba97b738cf2da..07ccd50074667723f638cc89f7c04d82f9246cfe 100644 --- a/apps/files_external/l10n/bn_BD.php +++ b/apps/files_external/l10n/bn_BD.php @@ -2,16 +2,12 @@ "Access granted" => "অধিগমনের অনুমতি প্রদান করা হলো", "Error configuring Dropbox storage" => "Dropbox সংরক্ষণাগার নির্ধারণ করতে সমস্যা ", "Grant access" => "অধিগমনের অনুমতি প্রদান কর", -"Fill out all required fields" => "আবশ্যিক সমস্ত ক্ষেত্র পূরণ করুন", "Please provide a valid Dropbox app key and secret." => "দয়া করে সঠিক এবং বৈধ Dropbox app key and secret প্রদান করুন।", "Error configuring Google Drive storage" => "Google Drive সংরক্ষণাগার নির্ধারণ করতে সমস্যা ", "External Storage" => "বাহ্যিক সংরক্ষণাগার", -"Mount point" => "মাউন্ট পয়েন্ট", -"Backend" => "পশ্চাদপট", "Configuration" => "কনফিগারেসন", "Options" => "বিকল্পসমূহ", "Applicable" => "প্রযোজ্য", -"Add mount point" => "মাউন্ট পয়েন্ট যোগ কর", "None set" => "কোনটিই নির্ধারণ করা হয় নি", "All Users" => "সমস্ত ব্যবহারকারী", "Groups" => "গোষ্ঠীসমূহ", diff --git a/apps/files_external/l10n/ca.php b/apps/files_external/l10n/ca.php index e8a922ca0f9f22bfe09dcc080aec02411946de4b..aa9304d3301a234835b4cdf0291e8a31a0682632 100644 --- a/apps/files_external/l10n/ca.php +++ b/apps/files_external/l10n/ca.php @@ -2,18 +2,17 @@ "Access granted" => "S'ha concedit l'accés", "Error configuring Dropbox storage" => "Error en configurar l'emmagatzemament Dropbox", "Grant access" => "Concedeix accés", -"Fill out all required fields" => "Ompliu els camps requerits", "Please provide a valid Dropbox app key and secret." => "Proporcioneu una clau d'aplicació i secret vàlids per a Dropbox", "Error configuring Google Drive storage" => "Error en configurar l'emmagatzemament Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Avís: \"smbclient\" no està instal·lat. No es pot muntar la compartició CIFS/SMB. Demaneu a l'administrador del sistema que l'instal·li.", "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." => "Avís: El suport FTP per PHP no està activat o no està instal·lat. No es pot muntar la compartició FTP. Demaneu a l'administrador del sistema que l'instal·li.", "External Storage" => "Emmagatzemament extern", -"Mount point" => "Punt de muntatge", -"Backend" => "Dorsal", +"Folder name" => "Nom de la carpeta", +"External storage" => "Emmagatzemament extern", "Configuration" => "Configuració", "Options" => "Options", "Applicable" => "Aplicable", -"Add mount point" => "Afegeix punt de muntatge", +"Add storage" => "Afegeix emmagatzemament", "None set" => "Cap d'establert", "All Users" => "Tots els usuaris", "Groups" => "Grups", diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php index 9c647fad939862d8810b34429821a7ac16e604a0..20bbe8acbaaf42d5b7f3e35b1f92481fcd14bbe1 100644 --- a/apps/files_external/l10n/cs_CZ.php +++ b/apps/files_external/l10n/cs_CZ.php @@ -2,18 +2,17 @@ "Access granted" => "Přístup povolen", "Error configuring Dropbox storage" => "Chyba při nastavení úložiště Dropbox", "Grant access" => "Povolit přístup", -"Fill out all required fields" => "Vyplňte všechna povinná pole", "Please provide a valid Dropbox app key and secret." => "Zadejte, prosím, platný klíč a bezpečnostní frázi aplikace Dropbox.", "Error configuring Google Drive storage" => "Chyba při nastavení úložiště Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varování: není nainstalován program \"smbclient\". Není možné připojení oddílů CIFS/SMB. Prosím požádejte svého správce systému ať jej nainstaluje.", "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." => "Varování: není nainstalována, nebo povolena, podpora FTP v PHP. Není možné připojení oddílů FTP. Prosím požádejte svého správce systému ať ji nainstaluje.", "External Storage" => "Externí úložiště", -"Mount point" => "Přípojný bod", -"Backend" => "Podpůrná vrstva", +"Folder name" => "Název složky", +"External storage" => "Externí úložiště", "Configuration" => "Nastavení", "Options" => "Možnosti", "Applicable" => "Přístupný pro", -"Add mount point" => "Přidat bod připojení", +"Add storage" => "Přidat úložiště", "None set" => "Nenastaveno", "All Users" => "Všichni uživatelé", "Groups" => "Skupiny", diff --git a/apps/files_external/l10n/da.php b/apps/files_external/l10n/da.php index bae89a6cfdd6fc8c593cbbcdff91123be7a6eebc..0c9c6c390443c5a789b73dad88908215cf08f4e4 100644 --- a/apps/files_external/l10n/da.php +++ b/apps/files_external/l10n/da.php @@ -2,18 +2,15 @@ "Access granted" => "Adgang godkendt", "Error configuring Dropbox storage" => "Fejl ved konfiguration af Dropbox plads", "Grant access" => "Godkend adgang", -"Fill out all required fields" => "Udfyld alle nødvendige felter", "Please provide a valid Dropbox app key and secret." => "Angiv venligst en valid Dropbox app nøgle og hemmelighed", "Error configuring Google Drive storage" => "Fejl ved konfiguration af Google Drive plads", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => " Advarsel: \"smbclient\" ikke er installeret. Montering af CIFS / SMB delinger er ikke muligt. Spørg din systemadministrator om at installere det.", "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." => " Advarsel: FTP-understøttelse i PHP ikke er aktiveret eller installeret. Montering af FTP delinger er ikke muligt. Spørg din systemadministrator om at installere det.", "External Storage" => "Ekstern opbevaring", -"Mount point" => "Monteringspunkt", -"Backend" => "Backend", +"Folder name" => "Mappenavn", "Configuration" => "Opsætning", "Options" => "Valgmuligheder", "Applicable" => "Kan anvendes", -"Add mount point" => "Tilføj monteringspunkt", "None set" => "Ingen sat", "All Users" => "Alle brugere", "Groups" => "Grupper", diff --git a/apps/files_external/l10n/de.php b/apps/files_external/l10n/de.php index 277cc2e6efe64e39568bfabb77a3dbfd105e8094..24183772217a0d59ce1b14f7569b5ac653319aef 100644 --- a/apps/files_external/l10n/de.php +++ b/apps/files_external/l10n/de.php @@ -2,18 +2,17 @@ "Access granted" => "Zugriff gestattet", "Error configuring Dropbox storage" => "Fehler beim Einrichten von Dropbox", "Grant access" => "Zugriff gestatten", -"Fill out all required fields" => "Bitte alle notwendigen Felder füllen", "Please provide a valid Dropbox app key and secret." => "Bitte trage einen gültigen Dropbox-App-Key mit Secret ein.", "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Warnung: \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitte Deinen System-Administrator, dies zu installieren.", "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." => "Warnung:: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wende Dich an Deinen Systemadministrator.", "External Storage" => "Externer Speicher", -"Mount point" => "Mount-Point", -"Backend" => "Backend", +"Folder name" => "Ordnername", +"External storage" => "Externer Speicher", "Configuration" => "Konfiguration", "Options" => "Optionen", "Applicable" => "Zutreffend", -"Add mount point" => "Mount-Point hinzufügen", +"Add storage" => "Speicher hinzufügen", "None set" => "Nicht definiert", "All Users" => "Alle Benutzer", "Groups" => "Gruppen", diff --git a/apps/files_external/l10n/de_DE.php b/apps/files_external/l10n/de_DE.php index 5675eb057f019e7333fecdf5f89965da7d40140b..d55c0c6909de06d2c5b26b5fe74ef97e4c4ee181 100644 --- a/apps/files_external/l10n/de_DE.php +++ b/apps/files_external/l10n/de_DE.php @@ -2,18 +2,17 @@ "Access granted" => "Zugriff gestattet", "Error configuring Dropbox storage" => "Fehler beim Einrichten von Dropbox", "Grant access" => "Zugriff gestatten", -"Fill out all required fields" => "Bitte alle notwendigen Felder füllen", "Please provide a valid Dropbox app key and secret." => "Bitte tragen Sie einen gültigen Dropbox-App-Key mit Secret ein.", "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Warnung: \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitten Sie Ihren Systemadministrator, dies zu installieren.", "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." => "Warnung:: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wenden Sie sich an Ihren Systemadministrator.", "External Storage" => "Externer Speicher", -"Mount point" => "Mount-Point", -"Backend" => "Backend", +"Folder name" => "Ordnername", +"External storage" => "Externer Speicher", "Configuration" => "Konfiguration", "Options" => "Optionen", "Applicable" => "Zutreffend", -"Add mount point" => "Mount-Point hinzufügen", +"Add storage" => "Speicher hinzufügen", "None set" => "Nicht definiert", "All Users" => "Alle Benutzer", "Groups" => "Gruppen", diff --git a/apps/files_external/l10n/el.php b/apps/files_external/l10n/el.php index 9bf499a911d3a47cfbb8d53834f4a0a021877853..38b5a098f6266fe169329cde07322dff6e65f3e6 100644 --- a/apps/files_external/l10n/el.php +++ b/apps/files_external/l10n/el.php @@ -2,18 +2,15 @@ "Access granted" => "Προσβαση παρασχέθηκε", "Error configuring Dropbox storage" => "Σφάλμα ρυθμίζωντας αποθήκευση Dropbox ", "Grant access" => "Παροχή πρόσβασης", -"Fill out all required fields" => "Συμπληρώστε όλα τα απαιτούμενα πεδία", "Please provide a valid Dropbox app key and secret." => "Παρακαλούμε δώστε έγκυρο κλειδί Dropbox και μυστικό.", "Error configuring Google Drive storage" => "Σφάλμα ρυθμίζωντας αποθήκευση Google Drive ", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Προσοχή: Ο \"smbclient\" δεν εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση CIFS/SMB. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.", "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." => "Προσοχή: Η υποστήριξη FTP στην PHP δεν ενεργοποιήθηκε ή εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση FTP. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.", "External Storage" => "Εξωτερικό Αποθηκευτικό Μέσο", -"Mount point" => "Σημείο προσάρτησης", -"Backend" => "Σύστημα υποστήριξης", +"Folder name" => "Όνομα φακέλου", "Configuration" => "Ρυθμίσεις", "Options" => "Επιλογές", "Applicable" => "Εφαρμόσιμο", -"Add mount point" => "Προσθήκη σημείου προσάρτησης", "None set" => "Κανένα επιλεγμένο", "All Users" => "Όλοι οι Χρήστες", "Groups" => "Ομάδες", diff --git a/apps/files_external/l10n/eo.php b/apps/files_external/l10n/eo.php index 97453aafedb92eabac851765877f9cd71cc37799..b0afb77498f28d39dcc65fa278b1172f8eda92a2 100644 --- a/apps/files_external/l10n/eo.php +++ b/apps/files_external/l10n/eo.php @@ -2,16 +2,13 @@ "Access granted" => "Alirpermeso donita", "Error configuring Dropbox storage" => "Eraro dum agordado de la memorservo Dropbox", "Grant access" => "Doni alirpermeson", -"Fill out all required fields" => "Plenigu ĉiujn neprajn kampojn", "Please provide a valid Dropbox app key and secret." => "Bonvolu provizi ŝlosilon de la aplikaĵo Dropbox validan kaj sekretan.", "Error configuring Google Drive storage" => "Eraro dum agordado de la memorservo Google Drive", "External Storage" => "Malena memorilo", -"Mount point" => "Surmetingo", -"Backend" => "Motoro", +"Folder name" => "Dosierujnomo", "Configuration" => "Agordo", "Options" => "Malneproj", "Applicable" => "Aplikebla", -"Add mount point" => "Aldoni surmetingon", "None set" => "Nenio agordita", "All Users" => "Ĉiuj uzantoj", "Groups" => "Grupoj", diff --git a/apps/files_external/l10n/es.php b/apps/files_external/l10n/es.php index d4e566276496b028601743484c0373e1dd02e418..da22f41032070f52d6f585874e05fe778d487319 100644 --- a/apps/files_external/l10n/es.php +++ b/apps/files_external/l10n/es.php @@ -2,18 +2,17 @@ "Access granted" => "Acceso garantizado", "Error configuring Dropbox storage" => "Error configurando el almacenamiento de Dropbox", "Grant access" => "Garantizar acceso", -"Fill out all required fields" => "Rellenar todos los campos requeridos", "Please provide a valid Dropbox app key and secret." => "Por favor , proporcione un secreto y una contraseña válida de la app Dropbox.", "Error configuring Google Drive storage" => "Error configurando el almacenamiento de Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Advertencia: El cliente smb (smbclient) no se encuentra instalado. El montado de archivos o ficheros CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.", "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 archivos o ficheros FTP no es posible. Por favor pida al administrador de su sistema que lo instale.", "External Storage" => "Almacenamiento externo", -"Mount point" => "Punto de montaje", -"Backend" => "Motor", +"Folder name" => "Nombre de la carpeta", +"External storage" => "Almacenamiento externo", "Configuration" => "Configuración", "Options" => "Opciones", "Applicable" => "Aplicable", -"Add mount point" => "Añadir punto de montaje", +"Add storage" => "Añadir almacenamiento", "None set" => "No se ha configurado", "All Users" => "Todos los usuarios", "Groups" => "Grupos", diff --git a/apps/files_external/l10n/es_AR.php b/apps/files_external/l10n/es_AR.php index aa117e802743c5c661afb752bbcabbacb7d496ac..6706aa43a311ffde100251e5f4d1bdb1b840bb65 100644 --- a/apps/files_external/l10n/es_AR.php +++ b/apps/files_external/l10n/es_AR.php @@ -2,18 +2,17 @@ "Access granted" => "Acceso permitido", "Error configuring Dropbox storage" => "Error al configurar el almacenamiento de Dropbox", "Grant access" => "Permitir acceso", -"Fill out all required fields" => "Rellenar todos los campos requeridos", "Please provide a valid Dropbox app key and secret." => "Por favor, proporcioná un secreto y una contraseña válida para la aplicación Dropbox.", "Error configuring Google Drive storage" => "Error al configurar el almacenamiento de Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Advertencia: El cliente smb (smbclient) no se encuentra instalado. El montado de archivos o ficheros CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.", "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 archivos o ficheros FTP no es posible. Por favor pida al administrador de su sistema que lo instale.", "External Storage" => "Almacenamiento externo", -"Mount point" => "Punto de montaje", -"Backend" => "Motor", +"Folder name" => "Nombre de la carpeta", +"External storage" => "Almacenamiento externo", "Configuration" => "Configuración", "Options" => "Opciones", "Applicable" => "Aplicable", -"Add mount point" => "Añadir punto de montaje", +"Add storage" => "Añadir almacenamiento", "None set" => "No fue configurado", "All Users" => "Todos los usuarios", "Groups" => "Grupos", diff --git a/apps/files_external/l10n/et_EE.php b/apps/files_external/l10n/et_EE.php index 86922bc751b3971a94350bf0d04c951655a8ee4f..fd0cdefd347cd7bfb232ebc93777c66aeb847390 100644 --- a/apps/files_external/l10n/et_EE.php +++ b/apps/files_external/l10n/et_EE.php @@ -2,16 +2,13 @@ "Access granted" => "Ligipääs on antud", "Error configuring Dropbox storage" => "Viga Dropboxi salvestusruumi seadistamisel", "Grant access" => "Anna ligipääs", -"Fill out all required fields" => "Täida kõik kohustuslikud lahtrid", "Please provide a valid Dropbox app key and secret." => "Palun sisesta korrektne Dropboxi rakenduse võti ja salasõna.", "Error configuring Google Drive storage" => "Viga Google Drive'i salvestusruumi seadistamisel", "External Storage" => "Väline salvestuskoht", -"Mount point" => "Ühenduspunkt", -"Backend" => "Admin", +"Folder name" => "Kausta nimi", "Configuration" => "Seadistamine", "Options" => "Valikud", "Applicable" => "Rakendatav", -"Add mount point" => "Lisa ühenduspunkt", "None set" => "Pole määratud", "All Users" => "Kõik kasutajad", "Groups" => "Grupid", diff --git a/apps/files_external/l10n/eu.php b/apps/files_external/l10n/eu.php index 597204c894d4e14c21a83c8beaee91dc96e28088..83be50deb00e2cbdffcc7b460c62222dd82bdb43 100644 --- a/apps/files_external/l10n/eu.php +++ b/apps/files_external/l10n/eu.php @@ -2,18 +2,17 @@ "Access granted" => "Sarrera baimendua", "Error configuring Dropbox storage" => "Errore bat egon da Dropbox biltegiratzea konfiguratzean", "Grant access" => "Baimendu sarrera", -"Fill out all required fields" => "Bete eskatutako eremu guztiak", "Please provide a valid Dropbox app key and secret." => "Mesedez eman baliozkoa den Dropbox app giltza eta sekretua", "Error configuring Google Drive storage" => "Errore bat egon da Google Drive biltegiratzea konfiguratzean", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Abisua: \"smbclient\" ez dago instalatuta. CIFS/SMB partekatutako karpetak montatzea ez da posible. Mesedez eskatu zure sistema kudeatzaileari instalatzea.", "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." => "Abisua: PHPren FTP modulua ez dago instalatuta edo gaitua. FTP partekatutako karpetak montatzea ez da posible. Mesedez eskatu zure sistema kudeatzaileari instalatzea.", "External Storage" => "Kanpoko Biltegiratzea", -"Mount point" => "Montatze puntua", -"Backend" => "Motorra", +"Folder name" => "Karpetaren izena", +"External storage" => "Kanpoko biltegiratzea", "Configuration" => "Konfigurazioa", "Options" => "Aukerak", "Applicable" => "Aplikagarria", -"Add mount point" => "Gehitu muntatze puntua", +"Add storage" => "Gehitu biltegiratzea", "None set" => "Ezarri gabe", "All Users" => "Erabiltzaile guztiak", "Groups" => "Taldeak", diff --git a/apps/files_external/l10n/fa.php b/apps/files_external/l10n/fa.php index 5acf3eac5a59a4e4cea5159330911a442d707e5b..a7eac596b04925a0c0c636fc751356180857a65a 100644 --- a/apps/files_external/l10n/fa.php +++ b/apps/files_external/l10n/fa.php @@ -3,6 +3,7 @@ "Configuration" => "پیکربندی", "Options" => "تنظیمات", "Applicable" => "قابل اجرا", +"All Users" => "تمام کاربران", "Groups" => "گروه ها", "Users" => "کاربران", "Delete" => "حذف", diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php index 8c7381db71d822e5789bf3fa0ff436ec9ef9e56b..4cf97f2fc35db8e525376a5be388b119e831133b 100644 --- a/apps/files_external/l10n/fi_FI.php +++ b/apps/files_external/l10n/fi_FI.php @@ -2,17 +2,15 @@ "Access granted" => "Pääsy sallittu", "Error configuring Dropbox storage" => "Virhe Dropbox levyn asetuksia tehtäessä", "Grant access" => "Salli pääsy", -"Fill out all required fields" => "Täytä kaikki vaaditut kentät", +"Please provide a valid Dropbox app key and secret." => "Anna kelvollinen Dropbox-sovellusavain ja salainen vastaus.", "Error configuring Google Drive storage" => "Virhe Google Drive levyn asetuksia tehtäessä", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varoitus: \"smbclient\" ei ole asennettuna. CIFS-/SMB-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää asentamaan smbclient.", "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." => "Varoitus: PHP:n FTP-tuki ei ole käytössä tai sitä ei ole asennettu. FTP-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan FTP-tuki käyttöön.", "External Storage" => "Erillinen tallennusväline", -"Mount point" => "Liitospiste", -"Backend" => "Taustaosa", +"Folder name" => "Kansion nimi", "Configuration" => "Asetukset", "Options" => "Valinnat", "Applicable" => "Sovellettavissa", -"Add mount point" => "Lisää liitospiste", "None set" => "Ei asetettu", "All Users" => "Kaikki käyttäjät", "Groups" => "Ryhmät", diff --git a/apps/files_external/l10n/fr.php b/apps/files_external/l10n/fr.php index 0825a961b1c26ee555389a9a4e41f372fc826575..db4140b483db83aed824e20b16c3299d5a8bcf8e 100644 --- a/apps/files_external/l10n/fr.php +++ b/apps/files_external/l10n/fr.php @@ -2,18 +2,15 @@ "Access granted" => "Accès autorisé", "Error configuring Dropbox storage" => "Erreur lors de la configuration du support de stockage Dropbox", "Grant access" => "Autoriser l'accès", -"Fill out all required fields" => "Veuillez remplir tous les champs requis", "Please provide a valid Dropbox app key and secret." => "Veuillez fournir une clé d'application (app key) ainsi qu'un mot de passe valides.", "Error configuring Google Drive storage" => "Erreur lors de la configuration du support de stockage Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Attention : \"smbclient\" n'est pas installé. Le montage des partages CIFS/SMB n'est pas disponible. Contactez votre administrateur système pour l'installer.", "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." => "Attention : Le support FTP de PHP n'est pas activé ou installé. Le montage des partages FTP n'est pas disponible. Contactez votre administrateur système pour l'installer.", "External Storage" => "Stockage externe", -"Mount point" => "Point de montage", -"Backend" => "Infrastructure", +"Folder name" => "Nom du dossier", "Configuration" => "Configuration", "Options" => "Options", "Applicable" => "Disponible", -"Add mount point" => "Ajouter un point de montage", "None set" => "Aucun spécifié", "All Users" => "Tous les utilisateurs", "Groups" => "Groupes", diff --git a/apps/files_external/l10n/gl.php b/apps/files_external/l10n/gl.php index f8100e14620a97972c0d0e1ba5956505328e7c8e..715417e25a0d96dde674f0cec29d1ce6fbe24bb6 100644 --- a/apps/files_external/l10n/gl.php +++ b/apps/files_external/l10n/gl.php @@ -2,18 +2,17 @@ "Access granted" => "Concedeuse acceso", "Error configuring Dropbox storage" => "Produciuse un erro ao configurar o almacenamento en Dropbox", "Grant access" => "Permitir o acceso", -"Fill out all required fields" => "Cubrir todos os campos obrigatorios", "Please provide a valid Dropbox app key and secret." => "Forneza unha chave correcta e segreda do Dropbox.", "Error configuring Google Drive storage" => "Produciuse un erro ao configurar o almacenamento en Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: «smbclient» non está instalado. Non é posibel a montaxe de comparticións CIFS/SMB. Consulte co administrador do sistema para instalalo.", "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." => "Aviso: A compatibilidade de FTP en PHP non está activada ou instalada. Non é posibel a montaxe de comparticións FTP. Consulte co administrador do sistema para instalalo.", "External Storage" => "Almacenamento externo", -"Mount point" => "Punto de montaxe", -"Backend" => "Infraestrutura", +"Folder name" => "Nome do cartafol", +"External storage" => "Almacenamento externo", "Configuration" => "Configuración", "Options" => "Opcións", "Applicable" => "Aplicábel", -"Add mount point" => "Engadir un punto de montaxe", +"Add storage" => "Engadir almacenamento", "None set" => "Ningún definido", "All Users" => "Todos os usuarios", "Groups" => "Grupos", diff --git a/apps/files_external/l10n/he.php b/apps/files_external/l10n/he.php index 3dc04d4e79c81ef34a48d6232d261b9e3dbb6221..9cba045d1eafe5d12b01ba1c05bbb5261af7f44c 100644 --- a/apps/files_external/l10n/he.php +++ b/apps/files_external/l10n/he.php @@ -2,16 +2,13 @@ "Access granted" => "הוענקה גישה", "Error configuring Dropbox storage" => "אירעה שגיאה בעת הגדרת אחסון ב־Dropbox", "Grant access" => "הענקת גישה", -"Fill out all required fields" => "נא למלא את כל השדות הנדרשים", "Please provide a valid Dropbox app key and secret." => "נא לספק קוד יישום וסוד תקניים של Dropbox.", "Error configuring Google Drive storage" => "אירעה שגיאה בעת הגדרת אחסון ב־Google Drive", "External Storage" => "אחסון חיצוני", -"Mount point" => "נקודת עגינה", -"Backend" => "מנגנון", +"Folder name" => "שם התיקייה", "Configuration" => "הגדרות", "Options" => "אפשרויות", "Applicable" => "ניתן ליישום", -"Add mount point" => "הוספת נקודת עגינה", "None set" => "לא הוגדרה", "All Users" => "כל המשתמשים", "Groups" => "קבוצות", diff --git a/apps/files_external/l10n/hi.php b/apps/files_external/l10n/hi.php new file mode 100644 index 0000000000000000000000000000000000000000..0482efc4b23d20d9675fd371d1f1d8fc8a55e4a5 --- /dev/null +++ b/apps/files_external/l10n/hi.php @@ -0,0 +1,3 @@ + "उपयोगकर्ता" +); diff --git a/apps/files_external/l10n/hu_HU.php b/apps/files_external/l10n/hu_HU.php index b8973c9641111ee2a505385899b83191b3d0b94e..9ecd2d1088b7a0d348409b470ee196c2d4c52309 100644 --- a/apps/files_external/l10n/hu_HU.php +++ b/apps/files_external/l10n/hu_HU.php @@ -2,18 +2,17 @@ "Access granted" => "Érvényes hozzáférés", "Error configuring Dropbox storage" => "A Dropbox tárolót nem sikerült beállítani", "Grant access" => "Megadom a hozzáférést", -"Fill out all required fields" => "Töltse ki az összes szükséges mezőt", "Please provide a valid Dropbox app key and secret." => "Adjon meg egy érvényes Dropbox app key-t és secretet!", "Error configuring Google Drive storage" => "A Google Drive tárolót nem sikerült beállítani", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Figyelem: az \"smbclient\" nincs telepítve a kiszolgálón. Emiatt nem lehet CIFS/SMB megosztásokat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.", "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." => "Figyelem: a PHP FTP támogatása vagy nincs telepítve, vagy nincs engedélyezve a kiszolgálón. Emiatt nem lehetséges FTP-tárolókat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.", "External Storage" => "Külső tárolási szolgáltatások becsatolása", -"Mount point" => "Hova csatoljuk", -"Backend" => "Külső tárolórendszer", +"Folder name" => "Mappanév", +"External storage" => "Külső tárolók", "Configuration" => "Beállítások", "Options" => "Opciók", "Applicable" => "Érvényességi kör", -"Add mount point" => "Új csatolás létrehozása", +"Add storage" => "Tároló becsatolása", "None set" => "Nincs beállítva", "All Users" => "Az összes felhasználó", "Groups" => "Csoportok", diff --git a/apps/files_external/l10n/hy.php b/apps/files_external/l10n/hy.php new file mode 100644 index 0000000000000000000000000000000000000000..3b80487278a16a8f1f7b407908ea855f3f5def10 --- /dev/null +++ b/apps/files_external/l10n/hy.php @@ -0,0 +1,3 @@ + "Ջնջել" +); diff --git a/apps/files_external/l10n/id.php b/apps/files_external/l10n/id.php index 4b7850025f4e845a9165809b20d48ac204e9b7f2..647220706bae67ec6469ea2ae3c8c6e0ff100e71 100644 --- a/apps/files_external/l10n/id.php +++ b/apps/files_external/l10n/id.php @@ -1,14 +1,22 @@ "akses diberikan", -"Grant access" => "berikan hak akses", -"Fill out all required fields" => "isi semua field yang dibutuhkan", -"External Storage" => "penyimpanan eksternal", -"Configuration" => "konfigurasi", -"Options" => "pilihan", -"Applicable" => "berlaku", -"None set" => "tidak satupun di set", -"All Users" => "semua pengguna", -"Groups" => "grup", -"Users" => "pengguna", -"Delete" => "hapus" +"Access granted" => "Akses diberikan", +"Error configuring Dropbox storage" => "Kesalahan dalam mengkonfigurasi penyimpanan Dropbox", +"Grant access" => "Berikan hak akses", +"Please provide a valid Dropbox app key and secret." => "Masukkan kunci dan sandi aplikasi Dropbox yang benar.", +"Error configuring Google Drive storage" => "Kesalahan dalam mengkonfigurasi penyimpanan Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Peringatan: \"smbclient\" tidak terpasang. Mount direktori CIFS/SMB tidak dapat dilakukan. Silakan minta administrator sistem untuk memasangnya.", +"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." => "Peringatan: Dukungan FTP di PHP tidak aktif atau tidak terpasang. Mount direktori FTP tidak dapat dilakukan. Silakan minta administrator sistem untuk memasangnya.", +"External Storage" => "Penyimpanan Eksternal", +"Configuration" => "Konfigurasi", +"Options" => "Pilihan", +"Applicable" => "Berlaku", +"None set" => "Tidak satupun di set", +"All Users" => "Semua Pengguna", +"Groups" => "Grup", +"Users" => "Pengguna", +"Delete" => "Hapus", +"Enable User External Storage" => "Aktifkan Penyimpanan Eksternal Pengguna", +"Allow users to mount their own external storage" => "Ijinkan pengguna untuk me-mount penyimpanan eksternal mereka", +"SSL root certificates" => "Sertifikat root SSL", +"Import Root Certificate" => "Impor Sertifikat Root" ); diff --git a/apps/files_external/l10n/is.php b/apps/files_external/l10n/is.php index 5110bf5ad2704d3c86b9aef8c99db2df8a2f1f39..6af53096facafd5205ff3deb9529ab3576397049 100644 --- a/apps/files_external/l10n/is.php +++ b/apps/files_external/l10n/is.php @@ -2,18 +2,15 @@ "Access granted" => "Aðgengi veitt", "Error configuring Dropbox storage" => "Villa við að setja upp Dropbox gagnasvæði", "Grant access" => "Veita aðgengi", -"Fill out all required fields" => "Fylltu út alla skilyrta reiti", "Please provide a valid Dropbox app key and secret." => "Gefðu upp virkan Dropbox lykil og leynikóða", "Error configuring Google Drive storage" => "Villa kom upp við að setja upp Google Drive gagnasvæði", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aðvörun: \"smbclient\" er ekki uppsettur. Uppsetning á CIFS/SMB gagnasvæðum er ekki möguleg. Hafðu samband við kerfisstjóra til að fá hann uppsettan.", "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." => "Aðvörun: FTP stuðningur í PHP er ekki virkur. Uppsetning á FTP gagnasvæðum er ekki möguleg. Hafðu samband við kerfisstjóra til að fá hann uppsettan.", "External Storage" => "Ytri gagnageymsla", -"Mount point" => "Mount svæði", -"Backend" => "Stjórnun", +"Folder name" => "Nafn möppu", "Configuration" => "Uppsetning", "Options" => "Stillingar", "Applicable" => "Gilt", -"Add mount point" => "Bæta við mount svæði", "None set" => "Ekkert sett", "All Users" => "Allir notendur", "Groups" => "Hópar", diff --git a/apps/files_external/l10n/it.php b/apps/files_external/l10n/it.php index 98c83146d48456e28d571ed1be0cbefe1ffcbc20..d7e0c81a0b0b6e0f23f4fbb9c2e4dea96774d88a 100644 --- a/apps/files_external/l10n/it.php +++ b/apps/files_external/l10n/it.php @@ -2,18 +2,17 @@ "Access granted" => "Accesso consentito", "Error configuring Dropbox storage" => "Errore durante la configurazione dell'archivio Dropbox", "Grant access" => "Concedi l'accesso", -"Fill out all required fields" => "Compila tutti i campi richiesti", "Please provide a valid Dropbox app key and secret." => "Fornisci chiave di applicazione e segreto di Dropbox validi.", "Error configuring Google Drive storage" => "Errore durante la configurazione dell'archivio Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Avviso: \"smbclient\" non è installato. Impossibile montare condivisioni CIFS/SMB. Chiedi all'amministratore di sistema di installarlo.", "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." => "Avviso: il supporto FTP di PHP non è abilitato o non è installato. Impossibile montare condivisioni FTP. Chiedi all'amministratore di sistema di installarlo.", "External Storage" => "Archiviazione esterna", -"Mount point" => "Punto di mount", -"Backend" => "Motore", +"Folder name" => "Nome della cartella", +"External storage" => "Archiviazione esterna", "Configuration" => "Configurazione", "Options" => "Opzioni", "Applicable" => "Applicabile", -"Add mount point" => "Aggiungi punto di mount", +"Add storage" => "Aggiungi archiviazione", "None set" => "Nessuna impostazione", "All Users" => "Tutti gli utenti", "Groups" => "Gruppi", diff --git a/apps/files_external/l10n/ja_JP.php b/apps/files_external/l10n/ja_JP.php index cd09bb43db79c92a192bb834cc879b7ad8db4662..12a0b30938a5e668377371ead59d07e9005451fa 100644 --- a/apps/files_external/l10n/ja_JP.php +++ b/apps/files_external/l10n/ja_JP.php @@ -2,18 +2,17 @@ "Access granted" => "アクセスは許可されました", "Error configuring Dropbox storage" => "Dropboxストレージの設定エラー", "Grant access" => "アクセスを許可", -"Fill out all required fields" => "すべての必須フィールドを埋めて下さい", "Please provide a valid Dropbox app key and secret." => "有効なDropboxアプリのキーとパスワードを入力して下さい。", "Error configuring Google Drive storage" => "Googleドライブストレージの設定エラー", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "警告: \"smbclient\" はインストールされていません。CIFS/SMB 共有のマウントはできません。システム管理者にインストールをお願いして下さい。", "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." => "警告: PHPのFTPサポートは無効もしくはインストールされていません。FTP共有のマウントはできません。システム管理者にインストールをお願いして下さい。", "External Storage" => "外部ストレージ", -"Mount point" => "マウントポイント", -"Backend" => "バックエンド", +"Folder name" => "フォルダ名", +"External storage" => "外部ストレージ", "Configuration" => "設定", "Options" => "オプション", "Applicable" => "適用範囲", -"Add mount point" => "マウントポイントを追加", +"Add storage" => "ストレージを追加", "None set" => "未設定", "All Users" => "すべてのユーザ", "Groups" => "グループ", diff --git a/apps/files_external/l10n/ka.php b/apps/files_external/l10n/ka.php new file mode 100644 index 0000000000000000000000000000000000000000..14c0625e1f280d2661e7dd33cb07e27814ef1fe4 --- /dev/null +++ b/apps/files_external/l10n/ka.php @@ -0,0 +1,3 @@ + "მომხმარებლები" +); diff --git a/apps/files_external/l10n/ko.php b/apps/files_external/l10n/ko.php index 47b75f74b86cbdd05b197877b5c850eba041d44f..47de9aad5e0ff566e4812e0f00be6136605133d6 100644 --- a/apps/files_external/l10n/ko.php +++ b/apps/files_external/l10n/ko.php @@ -2,18 +2,15 @@ "Access granted" => "접근 허가됨", "Error configuring Dropbox storage" => "Dropbox 저장소 설정 오류", "Grant access" => "접근 권한 부여", -"Fill out all required fields" => "모든 필수 항목을 입력하십시오", "Please provide a valid Dropbox app key and secret." => "올바른 Dropbox 앱 키와 암호를 입력하십시오.", "Error configuring Google Drive storage" => "Google 드라이브 저장소 설정 오류", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "경고: \"smbclient\"가 설치되지 않았습니다. CIFS/SMB 공유 자원에 연결할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", "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." => "경고: PHP FTP 지원이 비활성화되어 있거나 설치되지 않았습니다. FTP 공유를 마운트할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", "External Storage" => "외부 저장소", -"Mount point" => "마운트 지점", -"Backend" => "백엔드", +"Folder name" => "폴더 이름", "Configuration" => "설정", "Options" => "옵션", "Applicable" => "적용 가능", -"Add mount point" => "마운트 지점 추가", "None set" => "설정되지 않음", "All Users" => "모든 사용자", "Groups" => "그룹", diff --git a/apps/files_external/l10n/ku_IQ.php b/apps/files_external/l10n/ku_IQ.php index c614168d773376d802f17789c1757cb4c211e0b3..59eabb36c9ffd7f5779582ff862c0ff9723ee4ea 100644 --- a/apps/files_external/l10n/ku_IQ.php +++ b/apps/files_external/l10n/ku_IQ.php @@ -1,3 +1,4 @@ "ناوی بوخچه", "Users" => "به‌كارهێنه‌ر" ); diff --git a/apps/files_external/l10n/lb.php b/apps/files_external/l10n/lb.php index 7cbfaea6a57f851b2f1d96ca166dc103d220a6f7..2a62cad3fe90c3a6e2e97a7de790f5faf8ef0958 100644 --- a/apps/files_external/l10n/lb.php +++ b/apps/files_external/l10n/lb.php @@ -1,4 +1,5 @@ "Dossiers Numm:", "Groups" => "Gruppen", "Delete" => "Läschen" ); diff --git a/apps/files_external/l10n/lt_LT.php b/apps/files_external/l10n/lt_LT.php index cdb168dd38515e081f5f87921aea5f64e20a4bf2..f7dca99ef654bb245aaf65135c2e4977990c4e3b 100644 --- a/apps/files_external/l10n/lt_LT.php +++ b/apps/files_external/l10n/lt_LT.php @@ -2,16 +2,13 @@ "Access granted" => "Priėjimas suteiktas", "Error configuring Dropbox storage" => "Klaida nustatinėjant Dropbox talpyklą", "Grant access" => "Suteikti priėjimą", -"Fill out all required fields" => "Užpildykite visus reikalingus laukelius", "Please provide a valid Dropbox app key and secret." => "Prašome įvesti teisingus Dropbox \"app key\" ir \"secret\".", "Error configuring Google Drive storage" => "Klaida nustatinėjant Google Drive talpyklą", "External Storage" => "Išorinės saugyklos", -"Mount point" => "Saugyklos pavadinimas", -"Backend" => "Posistemės pavadinimas", +"Folder name" => "Katalogo pavadinimas", "Configuration" => "Konfigūracija", "Options" => "Nustatymai", "Applicable" => "Pritaikyti", -"Add mount point" => "Pridėti išorinę saugyklą", "None set" => "Nieko nepasirinkta", "All Users" => "Visi vartotojai", "Groups" => "Grupės", diff --git a/apps/files_external/l10n/lv.php b/apps/files_external/l10n/lv.php index ee53346fcdeb3e8600539fc3e6666d46a9faed71..b37dbcbdc16adbeeace1cbaf2588e5ce6bec524f 100644 --- a/apps/files_external/l10n/lv.php +++ b/apps/files_external/l10n/lv.php @@ -2,18 +2,17 @@ "Access granted" => "Piešķirta pieeja", "Error configuring Dropbox storage" => "Kļūda, konfigurējot Dropbox krātuvi", "Grant access" => "Piešķirt pieeju", -"Fill out all required fields" => "Aizpildīt visus pieprasītos laukus", "Please provide a valid Dropbox app key and secret." => "Lūdzu, norādiet derīgu Dropbox lietotnes atslēgu un noslēpumu.", "Error configuring Google Drive storage" => "Kļūda, konfigurējot Google Drive krātuvi", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Brīdinājums: nav uzinstalēts “smbclient”. Nevar montēt CIFS/SMB koplietojumus. Lūdzu, vaicājiet savam sistēmas administratoram, lai to uzinstalē.", "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." => "Brīdinājums: uz PHP nav aktivēts vai instalēts FTP atbalsts. Nevar montēt FTP koplietojumus. Lūdzu, vaicājiet savam sistēmas administratoram, lai to uzinstalē.", "External Storage" => "Ārējā krātuve", -"Mount point" => "Montēšanas punkts", -"Backend" => "Aizmugure", +"Folder name" => "Mapes nosaukums", +"External storage" => "Ārējā krātuve", "Configuration" => "Konfigurācija", "Options" => "Opcijas", "Applicable" => "Piemērojams", -"Add mount point" => "Pievienot montēšanas punktu", +"Add storage" => "Pievienot krātuvi", "None set" => "Neviens nav iestatīts", "All Users" => "Visi lietotāji", "Groups" => "Grupas", diff --git a/apps/files_external/l10n/mk.php b/apps/files_external/l10n/mk.php index e3c1e4652b3ff53d9537af9b23da7cc318b240ad..1f1a1c27831fd1a6d97e8f5bb4b23b0c13068082 100644 --- a/apps/files_external/l10n/mk.php +++ b/apps/files_external/l10n/mk.php @@ -2,18 +2,15 @@ "Access granted" => "Пристапот е дозволен", "Error configuring Dropbox storage" => "Грешка при конфигурација на Dropbox", "Grant access" => "Дозволи пристап", -"Fill out all required fields" => "Пополни ги сите задолжителни полиња", "Please provide a valid Dropbox app key and secret." => "Ве молам доставите валиден Dropbox клуч и тајна лозинка.", "Error configuring Google Drive storage" => "Грешка при конфигурација на Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Внимание: \"smbclient\" не е инсталиран. Не е можно монтирање на CIFS/SMB дискови. Замолете го Вашиот систем администратор да го инсталира.", "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." => "Внимание: Не е овозможена или инсталирани FTP подршка во PHP. Не е можно монтирање на FTP дискови. Замолете го Вашиот систем администратор да го инсталира.", "External Storage" => "Надворешно складиште", -"Mount point" => "Точка на монтирање", -"Backend" => "Админ", +"Folder name" => "Име на папка", "Configuration" => "Конфигурација", "Options" => "Опции", "Applicable" => "Применливо", -"Add mount point" => "Додади точка на монтирање", "None set" => "Ништо поставено", "All Users" => "Сите корисници", "Groups" => "Групи", diff --git a/apps/files_external/l10n/my_MM.php b/apps/files_external/l10n/my_MM.php new file mode 100644 index 0000000000000000000000000000000000000000..5acfbb0321e780fbde7413815f045f9ec50a4366 --- /dev/null +++ b/apps/files_external/l10n/my_MM.php @@ -0,0 +1,3 @@ + "သုံးစွဲသူ" +); diff --git a/apps/files_external/l10n/nb_NO.php b/apps/files_external/l10n/nb_NO.php index 273b40cf8eec029f3b887f06e01b47e234895f24..65f9e9bbaf144b90ce53e43c967621005b5a7743 100644 --- a/apps/files_external/l10n/nb_NO.php +++ b/apps/files_external/l10n/nb_NO.php @@ -1,4 +1,5 @@ "Mappenavn", "Configuration" => "Konfigurasjon", "Options" => "Innstillinger", "All Users" => "Alle brukere", diff --git a/apps/files_external/l10n/nl.php b/apps/files_external/l10n/nl.php index 132c8cf3ac2713e6abfbda15fcfc1efe8b91e28c..ef03f46379addc995c902a46541e7caeef9a3f89 100644 --- a/apps/files_external/l10n/nl.php +++ b/apps/files_external/l10n/nl.php @@ -2,18 +2,15 @@ "Access granted" => "Toegang toegestaan", "Error configuring Dropbox storage" => "Fout tijdens het configureren van Dropbox opslag", "Grant access" => "Sta toegang toe", -"Fill out all required fields" => "Vul alle verplichte in", "Please provide a valid Dropbox app key and secret." => "Geef een geldige Dropbox key en secret.", "Error configuring Google Drive storage" => "Fout tijdens het configureren van Google Drive opslag", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Waarschuwing: \"smbclient\" is niet geïnstalleerd. Mounten van CIFS/SMB shares is niet mogelijk. Vraag uw beheerder om smbclient te installeren.", "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." => "Waarschuwing: FTP ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van FTP shares is niet mogelijk. Vraag uw beheerder FTP ondersteuning te installeren.", "External Storage" => "Externe opslag", -"Mount point" => "Aankoppelpunt", -"Backend" => "Backend", +"Folder name" => "Mapnaam", "Configuration" => "Configuratie", "Options" => "Opties", "Applicable" => "Van toepassing", -"Add mount point" => "Aankoppelpunt toevoegen", "None set" => "Niets ingesteld", "All Users" => "Alle gebruikers", "Groups" => "Groepen", diff --git a/apps/files_external/l10n/pl.php b/apps/files_external/l10n/pl.php index 0da31bb6b4ac11962cf8d3f44303ccf23c5ca942..cd1b1fe84a1f4145a50bd3ba44a5b9d7c68c11f3 100644 --- a/apps/files_external/l10n/pl.php +++ b/apps/files_external/l10n/pl.php @@ -2,18 +2,17 @@ "Access granted" => "Dostęp do", "Error configuring Dropbox storage" => "Wystąpił błąd podczas konfigurowania zasobu Dropbox", "Grant access" => "Udziel dostępu", -"Fill out all required fields" => "Wypełnij wszystkie wymagane pola", "Please provide a valid Dropbox app key and secret." => "Proszę podać prawidłowy klucz aplikacji Dropbox i klucz sekretny.", "Error configuring Google Drive storage" => "Wystąpił błąd podczas konfigurowania zasobu Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Ostrzeżenie: \"smbclient\" nie jest zainstalowany. Zamontowanie katalogów CIFS/SMB nie jest możliwe. Skontaktuj sie z administratorem w celu zainstalowania.", "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." => "Ostrzeżenie: Wsparcie dla FTP w PHP nie jest zainstalowane lub włączone. Skontaktuj sie z administratorem w celu zainstalowania lub włączenia go.", "External Storage" => "Zewnętrzna zasoby dyskowe", -"Mount point" => "Punkt montowania", -"Backend" => "Zaplecze", +"Folder name" => "Nazwa folderu", +"External storage" => "Zewnętrzne zasoby dyskowe", "Configuration" => "Konfiguracja", "Options" => "Opcje", "Applicable" => "Zastosowanie", -"Add mount point" => "Dodaj punkt montowania", +"Add storage" => "Dodaj zasoby dyskowe", "None set" => "Nie ustawione", "All Users" => "Wszyscy uzytkownicy", "Groups" => "Grupy", diff --git a/apps/files_external/l10n/pt_BR.php b/apps/files_external/l10n/pt_BR.php index 85393954886e39ab7d659d99169dee7edf6f5e8f..908c95b00904568c3766fa00dfa33f5537a990cc 100644 --- a/apps/files_external/l10n/pt_BR.php +++ b/apps/files_external/l10n/pt_BR.php @@ -2,18 +2,15 @@ "Access granted" => "Acesso concedido", "Error configuring Dropbox storage" => "Erro ao configurar armazenamento do Dropbox", "Grant access" => "Permitir acesso", -"Fill out all required fields" => "Preencha todos os campos obrigatórios", "Please provide a valid Dropbox app key and secret." => "Por favor forneça um app key e secret válido do Dropbox", "Error configuring Google Drive storage" => "Erro ao configurar armazenamento do Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: \"smbclient\" não está instalado. Não será possível montar compartilhamentos de CIFS/SMB. Por favor, peça ao seu administrador do sistema para instalá-lo.", "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." => "Aviso: O suporte para FTP do PHP não está ativado ou instalado. Não será possível montar compartilhamentos FTP. Por favor, peça ao seu administrador do sistema para instalá-lo.", "External Storage" => "Armazenamento Externo", -"Mount point" => "Ponto de montagem", -"Backend" => "Backend", +"Folder name" => "Nome da pasta", "Configuration" => "Configuração", "Options" => "Opções", "Applicable" => "Aplicável", -"Add mount point" => "Adicionar ponto de montagem", "None set" => "Nenhum definido", "All Users" => "Todos os Usuários", "Groups" => "Grupos", diff --git a/apps/files_external/l10n/pt_PT.php b/apps/files_external/l10n/pt_PT.php index 06dbc0cf6bd15cdfea768e35f14333ff6b96abd2..1204f13f42f758948d387c39e968a53755462da4 100644 --- a/apps/files_external/l10n/pt_PT.php +++ b/apps/files_external/l10n/pt_PT.php @@ -2,18 +2,15 @@ "Access granted" => "Acesso autorizado", "Error configuring Dropbox storage" => "Erro ao configurar o armazenamento do Dropbox", "Grant access" => "Conceder acesso", -"Fill out all required fields" => "Preencha todos os campos obrigatórios", "Please provide a valid Dropbox app key and secret." => "Por favor forneça uma \"app key\" e \"secret\" do Dropbox válidas.", "Error configuring Google Drive storage" => "Erro ao configurar o armazenamento do Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: O cliente \"smbclient\" não está instalado. Não é possível montar as partilhas CIFS/SMB . Peça ao seu administrador para instalar.", "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." => "Aviso: O suporte FTP no PHP não está activate ou instalado. Não é possível montar as partilhas FTP. Peça ao seu administrador para instalar.", "External Storage" => "Armazenamento Externo", -"Mount point" => "Ponto de montagem", -"Backend" => "Backend", +"Folder name" => "Nome da pasta", "Configuration" => "Configuração", "Options" => "Opções", "Applicable" => "Aplicável", -"Add mount point" => "Adicionar ponto de montagem", "None set" => "Nenhum configurado", "All Users" => "Todos os utilizadores", "Groups" => "Grupos", diff --git a/apps/files_external/l10n/ro.php b/apps/files_external/l10n/ro.php index ca2c9f7e5c8c0eda8e01380f9b5fd7e5569b2272..5747205dc0590730806b6e5a0bff56918f86dc26 100644 --- a/apps/files_external/l10n/ro.php +++ b/apps/files_external/l10n/ro.php @@ -2,18 +2,15 @@ "Access granted" => "Acces permis", "Error configuring Dropbox storage" => "Eroare la configurarea mediului de stocare Dropbox", "Grant access" => "Permite accesul", -"Fill out all required fields" => "Completează toate câmpurile necesare", "Please provide a valid Dropbox app key and secret." => "Prezintă te rog o cheie de Dropbox validă și parola", "Error configuring Google Drive storage" => "Eroare la configurarea mediului de stocare Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Atenție: \"smbclient\" nu este instalat. Montarea mediilor CIFS/SMB partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleaze.", "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." => "Atenție: suportul pentru FTP în PHP nu este activat sau instalat. Montarea mediilor FPT partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleze.", "External Storage" => "Stocare externă", -"Mount point" => "Punctul de montare", -"Backend" => "Backend", +"Folder name" => "Denumire director", "Configuration" => "Configurație", "Options" => "Opțiuni", "Applicable" => "Aplicabil", -"Add mount point" => "Adaugă punct de montare", "None set" => "Niciunul", "All Users" => "Toți utilizatorii", "Groups" => "Grupuri", diff --git a/apps/files_external/l10n/ru.php b/apps/files_external/l10n/ru.php index b8b5f5b1cb2add548662c7bc8906f2c8863c7612..46b73a67f046d8bc53d8eaf4c29d0e5500821ff7 100644 --- a/apps/files_external/l10n/ru.php +++ b/apps/files_external/l10n/ru.php @@ -2,18 +2,17 @@ "Access granted" => "Доступ предоставлен", "Error configuring Dropbox storage" => "Ошибка при настройке хранилища Dropbox", "Grant access" => "Предоставление доступа", -"Fill out all required fields" => "Заполните все обязательные поля", "Please provide a valid Dropbox app key and secret." => "Пожалуйста, предоставьте действующий ключ Dropbox и пароль.", "Error configuring Google Drive storage" => "Ошибка при настройке хранилища Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Внимание: \"smbclient\" не установлен. Подключение по CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.", "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." => "Внимание: Поддержка FTP не включена в PHP. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы включить.", "External Storage" => "Внешний носитель", -"Mount point" => "Точка монтирования", -"Backend" => "Подсистема", +"Folder name" => "Имя папки", +"External storage" => "Внешний носитель данных", "Configuration" => "Конфигурация", "Options" => "Опции", "Applicable" => "Применимый", -"Add mount point" => "Добавить точку монтирования", +"Add storage" => "Добавить носитель данных", "None set" => "Не установлено", "All Users" => "Все пользователи", "Groups" => "Группы", diff --git a/apps/files_external/l10n/ru_RU.php b/apps/files_external/l10n/ru_RU.php index e539b3cb2cf5b8368f02029367eed46b92d138a5..406e284b2781734bdcdde8fb67bf3c99845057a9 100644 --- a/apps/files_external/l10n/ru_RU.php +++ b/apps/files_external/l10n/ru_RU.php @@ -2,18 +2,15 @@ "Access granted" => "Доступ разрешен", "Error configuring Dropbox storage" => "Ошибка при конфигурировании хранилища Dropbox", "Grant access" => "Предоставить доступ", -"Fill out all required fields" => "Заполните все требуемые поля", "Please provide a valid Dropbox app key and secret." => "Пожалуйста представьте допустимый ключ приложения Dropbox и пароль.", "Error configuring Google Drive storage" => "Ошибка настройки хранилища Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Предупреждение: \"smbclient\" не установлен. Подключение общих папок CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.", "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." => "Предупреждение: Поддержка FTP в PHP не включена или не установлена. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить ее.", "External Storage" => "Внешние системы хранения данных", -"Mount point" => "Точка монтирования", -"Backend" => "Бэкэнд", +"Folder name" => "Имя папки", "Configuration" => "Конфигурация", "Options" => "Опции", "Applicable" => "Применимый", -"Add mount point" => "Добавить точку монтирования", "None set" => "Не задан", "All Users" => "Все пользователи", "Groups" => "Группы", diff --git a/apps/files_external/l10n/si_LK.php b/apps/files_external/l10n/si_LK.php index b8e2c5714b3f3d6555578425ceef2fbf1999c80c..cc9cb9b9ce50084b2bb06281e90782e5c9df89c9 100644 --- a/apps/files_external/l10n/si_LK.php +++ b/apps/files_external/l10n/si_LK.php @@ -2,16 +2,13 @@ "Access granted" => "පිවිසීමට හැක", "Error configuring Dropbox storage" => "Dropbox ගබඩාව වින්‍යාස කිරීමේ දෝශයක් ඇත", "Grant access" => "පිවිසුම ලබාදෙන්න", -"Fill out all required fields" => "අත්‍යාවශ්‍ය තොරතුරු සියල්ල සම්පුර්ණ කරන්න", "Please provide a valid Dropbox app key and secret." => "කරුණාකර වලංගු Dropbox යෙදුම් යතුරක් හා රහසක් ලබාදෙන්න.", "Error configuring Google Drive storage" => "Google Drive ගබඩාව වින්‍යාස කිරීමේ දෝශයක් ඇත", "External Storage" => "භාහිර ගබඩාව", -"Mount point" => "මවුන්ට් කළ ස්ථානය", -"Backend" => "පසු පද්ධතිය", +"Folder name" => "ෆොල්ඩරයේ නම", "Configuration" => "වින්‍යාසය", "Options" => "විකල්පයන්", "Applicable" => "අදාළ", -"Add mount point" => "මවුන්ට් කරන ස්ථානයක් එකතු කරන්න", "None set" => "කිසිවක් නැත", "All Users" => "සියළු පරිශීලකයන්", "Groups" => "කණ්ඩායම්", diff --git a/apps/files_external/l10n/sk_SK.php b/apps/files_external/l10n/sk_SK.php index 0b6878a5427c30a89380938caf0cdea75c0419a2..af6b7b4ae6e2bb3fd4a6489735f0c7fda3ca8b4d 100644 --- a/apps/files_external/l10n/sk_SK.php +++ b/apps/files_external/l10n/sk_SK.php @@ -2,25 +2,24 @@ "Access granted" => "Prístup povolený", "Error configuring Dropbox storage" => "Chyba pri konfigurácii úložiska Dropbox", "Grant access" => "Povoliť prístup", -"Fill out all required fields" => "Vyplňte všetky vyžadované kolónky", "Please provide a valid Dropbox app key and secret." => "Zadajte platný kľúč aplikácie a heslo Dropbox", "Error configuring Google Drive storage" => "Chyba pri konfigurácii úložiska Google drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Upozornenie: \"smbclient\" nie je nainštalovaný. Nie je možné pripojenie oddielov CIFS/SMB. Požiadajte administrátora systému, nech ho nainštaluje.", "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." => "Upozornenie: Podpora FTP v PHP nie je povolená alebo nainštalovaná. Nie je možné pripojenie oddielov FTP. Požiadajte administrátora systému, nech ho nainštaluje.", "External Storage" => "Externé úložisko", -"Mount point" => "Prípojný bod", -"Backend" => "Backend", +"Folder name" => "Meno priečinka", +"External storage" => "Externé úložisko", "Configuration" => "Nastavenia", "Options" => "Možnosti", "Applicable" => "Aplikovateľné", -"Add mount point" => "Pridať prípojný bod", +"Add storage" => "Pridať úložisko", "None set" => "Žiadne nastavené", -"All Users" => "Všetci užívatelia", +"All Users" => "Všetci používatelia", "Groups" => "Skupiny", -"Users" => "Užívatelia", +"Users" => "Používatelia", "Delete" => "Odstrániť", "Enable User External Storage" => "Povoliť externé úložisko", -"Allow users to mount their own external storage" => "Povoliť užívateľom pripojiť ich vlastné externé úložisko", +"Allow users to mount their own external storage" => "Povoliť používateľom pripojiť ich vlastné externé úložisko", "SSL root certificates" => "Koreňové SSL certifikáty", "Import Root Certificate" => "Importovať koreňový certifikát" ); diff --git a/apps/files_external/l10n/sl.php b/apps/files_external/l10n/sl.php index f0db66ded963e1cad1c76791e820d27983159283..5614c93585adb26bdbb0e87dc76904470d0a2751 100644 --- a/apps/files_external/l10n/sl.php +++ b/apps/files_external/l10n/sl.php @@ -2,18 +2,15 @@ "Access granted" => "Dostop je odobren", "Error configuring Dropbox storage" => "Napaka nastavljanja shrambe Dropbox", "Grant access" => "Odobri dostop", -"Fill out all required fields" => "Zapolni vsa zahtevana polja", "Please provide a valid Dropbox app key and secret." => "Vpišite veljaven ključ programa in kodo za Dropbox", "Error configuring Google Drive storage" => "Napaka nastavljanja shrambe Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Opozorilo: \"smbclient\" ni nameščen. Priklapljanje CIFS/SMB pogonov ni mogoče. Prosimo, prosite vašega skrbnika, če ga namesti.", "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." => "Opozorilo: FTP podpora v PHP ni omogočena ali nameščena. Priklapljanje FTP pogonov ni mogoče. Prosimo, prosite vašega skrbnika, če jo namesti ali omogoči.", "External Storage" => "Zunanja podatkovna shramba", -"Mount point" => "Priklopna točka", -"Backend" => "Zaledje", +"Folder name" => "Ime mape", "Configuration" => "Nastavitve", "Options" => "Možnosti", "Applicable" => "Se uporablja", -"Add mount point" => "Dodaj priklopno točko", "None set" => "Ni nastavljeno", "All Users" => "Vsi uporabniki", "Groups" => "Skupine", diff --git a/apps/files_external/l10n/sv.php b/apps/files_external/l10n/sv.php index 0d42a1f4682c163c7f8c010016ba230e33a6d623..db0ed7a6af34e04d353c7ebcd8e5e3088a5d217b 100644 --- a/apps/files_external/l10n/sv.php +++ b/apps/files_external/l10n/sv.php @@ -2,18 +2,15 @@ "Access granted" => "Åtkomst beviljad", "Error configuring Dropbox storage" => "Fel vid konfigurering av Dropbox", "Grant access" => "Bevilja åtkomst", -"Fill out all required fields" => "Fyll i alla obligatoriska fält", "Please provide a valid Dropbox app key and secret." => "Ange en giltig Dropbox nyckel och hemlighet.", "Error configuring Google Drive storage" => "Fel vid konfigurering av Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varning: \"smb-klienten\" är inte installerad. Montering av CIFS/SMB delningar är inte möjligt. Kontakta din systemadministratör för att få den installerad.", "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." => "Varning: Stöd för FTP i PHP är inte aktiverat eller installerat. Montering av FTP-delningar är inte möjligt. Kontakta din systemadministratör för att få det installerat.", "External Storage" => "Extern lagring", -"Mount point" => "Monteringspunkt", -"Backend" => "Källa", +"Folder name" => "Mappnamn", "Configuration" => "Konfiguration", "Options" => "Alternativ", "Applicable" => "Tillämplig", -"Add mount point" => "Lägg till monteringspunkt", "None set" => "Ingen angiven", "All Users" => "Alla användare", "Groups" => "Grupper", diff --git a/apps/files_external/l10n/ta_LK.php b/apps/files_external/l10n/ta_LK.php index 1e01b22efa00a75d9b7e220547dc82a514d11794..cee3b6edb8a2b669287a99296b232c942e17c651 100644 --- a/apps/files_external/l10n/ta_LK.php +++ b/apps/files_external/l10n/ta_LK.php @@ -2,16 +2,13 @@ "Access granted" => "அனுமதி வழங்கப்பட்டது", "Error configuring Dropbox storage" => "Dropbox சேமிப்பை தகவமைப்பதில் வழு", "Grant access" => "அனுமதியை வழங்கல்", -"Fill out all required fields" => "தேவையான எல்லா புலங்களையும் நிரப்புக", "Please provide a valid Dropbox app key and secret." => "தயவுசெய்து ஒரு செல்லுபடியான Dropbox செயலி சாவி மற்றும் இரகசியத்தை வழங்குக. ", "Error configuring Google Drive storage" => "Google இயக்க சேமிப்பகத்தை தகமைப்பதில் வழு", "External Storage" => "வெளி சேமிப்பு", -"Mount point" => "ஏற்றப்புள்ளி", -"Backend" => "பின்நிலை", +"Folder name" => "கோப்புறை பெயர்", "Configuration" => "தகவமைப்பு", "Options" => "தெரிவுகள்", "Applicable" => "பயன்படத்தக்க", -"Add mount point" => "ஏற்றப்புள்ளியை சேர்க்க", "None set" => "தொகுப்பில்லா", "All Users" => "பயனாளர்கள் எல்லாம்", "Groups" => "குழுக்கள்", diff --git a/apps/files_external/l10n/th_TH.php b/apps/files_external/l10n/th_TH.php index 870995c8e7a111b29725c696f7b8a7e62531c4c2..2f733eab9e8f4efca77ffada056b5a28167bb900 100644 --- a/apps/files_external/l10n/th_TH.php +++ b/apps/files_external/l10n/th_TH.php @@ -2,18 +2,15 @@ "Access granted" => "การเข้าถึงได้รับอนุญาตแล้ว", "Error configuring Dropbox storage" => "เกิดข้อผิดพลาดในการกำหนดค่าพื้นที่จัดเก็บข้อมูล Dropbox", "Grant access" => "อนุญาตให้เข้าถึงได้", -"Fill out all required fields" => "กรอกข้อมูลในช่องข้อมูลที่จำเป็นต้องกรอกทั้งหมด", "Please provide a valid Dropbox app key and secret." => "กรุณากรอกรหัส app key ของ Dropbox และรหัสลับ", "Error configuring Google Drive storage" => "เกิดข้อผิดพลาดในการกำหนดค่าการจัดเก็บข้อมูลในพื้นที่ของ Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "คำเตือน: \"smbclient\" ยังไม่ได้ถูกติดตั้ง. การชี้ CIFS/SMB เพื่อแชร์ข้อมูลไม่สามารถกระทำได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง.", "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." => "คำเตือน: การสนับสนุนการใช้งาน FTP ในภาษา PHP ยังไม่ได้ถูกเปิดใช้งานหรือถูกติดตั้ง. การชี้ FTP เพื่อแชร์ข้อมูลไม่สามารถดำเนินการได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง", "External Storage" => "พื้นทีจัดเก็บข้อมูลจากภายนอก", -"Mount point" => "จุดชี้ตำแหน่ง", -"Backend" => "ด้านหลังระบบ", +"Folder name" => "ชื่อโฟลเดอร์", "Configuration" => "การกำหนดค่า", "Options" => "ตัวเลือก", "Applicable" => "สามารถใช้งานได้", -"Add mount point" => "เพิ่มจุดชี้ตำแหน่ง", "None set" => "ยังไม่มีการกำหนด", "All Users" => "ผู้ใช้งานทั้งหมด", "Groups" => "กลุ่ม", diff --git a/apps/files_external/l10n/tr.php b/apps/files_external/l10n/tr.php index e9a045aab572b6749197bd27e2093387ad403181..cddb2b35e03e4ca2d1311a9d5c65d33f39a9a0e0 100644 --- a/apps/files_external/l10n/tr.php +++ b/apps/files_external/l10n/tr.php @@ -1,16 +1,19 @@ "Giriş kabul edildi", +"Grant access" => "Erişim sağlandı", +"Please provide a valid Dropbox app key and secret." => "Lütfen Dropbox app key ve secret temin ediniz", "External Storage" => "Harici Depolama", -"Mount point" => "Bağlama Noktası", -"Backend" => "Yönetici", +"Folder name" => "Dizin ismi", "Configuration" => "Yapılandırma", "Options" => "Seçenekler", "Applicable" => "Uygulanabilir", -"Add mount point" => "Bağlama noktası ekle", "None set" => "Hiçbiri", "All Users" => "Tüm Kullanıcılar", "Groups" => "Gruplar", "Users" => "Kullanıcılar", "Delete" => "Sil", +"Enable User External Storage" => "Kullanıcılar için Harici Depolamayı Etkinleştir", +"Allow users to mount their own external storage" => "Kullanıcıların kendi harici depolamalarını bağlamalarına izin ver", "SSL root certificates" => "SSL kök sertifikaları", "Import Root Certificate" => "Kök Sertifikalarını İçe Aktar" ); diff --git a/apps/files_external/l10n/uk.php b/apps/files_external/l10n/uk.php index 56169171f64ccf64611ceea23669cedf66342ec9..f83c1a7eb6ac939f1d0caacd69e1186d5f535668 100644 --- a/apps/files_external/l10n/uk.php +++ b/apps/files_external/l10n/uk.php @@ -2,18 +2,15 @@ "Access granted" => "Доступ дозволено", "Error configuring Dropbox storage" => "Помилка при налаштуванні сховища Dropbox", "Grant access" => "Дозволити доступ", -"Fill out all required fields" => "Заповніть всі обов'язкові поля", "Please provide a valid Dropbox app key and secret." => "Будь ласка, надайте дійсний ключ та пароль Dropbox.", "Error configuring Google Drive storage" => "Помилка при налаштуванні сховища Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Попередження: Клієнт \"smbclient\" не встановлено. Під'єднанатися до CIFS/SMB тек неможливо. Попрохайте системного адміністратора встановити його.", "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." => "Попередження: Підтримка FTP в PHP не увімкнута чи не встановлена. Під'єднанатися до FTP тек неможливо. Попрохайте системного адміністратора встановити її.", "External Storage" => "Зовнішні сховища", -"Mount point" => "Точка монтування", -"Backend" => "Backend", +"Folder name" => "Ім'я теки", "Configuration" => "Налаштування", "Options" => "Опції", "Applicable" => "Придатний", -"Add mount point" => "Додати точку монтування", "None set" => "Не встановлено", "All Users" => "Усі користувачі", "Groups" => "Групи", diff --git a/apps/files_external/l10n/ur_PK.php b/apps/files_external/l10n/ur_PK.php new file mode 100644 index 0000000000000000000000000000000000000000..278357b4d68c5ec2cb0ed08de2979f0c75aeacb1 --- /dev/null +++ b/apps/files_external/l10n/ur_PK.php @@ -0,0 +1,3 @@ + "یوزرز" +); diff --git a/apps/files_external/l10n/vi.php b/apps/files_external/l10n/vi.php index 0160692cb65864fc8441d09446f52c2da5692a36..84f31e88924a8fafd610a0e98f68077b778406ad 100644 --- a/apps/files_external/l10n/vi.php +++ b/apps/files_external/l10n/vi.php @@ -2,16 +2,15 @@ "Access granted" => "Đã cấp quyền truy cập", "Error configuring Dropbox storage" => "Lỗi cấu hình lưu trữ Dropbox ", "Grant access" => "Cấp quyền truy cập", -"Fill out all required fields" => "Điền vào tất cả các trường bắt buộc", "Please provide a valid Dropbox app key and secret." => "Xin vui lòng cung cấp một ứng dụng Dropbox hợp lệ và mã bí mật.", "Error configuring Google Drive storage" => "Lỗi cấu hình lưu trữ Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Cảnh báo: \"smbclient\" chưa được cài đặt. Mount CIFS/SMB shares là không thể thực hiện được. Hãy hỏi người quản trị hệ thống để cài đặt nó.", +"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." => "Cảnh báo: FTP trong PHP chưa được cài đặt hoặc chưa được mở. Mount FTP shares là không thể. Xin hãy yêu cầu quản trị hệ thống của bạn cài đặt nó.", "External Storage" => "Lưu trữ ngoài", -"Mount point" => "Điểm gắn", -"Backend" => "phụ trợ", +"Folder name" => "Tên thư mục", "Configuration" => "Cấu hình", "Options" => "Tùy chọn", "Applicable" => "Áp dụng", -"Add mount point" => "Thêm điểm lắp", "None set" => "không", "All Users" => "Tất cả người dùng", "Groups" => "Nhóm", diff --git a/apps/files_external/l10n/zh_CN.GB2312.php b/apps/files_external/l10n/zh_CN.GB2312.php index 47983d3d7d4b93841c7d00e1bc7ed8c4ab89b7fa..74b9e3cad8134419b79993227992135b41cf5e93 100644 --- a/apps/files_external/l10n/zh_CN.GB2312.php +++ b/apps/files_external/l10n/zh_CN.GB2312.php @@ -2,16 +2,13 @@ "Access granted" => "已授予权限", "Error configuring Dropbox storage" => "配置 Dropbox 存储出错", "Grant access" => "授予权限", -"Fill out all required fields" => "填充全部必填字段", "Please provide a valid Dropbox app key and secret." => "请提供一个有效的 Dropbox app key 和 secret。", "Error configuring Google Drive storage" => "配置 Google Drive 存储失败", "External Storage" => "外部存储", -"Mount point" => "挂载点", -"Backend" => "后端", +"Folder name" => "文件夹名", "Configuration" => "配置", "Options" => "选项", "Applicable" => "可应用", -"Add mount point" => "添加挂载点", "None set" => "未设置", "All Users" => "所有用户", "Groups" => "群组", diff --git a/apps/files_external/l10n/zh_CN.php b/apps/files_external/l10n/zh_CN.php index 1bb88564630c7e54900a5ad5c0ce8aa9be2c2b8f..1860b6f70d71506eb2b75e34d7b16f6e32b6c3b5 100644 --- a/apps/files_external/l10n/zh_CN.php +++ b/apps/files_external/l10n/zh_CN.php @@ -2,18 +2,15 @@ "Access granted" => "权限已授予。", "Error configuring Dropbox storage" => "配置Dropbox存储时出错", "Grant access" => "授权", -"Fill out all required fields" => "完成所有必填项", "Please provide a valid Dropbox app key and secret." => "请提供有效的Dropbox应用key和secret", "Error configuring Google Drive storage" => "配置Google Drive存储时出错", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "警告:“smbclient” 尚未安装。CIFS/SMB 分享挂载无法实现。请咨询系统管理员进行安装。", "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." => "警告:PHP中尚未启用或安装FTP。FTP 分享挂载无法实现。请咨询系统管理员进行安装。", "External Storage" => "外部存储", -"Mount point" => "挂载点", -"Backend" => "后端", +"Folder name" => "目录名称", "Configuration" => "配置", "Options" => "选项", "Applicable" => "适用的", -"Add mount point" => "增加挂载点", "None set" => "未设置", "All Users" => "所有用户", "Groups" => "组", diff --git a/apps/files_external/l10n/zh_TW.php b/apps/files_external/l10n/zh_TW.php index ab8c4caf24a480f2756b3370c9fb1edaca002a55..512a151a3c88b3c9756f725f832564419a0b2513 100644 --- a/apps/files_external/l10n/zh_TW.php +++ b/apps/files_external/l10n/zh_TW.php @@ -1,6 +1,6 @@ "外部儲存裝置", -"Mount point" => "掛載點", +"Folder name" => "資料夾名稱", "None set" => "尚未設定", "All Users" => "所有使用者", "Groups" => "群組", diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index 494885a1dd3f3d1d69a097730d8fc1a3ab40885f..7bcefd4176c94ac9da2e6a690c34a4a7b09cd98e 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -33,12 +33,16 @@ class AmazonS3 extends \OC\Files\Storage\Common { private static $tempFiles = array(); - // TODO options: storage class, encryption server side, encrypt before upload? + // TODO Update to new AWS SDK public function __construct($params) { - $this->id = 'amazon::' . $params['key'] . md5($params['secret']); - $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); - $this->bucket = $params['bucket']; + if (isset($params['key']) && isset($params['secret']) && isset($params['bucket'])) { + $this->id = 'amazon::' . $params['key'] . md5($params['secret']); + $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); + $this->bucket = $params['bucket']; + } else { + throw new \Exception(); + } } private function getObject($path) { @@ -229,11 +233,6 @@ class AmazonS3 extends \OC\Files\Storage\Common { return false; } - public function free_space($path) { - // Infinite? - return false; - } - public function touch($path, $mtime = null) { if (is_null($mtime)) { $mtime = time(); @@ -245,4 +244,12 @@ class AmazonS3 extends \OC\Files\Storage\Common { return $response->isOK(); } + public function test() { + $test = $this->s3->get_canonical_user_id(); + if (isset($test['id']) && $test['id'] != '') { + return true; + } + return false; + } + } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 6b0df21461baee69a745f68940475dd0c1d41f65..430269e03d9061f82bc98fa92ae6823251a33cf8 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -38,7 +38,7 @@ class OC_Mount_Config { * @return array */ public static function getBackends() { - + $backends['\OC\Files\Storage\Local']=array( 'backend' => 'Local', 'configuration' => array( @@ -77,7 +77,7 @@ class OC_Mount_Config { 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'); - + $backends['\OC\Files\Storage\SWIFT']=array( 'backend' => 'OpenStack Swift', 'configuration' => array( @@ -86,7 +86,7 @@ class OC_Mount_Config { 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')); - + if(OC_Mount_Config::checksmbclient()) $backends['\OC\Files\Storage\SMB']=array( 'backend' => 'SMB / CIFS', 'configuration' => array( @@ -95,7 +95,7 @@ class OC_Mount_Config { 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')); - + $backends['\OC\Files\Storage\DAV']=array( 'backend' => 'ownCloud / WebDAV', 'configuration' => array( @@ -105,6 +105,14 @@ class OC_Mount_Config { 'root' => '&Root', 'secure' => '!Secure https://')); + $backends['\OC\Files\Storage\SFTP']=array( + 'backend' => 'SFTP', + 'configuration' => array( + 'host' => 'URL', + 'user' => 'Username', + 'password' => '*Password', + 'root' => '&Root')); + return($backends); } @@ -135,7 +143,9 @@ class OC_Mount_Config { 'class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], - 'applicable' => array('groups' => array($group), 'users' => array())); + 'applicable' => array('groups' => array($group), 'users' => array()), + 'status' => self::getBackendStatus($mount['class'], $mount['options']) + ); } } } @@ -154,10 +164,13 @@ class OC_Mount_Config { $system[$mountPoint]['applicable']['users'] = array_merge($system[$mountPoint]['applicable']['users'], array($user)); } else { - $system[$mountPoint] = array('class' => $mount['class'], + $system[$mountPoint] = array( + 'class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], - 'applicable' => array('groups' => array(), 'users' => array($user))); + 'applicable' => array('groups' => array(), 'users' => array($user)), + 'status' => self::getBackendStatus($mount['class'], $mount['options']) + ); } } } @@ -182,14 +195,32 @@ class OC_Mount_Config { $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } // Remove '/uid/files/' from mount point - $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], - 'backend' => $backends[$mount['class']]['backend'], - 'configuration' => $mount['options']); + $personal[substr($mountPoint, strlen($uid) + 8)] = array( + 'class' => $mount['class'], + 'backend' => $backends[$mount['class']]['backend'], + 'configuration' => $mount['options'], + 'status' => self::getBackendStatus($mount['class'], $mount['options']) + ); } } return $personal; } + private static function getBackendStatus($class, $options) { + foreach ($options as &$option) { + $option = str_replace('$user', OCP\User::getUser(), $option); + } + if (class_exists($class)) { + try { + $storage = new $class($options); + return $storage->test(); + } catch (Exception $exception) { + return false; + } + } + return false; + } + /** * Add a mount point to the filesystem * @param string Mount point @@ -230,7 +261,7 @@ class OC_Mount_Config { $mountPoints[$mountType] = $mount; } self::writeData($isPersonal, $mountPoints); - return true; + return self::getBackendStatus($class, $classOptions); } /** @@ -271,13 +302,21 @@ class OC_Mount_Config { * @return array */ private static function readData($isPersonal) { + $parser = new \OC\ArrayParser(); if ($isPersonal) { - $file = OC_User::getHome(OCP\User::getUser()).'/mount.php'; + $phpFile = OC_User::getHome(OCP\User::getUser()).'/mount.php'; + $jsonFile = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { - $file = OC::$SERVERROOT.'/config/mount.php'; + $phpFile = OC::$SERVERROOT.'/config/mount.php'; + $jsonFile = OC::$SERVERROOT.'/config/mount.json'; } - if (is_file($file)) { - $mountPoints = include $file; + if (is_file($jsonFile)) { + $mountPoints = json_decode(file_get_contents($jsonFile), true); + if (is_array($mountPoints)) { + return $mountPoints; + } + } elseif (is_file($phpFile)) { + $mountPoints = $parser->parsePHP(file_get_contents($phpFile)); if (is_array($mountPoints)) { return $mountPoints; } @@ -292,35 +331,11 @@ class OC_Mount_Config { */ private static function writeData($isPersonal, $data) { if ($isPersonal) { - $file = OC_User::getHome(OCP\User::getUser()).'/mount.php'; + $file = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { - $file = OC::$SERVERROOT.'/config/mount.php'; - } - $content = " array (\n"; - foreach ($data[self::MOUNT_TYPE_GROUP] as $group => $mounts) { - $content .= "\t\t'".$group."' => array (\n"; - foreach ($mounts as $mountPoint => $mount) { - $content .= "\t\t\t'".addcslashes($mountPoint,"'")."' => ".str_replace("\n", '', var_export($mount, true)).", \n"; - - } - $content .= "\t\t),\n"; - } - $content .= "\t),\n"; - } - if (isset($data[self::MOUNT_TYPE_USER])) { - $content .= "\t'user' => array (\n"; - foreach ($data[self::MOUNT_TYPE_USER] as $user => $mounts) { - $content .= "\t\t'".$user."' => array (\n"; - foreach ($mounts as $mountPoint => $mount) { - $content .= "\t\t\t'".addcslashes($mountPoint,"'")."' => ".str_replace("\n", '', var_export($mount, true)).",\n"; - } - $content .= "\t\t),\n"; - } - $content .= "\t),\n"; + $file = OC::$SERVERROOT.'/config/mount.json'; } - $content .= ");\n?>"; + $content = json_encode($data); @file_put_contents($file, $content); } @@ -402,8 +417,12 @@ class OC_Mount_Config { public static function checkDependencies() { $l= new OC_L10N('files_external'); $txt=''; - if(!OC_Mount_Config::checksmbclient()) $txt.=$l->t('Warning: "smbclient" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it.').'
'; - if(!OC_Mount_Config::checkphpftp()) $txt.=$l->t('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.').'
'; + if(!OC_Mount_Config::checksmbclient()) { + $txt.=$l->t('Warning: "smbclient" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it.').'
'; + } + if(!OC_Mount_Config::checkphpftp()) { + $txt.=$l->t('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.').'
'; + } return($txt); } diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 11644e4a2c8e16519e69ca7041f23c35262a17c5..cb04e557f8a6b2b3c01a26e0a32330bd3c04cd65 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -40,8 +40,8 @@ class Dropbox extends \OC\Files\Storage\Common { && isset($params['token']) && isset($params['token_secret']) ) { - $this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $params['root']; - $this->root=isset($params['root'])?$params['root']:''; + $this->root = isset($params['root']) ? $params['root'] : ''; + $this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $this->root; $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); $oauth->setToken($params['token'], $params['token_secret']); $this->dropbox = new \Dropbox_API($oauth, 'dropbox'); diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 9a27b63323af79a9a4b51eed27c40ea16db1bff9..8a7375ebe38730a270ba74ea1f275543e100fffd 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -18,26 +18,31 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ private static $tempFiles=array(); public function __construct($params) { - $this->host=$params['host']; - $this->user=$params['user']; - $this->password=$params['password']; - if (isset($params['secure'])) { - if (is_string($params['secure'])) { - $this->secure = ($params['secure'] === 'true'); + if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { + $this->host=$params['host']; + $this->user=$params['user']; + $this->password=$params['password']; + if (isset($params['secure'])) { + if (is_string($params['secure'])) { + $this->secure = ($params['secure'] === 'true'); + } else { + $this->secure = (bool)$params['secure']; + } } else { - $this->secure = (bool)$params['secure']; + $this->secure = false; + } + $this->root=isset($params['root'])?$params['root']:'/'; + if ( ! $this->root || $this->root[0]!='/') { + $this->root='/'.$this->root; + } + //create the root folder if necessary + if ( ! $this->is_dir('')) { + $this->mkdir(''); } } else { - $this->secure = false; - } - $this->root=isset($params['root'])?$params['root']:'/'; - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; - } - //create the root folder if necesary - if ( ! $this->is_dir('')) { - $this->mkdir(''); + throw new \Exception(); } + } public function getId(){ @@ -68,7 +73,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ case 'ab': //these are supported by the wrapper $context = stream_context_create(array('ftp' => array('overwrite' => true))); - return fopen($this->constructUrl($path),$mode, false,$context); + return fopen($this->constructUrl($path), $mode, false, $context); case 'r+': case 'w+': case 'wb+': @@ -83,13 +88,13 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ } else { $ext=''; } - $tmpFile=OCP\Files::tmpFile($ext); + $tmpFile=\OCP\Files::tmpFile($ext); \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $this->getFile($path, $tmpFile); } self::$tempFiles[$tmpFile]=$path; - return fopen('close://'.$tmpFile,$mode); + return fopen('close://'.$tmpFile, $mode); } return false; } diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 7396c7e3f2795dfdaabf3faa22cf6a0904c5bcc1..ec7de3f35709dec0e879dbfd42d3e14f957a70d4 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -268,7 +268,7 @@ class Google extends \OC\Files\Storage\Common { $name .= '.'.$extension; } } - $files[] = $name; + $files[] = basename($name); // Cache entry for future use $this->entries[$name] = $entry; } @@ -596,4 +596,11 @@ class Google extends \OC\Files\Storage\Common { } + public function test() { + if ($this->free_space('')) { + return true; + } + return false; + } + } diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php new file mode 100644 index 0000000000000000000000000000000000000000..ede6c251fd9e18463590b04ff7ee72a725c5f536 --- /dev/null +++ b/apps/files_external/lib/sftp.php @@ -0,0 +1,288 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Files\Storage; + +set_include_path(get_include_path() . PATH_SEPARATOR . + \OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib'); +require 'Net/SFTP.php'; + +class SFTP extends \OC\Files\Storage\Common { + private $host; + private $user; + private $password; + private $root; + + private $client; + + private static $tempFiles = array(); + + public function __construct($params) { + $this->host = $params['host']; + $proto = strpos($this->host, '://'); + if ($proto != false) { + $this->host = substr($this->host, $proto+3); + } + $this->user = $params['user']; + $this->password = $params['password']; + $this->root = isset($params['root']) ? $this->cleanPath($params['root']) : '/'; + if ($this->root[0] != '/') $this->root = '/' . $this->root; + if (substr($this->root, -1, 1) != '/') $this->root .= '/'; + + $host_keys = $this->read_host_keys(); + + $this->client = new \Net_SFTP($this->host); + if (!$this->client->login($this->user, $this->password)) { + throw new \Exception('Login failed'); + } + + $current_host_key = $this->client->getServerPublicHostKey(); + + if (array_key_exists($this->host, $host_keys)) { + if ($host_keys[$this->host] != $current_host_key) { + throw new \Exception('Host public key does not match known key'); + } + } else { + $host_keys[$this->host] = $current_host_key; + $this->write_host_keys($host_keys); + } + + if(!$this->file_exists('')){ + $this->mkdir(''); + } + } + + public function test() { + if (!isset($params['host']) || !isset($params['user']) || !isset($params['password'])) { + throw new \Exception("Required parameters not set"); + } + } + + public function getId(){ + return 'sftp::' . $this->user . '@' . $this->host . '/' . $this->root; + } + + private function abs_path($path) { + return $this->root . $this->cleanPath($path); + } + + private function host_keys_path() { + try { + $storage_view = \OCP\Files::getStorage('files_external'); + if ($storage_view) { + return \OCP\Config::getSystemValue('datadirectory') . + $storage_view->getAbsolutePath('') . + 'ssh_host_keys'; + } + } catch (\Exception $e) { + } + return false; + } + + private function write_host_keys($keys) { + try { + $key_path = $this->host_keys_path(); + $fp = fopen($key_path, 'w'); + foreach ($keys as $host => $key) { + fwrite($fp, $host . '::' . $key . "\n"); + } + fclose($fp); + return true; + } catch (\Exception $e) { + return false; + } + } + + private function read_host_keys() { + try { + $key_path = $this->host_keys_path(); + if (file_exists($key_path)) { + $hosts = array(); + $keys = array(); + $lines = file($key_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if ($lines) { + foreach ($lines as $line) { + $host_key_arr = explode("::", $line, 2); + if (count($host_key_arr) == 2) { + $hosts[] = $host_key_arr[0]; + $keys[] = $host_key_arr[1]; + } + } + return array_combine($hosts, $keys); + } + } + } catch (\Exception $e) { + } + return array(); + } + + public function mkdir($path) { + try { + return $this->client->mkdir($this->abs_path($path)); + } catch (\Exception $e) { + return false; + } + } + + public function rmdir($path) { + try { + return $this->client->delete($this->abs_path($path), true); + } catch (\Exception $e) { + return false; + } + } + + public function opendir($path) { + try { + $list = $this->client->nlist($this->abs_path($path)); + + $id = md5('sftp:' . $path); + $dir_stream = array(); + foreach($list as $file) { + if ($file != '.' && $file != '..') { + $dir_stream[] = $file; + } + } + \OC\Files\Stream\Dir::register($id, $dir_stream); + return opendir('fakedir://' . $id); + } catch(\Exception $e) { + return false; + } + } + + public function filetype($path) { + try { + $stat = $this->client->stat($this->abs_path($path)); + if ($stat['type'] == NET_SFTP_TYPE_REGULAR) return 'file'; + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) return 'dir'; + } catch (\Exeption $e) { + } + return false; + } + + public function isReadable($path) { + return true; + } + + public function isUpdatable($path) { + return true; + } + + public function file_exists($path) { + try { + return $this->client->stat($this->abs_path($path)) === false ? false : true; + } catch (\Exception $e) { + return false; + } + } + + public function unlink($path) { + try { + return $this->client->delete($this->abs_path($path), true); + } catch (\Exception $e) { + return false; + } + } + + public function fopen($path, $mode) { + try { + $abs_path = $this->abs_path($path); + switch($mode) { + case 'r': + case 'rb': + if ( !$this->file_exists($path)) return false; + if (strrpos($path, '.')!==false) { + $ext=substr($path, strrpos($path, '.')); + } else { + $ext=''; + } + $tmp = \OC_Helper::tmpFile($ext); + $this->getFile($abs_path, $tmp); + return fopen($tmp, $mode); + + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.')!==false) { + $ext=substr($path, strrpos($path, '.')); + } else { + $ext=''; + } + $tmpFile=\OC_Helper::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); + if ($this->file_exists($path)) { + $this->getFile($abs_path, $tmpFile); + } + self::$tempFiles[$tmpFile]=$abs_path; + return fopen('close://'.$tmpFile, $mode); + } + } catch (\Exception $e) { + } + return false; + } + + public function writeBack($tmpFile) { + if (array_key_exists($tmpFile, self::$tempFiles)) { + $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); + unlink($tmpFile); + unset(self::$tempFiles[$tmpFile]); + } + } + + public function touch($path, $mtime=null) { + try { + if (!is_null($mtime)) return false; + if (!$this->file_exists($path)) { + $this->client->put($this->abs_path($path), ''); + } else { + return false; + } + } catch (\Exception $e) { + return false; + } + return true; + } + + public function getFile($path, $target) { + $this->client->get($path, $target); + } + + public function uploadFile($path, $target) { + $this->client->put($target, $path, NET_SFTP_LOCAL_FILE); + } + + public function rename($source, $target) { + try { + return $this->client->rename($this->abs_path($source), $this->abs_path($target)); + } catch (\Exception $e) { + return false; + } + } + + public function stat($path) { + try { + $stat = $this->client->stat($this->abs_path($path)); + + $mtime = $stat ? $stat['mtime'] : -1; + $size = $stat ? $stat['size'] : 0; + + return array('mtime' => $mtime, 'size' => $size, 'ctime' => -1); + } catch (\Exception $e) { + return false; + } + + } +} diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 96778b0b2e1c30a8ba79d829ac4970536f9af133..961efb1a50a43123a43d299296cbb30d305875c7 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -18,22 +18,26 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ private $share; public function __construct($params) { - $this->host=$params['host']; - $this->user=$params['user']; - $this->password=$params['password']; - $this->share=$params['share']; - $this->root=isset($params['root'])?$params['root']:'/'; - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; - } - if (substr($this->root, -1, 1)!='/') { - $this->root.='/'; - } - if ( ! $this->share || $this->share[0]!='/') { - $this->share='/'.$this->share; - } - if(substr($this->share, -1, 1)=='/') { - $this->share = substr($this->share,0,-1); + if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) { + $this->host=$params['host']; + $this->user=$params['user']; + $this->password=$params['password']; + $this->share=$params['share']; + $this->root=isset($params['root'])?$params['root']:'/'; + if ( ! $this->root || $this->root[0]!='/') { + $this->root='/'.$this->root; + } + if (substr($this->root, -1, 1)!='/') { + $this->root.='/'; + } + if ( ! $this->share || $this->share[0]!='/') { + $this->share='/'.$this->share; + } + if (substr($this->share, -1, 1)=='/') { + $this->share = substr($this->share, 0, -1); + } + } else { + throw new \Exception(); } } @@ -45,7 +49,10 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ if (substr($path, -1)=='/') { $path=substr($path, 0, -1); } - return 'smb://'.$this->user.':'.$this->password.'@'.$this->host.$this->share.$this->root.$path; + $path = urlencode($path); + $user = urlencode($this->user); + $pass = urlencode($this->password); + return 'smb://'.$user.':'.$pass.'@'.$this->host.$this->share.$this->root.$path; } public function stat($path) { @@ -59,11 +66,6 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ } } - public function filetype($path) { - // using opendir causes the same amount of requests and caches the content of the folder in one go - return (bool)@$this->opendir($path) ? 'dir' : 'file'; - } - /** * check if a file or folder has been updated since $time * @param string $path diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 7c3ddcf8a2c2220f67a4302a932d59df25b49d21..4685877f26b7ea0bb0d7266bfceb897afa08f809 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -12,7 +12,7 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ private $ready = false; protected function init(){ - if($this->ready){ + if($this->ready) { return; } $this->ready = true; @@ -71,39 +71,35 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ return $succes; } - public function fopen($path,$mode) { + public function fopen($path, $mode) { $this->init(); - return fopen($this->constructUrl($path),$mode); + return fopen($this->constructUrl($path), $mode); } - public function free_space($path) { - return 0; - } - - public function touch($path,$mtime=null) { + public function touch($path, $mtime=null) { $this->init(); if(is_null($mtime)) { - $fh = $this->fopen($path,'a'); - fwrite($fh,''); + $fh = $this->fopen($path, 'a'); + fwrite($fh, ''); fclose($fh); } else { return false;//not supported } } - public function getFile($path,$target) { + public function getFile($path, $target) { $this->init(); - return copy($this->constructUrl($path),$target); + return copy($this->constructUrl($path), $target); } - public function uploadFile($path,$target) { + public function uploadFile($path, $target) { $this->init(); - return copy($path,$this->constructUrl($target)); + return copy($path, $this->constructUrl($target)); } - public function rename($path1,$path2) { + public function rename($path1, $path2) { $this->init(); - return rename($this->constructUrl($path1),$this->constructUrl($path2)); + return rename($this->constructUrl($path1), $this->constructUrl($path2)); } public function stat($path) { diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index cbf2007052bdafa9009a63ce15c9de9f9b4db679..68c4b48f17c18ae41562bc9a3f44122517bb10ac 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -210,7 +210,7 @@ class SWIFT extends \OC\Files\Storage\Common{ return false; } else { $fh=fopen($tmpFile, 'a'); - fwrite($fh,$name . "\n"); + fwrite($fh, $name . "\n"); } } catch(\Exception $e) { file_put_contents($tmpFile, $name . "\n"); @@ -264,33 +264,37 @@ class SWIFT extends \OC\Files\Storage\Common{ private function getSubContainerFile($container) { try { return $container->get_object(self::SUBCONTAINER_FILE); - } catch(NoSuchObjectException $e) { + } catch(\NoSuchObjectException $e) { return $container->create_object(self::SUBCONTAINER_FILE); } } public function __construct($params) { - $this->token=$params['token']; - $this->host=$params['host']; - $this->user=$params['user']; - $this->root=isset($params['root'])?$params['root']:'/'; - if (isset($params['secure'])) { - if (is_string($params['secure'])) { - $this->secure = ($params['secure'] === 'true'); + if (isset($params['token']) && isset($params['host']) && isset($params['user'])) { + $this->token=$params['token']; + $this->host=$params['host']; + $this->user=$params['user']; + $this->root=isset($params['root'])?$params['root']:'/'; + if (isset($params['secure'])) { + if (is_string($params['secure'])) { + $this->secure = ($params['secure'] === 'true'); + } else { + $this->secure = (bool)$params['secure']; + } } else { - $this->secure = (bool)$params['secure']; + $this->secure = false; + } + if ( ! $this->root || $this->root[0]!='/') { + $this->root='/'.$this->root; } } else { - $this->secure = false; - } - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; + throw new \Exception(); } } private function init(){ - if($this->ready){ + if($this->ready) { return; } $this->ready = true; @@ -478,10 +482,6 @@ class SWIFT extends \OC\Files\Storage\Common{ } } - public function free_space($path) { - return 1024*1024*1024*8; - } - public function touch($path, $mtime=null) { $this->init(); $obj=$this->getObject($path); diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 2a953ac63f45756f11bbe1d55c96e0a195c49ed0..3ba7c48cd5742adcabbb94bc831364ee4c2f2ae2 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -23,42 +23,46 @@ class DAV extends \OC\Files\Storage\Common{ private static $tempFiles=array(); public function __construct($params) { - $host = $params['host']; - //remove leading http[s], will be generated in createBaseUri() - if (substr($host, 0, 8) == "https://") $host = substr($host, 8); - else if (substr($host, 0, 7) == "http://") $host = substr($host, 7); - $this->host=$host; - $this->user=$params['user']; - $this->password=$params['password']; - if (isset($params['secure'])) { - if (is_string($params['secure'])) { - $this->secure = ($params['secure'] === 'true'); + if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { + $host = $params['host']; + //remove leading http[s], will be generated in createBaseUri() + if (substr($host, 0, 8) == "https://") $host = substr($host, 8); + else if (substr($host, 0, 7) == "http://") $host = substr($host, 7); + $this->host=$host; + $this->user=$params['user']; + $this->password=$params['password']; + if (isset($params['secure'])) { + if (is_string($params['secure'])) { + $this->secure = ($params['secure'] === 'true'); + } else { + $this->secure = (bool)$params['secure']; + } } else { - $this->secure = (bool)$params['secure']; + $this->secure = false; + } + $this->root=isset($params['root'])?$params['root']:'/'; + if ( ! $this->root || $this->root[0]!='/') { + $this->root='/'.$this->root; + } + if (substr($this->root, -1, 1)!='/') { + $this->root.='/'; } } else { - $this->secure = false; - } - $this->root=isset($params['root'])?$params['root']:'/'; - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; - } - if (substr($this->root, -1, 1)!='/') { - $this->root.='/'; + throw new \Exception(); } } private function init(){ - if($this->ready){ + if($this->ready) { return; } $this->ready = true; - $settings = array( - 'baseUri' => $this->createBaseUri(), - 'userName' => $this->user, - 'password' => $this->password, - ); + $settings = array( + 'baseUri' => $this->createBaseUri(), + 'userName' => $this->user, + 'password' => $this->password, + ); $this->client = new \Sabre_DAV_Client($settings); @@ -69,7 +73,7 @@ class DAV extends \OC\Files\Storage\Common{ $this->client->addTrustedCertificates($certPath); } } - //create the root folder if necesary + //create the root folder if necessary $this->mkdir(''); } @@ -153,10 +157,10 @@ class DAV extends \OC\Files\Storage\Common{ public function unlink($path) { $this->init(); - return $this->simpleResponse('DELETE', $path, null ,204); + return $this->simpleResponse('DELETE', $path, null, 204); } - public function fopen($path,$mode) { + public function fopen($path, $mode) { $this->init(); $path=$this->cleanPath($path); switch($mode) { @@ -222,7 +226,7 @@ class DAV extends \OC\Files\Storage\Common{ return 0; } } catch(\Exception $e) { - return 0; + return \OC\Files\FREE_SPACE_UNKNOWN; } } @@ -235,13 +239,13 @@ class DAV extends \OC\Files\Storage\Common{ $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime)); } - public function getFile($path,$target) { + public function getFile($path, $target) { $this->init(); - $source=$this->fopen($path,'r'); - file_put_contents($target,$source); + $source=$this->fopen($path, 'r'); + file_put_contents($target, $source); } - public function uploadFile($path,$target) { + public function uploadFile($path, $target) { $this->init(); $source=fopen($path, 'r'); @@ -256,7 +260,7 @@ class DAV extends \OC\Files\Storage\Common{ curl_close ($curl); } - public function rename($path1,$path2) { + public function rename($path1, $path2) { $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); @@ -268,7 +272,7 @@ class DAV extends \OC\Files\Storage\Common{ } } - public function copy($path1,$path2) { + public function copy($path1, $path2) { $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); @@ -313,7 +317,7 @@ class DAV extends \OC\Files\Storage\Common{ } } - private function cleanPath($path) { + public function cleanPath($path) { if ( ! $path || $path[0]=='/') { return substr($path, 1); } else { @@ -321,7 +325,7 @@ class DAV extends \OC\Files\Storage\Common{ } } - private function simpleResponse($method,$path,$body,$expected) { + private function simpleResponse($method, $path, $body, $expected) { $path=$this->cleanPath($path); try { $response=$this->client->request($method, $path, $body); diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 268d1880232674137598cc8f32e6208d0c356c2a..90f5e159535c65d675c0a41cd4eb78396a249c6e 100755 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -26,9 +26,9 @@ $backends = OC_Mount_Config::getBackends(); // Remove local storage unset($backends['\OC\Files\Storage\Local']); $tmpl = new OCP\Template('files_external', 'settings'); -$tmpl->assign('isAdminPage', false, false); +$tmpl->assign('isAdminPage', false); $tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints()); $tmpl->assign('certs', OC_Mount_Config::getCertificates()); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies(), false); +$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); $tmpl->assign('backends', $backends); return $tmpl->fetchPage(); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index cd0bfa99585a178ecad9856ecc4e0fe8f9f0ccd7..1a39affe2e6606e0d9cf439a8eaff63b139d578d 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -27,11 +27,11 @@ OCP\Util::addscript('3rdparty', 'chosen/chosen.jquery.min'); OCP\Util::addStyle('files_external', 'settings'); OCP\Util::addStyle('3rdparty', 'chosen/chosen'); $tmpl = new OCP\Template('files_external', 'settings'); -$tmpl->assign('isAdminPage', true, false); +$tmpl->assign('isAdminPage', true); $tmpl->assign('mounts', OC_Mount_Config::getSystemMountPoints()); $tmpl->assign('backends', OC_Mount_Config::getBackends()); $tmpl->assign('groups', OC_Group::getGroups()); $tmpl->assign('users', OCP\User::getUsers()); -$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies(), false); +$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies()); $tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes')); return $tmpl->fetchPage(); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 78ca1c87feefa3f1b22648bc4484fbfbb7ad2c6b..86492699fc2d3ffe0324c9b05ff3f21da9078215 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -1,38 +1,44 @@
- t('External Storage'); ?> - '')) echo ''.$_['dependencies'].''; ?> - '> + t('External Storage')); ?> + '')) print_unescaped(''.$_['dependencies'].''); ?> +
'> - - - - - '.$l->t('Applicable').''; ?> + + + + + + '.$l->t('Applicable').''); ?> array())); ?> $mount): ?> - > + > + + value="" + placeholder="t('Folder name')); ?>" /> + data-class=""> @@ -77,27 +82,27 @@ + src="" /> @@ -120,9 +125,9 @@ /> -
- t('Allow users to mount their own external storage'); ?> + value="1" /> +
+ t('Allow users to mount their own external storage')); ?> @@ -131,26 +136,27 @@ + action="">
- t('SSL root certificates');?> -
t('Mount point'); ?>t('Backend'); ?>t('Configuration'); ?>t('Folder name')); ?>t('External storage')); ?>t('Configuration')); ?> 
+ + + + - '> + style="display:none;">t('Add storage')); ?> $backend): ?> - + @@ -41,35 +47,34 @@ + data-parameter="" + value="" + placeholder="" /> + /> + data-parameter="" + value="" + placeholder="" /> + data-parameter="" + value="" /> + data-parameter="" + value="" + placeholder="" /> - - + + ' + print_unescaped(json_encode($mount['applicable']['groups'])); ?>' data-applicable-users=''> + print_unescaped(json_encode($mount['applicable']['users'])); ?>'> @@ -105,10 +110,10 @@ class="remove" style="visibility:hidden;" - ><?php echo $l->t('Delete'); ?>><?php p($l->t('Delete')); ?>
'> + t('SSL root certificates'));?> +
'> - - + + + src="" />
class="remove" style="visibility:hidden;" - ><?php echo $l->t('Delete'); ?>><?php p($l->t('Delete')); ?>
+ - +
diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index 65127175ad7dea93a567cf9c8bc4e893910844d8..1d4f30c713dd00e098172da16be1a81453f0754f 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -8,7 +8,7 @@ return array( 'root'=>'/test', ), 'webdav'=>array( - 'run'=>true, + 'run'=>false, 'host'=>'localhost', 'user'=>'test', 'password'=>'test', @@ -30,7 +30,7 @@ return array( 'root'=>'/', ), 'smb'=>array( - 'run'=>true, + 'run'=>false, 'user'=>'test', 'password'=>'test', 'host'=>'localhost', @@ -51,5 +51,12 @@ return array( 'app_secret' => '', 'token' => '', 'token_secret' => '' + ), + 'sftp' => array ( + 'run'=>false, + 'host'=>'localhost', + 'user'=>'test', + 'password'=>'test', + 'root'=>'/test' ) ); diff --git a/apps/files_external/tests/sftp.php b/apps/files_external/tests/sftp.php new file mode 100644 index 0000000000000000000000000000000000000000..16964e208781c34b4d8ecce4d90230496b51c2fe --- /dev/null +++ b/apps/files_external/tests/sftp.php @@ -0,0 +1,43 @@ +. + */ + +namespace Test\Files\Storage; + +class SFTP extends Storage { + private $config; + + public function setUp() { + $id = uniqid(); + $this->config = include('files_external/tests/config.php'); + if ( ! is_array($this->config) or ! isset($this->config['sftp']) or ! $this->config['sftp']['run']) { + $this->markTestSkipped('SFTP backend not configured'); + } + $this->config['sftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in + $this->instance = new \OC\Files\Storage\SFTP($this->config['sftp']); + } + + public function tearDown() { + if ($this->instance) { + $this->instance->rmdir('/'); + } + } +} \ No newline at end of file diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index 1f24a4dde83e50eb2a7f916fd0be89fbc5a99a04..9a199281a76f0eb072f372c9fe057a4e09aa6e13 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -5,7 +5,7 @@ File sharing between users AGPL Michael Gapczynski - 4.91 + 4.93 true diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index 1d22b32b503100f337393356f7d7d3aeacf4d5b8..48e41e9304889fe1c1356ded114ffb6b08f299c7 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -52,7 +52,10 @@ if (version_compare($installedVersion, '0.3', '<')) { } catch (Exception $e) { $update_error = true; - OCP\Util::writeLog('files_sharing', 'Upgrade Routine: Skipping sharing "'.$row['source'].'" to "'.$shareWith.'" (error is "'.$e->getMessage().'")', OCP\Util::WARN); + OCP\Util::writeLog('files_sharing', + 'Upgrade Routine: Skipping sharing "'.$row['source'].'" to "'.$shareWith + .'" (error is "'.$e->getMessage().'")', + OCP\Util::WARN); } OC_Util::tearDownFS(); } diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index 492014344f7318f6bba9f77e4ffb6c686cdd6199..13298f113f8f767f707374bffe612f37aa455624 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -34,9 +34,8 @@ body { background:#eee; border-bottom:1px solid #f8f8f8; min-height:30em; - padding-top:2em; text-align:center; - margin:50px auto; + margin:45px auto; } #noPreview { @@ -60,6 +59,7 @@ p.info a { #imgframe { height:75%; padding-bottom:2em; + padding-top:2em; width:80%; margin:0 auto; } @@ -67,4 +67,9 @@ p.info a { #imgframe img { max-height:100%; max-width:100%; -} \ No newline at end of file +} + +thead{ + background-color: white; + padding-left:0 !important; /* fixes multiselect bar offset on shared page */ +} diff --git a/apps/files_sharing/l10n/ka.php b/apps/files_sharing/l10n/ka.php new file mode 100644 index 0000000000000000000000000000000000000000..0270d5d6f8c6a5516cb1e294839a69882573385e --- /dev/null +++ b/apps/files_sharing/l10n/ka.php @@ -0,0 +1,4 @@ + "პაროლი", +"Download" => "გადმოწერა" +); diff --git a/apps/files_sharing/l10n/my_MM.php b/apps/files_sharing/l10n/my_MM.php new file mode 100644 index 0000000000000000000000000000000000000000..dc7ec17e9c58249113282639cac8aa27f1c2b42a --- /dev/null +++ b/apps/files_sharing/l10n/my_MM.php @@ -0,0 +1,6 @@ + "စကားဝှက်", +"Submit" => "ထည့်သွင်းမည်", +"Download" => "ဒေါင်းလုတ်", +"web services under your control" => "သင်၏ထိန်းချုပ်မှု့အောက်တွင်ရှိသော Web services" +); diff --git a/apps/files_sharing/l10n/ur_PK.php b/apps/files_sharing/l10n/ur_PK.php new file mode 100644 index 0000000000000000000000000000000000000000..f68b714350f8a7b318978c7110586c8c58bdae4d --- /dev/null +++ b/apps/files_sharing/l10n/ur_PK.php @@ -0,0 +1,4 @@ + "پاسورڈ", +"web services under your control" => "آپ کے اختیار میں ویب سروسیز" +); diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 9655e44787517b928af7f7101e5d92ca0a6d794c..fb0f6c7b5a6bd3b4fb4f8cad64b4f7e900471bac 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -71,8 +71,9 @@ class Shared_Cache extends Cache { } } else { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,' + .' `size`, `mtime`, `encrypted`' + .' FROM `*PREFIX*filecache` WHERE `fileid` = ?'); $result = $query->execute(array($file)); $data = $result->fetchRow(); $data['fileid'] = (int)$data['fileid']; diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 2b068ff93502f45ed68151b472ce9f7ca4826257..72c1ec96c46a1fe4861f37cdb482fd6853be0a99 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -33,7 +33,8 @@ class Shared_Permissions extends Permissions { if ($fileId == -1) { return \OCP\PERMISSION_READ; } - $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); + $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, + null, true); if ($source) { return $source['permissions']; } else { diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 6d3c55a008f0bdaf85fc14674f6a3f55f7d01f51..0aeb763d89ad21d0c99d44f22f1940ef32501158 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -72,7 +72,11 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { public function formatItems($items, $format, $parameters = null) { if ($format == self::FORMAT_SHARED_STORAGE) { // Only 1 item should come through for this format call - return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions'], 'uid_owner' => $items[key($items)]['uid_owner']); + return array( + 'path' => $items[key($items)]['path'], + 'permissions' => $items[key($items)]['permissions'], + 'uid_owner' => $items[key($items)]['uid_owner'] + ); } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { $files = array(); foreach ($items as $item) { @@ -99,7 +103,13 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { } $size += (int)$item['size']; } - return array('fileid' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size); + return array( + 'fileid' => -1, + 'name' => 'Shared', + 'mtime' => $mtime, + 'mimetype' => 'httpd/unix-directory', + 'size' => $size + ); } else if ($format == self::FORMAT_OPENDIR) { $files = array(); foreach ($items as $item) { diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index 11c8c6b1e8066bd7d84dfbee563a5982607d3486..4426beec6368fa34e17b60f3d114ca6a5b47e2e6 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -33,7 +33,8 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share } while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; - $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache` WHERE `parent` IN ('.$parents.')'); + $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache`' + .' WHERE `parent` IN ('.$parents.')'); $result = $query->execute(); $parents = array(); while ($file = $result->fetchRow()) { @@ -47,4 +48,4 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share return $children; } -} \ No newline at end of file +} diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index ea28ca69b9355cf028f53cd856b406e95747ccc8..5a9864b64bae035873c12cef62d7f8758069883a 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -240,7 +240,8 @@ class Shared extends \OC\Files\Storage\Common { public function file_put_contents($path, $data) { if ($source = $this->getSourcePath($path)) { // Check if permission is granted - if (($this->file_exists($path) && !$this->isUpdatable($path)) || ($this->is_dir($path) && !$this->isCreatable($path))) { + if (($this->file_exists($path) && !$this->isUpdatable($path)) + || ($this->is_dir($path) && !$this->isCreatable($path))) { return false; } $info = array( @@ -314,7 +315,8 @@ class Shared extends \OC\Files\Storage\Common { if ($this->isCreatable(dirname($path2))) { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); - return \OC_Helper::streamCopy($source, $target); + list ($count, $result) = \OC_Helper::streamCopy($source, $target); + return $result; } return false; } @@ -390,9 +392,12 @@ class Shared extends \OC\Files\Storage\Common { } public static function setup($options) { - if (\OCP\Share::getItemsSharedWith('file')) { + if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] + || \OCP\Share::getItemsSharedWith('file')) { $user_dir = $options['user_dir']; - \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', + array('sharedFolder' => '/Shared'), + $user_dir.'/Shared/'); } } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index a3e0ec192afab690188b151ffdbb81fa9fa9c2e1..e345b91e293c0e2244c8e3a91708b60984e95280 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -1,67 +1,18 @@ execute(array($_GET['token']))->fetchOne(); - if (isset($filepath)) { - $rootView = new \OC\Files\View(''); - $info = $rootView->getFileInfo($filepath, ''); - if (strtolower($info['mimetype']) == 'httpd/unix-directory') { - $_GET['dir'] = $filepath; - } else { - $_GET['file'] = $filepath; - } - \OCP\Util::writeLog('files_sharing', 'You have files that are shared by link originating from ownCloud 4.0.' - .' Redistribute the new links, because backwards compatibility will be removed in ownCloud 5.', - \OCP\Util::WARN); - } -} - -function getID($path) { - // use the share table from the db to find the item source if the file was reshared because shared files - //are not stored in the file cache. - if (substr(\OC\Files\Filesystem::getMountPoint($path), -7, 6) == "Shared") { - $path_parts = explode('/', $path, 5); - $user = $path_parts[1]; - $intPath = '/'.$path_parts[4]; - $query = \OC_DB::prepare('SELECT `item_source`' - .' FROM `*PREFIX*share`' - .' WHERE `uid_owner` = ?' - .' AND `file_target` = ? '); - $result = $query->execute(array($user, $intPath)); - $row = $result->fetchRow(); - $fileSource = $row['item_source']; +function fileCmp($a, $b) { + if ($a['type'] == 'dir' and $b['type'] != 'dir') { + return -1; + } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { + return 1; } else { - $rootView = new \OC\Files\View(''); - $meta = $rootView->getFileInfo($path); - $fileSource = $meta['fileid']; + return strnatcasecmp($a['name'], $b['name']); } - - return $fileSource; -} - -// Enf of backward compatibility - -/** - * lookup file path and owner by fetching it from the fscache - * needed because OC_FileCache::getPath($id, $user) already requires the user - * @param int $id - * @return array - */ -function getPathAndUser($id) { - $query = \OC_DB::prepare('SELECT `user`, `path` FROM `*PREFIX*fscache` WHERE `id` = ?'); - $result = $query->execute(array($id)); - $row = $result->fetchRow(); - return $row; } - if (isset($_GET['t'])) { $token = $_GET['t']; $linkItem = OCP\Share::getShareByToken($token); @@ -70,61 +21,30 @@ if (isset($_GET['t'])) { $type = $linkItem['item_type']; $fileSource = $linkItem['file_source']; $shareOwner = $linkItem['uid_owner']; - - if (OCP\User::userExists($shareOwner) && $fileSource != -1 ) { - - $pathAndUser = getPathAndUser($linkItem['file_source']); - $fileOwner = $pathAndUser['user']; - - //if this is a reshare check the file owner also exists - if ($shareOwner != $fileOwner && ! OCP\User::userExists($fileOwner)) { - OCP\Util::writeLog('share', 'original file owner '.$fileOwner - .' does not exist for share '.$linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - - //mount filesystem of file owner - OC_Util::setupFS($fileOwner); - } - } -} else { - if (isset($_GET['file']) || isset($_GET['dir'])) { - OCP\Util::writeLog('share', 'Missing token, trying fallback file/dir links', \OCP\Util::DEBUG); - if (isset($_GET['dir'])) { - $type = 'folder'; - $path = $_GET['dir']; - if (strlen($path) > 1 and substr($path, -1, 1) === '/') { - $path = substr($path, 0, -1); + $fileOwner = null; + $path = null; + if (isset($linkItem['parent'])) { + $parent = $linkItem['parent']; + while (isset($parent)) { + $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); + $item = $query->execute(array($parent))->fetchRow(); + if (isset($item['parent'])) { + $parent = $item['parent']; + } else { + $fileOwner = $item['uid_owner']; + break; + } } - $baseDir = $path; - $dir = $baseDir; } else { - $type = 'file'; - $path = $_GET['file']; - if (strlen($path) > 1 and substr($path, -1, 1) === '/') { - $path = substr($path, 0, -1); - } + $fileOwner = $shareOwner; } - $shareOwner = substr($path, 1, strpos($path, '/', 1) - 1); - - if (OCP\User::userExists($shareOwner)) { - OC_Util::setupFS($shareOwner); - $fileSource = getId($path); - if ($fileSource != -1) { - $linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $shareOwner); - $pathAndUser['path'] = $path; - $path_parts = explode('/', $path, 5); - $pathAndUser['user'] = $path_parts[1]; - $fileOwner = $path_parts[1]; - } + if (isset($fileOwner)) { + OC_Util::setupFS($fileOwner); + $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); } } } - -if ($linkItem) { +if (isset($path)) { if (!isset($linkItem['item_type'])) { OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); @@ -181,36 +101,23 @@ if ($linkItem) { } } } - $basePath = substr($pathAndUser['path'], strlen('/' . $fileOwner . '/files')); - $path = $basePath; - if (isset($_GET['path'])) { - $path .= $_GET['path']; - } - if (!$path || !\OC\Files\Filesystem::isValidPath($path) || !\OC\Files\Filesystem::file_exists($path)) { - OCP\Util::writeLog('share', 'Invalid path ' . $path . ' for share id ' . $linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); + $basePath = $path; + if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($basePath . $_GET['path'])) { + $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']); + $path .= $getPath; + } else { + $getPath = ''; } $dir = dirname($path); $file = basename($path); // Download the file if (isset($_GET['download'])) { - if (isset($_GET['path']) && $_GET['path'] !== '') { - if (isset($_GET['files'])) { // download selected files - OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else { - if (isset($_GET['path']) && $_GET['path'] != '') { // download a file from a shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else { // download the whole shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } - } - } else { // download a single shared file + if (isset($_GET['files'])) { // download selected files + OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } else { OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); } - + exit(); } else { OCP\Util::addStyle('files_sharing', 'public'); OCP\Util::addScript('files_sharing', 'public'); @@ -218,211 +125,89 @@ if ($linkItem) { $tmpl = new OCP\Template('files_sharing', 'public', 'base'); $tmpl->assign('uidOwner', $shareOwner); $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner)); - $tmpl->assign('dir', $dir); $tmpl->assign('filename', $file); $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - if (isset($_GET['path'])) { - $getPath = $_GET['path']; - } else { - $getPath = ''; - } - // + $tmpl->assign('fileTarget', basename($linkItem['file_target'])); $urlLinkIdentifiers= (isset($token)?'&t='.$token:'') .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') .(isset($_GET['file'])?'&file='.$_GET['file']:''); // Show file list if (\OC\Files\Filesystem::is_dir($path)) { + $tmpl->assign('dir', $getPath); + OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'files'); OCP\Util::addScript('files', 'filelist'); OCP\Util::addscript('files', 'keyboardshortcuts'); $files = array(); $rootLength = strlen($basePath) + 1; - foreach (OC_Files::getDirectoryContent($path) as $i) { + foreach (\OC\Files\Filesystem::getDirectoryContent($path) as $i) { $i['date'] = OCP\Util::formatDate($i['mtime']); if ($i['type'] == 'file') { $fileinfo = pathinfo($i['name']); $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; - } - $i['directory'] = '/' . substr($i['directory'], $rootLength); - if ($i['directory'] == '/') { - $i['directory'] = ''; + if (!empty($fileinfo['extension'])) { + $i['extension'] = '.' . $fileinfo['extension']; + } else { + $i['extension'] = ''; + } } + $i['directory'] = $getPath; $i['permissions'] = OCP\PERMISSION_READ; $files[] = $i; } + usort($files, "fileCmp"); + // Make breadcrumb $breadcrumb = array(); $pathtohere = ''; - - //add base breadcrumb - $breadcrumb[] = array('dir' => '/', 'name' => basename($basePath)); - - //add subdir breadcrumbs - foreach (explode('/', urldecode($getPath)) as $i) { + foreach (explode('/', $getPath) as $i) { if ($i != '') { $pathtohere .= '/' . $i; $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); - $path = $linkItem['path']; - if (isset($_GET['path'])) { - $path .= $_GET['path']; - $dir .= $_GET['path']; - if (!\OC\Files\Filesystem::file_exists($path)) { - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - } - - $list = new OCP\Template('files', 'part.list', ''); - $list->assign('files', $files, false); - $list->assign('publicListView', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); - $list->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=', false); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); - $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); - $folder = new OCP\Template('files', 'index', ''); - $folder->assign('fileList', $list->fetchPage(), false); - $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); - $folder->assign('isCreatable', false); - $folder->assign('permissions', 0); - $folder->assign('files', $files); - $folder->assign('uploadMaxFilesize', 0); - $folder->assign('uploadMaxHumanFilesize', 0); - $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('folder', $folder->fetchPage(), false); - $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); - } else { - // Show file preview if viewer is available - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); - } else { - OCP\Util::addStyle('files_sharing', 'public'); - OCP\Util::addScript('files_sharing', 'public'); - OCP\Util::addScript('files', 'fileactions'); - $tmpl = new OCP\Template('files_sharing', 'public', 'base'); - $tmpl->assign('owner', $uidOwner); - // Show file list - if (\OC\Files\Filesystem::is_dir($path)) { - OCP\Util::addStyle('files', 'files'); - OCP\Util::addScript('files', 'files'); - OCP\Util::addScript('files', 'filelist'); - $files = array(); - $rootLength = strlen($baseDir) + 1; - foreach (OC_Files::getDirectoryContent($path) as $i) { - $i['date'] = OCP\Util::formatDate($i['mtime']); - if ($i['type'] == 'file') { - $fileinfo = pathinfo($i['name']); - $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; - } - $i['directory'] = '/' . substr('/' . $uidOwner . '/files' . $i['directory'], $rootLength); - if ($i['directory'] == '/') { - $i['directory'] = ''; - } - $i['permissions'] = OCP\PERMISSION_READ; - $files[] = $i; - } - // Make breadcrumb - $breadcrumb = array(); - $pathtohere = ''; - $count = 1; - foreach (explode('/', $dir) as $i) { - if ($i != '') { - if ($i != $baseDir) { - $pathtohere .= '/' . $i; - } - if (strlen($pathtohere) < strlen($_GET['dir'])) { - continue; - } - $breadcrumb[] = array('dir' => str_replace($_GET['dir'], "", $pathtohere, $count), 'name' => $i); - } - } - $list = new OCP\Template('files', 'part.list', ''); - $list->assign('files', $files, false); - $list->assign('publicListView', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); - $list->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=', false); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); - $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); - $folder = new OCP\Template('files', 'index', ''); - $folder->assign('fileList', $list->fetchPage(), false); - $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); - $folder->assign('dir', basename($dir)); - $folder->assign('isCreatable', false); - $folder->assign('permissions', 0); - $folder->assign('files', $files); - $folder->assign('uploadMaxFilesize', 0); - $folder->assign('uploadMaxHumanFilesize', 0); - $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('folder', $folder->fetchPage(), false); - $tmpl->assign('uidOwner', $uidOwner); - $tmpl->assign('dir', basename($dir)); - $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - if (isset($_GET['path'])) { - $getPath = $_GET['path']; - } else { - $getPath = ''; - } - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); - } else { - // Show file preview if viewer is available - $tmpl->assign('uidOwner', $uidOwner); - $tmpl->assign('dir', dirname($path)); - $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&file=' . urlencode($_GET['file']) . '&download', false); - } else { - if (isset($_GET['path'])) { - $getPath = $_GET['path']; - } else { - $getPath = ''; - } - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); - } - } - $tmpl->printPage(); - } } - $tmpl->printPage(); } - $list = new OCP\Template('files', 'part.list', ''); - $list->assign('files', $files, false); + $list->assign('files', $files); $list->assign('disableSharing', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); - $list->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=', false); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); - $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); + $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); + $list->assign('downloadURL', + OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path='); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb); + $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); $folder = new OCP\Template('files', 'index', ''); - $folder->assign('fileList', $list->fetchPage(), false); - $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); - $folder->assign('dir', basename($dir)); + $folder->assign('fileList', $list->fetchPage()); + $folder->assign('breadcrumb', $breadcrumbNav->fetchPage()); + $folder->assign('dir', $getPath); $folder->assign('isCreatable', false); $folder->assign('permissions', 0); $folder->assign('files', $files); $folder->assign('uploadMaxFilesize', 0); $folder->assign('uploadMaxHumanFilesize', 0); $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('folder', $folder->fetchPage(), false); + $folder->assign('usedSpacePercent', 0); + $tmpl->assign('folder', $folder->fetchPage()); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') - .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); + $tmpl->assign('downloadURL', + OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); } else { - OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); + $tmpl->assign('dir', $dir); + + // Show file preview if viewer is available + if ($type == 'file') { + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); + } else { + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') + .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); + } } + $tmpl->printPage(); } + exit(); +} else { + OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); - diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 9695caebf1831d88e4d8aeb56a12cd71a5c3041e..b6ef82da6f09678a9fa972440a9eb608bf6861c3 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -1,9 +1,9 @@ -
+
-

- - - +

+ + +

\ No newline at end of file diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 71fca09ed6d9b3147f081a29147fc9f5b4916e62..88692445ec308d2f45f5c001a92b08bad738b050 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -1,35 +1,43 @@ - - - - + + + +
- +
- +
-

ownCloudt('web services under your control'); ?>

+

ownCloud – +t('web services under your control')); ?>

diff --git a/apps/files_trashbin/ajax/delete.php b/apps/files_trashbin/ajax/delete.php index 7a6bd1342ea278ea53d29f66d17ef4f31cd42ecf..1834fb54003a476aacf4b7c2a7339ff6d5e11ccd 100644 --- a/apps/files_trashbin/ajax/delete.php +++ b/apps/files_trashbin/ajax/delete.php @@ -3,22 +3,43 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); -$file = $_REQUEST['file']; +$files = $_POST['files']; +$dirlisting = $_POST['dirlisting']; +$list = json_decode($files); -$path_parts = pathinfo($file); -if ($path_parts['dirname'] == '.') { - $delimiter = strrpos($file, '.d'); - $filename = substr($file, 0, $delimiter); - $timestamp = substr($file, $delimiter+2); -} else { - $filename = $file; - $timestamp = null; +$error = array(); +$success = array(); + +$i = 0; +foreach ($list as $file) { + if ( $dirlisting=='0') { + $delimiter = strrpos($file, '.d'); + $filename = substr($file, 0, $delimiter); + $timestamp = substr($file, $delimiter+2); + } else { + $filename = $file; + $timestamp = null; + } + + OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp); + if (!OCA\Files_Trashbin\Trashbin::file_exists($filename, $timestamp)) { + $success[$i]['filename'] = $file; + $success[$i]['timestamp'] = $timestamp; + $i++; + } else { + $error[] = $filename; + } } -if (OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp)) { - OCP\JSON::success(array("data" => array("filename" => $file))); -} else { +if ( $error ) { + $filelist = ''; + foreach ( $error as $e ) { + $filelist .= $e.', '; + } $l = OC_L10N::get('files_trashbin'); - OCP\JSON::error(array("data" => array("message" => $l->t("Couldn't delete %s permanently", array($file))))); + $message = $l->t("Couldn't delete %s permanently", array(rtrim($filelist, ', '))); + OCP\JSON::error(array("data" => array("message" => $message, + "success" => $success, "error" => $error))); +} else { + OCP\JSON::success(array("data" => array("success" => $success))); } - diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index cc010979c51e28d655639c6c00f640bae69d27da..80de3c31f912379a955de633584afde20196891a 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -1,11 +1,11 @@ -t("Couldn't restore %s", array(rtrim($filelist,', '))); + $message = $l->t("Couldn't restore %s", array(rtrim($filelist, ', '))); OCP\JSON::error(array("data" => array("message" => $message, "success" => $success, "error" => $error))); } else { diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php index b1a15cd13d19b8a99e03c9404c941f9cf9b714e9..7c04e583db1515a88abdee18f1b313592baa120d 100644 --- a/apps/files_trashbin/appinfo/app.php +++ b/apps/files_trashbin/appinfo/app.php @@ -1,7 +1,7 @@ text true - 50 + 250 @@ -26,7 +26,7 @@ text true - 50 + 64 @@ -42,7 +42,7 @@ text true - 200 + 512 @@ -89,4 +89,30 @@ + + + *dbprefix*files_trashsize + + + + + user + text + + true + 64 + + + + size + text + + true + 50 + + + + +
+ diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml index 9b486126361b58fa662d35de74489f2cd13d3a95..7f807da579ea0a8013c251123650fc5636e29505 100644 --- a/apps/files_trashbin/appinfo/info.xml +++ b/apps/files_trashbin/appinfo/info.xml @@ -1,8 +1,8 @@ files_trashbin - Trash - Trash bin + Deleted files + Keep a copy of deleted files so that they can be restored if needed AGPL Bjoern Schiessle true diff --git a/apps/files_trashbin/appinfo/update.php b/apps/files_trashbin/appinfo/update.php new file mode 100644 index 0000000000000000000000000000000000000000..b0bf79cc510f4c5c22a1af8df09cb092df4f9c0a --- /dev/null +++ b/apps/files_trashbin/appinfo/update.php @@ -0,0 +1,40 @@ +file_exists($filename)) { header("HTTP/1.0 404 Not Found"); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 46a601cfdde60523138e5f2c7181b4b1b53453b1..8e726836f8a275731b37129ea3733247881e6253 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -1,24 +1,26 @@ getAbsolutePath($dir); $dirContent = opendir($fullpath); $i = 0; @@ -27,7 +29,7 @@ if ($dir) { $pos = strpos($dir.'/', '/', 1); $tmp = substr($dir, 0, $pos); $pos = strrpos($tmp, '.d'); - $timestamp = substr($tmp,$pos+2); + $timestamp = substr($tmp, $pos+2); $result[] = array( 'id' => $entryName, 'timestamp' => $timestamp, @@ -35,13 +37,13 @@ if ($dir) { 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', 'location' => $dir, ); - } + } } - closedir($fullpath); - + closedir($dirContent); + } else { $dirlisting = false; - $query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); + $query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE user = ?'); $result = $query->execute(array($user))->fetchAll(); } @@ -66,35 +68,47 @@ foreach ($result as $r) { $files[] = $i; } -// Make breadcrumb -$breadcrumb = array(array('dir' => '', 'name' => 'Trash')); -$pathtohere = ''; -foreach (explode('/', $dir) as $i) { +function fileCmp($a, $b) { + if ($a['type'] == 'dir' and $b['type'] != 'dir') { + return -1; + } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { + return 1; + } else { + return strnatcasecmp($a['name'], $b['name']); + } +} + +usort($files, "fileCmp"); + +// Make breadcrumb +$pathtohere = ''; +$breadcrumb = array(); +foreach (explode('/', $dir) as $i) { if ($i != '') { if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { $name = $match[1]; } else { $name = $i; - } - $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); - } + } + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); + } } -$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); -$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); -$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); +$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); +$breadcrumbNav->assign('breadcrumb', $breadcrumb); +$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir='); $list = new OCP\Template('files_trashbin', 'part.list', ''); -$list->assign('files', $files, false); -$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir, false); -$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir, false); +$list->assign('files', $files); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir); $list->assign('disableSharing', true); $list->assign('dirlisting', $dirlisting); $list->assign('disableDownloadActions', true); -$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); -$tmpl->assign('fileList', $list->fetchPage(), false); +$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage()); +$tmpl->assign('fileList', $list->fetchPage()); $tmpl->assign('files', $files); -$tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath())); +$tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($view->getAbsolutePath())); $tmpl->printPage(); diff --git a/apps/files_trashbin/js/disableDefaultActions.js b/apps/files_trashbin/js/disableDefaultActions.js index 27c3e13db4d12d05a8c8e9796b031d72c1232247..df08bfb1a50525ad29f4428643af471dc2fcf6b2 100644 --- a/apps/files_trashbin/js/disableDefaultActions.js +++ b/apps/files_trashbin/js/disableDefaultActions.js @@ -1,4 +1,4 @@ -/* disable download and sharing actions */ -var disableDownloadActions = true; -var disableSharing = true; +/* disable download and sharing actions */ +var disableDownloadActions = true; +var disableSharing = true; var trashBinApp = true; \ No newline at end of file diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 6c810e4c2bd017ac92e8ad1b2e62f1c3ebd2c31b..39e76e10c9c6792cab6411c72dc319cead3fcddb 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -6,9 +6,10 @@ $(document).ready(function() { var tr=$('tr').filterAttr('data-file', filename); var spinner = ''; var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date"); + var files = tr.attr('data-file'); undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, + {files:JSON.stringify([files]), dirlisting:tr.attr('data-dirlisting') }, function(result){ for (var i = 0; i < result.data.success.length; i++) { var row = document.getElementById(result.data.success[i].filename); @@ -18,35 +19,36 @@ $(document).ready(function() { OC.dialogs.alert(result.data.message, 'Error'); } }); - + }); }; - + FileActions.register('all', 'Delete', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/delete'); }, function (filename) { $('.tipsy').remove(); - + var tr=$('tr').filterAttr('data-file', filename); var deleteAction = $('tr').filterAttr('data-file',filename).children("td.date").children(".action.delete"); var oldHTML = deleteAction[0].outerHTML; var newHTML = ''; + var files = tr.attr('data-file'); deleteAction[0].outerHTML = newHTML; - + $.post(OC.filePath('files_trashbin','ajax','delete.php'), - {file:tr.attr('data-file') }, + {files:JSON.stringify([files]), dirlisting:tr.attr('data-dirlisting') }, function(result){ - if ( result.status == 'success' ) { - var row = document.getElementById(result.data.filename); + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename); row.parentNode.removeChild(row); - } else { - deleteAction[0].outerHTML = oldHTML; + } + if (result.status != 'success') { OC.dialogs.alert(result.data.message, 'Error'); } }); - + }); - + // Sets the select_all checkbox behaviour : $('#select_all').click(function() { if($(this).attr('checked')){ @@ -88,19 +90,19 @@ $(document).ready(function() { } } processSelection(); - }); - + }); + $('.undelete').click('click',function(event) { var spinner = ''; var files=getSelectedFiles('file'); - var fileslist=files.join(';'); + var fileslist = JSON.stringify(files); var dirlisting=getSelectedFiles('dirlisting')[0]; - - for (var i in files) { + + for (var i=0; i'; + var files=getSelectedFiles('file'); + var fileslist = JSON.stringify(files); + var dirlisting=getSelectedFiles('dirlisting')[0]; + + for (var i=0; i "Невъзможно изтриване на %s завинаги", +"Couldn't restore %s" => "Невъзможно възтановяване на %s", +"perform restore operation" => "извършване на действие по възстановяване", +"delete file permanently" => "изтриване на файла завинаги", +"Delete permanently" => "Изтриване завинаги", +"Name" => "Име", +"Deleted" => "Изтрито", +"1 folder" => "1 папка", +"{count} folders" => "{count} папки", +"1 file" => "1 файл", +"{count} files" => "{count} файла", +"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!", +"Restore" => "Възтановяване", +"Delete" => "Изтриване" ); diff --git a/apps/files_trashbin/l10n/bn_BD.php b/apps/files_trashbin/l10n/bn_BD.php index c669eff7e1fa48e713067caa9a110caa9a5c26ed..d61355c52c204f51da086053120b6fa9653cc910 100644 --- a/apps/files_trashbin/l10n/bn_BD.php +++ b/apps/files_trashbin/l10n/bn_BD.php @@ -3,5 +3,6 @@ "1 folder" => "১টি ফোল্ডার", "{count} folders" => "{count} টি ফোল্ডার", "1 file" => "১টি ফাইল", -"{count} files" => "{count} টি ফাইল" +"{count} files" => "{count} টি ফাইল", +"Delete" => "মুছে" ); diff --git a/apps/files_trashbin/l10n/ca.php b/apps/files_trashbin/l10n/ca.php index 803b0c81ef0e411c1e7e1f6ef8d37e23c005f235..894ed6a729d5307af31a160f85abdba8029e8b97 100644 --- a/apps/files_trashbin/l10n/ca.php +++ b/apps/files_trashbin/l10n/ca.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "No s'ha pogut restaurar %s", "perform restore operation" => "executa l'operació de restauració", "delete file permanently" => "esborra el fitxer permanentment", +"Delete permanently" => "Esborra permanentment", "Name" => "Nom", "Deleted" => "Eliminat", "1 folder" => "1 carpeta", @@ -10,5 +11,6 @@ "1 file" => "1 fitxer", "{count} files" => "{count} fitxers", "Nothing in here. Your trash bin is empty!" => "La paperera està buida!", -"Restore" => "Recupera" +"Restore" => "Recupera", +"Delete" => "Esborra" ); diff --git a/apps/files_trashbin/l10n/cs_CZ.php b/apps/files_trashbin/l10n/cs_CZ.php index eeb27784d3e8ee52eba471ff90fd954f662744f0..d32af39877b590810707e6ba749efe3ebf6d004b 100644 --- a/apps/files_trashbin/l10n/cs_CZ.php +++ b/apps/files_trashbin/l10n/cs_CZ.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "Nelze obnovit %s", "perform restore operation" => "provést obnovu", "delete file permanently" => "trvale odstranit soubor", +"Delete permanently" => "Trvale odstranit", "Name" => "Název", "Deleted" => "Smazáno", "1 folder" => "1 složka", @@ -10,5 +11,6 @@ "1 file" => "1 soubor", "{count} files" => "{count} soubory", "Nothing in here. Your trash bin is empty!" => "Žádný obsah. Váš koš je prázdný.", -"Restore" => "Obnovit" +"Restore" => "Obnovit", +"Delete" => "Smazat" ); diff --git a/apps/files_trashbin/l10n/da.php b/apps/files_trashbin/l10n/da.php index 3343b6fc8f6ee1079ebe5325e61e9349efd7d1be..ca4a2e8215d6ea2ee46925490851ccf856530646 100644 --- a/apps/files_trashbin/l10n/da.php +++ b/apps/files_trashbin/l10n/da.php @@ -1,8 +1,16 @@ "Kunne ikke slette %s permanent", +"Couldn't restore %s" => "Kunne ikke gendanne %s", +"perform restore operation" => "udfør gendannelsesoperation", +"delete file permanently" => "slet fil permanent", +"Delete permanently" => "Slet permanent", "Name" => "Navn", +"Deleted" => "Slettet", "1 folder" => "1 mappe", "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", -"Restore" => "Gendan" +"Nothing in here. Your trash bin is empty!" => "Intet at se her. Din papirkurv er tom!", +"Restore" => "Gendan", +"Delete" => "Slet" ); diff --git a/apps/files_trashbin/l10n/de.php b/apps/files_trashbin/l10n/de.php index 45dfb9d6057724021fc2563eda37c1e4f94c52d9..d1952c9635a14f85485bcae6837715c75e18f668 100644 --- a/apps/files_trashbin/l10n/de.php +++ b/apps/files_trashbin/l10n/de.php @@ -1,5 +1,9 @@ "Konnte %s nicht dauerhaft löschen", +"Couldn't restore %s" => "Konnte %s nicht wiederherstellen", "perform restore operation" => "Wiederherstellung ausführen", +"delete file permanently" => "Datei dauerhaft löschen", +"Delete permanently" => "Permanent löschen", "Name" => "Name", "Deleted" => "gelöscht", "1 folder" => "1 Ordner", @@ -7,5 +11,6 @@ "1 file" => "1 Datei", "{count} files" => "{count} Dateien", "Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, der Papierkorb ist leer!", -"Restore" => "Wiederherstellen" +"Restore" => "Wiederherstellen", +"Delete" => "Löschen" ); diff --git a/apps/files_trashbin/l10n/de_DE.php b/apps/files_trashbin/l10n/de_DE.php index e293bf0b2eb623eaf351318fef234cf2d85efd48..269680ca2c3f0ce5537998be967e2d7d8aa02e5a 100644 --- a/apps/files_trashbin/l10n/de_DE.php +++ b/apps/files_trashbin/l10n/de_DE.php @@ -1,6 +1,9 @@ "Führe die Wiederherstellung aus", -"delete file permanently" => "Datei entgültig löschen", +"Couldn't delete %s permanently" => "Konnte %s nicht dauerhaft löschen", +"Couldn't restore %s" => "Konnte %s nicht wiederherstellen", +"perform restore operation" => "Wiederherstellung ausführen", +"delete file permanently" => "Datei dauerhaft löschen", +"Delete permanently" => "Endgültig löschen", "Name" => "Name", "Deleted" => "Gelöscht", "1 folder" => "1 Ordner", @@ -8,5 +11,6 @@ "1 file" => "1 Datei", "{count} files" => "{count} Dateien", "Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, Ihr Papierkorb ist leer!", -"Restore" => "Wiederherstellen" +"Restore" => "Wiederherstellen", +"Delete" => "Löschen" ); diff --git a/apps/files_trashbin/l10n/el.php b/apps/files_trashbin/l10n/el.php index 83e359890ea53d4cc68f04f83e1cb4fa78508a0a..cf9471780c09264cdac51f51f09c375859c1127b 100644 --- a/apps/files_trashbin/l10n/el.php +++ b/apps/files_trashbin/l10n/el.php @@ -1,8 +1,16 @@ "Αδύνατη η μόνιμη διαγραφή του %s", +"Couldn't restore %s" => "Αδυναμία επαναφοράς %s", +"perform restore operation" => "εκτέλεση λειτουργία επαναφοράς", +"delete file permanently" => "μόνιμη διαγραφή αρχείου", +"Delete permanently" => "Μόνιμη διαγραφή", "Name" => "Όνομα", +"Deleted" => "Διαγράφηκε", "1 folder" => "1 φάκελος", "{count} folders" => "{count} φάκελοι", "1 file" => "1 αρχείο", "{count} files" => "{count} αρχεία", -"Restore" => "Επαναφορά" +"Nothing in here. Your trash bin is empty!" => "Δεν υπάρχει τίποτα εδώ. Ο κάδος σας είναι άδειος!", +"Restore" => "Επαναφορά", +"Delete" => "Διαγραφή" ); diff --git a/apps/files_trashbin/l10n/eo.php b/apps/files_trashbin/l10n/eo.php index f357e3c10c2cab4d9903aed0ae53573eded18e35..e1e5acbb5a80f6a9bb6b381976ba053db0a94b02 100644 --- a/apps/files_trashbin/l10n/eo.php +++ b/apps/files_trashbin/l10n/eo.php @@ -4,5 +4,6 @@ "{count} folders" => "{count} dosierujoj", "1 file" => "1 dosiero", "{count} files" => "{count} dosierujoj", -"Restore" => "Restaŭri" +"Restore" => "Restaŭri", +"Delete" => "Forigi" ); diff --git a/apps/files_trashbin/l10n/es.php b/apps/files_trashbin/l10n/es.php index c14b9776473022b05a89e3e977e55f9c9fc171cc..ebc01b1d21e43c702dc571212d9bba4410bbaf8f 100644 --- a/apps/files_trashbin/l10n/es.php +++ b/apps/files_trashbin/l10n/es.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "No se puede restaurar %s", "perform restore operation" => "Restaurar", "delete file permanently" => "Eliminar archivo permanentemente", +"Delete permanently" => "Eliminar permanentemente", "Name" => "Nombre", "Deleted" => "Eliminado", "1 folder" => "1 carpeta", @@ -10,5 +11,6 @@ "1 file" => "1 archivo", "{count} files" => "{count} archivos", "Nothing in here. Your trash bin is empty!" => "Nada aqui. La papelera esta vacia!", -"Restore" => "Recuperar" +"Restore" => "Recuperar", +"Delete" => "Eliminar" ); diff --git a/apps/files_trashbin/l10n/es_AR.php b/apps/files_trashbin/l10n/es_AR.php index d2c5f30428443cfe6287ea5a4597d0c85e457192..bb78a39d6c33c0744fc02c5aa24ea484ae516483 100644 --- a/apps/files_trashbin/l10n/es_AR.php +++ b/apps/files_trashbin/l10n/es_AR.php @@ -1,8 +1,16 @@ "No fue posible borrar %s de manera permanente", +"Couldn't restore %s" => "No se pudo restaurar %s", +"perform restore operation" => "Restaurar", +"delete file permanently" => "Borrar archivo de manera permanente", +"Delete permanently" => "Borrar de manera permanente", "Name" => "Nombre", +"Deleted" => "Borrado", "1 folder" => "1 directorio", "{count} folders" => "{count} directorios", "1 file" => "1 archivo", "{count} files" => "{count} archivos", -"Restore" => "Recuperar" +"Nothing in here. Your trash bin is empty!" => "No hay nada acá. ¡La papelera está vacía!", +"Restore" => "Recuperar", +"Delete" => "Borrar" ); diff --git a/apps/files_trashbin/l10n/et_EE.php b/apps/files_trashbin/l10n/et_EE.php index 4f46f3880208f2f650e176974ca30125ea487cb2..73ae9dbee18023376de4214899a8f3d721136871 100644 --- a/apps/files_trashbin/l10n/et_EE.php +++ b/apps/files_trashbin/l10n/et_EE.php @@ -1,7 +1,16 @@ "%s jäädavalt kustutamine ebaõnnestus", +"Couldn't restore %s" => "%s ei saa taastada", +"perform restore operation" => "soorita taastamine", +"delete file permanently" => "kustuta fail jäädavalt", +"Delete permanently" => "Kustuta jäädavalt", "Name" => "Nimi", +"Deleted" => "Kustutatud", "1 folder" => "1 kaust", "{count} folders" => "{count} kausta", "1 file" => "1 fail", -"{count} files" => "{count} faili" +"{count} files" => "{count} faili", +"Nothing in here. Your trash bin is empty!" => "Siin pole midagi. Sinu prügikast on tühi!", +"Restore" => "Taasta", +"Delete" => "Kustuta" ); diff --git a/apps/files_trashbin/l10n/eu.php b/apps/files_trashbin/l10n/eu.php index a1e3ca53e61caba0f81cfdf037d8cf7b6e391a06..5a565436ede778f74ae895bfc139adc52900375d 100644 --- a/apps/files_trashbin/l10n/eu.php +++ b/apps/files_trashbin/l10n/eu.php @@ -1,8 +1,16 @@ "Ezin izan da %s betirako ezabatu", +"Couldn't restore %s" => "Ezin izan da %s berreskuratu", +"perform restore operation" => "berreskuratu", +"delete file permanently" => "ezabatu fitxategia betirako", +"Delete permanently" => "Ezabatu betirako", "Name" => "Izena", +"Deleted" => "Ezabatuta", "1 folder" => "karpeta bat", "{count} folders" => "{count} karpeta", "1 file" => "fitxategi bat", "{count} files" => "{count} fitxategi", -"Restore" => "Berrezarri" +"Nothing in here. Your trash bin is empty!" => "Ez dago ezer ez. Zure zakarrontzia hutsik dago!", +"Restore" => "Berrezarri", +"Delete" => "Ezabatu" ); diff --git a/apps/files_trashbin/l10n/fa.php b/apps/files_trashbin/l10n/fa.php index 487d1657985c4b0e318e5cacb699a8fb8cc85d60..7cc695215e617500373d1d7e3b400de72c580bd3 100644 --- a/apps/files_trashbin/l10n/fa.php +++ b/apps/files_trashbin/l10n/fa.php @@ -4,5 +4,6 @@ "{count} folders" => "{ شمار} پوشه ها", "1 file" => "1 پرونده", "{count} files" => "{ شمار } فایل ها", -"Restore" => "بازیابی" +"Restore" => "بازیابی", +"Delete" => "حذف" ); diff --git a/apps/files_trashbin/l10n/fi_FI.php b/apps/files_trashbin/l10n/fi_FI.php index de25027f9a8737d27117440bf327dfd576da14d8..e18689956d08e11b9d4a8d19049444bed59a176a 100644 --- a/apps/files_trashbin/l10n/fi_FI.php +++ b/apps/files_trashbin/l10n/fi_FI.php @@ -1,5 +1,9 @@ "Kohdetta %s ei voitu poistaa pysyvästi", +"Couldn't restore %s" => "Kohteen %s palautus epäonnistui", "perform restore operation" => "suorita palautustoiminto", +"delete file permanently" => "poista tiedosto pysyvästi", +"Delete permanently" => "Poista pysyvästi", "Name" => "Nimi", "Deleted" => "Poistettu", "1 folder" => "1 kansio", @@ -7,5 +11,6 @@ "1 file" => "1 tiedosto", "{count} files" => "{count} tiedostoa", "Nothing in here. Your trash bin is empty!" => "Tyhjää täynnä! Roskakorissa ei ole mitään.", -"Restore" => "Palauta" +"Restore" => "Palauta", +"Delete" => "Poista" ); diff --git a/apps/files_trashbin/l10n/fr.php b/apps/files_trashbin/l10n/fr.php index 609b2fa9bd75989ec4e5154dcd95439407b49467..0a783785efb5354687c58a73a59dad3198581173 100644 --- a/apps/files_trashbin/l10n/fr.php +++ b/apps/files_trashbin/l10n/fr.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "Impossible de restaurer %s", "perform restore operation" => "effectuer l'opération de restauration", "delete file permanently" => "effacer définitivement le fichier", +"Delete permanently" => "Supprimer de façon définitive", "Name" => "Nom", "Deleted" => "Effacé", "1 folder" => "1 dossier", @@ -10,5 +11,6 @@ "1 file" => "1 fichier", "{count} files" => "{count} fichiers", "Nothing in here. Your trash bin is empty!" => "Il n'y a rien ici. Votre corbeille est vide !", -"Restore" => "Restaurer" +"Restore" => "Restaurer", +"Delete" => "Supprimer" ); diff --git a/apps/files_trashbin/l10n/gl.php b/apps/files_trashbin/l10n/gl.php index bdc3187b20b59f246f86441857f186650a47d687..4e31dd0018a2cdc5a3e07a54ac29902bfd0ed79f 100644 --- a/apps/files_trashbin/l10n/gl.php +++ b/apps/files_trashbin/l10n/gl.php @@ -1,8 +1,16 @@ "Non foi posíbel eliminar %s permanente", +"Couldn't restore %s" => "Non foi posíbel restaurar %s", +"perform restore operation" => "realizar a operación de restauración", +"delete file permanently" => "eliminar o ficheiro permanentemente", +"Delete permanently" => "Eliminar permanentemente", "Name" => "Nome", +"Deleted" => "Eliminado", "1 folder" => "1 cartafol", "{count} folders" => "{count} cartafoles", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", -"Restore" => "Restablecer" +"Nothing in here. Your trash bin is empty!" => "Aquí non hai nada. O cesto do lixo está baleiro!", +"Restore" => "Restablecer", +"Delete" => "Eliminar" ); diff --git a/apps/files_trashbin/l10n/he.php b/apps/files_trashbin/l10n/he.php index d026add5d750ff13ad4f2a8b5aac10685c1ffde0..9c767d2222c818a39039bf5a13a07c29d322e059 100644 --- a/apps/files_trashbin/l10n/he.php +++ b/apps/files_trashbin/l10n/he.php @@ -1,7 +1,16 @@ "בלתי אפשרי למחוק את %s לצמיתות", +"Couldn't restore %s" => "בלתי אפשרי לשחזר את %s", +"perform restore operation" => "בצע פעולת שחזור", +"delete file permanently" => "מחק קובץ לצמיתות", +"Delete permanently" => "מחק לצמיתות", "Name" => "שם", +"Deleted" => "נמחק", "1 folder" => "תיקייה אחת", "{count} folders" => "{count} תיקיות", "1 file" => "קובץ אחד", -"{count} files" => "{count} קבצים" +"{count} files" => "{count} קבצים", +"Nothing in here. Your trash bin is empty!" => "שום דבר כאן. סל המחזור שלך ריק!", +"Restore" => "שחזר", +"Delete" => "מחיקה" ); diff --git a/apps/files_trashbin/l10n/hr.php b/apps/files_trashbin/l10n/hr.php index 52255c7429a1ec582c450b8525f4109a2ca28a46..2cb86adfd405423190d97c24b545ae11bc04795f 100644 --- a/apps/files_trashbin/l10n/hr.php +++ b/apps/files_trashbin/l10n/hr.php @@ -1,3 +1,4 @@ "Ime" +"Name" => "Ime", +"Delete" => "Obriši" ); diff --git a/apps/files_trashbin/l10n/hu_HU.php b/apps/files_trashbin/l10n/hu_HU.php index c4e2b5e21250737005edb4ade3aaf0f2946ed8c8..9c158c2b9ecae46bf74d3039f7eaccd4a468458f 100644 --- a/apps/files_trashbin/l10n/hu_HU.php +++ b/apps/files_trashbin/l10n/hu_HU.php @@ -1,8 +1,16 @@ "Nem sikerült %s végleges törlése", +"Couldn't restore %s" => "Nem sikerült %s visszaállítása", +"perform restore operation" => "a visszaállítás végrehajtása", +"delete file permanently" => "az állomány végleges törlése", +"Delete permanently" => "Végleges törlés", "Name" => "Név", +"Deleted" => "Törölve", "1 folder" => "1 mappa", "{count} folders" => "{count} mappa", "1 file" => "1 fájl", "{count} files" => "{count} fájl", -"Restore" => "Visszaállítás" +"Nothing in here. Your trash bin is empty!" => "Itt nincs semmi. Az Ön szemetes mappája üres!", +"Restore" => "Visszaállítás", +"Delete" => "Törlés" ); diff --git a/apps/files_trashbin/l10n/hy.php b/apps/files_trashbin/l10n/hy.php new file mode 100644 index 0000000000000000000000000000000000000000..3b80487278a16a8f1f7b407908ea855f3f5def10 --- /dev/null +++ b/apps/files_trashbin/l10n/hy.php @@ -0,0 +1,3 @@ + "Ջնջել" +); diff --git a/apps/files_trashbin/l10n/ia.php b/apps/files_trashbin/l10n/ia.php index c2581f3de179c68b450cc8c311ef8d9a63a8c4c4..0a51752312f37a994aeda4b736d693241143e46a 100644 --- a/apps/files_trashbin/l10n/ia.php +++ b/apps/files_trashbin/l10n/ia.php @@ -1,3 +1,4 @@ "Nomine" +"Name" => "Nomine", +"Delete" => "Deler" ); diff --git a/apps/files_trashbin/l10n/id.php b/apps/files_trashbin/l10n/id.php index 1a14d8b7c21bc7ace660c443bcd4ef7ce202a049..ab5fe25ac6b2ab270c59f328bd464ed148c2e0a0 100644 --- a/apps/files_trashbin/l10n/id.php +++ b/apps/files_trashbin/l10n/id.php @@ -1,3 +1,15 @@ "nama" +"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s", +"Couldn't restore %s" => "Tidak dapat memulihkan %s", +"perform restore operation" => "jalankan operasi pemulihan", +"delete file permanently" => "hapus berkas secara permanen", +"Name" => "Nama", +"Deleted" => "Dihapus", +"1 folder" => "1 map", +"{count} folders" => "{count} map", +"1 file" => "1 berkas", +"{count} files" => "{count} berkas", +"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!", +"Restore" => "Pulihkan", +"Delete" => "Hapus" ); diff --git a/apps/files_trashbin/l10n/is.php b/apps/files_trashbin/l10n/is.php index 416f641a8efa412078713cdc94d520d91b094096..fba36c91cb53b0184573d9df6884f5ee933ea71f 100644 --- a/apps/files_trashbin/l10n/is.php +++ b/apps/files_trashbin/l10n/is.php @@ -3,5 +3,6 @@ "1 folder" => "1 mappa", "{count} folders" => "{count} möppur", "1 file" => "1 skrá", -"{count} files" => "{count} skrár" +"{count} files" => "{count} skrár", +"Delete" => "Eyða" ); diff --git a/apps/files_trashbin/l10n/it.php b/apps/files_trashbin/l10n/it.php index 8627682d088d08ab2a646ce43bd6094ef3abfde1..027b22c91ac0baa0a5f7529007c00bf6f3e45289 100644 --- a/apps/files_trashbin/l10n/it.php +++ b/apps/files_trashbin/l10n/it.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "Impossibile ripristinare %s", "perform restore operation" => "esegui operazione di ripristino", "delete file permanently" => "elimina il file definitivamente", +"Delete permanently" => "Elimina definitivamente", "Name" => "Nome", "Deleted" => "Eliminati", "1 folder" => "1 cartella", @@ -10,5 +11,6 @@ "1 file" => "1 file", "{count} files" => "{count} file", "Nothing in here. Your trash bin is empty!" => "Qui non c'è niente. Il tuo cestino è vuoto.", -"Restore" => "Ripristina" +"Restore" => "Ripristina", +"Delete" => "Elimina" ); diff --git a/apps/files_trashbin/l10n/ja_JP.php b/apps/files_trashbin/l10n/ja_JP.php index 2bccf3f3bd5c036b95116f0969e11d682dd5f01d..478aa400664677d1d898dfb78ea24db5800b7a0b 100644 --- a/apps/files_trashbin/l10n/ja_JP.php +++ b/apps/files_trashbin/l10n/ja_JP.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "%s を復元出来ませんでした", "perform restore operation" => "復元操作を実行する", "delete file permanently" => "ファイルを完全に削除する", +"Delete permanently" => "完全に削除する", "Name" => "名前", "Deleted" => "削除済み", "1 folder" => "1 フォルダ", @@ -10,5 +11,6 @@ "1 file" => "1 ファイル", "{count} files" => "{count} ファイル", "Nothing in here. Your trash bin is empty!" => "ここには何もありません。ゴミ箱は空です!", -"Restore" => "復元" +"Restore" => "復元", +"Delete" => "削除" ); diff --git a/apps/files_trashbin/l10n/ka_GE.php b/apps/files_trashbin/l10n/ka_GE.php index 43dba38f5c747886010870f70d5b4da53b089b0e..05068767863df0a674b962cc1b8fab5db515a332 100644 --- a/apps/files_trashbin/l10n/ka_GE.php +++ b/apps/files_trashbin/l10n/ka_GE.php @@ -3,5 +3,6 @@ "1 folder" => "1 საქაღალდე", "{count} folders" => "{count} საქაღალდე", "1 file" => "1 ფაილი", -"{count} files" => "{count} ფაილი" +"{count} files" => "{count} ფაილი", +"Delete" => "წაშლა" ); diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php index 61acd1276a75999d99c223e4e462fb21c400c700..b40546e34b8365be4d055b17493e79935800c29f 100644 --- a/apps/files_trashbin/l10n/ko.php +++ b/apps/files_trashbin/l10n/ko.php @@ -4,5 +4,6 @@ "{count} folders" => "폴더 {count}개", "1 file" => "파일 1개", "{count} files" => "파일 {count}개", -"Restore" => "복원" +"Restore" => "복원", +"Delete" => "삭제" ); diff --git a/apps/files_trashbin/l10n/lb.php b/apps/files_trashbin/l10n/lb.php index d1bd7518663b72a34e7039db3f25774ae07736a1..01deea2350037c96bc26ede0753a6124fb31876e 100644 --- a/apps/files_trashbin/l10n/lb.php +++ b/apps/files_trashbin/l10n/lb.php @@ -1,3 +1,4 @@ "Numm" +"Name" => "Numm", +"Delete" => "Läschen" ); diff --git a/apps/files_trashbin/l10n/lt_LT.php b/apps/files_trashbin/l10n/lt_LT.php index 4933e97202fcb949961159eebd6f8524a7d2bcae..513c7626c4e75f4dcae8d5f5428463c4e80b4b35 100644 --- a/apps/files_trashbin/l10n/lt_LT.php +++ b/apps/files_trashbin/l10n/lt_LT.php @@ -3,5 +3,6 @@ "1 folder" => "1 aplankalas", "{count} folders" => "{count} aplankalai", "1 file" => "1 failas", -"{count} files" => "{count} failai" +"{count} files" => "{count} failai", +"Delete" => "Ištrinti" ); diff --git a/apps/files_trashbin/l10n/lv.php b/apps/files_trashbin/l10n/lv.php index 5ecb99b9892bc777115aacf5a3e036a5b8e4a270..d3c68e2a778ee342aae530370a0b61004cda85bf 100644 --- a/apps/files_trashbin/l10n/lv.php +++ b/apps/files_trashbin/l10n/lv.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "Nevarēja atjaunot %s", "perform restore operation" => "veikt atjaunošanu", "delete file permanently" => "dzēst datni pavisam", +"Delete permanently" => "Dzēst pavisam", "Name" => "Nosaukums", "Deleted" => "Dzēsts", "1 folder" => "1 mape", @@ -10,5 +11,6 @@ "1 file" => "1 datne", "{count} files" => "{count} datnes", "Nothing in here. Your trash bin is empty!" => "Šeit nekā nav. Jūsu miskaste ir tukša!", -"Restore" => "Atjaunot" +"Restore" => "Atjaunot", +"Delete" => "Dzēst" ); diff --git a/apps/files_trashbin/l10n/mk.php b/apps/files_trashbin/l10n/mk.php index b983c341e8cba6a6d2e57508e46de4110cd99c44..22b288b002f6e9476b9f2b4594dc4ac9d49e6069 100644 --- a/apps/files_trashbin/l10n/mk.php +++ b/apps/files_trashbin/l10n/mk.php @@ -3,5 +3,6 @@ "1 folder" => "1 папка", "{count} folders" => "{count} папки", "1 file" => "1 датотека", -"{count} files" => "{count} датотеки" +"{count} files" => "{count} датотеки", +"Delete" => "Избриши" ); diff --git a/apps/files_trashbin/l10n/ms_MY.php b/apps/files_trashbin/l10n/ms_MY.php index 73e97b496e4d4773b13a1f676a100568d3e39dd4..381d599865ee81c0bb00ea74a894fb57f3233823 100644 --- a/apps/files_trashbin/l10n/ms_MY.php +++ b/apps/files_trashbin/l10n/ms_MY.php @@ -1,3 +1,4 @@ "Nama" +"Name" => "Nama", +"Delete" => "Padam" ); diff --git a/apps/files_trashbin/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php index 49364753d13a8179652a8d52e955b078dcfe7f9b..dc7b8fe97c5a0fa0968ff8af45a2169130fdffec 100644 --- a/apps/files_trashbin/l10n/nb_NO.php +++ b/apps/files_trashbin/l10n/nb_NO.php @@ -3,5 +3,6 @@ "1 folder" => "1 mappe", "{count} folders" => "{count} mapper", "1 file" => "1 fil", -"{count} files" => "{count} filer" +"{count} files" => "{count} filer", +"Delete" => "Slett" ); diff --git a/apps/files_trashbin/l10n/nl.php b/apps/files_trashbin/l10n/nl.php index a41a5c2fd9cfa6d81eb9e9cf3e86abdceb83938b..c576e5d81f8cc19b69fcc01463dc3b76f9954a33 100644 --- a/apps/files_trashbin/l10n/nl.php +++ b/apps/files_trashbin/l10n/nl.php @@ -1,6 +1,9 @@ "Kon %s niet permanent verwijderen", +"Couldn't restore %s" => "Kon %s niet herstellen", "perform restore operation" => "uitvoeren restore operatie", "delete file permanently" => "verwijder bestanden definitief", +"Delete permanently" => "Verwijder definitief", "Name" => "Naam", "Deleted" => "Verwijderd", "1 folder" => "1 map", @@ -8,5 +11,6 @@ "1 file" => "1 bestand", "{count} files" => "{count} bestanden", "Nothing in here. Your trash bin is empty!" => "Niets te vinden. Uw prullenbak is leeg!", -"Restore" => "Herstellen" +"Restore" => "Herstellen", +"Delete" => "Verwijder" ); diff --git a/apps/files_trashbin/l10n/nn_NO.php b/apps/files_trashbin/l10n/nn_NO.php index be60dabdf01029e0c4a5cbe92a850ccd558ecdd8..f8ab465ee42d5837bf5e2056ee07254860942f26 100644 --- a/apps/files_trashbin/l10n/nn_NO.php +++ b/apps/files_trashbin/l10n/nn_NO.php @@ -1,3 +1,4 @@ "Namn" +"Name" => "Namn", +"Delete" => "Slett" ); diff --git a/apps/files_trashbin/l10n/oc.php b/apps/files_trashbin/l10n/oc.php index 2c705193c15d437b161b764f4c83780d9b102312..e6b939dac0c3565332ac03c58d7bdec703ed1798 100644 --- a/apps/files_trashbin/l10n/oc.php +++ b/apps/files_trashbin/l10n/oc.php @@ -1,3 +1,4 @@ "Nom" +"Name" => "Nom", +"Delete" => "Escafa" ); diff --git a/apps/files_trashbin/l10n/pl.php b/apps/files_trashbin/l10n/pl.php index d2ada4c9466f157976bfeb9ab77027601f96f251..e5ea45b88aeee81c6601b0867100caedc733f444 100644 --- a/apps/files_trashbin/l10n/pl.php +++ b/apps/files_trashbin/l10n/pl.php @@ -1,8 +1,16 @@ "Nie można trwale usunąć %s", +"Couldn't restore %s" => "Nie można przywrócić %s", +"perform restore operation" => "wykonywanie operacji przywracania", +"delete file permanently" => "trwale usuń plik", +"Delete permanently" => "Trwale usuń", "Name" => "Nazwa", +"Deleted" => "Usunięte", "1 folder" => "1 folder", "{count} folders" => "{count} foldery", "1 file" => "1 plik", "{count} files" => "{count} pliki", -"Restore" => "Przywróć" +"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!", +"Restore" => "Przywróć", +"Delete" => "Usuń" ); diff --git a/apps/files_trashbin/l10n/pt_BR.php b/apps/files_trashbin/l10n/pt_BR.php index db5737d92381d5bff71fcf80f4b89fda05be38e8..b9932a71023f7a344a385e5e23a162edc15b0ce9 100644 --- a/apps/files_trashbin/l10n/pt_BR.php +++ b/apps/files_trashbin/l10n/pt_BR.php @@ -1,5 +1,9 @@ "Não foi possível excluir %s permanentemente", +"Couldn't restore %s" => "Não foi possível restaurar %s", "perform restore operation" => "realizar operação de restauração", +"delete file permanently" => "excluir arquivo permanentemente", +"Delete permanently" => "Excluir permanentemente", "Name" => "Nome", "Deleted" => "Excluído", "1 folder" => "1 pasta", @@ -7,5 +11,6 @@ "1 file" => "1 arquivo", "{count} files" => "{count} arquivos", "Nothing in here. Your trash bin is empty!" => "Nada aqui. Sua lixeira está vazia!", -"Restore" => "Restaurar" +"Restore" => "Restaurar", +"Delete" => "Excluir" ); diff --git a/apps/files_trashbin/l10n/pt_PT.php b/apps/files_trashbin/l10n/pt_PT.php index 79930315b0eb9bca9ed9484494bc5ba713d701e3..a621587ea0bb0ef94690a7fe96bbc441a1edaad5 100644 --- a/apps/files_trashbin/l10n/pt_PT.php +++ b/apps/files_trashbin/l10n/pt_PT.php @@ -1,5 +1,9 @@ "Não foi possível eliminar %s de forma permanente", +"Couldn't restore %s" => "Não foi possível restaurar %s", "perform restore operation" => "Restaurar", +"delete file permanently" => "Eliminar permanentemente o(s) ficheiro(s)", +"Delete permanently" => "Eliminar permanentemente", "Name" => "Nome", "Deleted" => "Apagado", "1 folder" => "1 pasta", @@ -7,5 +11,6 @@ "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", "Nothing in here. Your trash bin is empty!" => "Não ha ficheiros. O lixo está vazio", -"Restore" => "Restaurar" +"Restore" => "Restaurar", +"Delete" => "Apagar" ); diff --git a/apps/files_trashbin/l10n/ro.php b/apps/files_trashbin/l10n/ro.php index 6ece51e02cfd9e7673bb489e04cd2f67ca609540..6a919b62dfbb8a80edaff20fd98af6ac62aee07c 100644 --- a/apps/files_trashbin/l10n/ro.php +++ b/apps/files_trashbin/l10n/ro.php @@ -3,5 +3,6 @@ "1 folder" => "1 folder", "{count} folders" => "{count} foldare", "1 file" => "1 fisier", -"{count} files" => "{count} fisiere" +"{count} files" => "{count} fisiere", +"Delete" => "Șterge" ); diff --git a/apps/files_trashbin/l10n/ru.php b/apps/files_trashbin/l10n/ru.php index f6c85a6800ef0c2ef47b678c009d62be89cc12a2..4cf9af1fc39f731232d3831902556a69819e26e1 100644 --- a/apps/files_trashbin/l10n/ru.php +++ b/apps/files_trashbin/l10n/ru.php @@ -3,6 +3,7 @@ "Couldn't restore %s" => "%s не может быть восстановлен", "perform restore operation" => "выполнить операцию восстановления", "delete file permanently" => "удалить файл навсегда", +"Delete permanently" => "Удалено навсегда", "Name" => "Имя", "Deleted" => "Удалён", "1 folder" => "1 папка", @@ -10,5 +11,6 @@ "1 file" => "1 файл", "{count} files" => "{count} файлов", "Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!", -"Restore" => "Восстановить" +"Restore" => "Восстановить", +"Delete" => "Удалить" ); diff --git a/apps/files_trashbin/l10n/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php index c5b1408e2cc0e9a75f2ff3a5e1ca1fd6ffe12fd3..04a899bdd5dfb6b2a0945b1198164b17079ce3f4 100644 --- a/apps/files_trashbin/l10n/ru_RU.php +++ b/apps/files_trashbin/l10n/ru_RU.php @@ -1,8 +1,16 @@ "%s не может быть удалён навсегда", +"Couldn't restore %s" => "%s не может быть восстановлен", +"perform restore operation" => "выполнить операцию восстановления", +"delete file permanently" => "удалить файл навсегда", +"Delete permanently" => "Удалить навсегда", "Name" => "Имя", +"Deleted" => "Удалён", "1 folder" => "1 папка", "{count} folders" => "{количество} папок", "1 file" => "1 файл", "{count} files" => "{количество} файлов", -"Restore" => "Восстановить" +"Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!", +"Restore" => "Восстановить", +"Delete" => "Удалить" ); diff --git a/apps/files_trashbin/l10n/si_LK.php b/apps/files_trashbin/l10n/si_LK.php index cb351afaec94b3d90407a2263c3e4387d8199fbf..71c5632977654828af903c662fb900a122fd0cd3 100644 --- a/apps/files_trashbin/l10n/si_LK.php +++ b/apps/files_trashbin/l10n/si_LK.php @@ -1,5 +1,6 @@ "නම", "1 folder" => "1 ෆොල්ඩරයක්", -"1 file" => "1 ගොනුවක්" +"1 file" => "1 ගොනුවක්", +"Delete" => "මකා දමන්න" ); diff --git a/apps/files_trashbin/l10n/sk_SK.php b/apps/files_trashbin/l10n/sk_SK.php index 759850783e268a6e6ab38966efd2dbd4b839de72..c5806a5dee8da3063108527bd54c971ca245a327 100644 --- a/apps/files_trashbin/l10n/sk_SK.php +++ b/apps/files_trashbin/l10n/sk_SK.php @@ -1,7 +1,9 @@ "Nemožno zmazať %s navždy", "Couldn't restore %s" => "Nemožno obnoviť %s", "perform restore operation" => "vykonať obnovu", "delete file permanently" => "trvalo zmazať súbor", +"Delete permanently" => "Zmazať trvalo", "Name" => "Meno", "Deleted" => "Zmazané", "1 folder" => "1 priečinok", @@ -9,5 +11,6 @@ "1 file" => "1 súbor", "{count} files" => "{count} súborov", "Nothing in here. Your trash bin is empty!" => "Žiadny obsah. Kôš je prázdny!", -"Restore" => "Obnoviť" +"Restore" => "Obnoviť", +"Delete" => "Zmazať" ); diff --git a/apps/files_trashbin/l10n/sl.php b/apps/files_trashbin/l10n/sl.php index 2579f95c86202115c13dc78cfeb15316585c8600..4765945747de1efdac7463745a662382e1c126c5 100644 --- a/apps/files_trashbin/l10n/sl.php +++ b/apps/files_trashbin/l10n/sl.php @@ -3,5 +3,6 @@ "1 folder" => "1 mapa", "{count} folders" => "{count} map", "1 file" => "1 datoteka", -"{count} files" => "{count} datotek" +"{count} files" => "{count} datotek", +"Delete" => "Izbriši" ); diff --git a/apps/files_trashbin/l10n/sr.php b/apps/files_trashbin/l10n/sr.php index 36659e7080314690a3e7589bcbdcd614038512fc..2e7c139e389ef544ae1d67c0ef41ce9d01801a78 100644 --- a/apps/files_trashbin/l10n/sr.php +++ b/apps/files_trashbin/l10n/sr.php @@ -7,5 +7,6 @@ "1 file" => "1 датотека", "{count} files" => "{count} датотеке/а", "Nothing in here. Your trash bin is empty!" => "Овде нема ништа. Корпа за отпатке је празна.", -"Restore" => "Врати" +"Restore" => "Врати", +"Delete" => "Обриши" ); diff --git a/apps/files_trashbin/l10n/sr@latin.php b/apps/files_trashbin/l10n/sr@latin.php index 52255c7429a1ec582c450b8525f4109a2ca28a46..2cb86adfd405423190d97c24b545ae11bc04795f 100644 --- a/apps/files_trashbin/l10n/sr@latin.php +++ b/apps/files_trashbin/l10n/sr@latin.php @@ -1,3 +1,4 @@ "Ime" +"Name" => "Ime", +"Delete" => "Obriši" ); diff --git a/apps/files_trashbin/l10n/sv.php b/apps/files_trashbin/l10n/sv.php index 5bde85e7056c0ca62647f39eda2140e5a5f0cbe4..6f8c2365818a0abcdaf820db7d317730921685fd 100644 --- a/apps/files_trashbin/l10n/sv.php +++ b/apps/files_trashbin/l10n/sv.php @@ -1,5 +1,9 @@ "Kunde inte radera %s permanent", +"Couldn't restore %s" => "Kunde inte återställa %s", "perform restore operation" => "utför återställning", +"delete file permanently" => "radera filen permanent", +"Delete permanently" => "Radera permanent", "Name" => "Namn", "Deleted" => "Raderad", "1 folder" => "1 mapp", @@ -7,5 +11,6 @@ "1 file" => "1 fil", "{count} files" => "{count} filer", "Nothing in here. Your trash bin is empty!" => "Ingenting här. Din papperskorg är tom!", -"Restore" => "Återskapa" +"Restore" => "Återskapa", +"Delete" => "Radera" ); diff --git a/apps/files_trashbin/l10n/ta_LK.php b/apps/files_trashbin/l10n/ta_LK.php index a436e2344a4fd0b5fb18b88e570cc04b9c09770f..f21e5fc750de22b2f683f7fa128535f698fdfa17 100644 --- a/apps/files_trashbin/l10n/ta_LK.php +++ b/apps/files_trashbin/l10n/ta_LK.php @@ -3,5 +3,6 @@ "1 folder" => "1 கோப்புறை", "{count} folders" => "{எண்ணிக்கை} கோப்புறைகள்", "1 file" => "1 கோப்பு", -"{count} files" => "{எண்ணிக்கை} கோப்புகள்" +"{count} files" => "{எண்ணிக்கை} கோப்புகள்", +"Delete" => "நீக்குக" ); diff --git a/apps/files_trashbin/l10n/th_TH.php b/apps/files_trashbin/l10n/th_TH.php index 8a031fb0d70dce9434a3a316843ab9682701ed66..3f5d44c763e819c6d4a2bebff5a63e88543a1482 100644 --- a/apps/files_trashbin/l10n/th_TH.php +++ b/apps/files_trashbin/l10n/th_TH.php @@ -7,5 +7,6 @@ "1 file" => "1 ไฟล์", "{count} files" => "{count} ไฟล์", "Nothing in here. Your trash bin is empty!" => "ไม่มีอะไรอยู่ในนี้ ถังขยะของคุณยังว่างอยู่", -"Restore" => "คืนค่า" +"Restore" => "คืนค่า", +"Delete" => "ลบ" ); diff --git a/apps/files_trashbin/l10n/tr.php b/apps/files_trashbin/l10n/tr.php index 5b7064eceaf14f388ebadc3c3f5f0bf59383c965..c874d73316a22fd085e320944a10c91e73413662 100644 --- a/apps/files_trashbin/l10n/tr.php +++ b/apps/files_trashbin/l10n/tr.php @@ -1,7 +1,16 @@ "%s Kalıcı olarak silinemedi", +"Couldn't restore %s" => "%s Geri yüklenemedi", +"perform restore operation" => "Geri yükleme işlemini gerçekleştir", +"delete file permanently" => "Dosyayı kalıcı olarak sil", +"Delete permanently" => "Kalıcı olarak sil", "Name" => "İsim", +"Deleted" => "Silindi", "1 folder" => "1 dizin", "{count} folders" => "{count} dizin", "1 file" => "1 dosya", -"{count} files" => "{count} dosya" +"{count} files" => "{count} dosya", +"Nothing in here. Your trash bin is empty!" => "Burası boş. Çöp kutun tamamen boş.", +"Restore" => "Geri yükle", +"Delete" => "Sil" ); diff --git a/apps/files_trashbin/l10n/uk.php b/apps/files_trashbin/l10n/uk.php index 14c6931255a8550ea5fe7de0623d6ae7b7d030a6..df7dd24864c5d945e4c202f1178d006d418a61f7 100644 --- a/apps/files_trashbin/l10n/uk.php +++ b/apps/files_trashbin/l10n/uk.php @@ -1,7 +1,16 @@ "Неможливо видалити %s назавжди", +"Couldn't restore %s" => "Неможливо відновити %s", +"perform restore operation" => "виконати операцію відновлення", +"delete file permanently" => "видалити файл назавжди", +"Delete permanently" => "Видалити назавжди", "Name" => "Ім'я", +"Deleted" => "Видалено", "1 folder" => "1 папка", "{count} folders" => "{count} папок", "1 file" => "1 файл", -"{count} files" => "{count} файлів" +"{count} files" => "{count} файлів", +"Nothing in here. Your trash bin is empty!" => "Нічого немає. Ваший кошик для сміття пустий!", +"Restore" => "Відновити", +"Delete" => "Видалити" ); diff --git a/apps/files_trashbin/l10n/vi.php b/apps/files_trashbin/l10n/vi.php index 2c51c69aaf25944967175e07a4c113677e012a6f..165fa9cfd9497bc0af710679602d84cc2db9ab3a 100644 --- a/apps/files_trashbin/l10n/vi.php +++ b/apps/files_trashbin/l10n/vi.php @@ -1,7 +1,16 @@ "Không thể óa %s vĩnh viễn", +"Couldn't restore %s" => "Không thể khôi phục %s", +"perform restore operation" => "thực hiện phục hồi", +"delete file permanently" => "xóa file vĩnh viễn", +"Delete permanently" => "Xóa vĩnh vễn", "Name" => "Tên", +"Deleted" => "Đã xóa", "1 folder" => "1 thư mục", "{count} folders" => "{count} thư mục", "1 file" => "1 tập tin", -"{count} files" => "{count} tập tin" +"{count} files" => "{count} tập tin", +"Nothing in here. Your trash bin is empty!" => "Không có gì ở đây. Thùng rác của bạn rỗng!", +"Restore" => "Khôi phục", +"Delete" => "Xóa" ); diff --git a/apps/files_trashbin/l10n/zh_CN.GB2312.php b/apps/files_trashbin/l10n/zh_CN.GB2312.php index 2c6a7891e98078c2fd8c4356c8b16e9b1ced6bdd..606d80d441b0cb130b1758bb44b042004d4c75af 100644 --- a/apps/files_trashbin/l10n/zh_CN.GB2312.php +++ b/apps/files_trashbin/l10n/zh_CN.GB2312.php @@ -3,5 +3,6 @@ "1 folder" => "1 个文件夹", "{count} folders" => "{count} 个文件夹", "1 file" => "1 个文件", -"{count} files" => "{count} 个文件" +"{count} files" => "{count} 个文件", +"Delete" => "删除" ); diff --git a/apps/files_trashbin/l10n/zh_CN.php b/apps/files_trashbin/l10n/zh_CN.php index 0060b1f31d63884502ca5f28fdb28ba7d27f841b..327a3e247079f4db06c3e201dad4bc12172ae30f 100644 --- a/apps/files_trashbin/l10n/zh_CN.php +++ b/apps/files_trashbin/l10n/zh_CN.php @@ -3,5 +3,6 @@ "1 folder" => "1个文件夹", "{count} folders" => "{count} 个文件夹", "1 file" => "1 个文件", -"{count} files" => "{count} 个文件" +"{count} files" => "{count} 个文件", +"Delete" => "删除" ); diff --git a/apps/files_trashbin/l10n/zh_TW.php b/apps/files_trashbin/l10n/zh_TW.php index be61d9b0b6db63d1999d11cfe1ab32fa5c8e6ee1..0142e901f5848d62514438bb2b5409b5691cae18 100644 --- a/apps/files_trashbin/l10n/zh_TW.php +++ b/apps/files_trashbin/l10n/zh_TW.php @@ -1,7 +1,9 @@ "永久刪除", "Name" => "名稱", "1 folder" => "1 個資料夾", "{count} folders" => "{count} 個資料夾", "1 file" => "1 個檔案", -"{count} files" => "{count} 個檔案" +"{count} files" => "{count} 個檔案", +"Delete" => "刪除" ); diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php index d6a62d447b88a580c0ba3d21c6464426b3f8e53b..9081706a2c56defdf26eb609a4f89ff8d2606b0b 100644 --- a/apps/files_trashbin/lib/hooks.php +++ b/apps/files_trashbin/lib/hooks.php @@ -1,23 +1,23 @@ . - * +/** + * ownCloud - trash bin + * + * @author Bjoern Schiessle + * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * */ /** @@ -36,7 +36,7 @@ class Hooks { * to copy the file to the trash bin */ public static function remove_hook($params) { - + if ( \OCP\App::isEnabled('files_trashbin') ) { $path = $params['path']; Trashbin::move2trash($path); diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index bc6562b2080d569b59262ccfa1290e12efd2e973..2fc8a8bc3c64fab96dcb3408cc900bcb9b628b55 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -1,84 +1,134 @@ . - * - */ - -namespace OCA\Files_Trashbin; - +/** + * ownCloud - trash bin + * + * @author Bjoern Schiessle + * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +namespace OCA\Files_Trashbin; + class Trashbin { - - const DEFAULT_RETENTION_OBLIGATION=180; // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days) + // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days) + const DEFAULT_RETENTION_OBLIGATION=180; + + // unit: percentage; 50% of available disk space/quota + const DEFAULTMAXSIZE=50; + /** * move file to the trash bin - * + * * @param $file_path path to the deleted file/directory relative to the files root directory */ public static function move2trash($file_path) { $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView('/'. $user); - if (!$view->is_dir('files_trashbin')) { - $view->mkdir('files_trashbin'); - $view->mkdir("versions_trashbin"); - } - - $path_parts = pathinfo($file_path); - - $deleted = $path_parts['basename']; - $location = $path_parts['dirname']; - $timestamp = time(); - $mime = $view->getMimeType('files'.$file_path); - - if ( $view->is_dir('files'.$file_path) ) { - $type = 'dir'; - } else { - $type = 'file'; - } - - self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); + $view = new \OC\Files\View('/'. $user); + if (!$view->is_dir('files_trashbin')) { + $view->mkdir('files_trashbin'); + $view->mkdir("files_trashbin/files"); + $view->mkdir("files_trashbin/versions"); + $view->mkdir("files_trashbin/keyfiles"); + } + + $path_parts = pathinfo($file_path); + + $deleted = $path_parts['basename']; + $location = $path_parts['dirname']; + $timestamp = time(); + $mime = $view->getMimeType('files'.$file_path); + + if ( $view->is_dir('files'.$file_path) ) { + $type = 'dir'; + } else { + $type = 'file'; + } - if ( $view->file_exists('files_trashbin/'.$deleted.'.d'.$timestamp) ) { - $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); + $trashbinSize = self::getTrashbinSize($user); + if ( $trashbinSize === false || $trashbinSize < 0 ) { + $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); + } + $trashbinSize += self::copy_recursive($file_path, 'files_trashbin/files/'.$deleted.'.d'.$timestamp, $view); + + if ( $view->file_exists('files_trashbin/files/'.$deleted.'.d'.$timestamp) ) { + $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin. - $view->deleteAll('files_trashbin/'.$deleted.'.d'.$timestamp); + $view->deleteAll('files_trashbin/files/'.$deleted.'.d'.$timestamp); \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR); return; - } - - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $view->is_dir('files_versions'.$file_path) ) { - $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); - } else if ( $versions = \OCA\Files_Versions\Storage::getVersions($file_path) ) { - foreach ($versions as $v) { - $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); - } - } + } + \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', + array('filePath' => \OC\Files\Filesystem::normalizePath($file_path), + 'trashPath' => \OC\Files\Filesystem::normalizePath($deleted.'.d'.$timestamp))); + + // Take care of file versions + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $view->is_dir('files_versions/'.$file_path) ) { + $trashbinSize += self::calculateSize(new \OC\Files\View('/'. $user.'/files_versions/'.$file_path)); + $view->rename('files_versions/'.$file_path, 'files_trashbin/versions'. $deleted.'.d'.$timestamp); + } else if ( $versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path) ) { + foreach ($versions as $v) { + $trashbinSize += $view->filesize('files_versions'.$v['path'].'.v'.$v['version']); + $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'files_trashbin/versions/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); + } + } + } + + // Take care of encryption keys + $keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/'.$file_path); + if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile.'.key') ) { + if ( $view->is_dir('files'.$file_path) ) { + $trashbinSize += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); + $view->rename($keyfile, 'files_trashbin/keyfiles/'. $deleted.'.d'.$timestamp); + } else { + $trashbinSize += $view->filesize($keyfile.'.key'); + $view->rename($keyfile.'.key', 'files_trashbin/keyfiles/'. $deleted.'.key.d'.$timestamp); + } } } else { - \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin' , \OC_log::ERROR); + \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin', \OC_log::ERROR); } - self::expire(); + // get available disk space for user + $quota = \OC_Preferences::getValue($user, 'files', 'quota'); + if ( $quota === null || $quota === 'default') { + $quota = \OC_Appconfig::getValue('files', 'default_quota'); + } + if ( $quota === null || $quota === 'none' ) { + $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers()); + } else { + $quota = \OCP\Util::computerFileSize($quota); + } + + // calculate available space for trash bin + $rootInfo = $view->getFileInfo('/files'); + $free = $quota-$rootInfo['size']; // remaining free space for user + if ( $free > 0 ) { + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions + } else { + $availableSpace = $free-$trashbinSize; + } + $trashbinSize -= self::expire($availableSpace); + + self::setTrashbinSize($user, $trashbinSize); + } - - + + /** * restore files from trash bin * @param $file path to the deleted file @@ -86,199 +136,309 @@ class Trashbin { * @param $timestamp time when the file was deleted */ public static function restore($file, $filename, $timestamp) { - $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView('/'.$user); + $view = new \OC\Files\View('/'.$user); + $trashbinSize = self::getTrashbinSize($user); + if ( $trashbinSize === false || $trashbinSize < 0 ) { + $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); + } if ( $timestamp ) { - $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash' + .' WHERE user=? AND id=? AND timestamp=?'); $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); if ( count($result) != 1 ) { \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR); return false; } - - // if location no longer exists, restore file in the root directory - $location = $result[0]['location']; - if ( $result[0]['location'] != '/' && - (!$view->is_dir('files'.$result[0]['location']) || - !$view->isUpdatable('files'.$result[0]['location'])) ) { - $location = ''; + + // if location no longer exists, restore file in the root directory + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && + (!$view->is_dir('files'.$result[0]['location']) || + !$view->isUpdatable('files'.$result[0]['location'])) ) { + $location = ''; } } else { - $path_parts = pathinfo($filename); + $path_parts = pathinfo($file); $result[] = array( 'location' => $path_parts['dirname'], - 'type' => $view->is_dir('/files_trashbin/'.$file) ? 'dir' : 'files', + 'type' => $view->is_dir('/files_trashbin/files/'.$file) ? 'dir' : 'files', ); $location = ''; } - $source = \OC_Filesystem::normalizePath('files_trashbin/'.$file); - $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); - + $source = \OC\Files\Filesystem::normalizePath('files_trashbin/files/'.$file); + $target = \OC\Files\Filesystem::normalizePath('files/'.$location.'/'.$filename); + // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); $mtime = $view->filemtime($source); if( $view->rename($source, $target.$ext) ) { $view->touch($target.$ext, $mtime); + \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', + array('filePath' => \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext), + 'trashPath' => \OC\Files\Filesystem::normalizePath($file))); + if ($view->is_dir($target.$ext)) { + $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.$target.$ext)); + } else { + $trashbinSize -= $view->filesize($target.$ext); + } // if versioning app is enabled, copy versions from the trash bin back to the original location - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $result[0]['type'] == 'dir' ) { - $view->rename(\OC_Filesystem::normalizePath('versions_trashbin/'. $file), \OC_Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); - } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) { + if ( \OCP\App::isEnabled('files_versions') ) { + if ($timestamp ) { + $versionedFile = $filename; + } else { + $versionedFile = $file; + } + if ( $result[0]['type'] === 'dir' ) { + $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.'files_trashbin/versions/'. $file)); + $view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/'. $file), \OC\Files\Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); + } else if ( $versions = self::getVersionsFromTrash($versionedFile, $timestamp) ) { foreach ($versions as $v) { if ($timestamp ) { - $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp); + $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); } else { - $view->rename('versions_trashbin/'.$file.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); - } - } - } + $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v); + $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } + } + } } - + + // Take care of encryption keys TODO! Get '.key' in file between file name and delete date (also for permanent delete!) + $parts = pathinfo($file); + if ( $result[0]['type'] === 'dir' ) { + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename); + } else { + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename.'.key'); + } + if ($timestamp) { + $keyfile .= '.d'.$timestamp; + } + if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) { + if ( $result[0]['type'] === 'dir' ) { + $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); + $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename); + } else { + $trashbinSize -= $view->filesize($keyfile); + $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename.'.key'); + } + } + if ( $timestamp ) { - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); $query->execute(array($user,$filename,$timestamp)); } + self::setTrashbinSize($user, $trashbinSize); + return true; } else { - \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename , \OC_log::ERROR); + \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename, \OC_log::ERROR); } return false; } - - /** - * delete file from trash bin permanently + + /** + * delete file from trash bin permanently * @param $filename path to the file - * @param $timestamp of deletion time - * @return true/false - */ - public static function delete($filename, $timestamp=null) { - - $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView('/'.$user); - - if ( $timestamp ) { - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $query->execute(array($user,$filename,$timestamp)); + * @param $timestamp of deletion time + * @return size of deleted files + */ + public static function delete($filename, $timestamp=null) { + $user = \OCP\User::getUser(); + $view = new \OC\Files\View('/'.$user); + $size = 0; + + $trashbinSize = self::getTrashbinSize($user); + if ( $trashbinSize === false || $trashbinSize < 0 ) { + $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); + } + + if ( $timestamp ) { + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); $file = $filename.'.d'.$timestamp; } else { $file = $filename; } - + if ( \OCP\App::isEnabled('files_versions') ) { - if ($view->is_dir('versions_trashbin/'.$file)) { - $view->unlink('versions_trashbin/'.$file); - } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) { + if ($view->is_dir('files_trashbin/versions/'.$file)) { + $size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file)); + $view->unlink('files_trashbin/versions/'.$file); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { foreach ($versions as $v) { if ($timestamp ) { - $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp); + $size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp); + $view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp); } else { - $view->unlink('versions_trashbin/'.$file.'.v'.$v); + $size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v); + $view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v); } } } - } - - $view->unlink('/files_trashbin/'.$file); - - return true; - } - + } + + // Take care of encryption keys + $parts = pathinfo($file); + if ( $view->is_dir('/files_trashbin/files/'.$file) ) { + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename); + } else { + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename.'.key'); + } + if ($timestamp) { + $keyfile .= '.d'.$timestamp; + } + if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) { + if ( $view->is_dir($keyfile) ) { + $size += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); + } else { + $size += $view->filesize($keyfile); + } + $view->unlink($keyfile); + } - /** - * clean up the trash bin - */ - private static function expire() { + if ($view->is_dir('/files_trashbin/files/'.$file)) { + $size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file)); + } else { + $size += $view->filesize('/files_trashbin/files/'.$file); + } + $view->unlink('/files_trashbin/files/'.$file); + $trashbinSize -= $size; + self::setTrashbinSize($user, $trashbinSize); - $view = new \OC_FilesystemView('/'.\OCP\User::getUser()); + return $size; + } + + /** + * check to see whether a file exists in trashbin + * @param $filename path to the file + * @param $timestamp of deletion time + * @return true if file exists, otherwise false + */ + public static function file_exists($filename, $timestamp=null) { $user = \OCP\User::getUser(); - - $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); + $view = new \OC\Files\View('/'.$user); + + if ($timestamp) { + $filename = $filename.'.d'.$timestamp; + } else { + $filename = $filename; + } + + $target = \OC\Files\Filesystem::normalizePath('files_trashbin/files/'.$filename); + return $view->file_exists($target); + } + + /** + * clean up the trash bin + * @param max. available disk space for trashbin + */ + private static function expire($availableSpace) { + + $user = \OCP\User::getUser(); + $view = new \OC\Files\View('/'.$user); + $size = 0; + + $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); $result = $query->execute(array($user))->fetchAll(); - - $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION); - + + $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', + self::DEFAULT_RETENTION_OBLIGATION); + $limit = time() - ($retention_obligation * 86400); foreach ( $result as $r ) { $timestamp = $r['timestamp']; $filename = $r['id']; if ( $r['timestamp'] < $limit ) { - $view->unlink('files_trashbin/'.$filename.'.d'.$timestamp); - if ($r['type'] == 'dir') { - $view->unlink('versions_trashbin/'.$filename.'.d'.$timestamp); - } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { - foreach ($versions as $v) { - $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp); - } - } + $size += self::delete($filename, $timestamp); } } - - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND timestampexecute(array($user,$limit)); + $availableSpace = $availableSpace + $size; + // if size limit for trash bin reached, delete oldest files in trash bin + if ($availableSpace < 0) { + $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash' + .' WHERE user=? ORDER BY timestamp ASC'); + $result = $query->execute(array($user))->fetchAll(); + $length = count($result); + $i = 0; + while ( $i < $length && $availableSpace < 0 ) { + $tmp = self::delete($result[$i]['id'], $result[$i]['timestamp']); + $availableSpace += $tmp; + $size += $tmp; + $i++; + } + + + } + + return $size; } - + /** * recursive copy to copy a whole directory - * + * * @param $source source path, relative to the users files directory * @param $destination destination path relative to the users root directoy * @param $view file view for the users root directory */ private static function copy_recursive( $source, $destination, $view ) { + $size = 0; if ( $view->is_dir( 'files'.$source ) ) { $view->mkdir( $destination ); $view->touch($destination, $view->filemtime('files'.$source)); foreach ( \OC_Files::getDirectoryContent($source) as $i ) { $pathDir = $source.'/'.$i['name']; if ( $view->is_dir('files'.$pathDir) ) { - self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view); + $size += self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view); } else { + $size += $view->filesize('files'.$pathDir); $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] ); $view->touch($destination . '/' . $i['name'], $view->filemtime('files'.$pathDir)); } } } else { + $size += $view->filesize('files'.$source); $view->copy( 'files'.$source, $destination ); $view->touch($destination, $view->filemtime('files'.$source)); } + return $size; } - + /** * find all versions which belong to the file we want to restore * @param $filename name of the file which should be restored * @param $timestamp timestamp when the file was deleted */ private static function getVersionsFromTrash($filename, $timestamp) { - $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/versions_trashbin'); - $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); - $versions = array(); - + $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_trashbin/versions'); + $versionsName = $view->getLocalFile($filename); + $versions = array(); if ($timestamp ) { - // fetch for old versions + // fetch for old versions $matches = glob( $versionsName.'.v*.d'.$timestamp ); $offset = -strlen($timestamp)-2; } else { $matches = glob( $versionsName.'.v*' ); - } - + } + foreach( $matches as $ma ) { if ( $timestamp ) { - $parts = explode( '.v', substr($ma, 0, $offset) ); + $parts = explode( '.v', substr($ma, 0, $offset) ); $versions[] = ( end( $parts ) ); } else { - $parts = explode( '.v', $ma ); + $parts = explode( '.v', $ma ); $versions[] = ( end( $parts ) ); } } return $versions; } - + /** * find unique extension for restored file if a file with the same name already exists * @param $location where the file should be restored @@ -287,17 +447,71 @@ class Trashbin { * @return string with unique extension */ private static function getUniqueExtension($location, $filename, $view) { - $ext = ''; - if ( $view->file_exists('files'.$location.'/'.$filename) ) { - $tmpext = '.restored'; - $ext = $tmpext; - $i = 1; - while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) { - $ext = $tmpext.$i; - $i++; - } + $ext = ''; + if ( $view->file_exists('files'.$location.'/'.$filename) ) { + $tmpext = '.restored'; + $ext = $tmpext; + $i = 1; + while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) { + $ext = $tmpext.$i; + $i++; + } } return $ext; } -} + /** + * @brief get the size from a given root folder + * @param $view file view on the root folder + * @return size of the folder + */ + private static function calculateSize($view) { + $root = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath(''); + if (!file_exists($root)) { + return 0; + } + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root), + \RecursiveIteratorIterator::CHILD_FIRST); + $size = 0; + + foreach ($iterator as $path) { + $relpath = substr($path, strlen($root)-1); + if ( !$view->is_dir($relpath) ) { + $size += $view->filesize($relpath); + } + } + return $size; + } + + /** + * get current size of trash bin from a given user + * + * @param $user user who owns the trash bin + * @return mixed trash bin size or false if no trash bin size is stored + */ + private static function getTrashbinSize($user) { + $query = \OC_DB::prepare('SELECT size FROM *PREFIX*files_trashsize WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); + + if ($result) { + return $result[0]['size']; + } + return false; + } + + /** + * write to the database how much space is in use for the trash bin + * + * @param $user owner of the trash bin + * @param $size size of the trash bin + */ + private static function setTrashbinSize($user, $size) { + if ( self::getTrashbinSize($user) === false) { + $query = \OC_DB::prepare('INSERT INTO *PREFIX*files_trashsize (size, user) VALUES (?, ?)'); + }else { + $query = \OC_DB::prepare('UPDATE *PREFIX*files_trashsize SET size=? WHERE user=?'); + } + $query->execute(array($size, $user)); + } + +} diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index 24e4a0e6c697249c5aeb5a5150028c747616bcaa..4c865d8981c30bf74bb125a168a55a7fd712b0eb 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -1,34 +1,41 @@
- +
- -
t('Nothing in here. Your trash bin is empty!')?>
+ +
t('Nothing in here. Your trash bin is empty!'))?>
- +
- +
- t( 'Name' ); ?> + t( 'Name' )); ?> - <?php echo $l->t( 'Restore' ); ?>" /> - t('Restore')?> + <?php p($l->t( 'Restore' )); ?>" /> + t('Restore'))?> - t( 'Deleted' ); ?> + t( 'Deleted' )); ?> + + + t('Delete'))?> + <?php p($l->t('Delete'))?>" /> + +
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index fe8a71f44e6514c3ee04af38a310fca3c3e97d34..92a38bd26350ce2bca8c382eba06fa8db05372a8 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -1,59 +1,55 @@ - + 200) $relative_date_color = 200; $name = str_replace('+', '%20', urlencode($file['name'])); $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> - ' + ' - id="" - data-file="" + id="" + data-file="" data-timestamp='' data-dirlisting=1 - id="" - data-file="" - data-timestamp='' + id="" + data-file="" + data-timestamp='' data-dirlisting=0 > - style="background-image:url()" + style="background-image:url()" - style="background-image:url()" + style="background-image:url()" > - + - + - + - + - + - + @@ -64,13 +60,12 @@ " + style="color:rgb()"> - + .$relative_date_color) ?>)"> + array( "message" => $l->t("Could not revert: %s", array($file) )))); } - diff --git a/apps/files_versions/ajax/togglesettings.php b/apps/files_versions/ajax/togglesettings.php deleted file mode 100644 index 546b37ae1aa85dc31054d759f681ed6aca633a09..0000000000000000000000000000000000000000 --- a/apps/files_versions/ajax/togglesettings.php +++ /dev/null @@ -1,10 +0,0 @@ - + + + *dbname* + true + false + + utf8 + + + + *dbprefix*files_versions + + + + + user + text + + true + 64 + + + size + text + + true + 50 + + + + +
+ +
diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml index 0155f8e830ff790b1ffe5ef09adea73248f85a74..44878da5e4d8ccabc2d4bd9a45a5ec5616fc54da 100644 --- a/apps/files_versions/appinfo/info.xml +++ b/apps/files_versions/appinfo/info.xml @@ -4,7 +4,7 @@ Versions AGPL Frank Karlitschek - 4.91 + 4.93 true Versioning of files diff --git a/apps/files_versions/appinfo/version b/apps/files_versions/appinfo/version index e6d5cb833c634c4e2972335825d1100492e7c96b..e4c0d46e55ffb2237c9e900aa77172886f6c8aa5 100644 --- a/apps/files_versions/appinfo/version +++ b/apps/files_versions/appinfo/version @@ -1 +1 @@ -1.0.2 \ No newline at end of file +1.0.3 \ No newline at end of file diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php index 437a3fec0652c281c9ea351e1bbab926d6550180..719a7208fedb1effff25b7b8366812279952fa73 100644 --- a/apps/files_versions/history.php +++ b/apps/files_versions/history.php @@ -59,7 +59,8 @@ if ( isset( $_GET['path'] ) ) { // show the history only if there is something to show $count = 999; //show the newest revisions - if( ($versions = OCA\Files_Versions\Storage::getVersions( $path, $count)) ) { + list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($path); + if( ($versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $count)) ) { $tmpl->assign( 'versions', array_reverse( $versions ) ); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index b9c54689813be409332a467055674047cf8dd0e5..a5b244174831654ac1277f419943c7e561337383 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -1,19 +1,9 @@ -$(document).ready(function() { - $('#versions').bind('change', function() { - var checked = 1; - if (!this.checked) { - checked = 0; - } - $.post(OC.filePath('files_versions','ajax','togglesettings.php'), 'versions='+checked); - }); -}); - $(document).ready(function(){ if (typeof FileActions !== 'undefined') { - // Add history button to 'files/index.php' + // Add versions button to 'files/index.php' FileActions.register( 'file' - , t('files_versions', 'History') + , t('files_versions', 'Versions') , OC.PERMISSION_UPDATE , function() { // Specify icon for hitory button @@ -41,6 +31,10 @@ $(document).ready(function(){ } }); +function goToVersionPage(url){ + window.location.assign(url); +} + function createVersionsDropdown(filename, files) { var historyUrl = OC.linkTo('files_versions', 'history.php') + '?path='+encodeURIComponent( $( '#dir' ).val() ).replace( /%2F/g, '/' )+'/'+encodeURIComponent( filename ); @@ -51,7 +45,7 @@ function createVersionsDropdown(filename, files) { html += ''; html += ''; html += '
'; - html += ''; + html += ''; html += ''; if (filename) { @@ -61,6 +55,10 @@ function createVersionsDropdown(filename, files) { $(html).appendTo($('thead .share')); } + $("#makelink").click(function() { + goToVersionPage(historyUrl); + }); + $.ajax({ type: 'GET', url: OC.filePath('files_versions', 'ajax', 'getVersions.php'), diff --git a/apps/files_versions/l10n/ar.php b/apps/files_versions/l10n/ar.php index 1f1f3100405941888a419eb9dcf8fff0c7496af4..b84445972d35eb2ac6efca53e5a037366fffef6a 100644 --- a/apps/files_versions/l10n/ar.php +++ b/apps/files_versions/l10n/ar.php @@ -1,5 +1,3 @@ "السجل الزمني", -"Files Versioning" => "أصدرة الملفات", -"Enable" => "تفعيل" +"Versions" => "الإصدارات" ); diff --git a/apps/files_versions/l10n/bg_BG.php b/apps/files_versions/l10n/bg_BG.php index 6ecf12d0b00c50686c7a413e339db78a6e3c764b..6a1882c2bfddb954b77451753f671f4cabc45ef9 100644 --- a/apps/files_versions/l10n/bg_BG.php +++ b/apps/files_versions/l10n/bg_BG.php @@ -1,4 +1,3 @@ "История", -"Enable" => "Включено" +"Versions" => "Версии" ); diff --git a/apps/files_versions/l10n/bn_BD.php b/apps/files_versions/l10n/bn_BD.php index dffa4d79a06f72c21afa13fd361bb5041512aa1e..f3b0071a356113deb1855f60080875bc13bff700 100644 --- a/apps/files_versions/l10n/bn_BD.php +++ b/apps/files_versions/l10n/bn_BD.php @@ -1,5 +1,3 @@ "ইতিহাস", -"Files Versioning" => "ফাইল ভার্সন করা", -"Enable" => "সক্রিয় " +"Versions" => "ভার্সন" ); diff --git a/apps/files_versions/l10n/ca.php b/apps/files_versions/l10n/ca.php index fc900c47dc7e5b857f11170c34d05fda8de2e785..433d974c8cb041d24d11c6caf0324f407cd4afb9 100644 --- a/apps/files_versions/l10n/ca.php +++ b/apps/files_versions/l10n/ca.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "El fitxer %s no s'ha pogut revertir a la versió %s", "No old versions available" => "No hi ha versións antigues disponibles", "No path specified" => "No heu especificat el camí", -"History" => "Historial", -"Revert a file to a previous version by clicking on its revert button" => "Reverteix un fitxer a una versió anterior fent clic en el seu botó de reverteix", -"Files Versioning" => "Fitxers de Versions", -"Enable" => "Habilita" +"Versions" => "Versions", +"Revert a file to a previous version by clicking on its revert button" => "Reverteix un fitxer a una versió anterior fent clic en el seu botó de reverteix" ); diff --git a/apps/files_versions/l10n/cs_CZ.php b/apps/files_versions/l10n/cs_CZ.php index 22d4a2ad827235619e465c7712732c96797aab6f..087d800137e7a1f5bf10c089ac5db8122a436e85 100644 --- a/apps/files_versions/l10n/cs_CZ.php +++ b/apps/files_versions/l10n/cs_CZ.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "Soubor %s nemohl být navrácen na verzi %s", "No old versions available" => "Nejsou dostupné žádné starší verze", "No path specified" => "Nezadána cesta", -"History" => "Historie", -"Revert a file to a previous version by clicking on its revert button" => "Navraťte soubor do předchozí verze kliknutím na tlačítko navrátit", -"Files Versioning" => "Verzování souborů", -"Enable" => "Povolit" +"Versions" => "Verze", +"Revert a file to a previous version by clicking on its revert button" => "Navraťte soubor do předchozí verze kliknutím na tlačítko navrátit" ); diff --git a/apps/files_versions/l10n/da.php b/apps/files_versions/l10n/da.php index 985797476439ed9074b3a24d57d4ed8aaddcd32b..76ababe665ae6841b403b78518a7fc36b2deae0b 100644 --- a/apps/files_versions/l10n/da.php +++ b/apps/files_versions/l10n/da.php @@ -1,5 +1,10 @@ "Historik", -"Files Versioning" => "Versionering af filer", -"Enable" => "Aktiver" +"Could not revert: %s" => "Kunne ikke genskabe: %s", +"success" => "success", +"File %s was reverted to version %s" => "Filen %s blev genskabt til version: %s", +"failure" => "fejl", +"File %s could not be reverted to version %s" => "Filen %s blev genskabt til version: %s", +"No old versions available" => "Ingen gamle version tilgængelige", +"No path specified" => "Ingen sti specificeret", +"Revert a file to a previous version by clicking on its revert button" => "Genskab en fil til en tidligere version ved at klikke på denne genskab knap." ); diff --git a/apps/files_versions/l10n/de.php b/apps/files_versions/l10n/de.php index 2fcb996de7bae035700e178be957b88e65c44bc7..c34a8c1fd3ed3e67291c3d79f44a7b31b2cc0672 100644 --- a/apps/files_versions/l10n/de.php +++ b/apps/files_versions/l10n/de.php @@ -1,5 +1,11 @@ "Historie", -"Files Versioning" => "Dateiversionierung", -"Enable" => "Aktivieren" +"Could not revert: %s" => "Konnte %s nicht zurücksetzen", +"success" => "Erfolgreich", +"File %s was reverted to version %s" => "Datei %s wurde auf Version %s zurückgesetzt", +"failure" => "Fehlgeschlagen", +"File %s could not be reverted to version %s" => "Datei %s konnte nicht auf Version %s zurückgesetzt werden", +"No old versions available" => "Keine älteren Versionen verfügbar", +"No path specified" => "Kein Pfad angegeben", +"Versions" => "Versionen", +"Revert a file to a previous version by clicking on its revert button" => "Setze eine Datei durch klicken auf den Zurücksetzen Button zurück" ); diff --git a/apps/files_versions/l10n/de_DE.php b/apps/files_versions/l10n/de_DE.php index cf33bb071e6e2b5bd9fc265150d57765cf432431..c0b2f2a83f77bbdd8b16a993d6d174f83e902453 100644 --- a/apps/files_versions/l10n/de_DE.php +++ b/apps/files_versions/l10n/de_DE.php @@ -1,9 +1,11 @@ "Konnte %s nicht zurücksetzen", "success" => "Erfolgreich", +"File %s was reverted to version %s" => "Die Datei %s wurde zur Version %s zurückgesetzt", "failure" => "Fehlgeschlagen", -"No old versions available" => "keine älteren Versionen verfügbar", +"File %s could not be reverted to version %s" => "Die Datei %s konnte nicht zur Version %s zurückgesetzt werden", +"No old versions available" => "Keine älteren Versionen verfügbar", "No path specified" => "Kein Pfad angegeben", -"History" => "Historie", -"Files Versioning" => "Dateiversionierung", -"Enable" => "Aktivieren" +"Versions" => "Versionen", +"Revert a file to a previous version by clicking on its revert button" => "Setze eine Datei durch Klicken auf den Zurücksetzen-Button auf eine frühere Version zurück" ); diff --git a/apps/files_versions/l10n/el.php b/apps/files_versions/l10n/el.php index 6b189c2cdd398168d89a99c53f1b90cccfa73253..6e1900b233b7a5ca016a93f1a9c710a2edc37108 100644 --- a/apps/files_versions/l10n/el.php +++ b/apps/files_versions/l10n/el.php @@ -1,5 +1,10 @@ "Ιστορικό", -"Files Versioning" => "Εκδόσεις Αρχείων", -"Enable" => "Ενεργοποίηση" +"Could not revert: %s" => "Αδυναμία επαναφοράς του: %s", +"success" => "επιτυχία", +"File %s was reverted to version %s" => "Το αρχείο %s επαναφέρθηκε στην έκδοση %s", +"failure" => "αποτυχία", +"File %s could not be reverted to version %s" => "Το αρχείο %s δεν είναι δυνατό να επαναφερθεί στην έκδοση %s", +"No old versions available" => "Μη διαθέσιμες παλιές εκδόσεις", +"No path specified" => "Δεν καθορίστηκε διαδρομή", +"Revert a file to a previous version by clicking on its revert button" => "Επαναφορά ενός αρχείου σε προηγούμενη έκδοση πατώντας στο κουμπί επαναφοράς" ); diff --git a/apps/files_versions/l10n/es.php b/apps/files_versions/l10n/es.php index 608e171a4b12a99b7f5efc4c043e77787675b1eb..8c1a30f822aeba4dbba2b3217cdce06a10d2bdb2 100644 --- a/apps/files_versions/l10n/es.php +++ b/apps/files_versions/l10n/es.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "El archivo %s no puede ser revertido a la version %s", "No old versions available" => "No hay versiones antiguas disponibles", "No path specified" => "Ruta no especificada", -"History" => "Historial", -"Revert a file to a previous version by clicking on its revert button" => "Revertir un archivo a una versión anterior haciendo clic en el boton de revertir", -"Files Versioning" => "Versionado de archivos", -"Enable" => "Habilitar" +"Versions" => "Revisiones", +"Revert a file to a previous version by clicking on its revert button" => "Revertir un archivo a una versión anterior haciendo clic en el boton de revertir" ); diff --git a/apps/files_versions/l10n/es_AR.php b/apps/files_versions/l10n/es_AR.php index 74d8907fc3539a3f4c73dd3b549d6de13e667e4b..363693c913b86701f400256af6bef2289c1c7d77 100644 --- a/apps/files_versions/l10n/es_AR.php +++ b/apps/files_versions/l10n/es_AR.php @@ -1,5 +1,11 @@ "Historia", -"Files Versioning" => "Versionado de archivos", -"Enable" => "Activar" +"Could not revert: %s" => "No se pudo revertir: %s ", +"success" => "Éxito", +"File %s was reverted to version %s" => "El archivo %s fue revertido a la versión %s", +"failure" => "error", +"File %s could not be reverted to version %s" => "El archivo %s no pudo ser revertido a la versión %s", +"No old versions available" => "No hay versiones antiguas disponibles", +"No path specified" => "Ruta de acceso no especificada", +"Versions" => "Versiones", +"Revert a file to a previous version by clicking on its revert button" => "Revertí un archivo a una versión anterior haciendo click en su botón de \"revertir\"" ); diff --git a/apps/files_versions/l10n/et_EE.php b/apps/files_versions/l10n/et_EE.php index ff119d5374efa7fc9ab4deccbd6c1b391e4aa47c..fa2a33f9dda11cef85b7f15bfa58a74d756c4604 100644 --- a/apps/files_versions/l10n/et_EE.php +++ b/apps/files_versions/l10n/et_EE.php @@ -1,5 +1,6 @@ "Ajalugu", -"Files Versioning" => "Failide versioonihaldus", -"Enable" => "Luba" +"success" => "korras", +"failure" => "ebaõnnestus", +"No old versions available" => "Vanu versioone pole saadaval", +"No path specified" => "Asukohta pole määratud" ); diff --git a/apps/files_versions/l10n/eu.php b/apps/files_versions/l10n/eu.php index c6b4cd7692dada9b554fc4a56d00edfca3cc9d8a..2a7f279af16ebe97f40838ec90b346496d915a4a 100644 --- a/apps/files_versions/l10n/eu.php +++ b/apps/files_versions/l10n/eu.php @@ -1,5 +1,11 @@ "Historia", -"Files Versioning" => "Fitxategien Bertsioak", -"Enable" => "Gaitu" +"Could not revert: %s" => "Ezin izan da leheneratu: %s", +"success" => "arrakasta", +"File %s was reverted to version %s" => "%s fitxategia %s bertsiora leheneratu da", +"failure" => "errorea", +"File %s could not be reverted to version %s" => "%s fitxategia ezin da %s bertsiora leheneratu", +"No old versions available" => "Ez dago bertsio zaharrik eskuragarri", +"No path specified" => "Ez da bidea zehaztu", +"Versions" => "Bertsioak", +"Revert a file to a previous version by clicking on its revert button" => "Itzuli fitxategi bat aurreko bertsio batera leheneratu bere leheneratu botoia sakatuz" ); diff --git a/apps/files_versions/l10n/fa.php b/apps/files_versions/l10n/fa.php index 9b618fdd320ddfde7bf0b49010aa024c9a3a96ed..4ec6aa1bbb434d1a2f4aa95e8b7c3fc2fc6ce0fb 100644 --- a/apps/files_versions/l10n/fa.php +++ b/apps/files_versions/l10n/fa.php @@ -1,4 +1,8 @@ "تاریخچه", -"Enable" => "فعال" +"Could not revert: %s" => "بازگردانی امکان ناپذیر است: %s", +"success" => "موفقیت", +"failure" => "شکست", +"No old versions available" => "هیچ نسخه قدیمی در دسترس نیست", +"No path specified" => "هیچ مسیری مشخص نشده است", +"Revert a file to a previous version by clicking on its revert button" => "بازگردانی یک پرورنده به نسخه قدیمی اش از طریق دکمه بازگردانی امکان پذیر است" ); diff --git a/apps/files_versions/l10n/fi_FI.php b/apps/files_versions/l10n/fi_FI.php index bdce8e9fe5209dc563dac6532bd64f17acda02d0..0dec7fc2580469145ed99fcade72ad5cf14dcaa0 100644 --- a/apps/files_versions/l10n/fi_FI.php +++ b/apps/files_versions/l10n/fi_FI.php @@ -1,5 +1,10 @@ "Historia", -"Files Versioning" => "Tiedostojen versiointi", -"Enable" => "Käytä" +"Could not revert: %s" => "Palautus epäonnistui: %s", +"success" => "onnistui", +"File %s was reverted to version %s" => "Tiedosto %s palautettiin versioon %s", +"failure" => "epäonnistui", +"File %s could not be reverted to version %s" => "Tiedoston %s palautus versioon %s epäonnistui", +"No old versions available" => "Vanhoja ei ole saatavilla", +"No path specified" => "Polkua ei ole määritetty", +"Revert a file to a previous version by clicking on its revert button" => "Palauta tiedoston edellinen versio napsauttamalla palautuspainiketta" ); diff --git a/apps/files_versions/l10n/fr.php b/apps/files_versions/l10n/fr.php index 6b2cf9ba6b5f141fd8b87e7447eb81f6e427aacd..76ad8fc97a6b8dff2f161e95fab4e2ce33efc17f 100644 --- a/apps/files_versions/l10n/fr.php +++ b/apps/files_versions/l10n/fr.php @@ -6,8 +6,5 @@ "File %s could not be reverted to version %s" => "Le fichier %s ne peut être restauré dans sa version %s", "No old versions available" => "Aucune ancienne version n'est disponible", "No path specified" => "Aucun chemin spécifié", -"History" => "Historique", -"Revert a file to a previous version by clicking on its revert button" => "Restaurez un fichier dans une version antérieure en cliquant sur son bouton de restauration", -"Files Versioning" => "Versionnage des fichiers", -"Enable" => "Activer" +"Revert a file to a previous version by clicking on its revert button" => "Restaurez un fichier dans une version antérieure en cliquant sur son bouton de restauration" ); diff --git a/apps/files_versions/l10n/gl.php b/apps/files_versions/l10n/gl.php index 7e44b8898bfa68e9f0d073922a5d824749f43ac4..586ef8c3a68efafd303c8dc4daa7db15b676e139 100644 --- a/apps/files_versions/l10n/gl.php +++ b/apps/files_versions/l10n/gl.php @@ -1,5 +1,11 @@ "Historial", -"Files Versioning" => "Sistema de versión de ficheiros", -"Enable" => "Activar" +"Could not revert: %s" => "Non foi posíbel reverter: %s", +"success" => "feito", +"File %s was reverted to version %s" => "O ficheiro %s foi revertido á versión %s", +"failure" => "produciuse un fallo", +"File %s could not be reverted to version %s" => "Non foi posíbel reverter o ficheiro %s á versión %s", +"No old versions available" => "Non hai versións antigas dispoñíbeis", +"No path specified" => "Non foi indicada a ruta", +"Versions" => "Versións", +"Revert a file to a previous version by clicking on its revert button" => "Reverta un ficheiro a unha versión anterior premendo no botón reversión" ); diff --git a/apps/files_versions/l10n/hu_HU.php b/apps/files_versions/l10n/hu_HU.php index 95d37ad06ed686b7b692b81802c5479785e91a5c..9f7420157e1a683619a8eee153e88e5bb49bd19f 100644 --- a/apps/files_versions/l10n/hu_HU.php +++ b/apps/files_versions/l10n/hu_HU.php @@ -1,5 +1,11 @@ "Korábbi változatok", -"Files Versioning" => "Az állományok verzionálása", -"Enable" => "engedélyezve" +"Could not revert: %s" => "Nem sikerült átállni a változatra: %s", +"success" => "sikerült", +"File %s was reverted to version %s" => "%s állományt átállítottuk erre a változatra: %s", +"failure" => "nem sikerült", +"File %s could not be reverted to version %s" => "%s állományt nem sikerült átállítani erre a változatra: %s", +"No old versions available" => "Nincs régebbi változat", +"No path specified" => "Nincs megadva az útvonal", +"Versions" => "Az állományok korábbi változatai", +"Revert a file to a previous version by clicking on its revert button" => "Az állomány átállítható egy régebbi változatra, ha a gombra kattint" ); diff --git a/apps/files_versions/l10n/id.php b/apps/files_versions/l10n/id.php index 6c553327c42aca3414bdffb9850465b8737d405a..4662aa86432630bce00ac0c08bfafd00642d9044 100644 --- a/apps/files_versions/l10n/id.php +++ b/apps/files_versions/l10n/id.php @@ -1,5 +1,10 @@ "riwayat", -"Files Versioning" => "pembuatan versi file", -"Enable" => "aktifkan" +"Could not revert: %s" => "Tidak dapat mengembalikan: %s", +"success" => "sukses", +"File %s was reverted to version %s" => "Berkas %s telah dikembalikan ke versi %s", +"failure" => "gagal", +"File %s could not be reverted to version %s" => "Berkas %s gagal dikembalikan ke versi %s", +"No old versions available" => "Versi lama tidak tersedia", +"No path specified" => "Lokasi tidak ditentukan", +"Revert a file to a previous version by clicking on its revert button" => "Kembalikan berkas ke versi sebelumnya dengan mengklik tombol kembalikan" ); diff --git a/apps/files_versions/l10n/is.php b/apps/files_versions/l10n/is.php index ccb8287b71e9be068471d5db9332e2b5fd34ea66..d165a78c31ef2c7b51d222bc2f657d16c42bb037 100644 --- a/apps/files_versions/l10n/is.php +++ b/apps/files_versions/l10n/is.php @@ -1,5 +1,3 @@ "Saga", -"Files Versioning" => "Útgáfur af skrám", -"Enable" => "Virkja" +"Versions" => "Útgáfur" ); diff --git a/apps/files_versions/l10n/it.php b/apps/files_versions/l10n/it.php index 3289f7f68d179e5bb696cb9be0a4440225f50237..bca00879993cd8b8d25ba51fb5ffe5df59e367af 100644 --- a/apps/files_versions/l10n/it.php +++ b/apps/files_versions/l10n/it.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "Il file %s non può essere ripristinato alla versione %s", "No old versions available" => "Non sono disponibili versioni precedenti", "No path specified" => "Nessun percorso specificato", -"History" => "Cronologia", -"Revert a file to a previous version by clicking on its revert button" => "Ripristina un file a una versione precedente facendo clic sul rispettivo pulsante di ripristino", -"Files Versioning" => "Controllo di versione dei file", -"Enable" => "Abilita" +"Versions" => "Versioni", +"Revert a file to a previous version by clicking on its revert button" => "Ripristina un file a una versione precedente facendo clic sul rispettivo pulsante di ripristino" ); diff --git a/apps/files_versions/l10n/ja_JP.php b/apps/files_versions/l10n/ja_JP.php index 16018765708033fcbf6b8ebeb30c19532b0d20a1..0c2dbd401c4eb1950686a7c5844ff1e6db2102d0 100644 --- a/apps/files_versions/l10n/ja_JP.php +++ b/apps/files_versions/l10n/ja_JP.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "ファイル %s をバージョン %s に戻せませんでした", "No old versions available" => "利用可能な古いバージョンはありません", "No path specified" => "パスが指定されていません", -"History" => "履歴", -"Revert a file to a previous version by clicking on its revert button" => "もとに戻すボタンをクリックすると、ファイルを過去のバージョンに戻します", -"Files Versioning" => "ファイルのバージョン管理", -"Enable" => "有効化" +"Versions" => "バージョン", +"Revert a file to a previous version by clicking on its revert button" => "もとに戻すボタンをクリックすると、ファイルを過去のバージョンに戻します" ); diff --git a/apps/files_versions/l10n/lv.php b/apps/files_versions/l10n/lv.php index 2203dc706b82565bcd4535c88b108d147791e2b0..bf8d40fa0f0b6fca378602ff349d4b40501bccef 100644 --- a/apps/files_versions/l10n/lv.php +++ b/apps/files_versions/l10n/lv.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "Datni %s nevarēja atgriezt uz versiju %s", "No old versions available" => "Nav pieejamu vecāku versiju", "No path specified" => "Nav norādīts ceļš", -"History" => "Vēsture", -"Revert a file to a previous version by clicking on its revert button" => "Atgriez datni uz iepriekšēju versiju, spiežot uz tās atgriešanas pogu", -"Files Versioning" => "Datņu versiju izskošana", -"Enable" => "Aktivēt" +"Versions" => "Versijas", +"Revert a file to a previous version by clicking on its revert button" => "Atgriez datni uz iepriekšēju versiju, spiežot uz tās atgriešanas pogu" ); diff --git a/apps/files_versions/l10n/mk.php b/apps/files_versions/l10n/mk.php index d3ec233fe4130e90f7a181a397a3ceac7e720951..6a1882c2bfddb954b77451753f671f4cabc45ef9 100644 --- a/apps/files_versions/l10n/mk.php +++ b/apps/files_versions/l10n/mk.php @@ -1,5 +1,3 @@ "Историја", -"Files Versioning" => "Верзии на датотеки", -"Enable" => "Овозможи" +"Versions" => "Версии" ); diff --git a/apps/files_versions/l10n/nl.php b/apps/files_versions/l10n/nl.php index cd147ca693f0af302b7a479a9d643bb1c51ebf35..c50f76c7addcddb36e62a9569f9fda51b7a80ea8 100644 --- a/apps/files_versions/l10n/nl.php +++ b/apps/files_versions/l10n/nl.php @@ -1,5 +1,10 @@ "Geschiedenis", -"Files Versioning" => "Bestand versies", -"Enable" => "Activeer" +"Could not revert: %s" => "Kon niet terugdraaien: %s", +"success" => "succes", +"File %s was reverted to version %s" => "Bestand %s is teruggedraaid naar versie %s", +"failure" => "mislukking", +"File %s could not be reverted to version %s" => "Bestand %s kon niet worden teruggedraaid naar versie %s", +"No old versions available" => "Geen oudere versies beschikbaar", +"No path specified" => "Geen pad opgegeven", +"Revert a file to a previous version by clicking on its revert button" => "Draai een bestand terug naar een voorgaande versie door te klikken op de terugdraai knop" ); diff --git a/apps/files_versions/l10n/pl.php b/apps/files_versions/l10n/pl.php index a0247b8abc62d5caa6a8279c347915901031ce8a..68944e86760cec54b3c80094ea8e4acd5c409d39 100644 --- a/apps/files_versions/l10n/pl.php +++ b/apps/files_versions/l10n/pl.php @@ -1,5 +1,11 @@ "Historia", -"Files Versioning" => "Wersjonowanie plików", -"Enable" => "Włącz" +"Could not revert: %s" => "Nie można było przywrócić: %s", +"success" => "sukces", +"File %s was reverted to version %s" => "Plik %s został przywrócony do wersji %s", +"failure" => "porażka", +"File %s could not be reverted to version %s" => "Plik %s nie mógł być przywrócony do wersji %s", +"No old versions available" => "Nie są dostępne żadne starsze wersje", +"No path specified" => "Nie podano ścieżki", +"Versions" => "Wersje", +"Revert a file to a previous version by clicking on its revert button" => "Przywróć plik do poprzedniej wersji klikając w jego przycisk przywrócenia" ); diff --git a/apps/files_versions/l10n/pt_BR.php b/apps/files_versions/l10n/pt_BR.php index 854a30e6beeab87a003cddd422d0a5209a91a533..f68197ef09219920fe97cd500c95e0bbb7143472 100644 --- a/apps/files_versions/l10n/pt_BR.php +++ b/apps/files_versions/l10n/pt_BR.php @@ -1,5 +1,10 @@ "Histórico", -"Files Versioning" => "Versionamento de Arquivos", -"Enable" => "Habilitar" +"Could not revert: %s" => "Não foi possível reverter: %s", +"success" => "sucesso", +"File %s was reverted to version %s" => "Arquivo %s revertido à versão %s", +"failure" => "falha", +"File %s could not be reverted to version %s" => "Arquivo %s não pôde ser revertido à versão %s", +"No old versions available" => "Nenhuma versão antiga disponível", +"No path specified" => "Nenhum caminho especificado", +"Revert a file to a previous version by clicking on its revert button" => "Reverta um arquivo a uma versão anterior clicando no botão reverter" ); diff --git a/apps/files_versions/l10n/pt_PT.php b/apps/files_versions/l10n/pt_PT.php index dc1bde08cad0853c72c4eef5e9ec028dca68247f..2baccf3def88ad72e116891a989aae007f4c8ba3 100644 --- a/apps/files_versions/l10n/pt_PT.php +++ b/apps/files_versions/l10n/pt_PT.php @@ -1,5 +1,10 @@ "Histórico", -"Files Versioning" => "Versionamento de Ficheiros", -"Enable" => "Activar" +"Could not revert: %s" => "Não foi possível reverter: %s", +"success" => "Sucesso", +"File %s was reverted to version %s" => "O ficheiro %s foi revertido para a versão %s", +"failure" => "Falha", +"File %s could not be reverted to version %s" => "Não foi possível reverter o ficheiro %s para a versão %s", +"No old versions available" => "Não existem versões mais antigas", +"No path specified" => "Nenhum caminho especificado", +"Revert a file to a previous version by clicking on its revert button" => "Reverter um ficheiro para uma versão anterior clicando no seu botão reverter." ); diff --git a/apps/files_versions/l10n/ru.php b/apps/files_versions/l10n/ru.php index 221d24ce8d118c0677a730d5a10a7519b8670820..7377fbb5382e1ace08f0a9e2c5fca494e84e7f9d 100644 --- a/apps/files_versions/l10n/ru.php +++ b/apps/files_versions/l10n/ru.php @@ -6,8 +6,6 @@ "File %s could not be reverted to version %s" => "Файл %s не может быть возвращён к версии %s", "No old versions available" => "Нет доступных старых версий", "No path specified" => "Путь не указан", -"History" => "История", -"Revert a file to a previous version by clicking on its revert button" => "Вернуть файл к предыдущей версии нажатием на кнопку возврата", -"Files Versioning" => "Версии файлов", -"Enable" => "Включить" +"Versions" => "Версии", +"Revert a file to a previous version by clicking on its revert button" => "Вернуть файл к предыдущей версии нажатием на кнопку возврата" ); diff --git a/apps/files_versions/l10n/sk_SK.php b/apps/files_versions/l10n/sk_SK.php index 8a59286b5a54b2de8cc82fe46efc3ed4c9a349fe..50e4af4d96490bbedccaa27af135cd2ddac3acaf 100644 --- a/apps/files_versions/l10n/sk_SK.php +++ b/apps/files_versions/l10n/sk_SK.php @@ -1,10 +1,11 @@ "uspech", -"File %s was reverted to version %s" => "Subror %s bol vrateny na verziu %s", +"Could not revert: %s" => "Nemožno obnoviť: %s", +"success" => "úspech", +"File %s was reverted to version %s" => "Súbor %s bol obnovený na verziu %s", "failure" => "chyba", +"File %s could not be reverted to version %s" => "Súbor %s nemohol byť obnovený na verziu %s", "No old versions available" => "Nie sú dostupné žiadne staršie verzie", "No path specified" => "Nevybrali ste cestu", -"History" => "História", -"Files Versioning" => "Vytváranie verzií súborov", -"Enable" => "Zapnúť" +"Versions" => "Verzie", +"Revert a file to a previous version by clicking on its revert button" => "Obnovte súbor do predošlej verzie kliknutím na tlačítko obnoviť" ); diff --git a/apps/files_versions/l10n/sl.php b/apps/files_versions/l10n/sl.php index 7f386c9edaa2d8a6d49755c776f34f414e73185f..b6ad6a1e9bb1f19615e8af948080d128afe34770 100644 --- a/apps/files_versions/l10n/sl.php +++ b/apps/files_versions/l10n/sl.php @@ -1,5 +1,3 @@ "Zgodovina", -"Files Versioning" => "Sledenje različicam", -"Enable" => "Omogoči" +"No old versions available" => "Starejših različic ni na voljo" ); diff --git a/apps/files_versions/l10n/sv.php b/apps/files_versions/l10n/sv.php index 6788d1fb0f969a942cb01fd191de89f91924843b..46e2c0f8bcf3db3616af8d82a23ddf06c750b7bb 100644 --- a/apps/files_versions/l10n/sv.php +++ b/apps/files_versions/l10n/sv.php @@ -1,5 +1,10 @@ "Historik", -"Files Versioning" => "Versionshantering av filer", -"Enable" => "Aktivera" +"Could not revert: %s" => "Kunde inte återställa: %s", +"success" => "lyckades", +"File %s was reverted to version %s" => "Filen %s återställdes till version %s", +"failure" => "misslyckades", +"File %s could not be reverted to version %s" => "Filen %s kunde inte återställas till version %s", +"No old versions available" => "Inga gamla versioner finns tillgängliga", +"No path specified" => "Ingen sökväg angiven", +"Revert a file to a previous version by clicking on its revert button" => "Återställ en fil till en tidigare version genom att klicka på knappen" ); diff --git a/apps/files_versions/l10n/tr.php b/apps/files_versions/l10n/tr.php index e9a4c4702e13b2d635ef060cf5469d57b0d958c4..745400d331c866257bfabed643675fa8d2e35ff7 100644 --- a/apps/files_versions/l10n/tr.php +++ b/apps/files_versions/l10n/tr.php @@ -1,5 +1,10 @@ "Geçmiş", -"Files Versioning" => "Dosya Sürümleri", -"Enable" => "Etkinleştir" +"Could not revert: %s" => "Geri alınamıyor: %s", +"success" => "Başarılı.", +"File %s was reverted to version %s" => "Dosya %s, %s versiyonuna döndürüldü", +"failure" => "hata", +"File %s could not be reverted to version %s" => "Dosya %s, %s versiyonuna döndürülemedi.", +"No old versions available" => "Eski versiyonlar mevcut değil.", +"No path specified" => "Yama belirtilmemiş", +"Versions" => "Sürümler" ); diff --git a/apps/files_versions/l10n/uk.php b/apps/files_versions/l10n/uk.php index 49acda810792942ad0243edac376e8e921195ea2..bfee066c63d061d0941aafc4625bbb325a3e1e40 100644 --- a/apps/files_versions/l10n/uk.php +++ b/apps/files_versions/l10n/uk.php @@ -1,5 +1,10 @@ "Історія", -"Files Versioning" => "Версії файлів", -"Enable" => "Включити" +"Could not revert: %s" => "Не вдалося відновити: %s", +"success" => "успішно", +"File %s was reverted to version %s" => "Файл %s був відновлений до версії %s", +"failure" => "неуспішно", +"File %s could not be reverted to version %s" => "Файл %s не може бути відновлений до версії %s", +"No old versions available" => "Старі версії недоступні", +"No path specified" => "Шлях не вказаний", +"Revert a file to a previous version by clicking on its revert button" => "Відновити файл на попередню версію, натиснувши на кнопку Відновити" ); diff --git a/apps/files_versions/l10n/vi.php b/apps/files_versions/l10n/vi.php index bb7163f6b1875bcafeb1f6f6f6b28502ee9adb52..f2499e7bf355fe4dae3b0bff6fb4caabc67e0ed5 100644 --- a/apps/files_versions/l10n/vi.php +++ b/apps/files_versions/l10n/vi.php @@ -1,5 +1,10 @@ "Lịch sử", -"Files Versioning" => "Phiên bản tập tin", -"Enable" => "Bật " +"Could not revert: %s" => "Không thể khôi phục: %s", +"success" => "thành công", +"File %s was reverted to version %s" => "File %s đã được khôi phục về phiên bản %s", +"failure" => "Thất bại", +"File %s could not be reverted to version %s" => "File %s không thể khôi phục về phiên bản %s", +"No old versions available" => "Không có phiên bản cũ nào", +"No path specified" => "Không chỉ ra đường dẫn rõ ràng", +"Revert a file to a previous version by clicking on its revert button" => "Khôi phục một file về phiên bản trước đó bằng cách click vào nút Khôi phục tương ứng" ); diff --git a/apps/files_versions/l10n/zh_CN.php b/apps/files_versions/l10n/zh_CN.php index 14301ff0c04a39f35edfe677115e8394fac33c49..65d0d284a076f4fe67bcaf7bf2f0902f6c07d66a 100644 --- a/apps/files_versions/l10n/zh_CN.php +++ b/apps/files_versions/l10n/zh_CN.php @@ -1,5 +1,11 @@ "历史", -"Files Versioning" => "文件版本", -"Enable" => "开启" +"Could not revert: %s" => "无法恢复: %s", +"success" => "成功", +"File %s was reverted to version %s" => "文件 %s 已被恢复到历史版本 %s", +"failure" => "失败", +"File %s could not be reverted to version %s" => "文件 %s 无法被恢复到历史版本 %s", +"No old versions available" => "该文件没有历史版本", +"No path specified" => "未指定路径", +"Versions" => "版本", +"Revert a file to a previous version by clicking on its revert button" => "点击恢复按钮可将文件恢复到之前的版本" ); diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index dc02c605c44ce51877a807dd7ffa4572c47550a6..7891b20e92fd2a74e6a28c3cd18d00a1e3efd9db 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -20,13 +20,10 @@ class Hooks { public static function write_hook( $params ) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC\Files\View('') ); - $path = $params[\OC\Files\Filesystem::signal_param_path]; - - if($path<>'') $versions->store( $path ); - + if($path<>'') { + Storage::store($path); + } } } @@ -40,13 +37,11 @@ class Hooks { */ public static function remove_hook($params) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC_FilesystemView('') ); - $path = $params[\OC\Files\Filesystem::signal_param_path]; - - if($path<>'') $versions->delete( $path ); - + if($path<>'') { + Storage::delete($path); + } + } } @@ -59,15 +54,13 @@ class Hooks { */ public static function rename_hook($params) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC_FilesystemView('') ); - $oldpath = $params['oldpath']; $newpath = $params['newpath']; - - if($oldpath<>'' && $newpath<>'') $versions->rename( $oldpath, $newpath ); - + if($oldpath<>'' && $newpath<>'') { + Storage::rename( $oldpath, $newpath ); + } + } } - + } diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index b54bc4a4422969f8f8d74cee253c03faa60b6cb8..178ef7227358c18edce4a64b7602b5169767f578 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -19,53 +19,74 @@ class Storage { const DEFAULTENABLED=true; const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota - + private static $max_versions_per_interval = array( - 1 => array('intervalEndsAfter' => 10, //first 10sec, one version every 2sec - 'step' => 2), - 2 => array('intervalEndsAfter' => 60, //next minute, one version every 10sec - 'step' => 10), - 3 => array('intervalEndsAfter' => 3600, //next hour, one version every minute - 'step' => 60), - 4 => array('intervalEndsAfter' => 86400, //next 24h, one version every hour - 'step' => 3600), - 5 => array('intervalEndsAfter' => 2592000, //next 30days, one version per day - 'step' => 86400), - 6 => array('intervalEndsAfter' => -1, //until the end one version per week - 'step' => 604800), - ); - - private static function getUidAndFilename($filename) - { - if (\OCP\App::isEnabled('files_sharing') - && substr($filename, 0, 7) == '/Shared' - && $source = \OCP\Share::getItemSharedWith('file', - substr($filename, 7), - \OC_Share_Backend_File::FORMAT_SHARED_STORAGE)) { - $filename = $source['path']; - $pos = strpos($filename, '/files', 1); - $uid = substr($filename, 1, $pos - 1); - $filename = substr($filename, $pos + 6); - } else { - $uid = \OCP\User::getUser(); + //first 10sec, one version every 2sec + 1 => array('intervalEndsAfter' => 10, 'step' => 2), + //next minute, one version every 10sec + 2 => array('intervalEndsAfter' => 60, 'step' => 10), + //next hour, one version every minute + 3 => array('intervalEndsAfter' => 3600, 'step' => 60), + //next 24h, one version every hour + 4 => array('intervalEndsAfter' => 86400, 'step' => 3600), + //next 30days, one version per day + 5 => array('intervalEndsAfter' => 2592000, 'step' => 86400), + //until the end one version per week + 6 => array('intervalEndsAfter' => -1, 'step' => 604800), + ); + + public static function getUidAndFilename($filename) { + $uid = \OC\Files\Filesystem::getOwner($filename); + \OC\Files\Filesystem::initMountPoints($uid); + if ( $uid != \OCP\User::getUser() ) { + $info = \OC\Files\Filesystem::getFileInfo($filename); + $ownerView = new \OC\Files\View('/'.$uid.'/files'); + $filename = $ownerView->getPath($info['fileid']); } return array($uid, $filename); } + /** + * get current size of all versions from a given user + * + * @param $user user who owns the versions + * @return mixed versions size or false if no versions size is stored + */ + private static function getVersionsSize($user) { + $query = \OC_DB::prepare('SELECT size FROM *PREFIX*files_versions WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); + + if ($result) { + return $result[0]['size']; + } + return false; + } + + /** + * write to the database how much space is in use for versions + * + * @param $user owner of the versions + * @param $size size of the versions + */ + private static function setVersionsSize($user, $size) { + if ( self::getVersionsSize($user) === false) { + $query = \OC_DB::prepare('INSERT INTO *PREFIX*files_versions (size, user) VALUES (?, ?)'); + }else { + $query = \OC_DB::prepare('UPDATE *PREFIX*files_versions SET size=? WHERE user=?'); + } + $query->execute(array($size, $user)); + } + /** * store a new version of a file. */ - public function store($filename) { + public static function store($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $files_view = new \OC\Files\View('/'.\OCP\User::getUser() .'/files'); - $users_view = new \OC\Files\View('/'.\OCP\User::getUser()); - //check if source file already exist as version to avoid recursions. - // todo does this check work? - if ($users_view->file_exists($filename)) { - return false; - } + $files_view = new \OC\Files\View('/'.$uid .'/files'); + $users_view = new \OC\Files\View('/'.$uid); + $versions_view = new \OC\Files\View('/'.$uid.'/files_versions'); // check if filename is a directory if($files_view->is_dir($filename)) { @@ -79,23 +100,25 @@ class Storage { // create all parent folders $info=pathinfo($filename); - $versionsFolderName=\OCP\Config::getSystemValue('datadirectory').$users_view->getAbsolutePath('files_versions/'); + $versionsFolderName=$versions_view->getLocalFolder(''); if(!file_exists($versionsFolderName.'/'.$info['dirname'])) { mkdir($versionsFolderName.'/'.$info['dirname'], 0750, true); } // store a new version of a file - $result = $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); - if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { + $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); + $versionsSize = self::getVersionsSize($uid); + if ( $versionsSize === false || $versionsSize < 0 ) { $versionsSize = self::calculateSize($uid); } + $versionsSize += $users_view->filesize('files'.$filename); - + // expire old revisions if necessary $newSize = self::expire($filename, $versionsSize); - - if ( $newSize != $versionsSize ) { - \OCP\Config::setAppValue('files_versions', 'size', $versionsSize); + + if ( $newSize != $versionsSize ) { + self::setVersionsSize($uid, $newSize); } } } @@ -106,43 +129,43 @@ class Storage { */ public static function delete($filename) { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid .'/files_versions'); - - $abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$filename.'.v'; - if( ($versions = self::getVersions($filename)) ) { - if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { + $versions_fileview = new \OC\Files\View('/'.$uid .'/files_versions'); + + $abs_path = $versions_fileview->getLocalFile($filename.'.v'); + if( ($versions = self::getVersions($uid, $filename)) ) { + $versionsSize = self::getVersionsSize($uid); + if ( $versionsSize === false || $versionsSize < 0 ) { $versionsSize = self::calculateSize($uid); } foreach ($versions as $v) { unlink($abs_path . $v['version']); $versionsSize -= $v['size']; } - \OCP\Config::setAppValue('files_versions', 'size', $versionsSize); + self::setVersionsSize($uid, $versionsSize); } } - + /** * rename versions of a file */ public static function rename($oldpath, $newpath) { list($uid, $oldpath) = self::getUidAndFilename($oldpath); list($uidn, $newpath) = self::getUidAndFilename($newpath); - $versions_view = new \OC_FilesystemView('/'.$uid .'/files_versions'); - $files_view = new \OC_FilesystemView('/'.$uid .'/files'); - $abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_view->getAbsolutePath('').$newpath; - + $versions_view = new \OC\Files\View('/'.$uid .'/files_versions'); + $files_view = new \OC\Files\View('/'.$uid .'/files'); + $abs_newpath = $versions_view->getLocalFile($newpath); + if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) { $versions_view->rename($oldpath, $newpath); - } else if ( ($versions = Storage::getVersions($oldpath)) ) { + } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) { $info=pathinfo($abs_newpath); if(!file_exists($info['dirname'])) mkdir($info['dirname'], 0750, true); - $versions = Storage::getVersions($oldpath); foreach ($versions as $v) { $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); } } } - + /** * rollback to an old version of a file. */ @@ -152,14 +175,14 @@ class Storage { list($uid, $filename) = self::getUidAndFilename($filename); $users_view = new \OC\Files\View('/'.$uid); $versionCreated = false; - + //first create a new version $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename); if ( !$users_view->file_exists($version)) { $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); $versionCreated = true; } - + // rollback if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { $users_view->touch('files'.$filename, $revision); @@ -177,23 +200,27 @@ class Storage { /** * @brief get a list of all available versions of a file in descending chronological order + * @param $uid user id from the owner of the file * @param $filename file to find versions of, relative to the user files dir * @param $count number of versions to return * @returns array */ - public static function getVersions( $filename, $count = 0 ) { + public static function getVersions($uid, $filename, $count = 0 ) { if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { - list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC\Files\View('/' . \OCP\User::getUser() . '/files_versions'); - - $versionsName = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); + $versions_fileview = new \OC\Files\View('/' . $uid . '/files_versions'); + $versionsName = $versions_fileview->getLocalFile($filename); + $versions = array(); // fetch for old versions - $matches = glob( $versionsName.'.v*' ); + $matches = glob(preg_quote($versionsName).'.v*' ); + + if ( !$matches ) { + return $versions; + } sort( $matches ); - $files_view = new \OC_FilesystemView('/'.$uid.'/files'); + $files_view = new \OC\Files\View('/'.$uid.'/files'); $local_file = $files_view->getLocalFile($filename); $local_file_md5 = \md5_file( $local_file ); @@ -244,24 +271,27 @@ class Storage { */ private static function calculateSize($uid) { if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); - $versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); - - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); - + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); + $versionsRoot = $versions_fileview->getLocalFolder(''); + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($versionsRoot), + \RecursiveIteratorIterator::CHILD_FIRST + ); + $size = 0; - + foreach ($iterator as $path) { if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) { $relpath = substr($path, strlen($versionsRoot)-1); $size += $versions_fileview->filesize($relpath); } } - + return $size; } } - + /** * @brief returns all stored file versions from a given user * @param $uid id to the user @@ -269,26 +299,29 @@ class Storage { */ private static function getAllVersions($uid) { if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); - $versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); - - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); - + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); + $versionsRoot = $versions_fileview->getLocalFolder(''); + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($versionsRoot), + \RecursiveIteratorIterator::CHILD_FIRST + ); + $versions = array(); - + foreach ($iterator as $path) { if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) { $relpath = substr($path, strlen($versionsRoot)-1); $versions[$match[1].'#'.$relpath] = array('path' => $relpath, 'timestamp' => $match[1]); } } - + ksort($versions); - + $i = 0; - + $result = array(); - + foreach( $versions as $key => $value ) { $i++; $size = $versions_fileview->filesize($value['path']); @@ -297,14 +330,14 @@ class Storage { $result['all'][$key]['version'] = $value['timestamp']; $result['all'][$key]['path'] = $filename; $result['all'][$key]['size'] = $size; - + $filename = substr($value['path'], 0, -strlen($value['timestamp'])-2); $result['by_file'][$filename][$key]['version'] = $value['timestamp']; $result['by_file'][$filename][$key]['path'] = $filename; $result['by_file'][$filename][$key]['size'] = $size; - + } - + return $result; } } @@ -314,68 +347,71 @@ class Storage { */ private static function expire($filename, $versionsSize = null) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); - + list($uid, $filename) = self::getUidAndFilename($filename); + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); + // get available disk space for user - $quota = \OCP\Util::computerFileSize(\OC_Preferences::getValue($uid, 'files', 'quota')); - if ( $quota == null ) { - $quota = \OCP\Util::computerFileSize(\OC_Appconfig::getValue('files', 'default_quota')); + $quota = \OC_Preferences::getValue($uid, 'files', 'quota'); + if ( $quota === null || $quota === 'default') { + $quota = \OC_Appconfig::getValue('files', 'default_quota'); } - if ( $quota == null ) { - $quota = \OC\Files\Filesystem::free_space('/'); + if ( $quota === null || $quota === 'none' ) { + $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers()); + } else { + $quota = \OCP\Util::computerFileSize($quota); } // make sure that we have the current size of the version history if ( $versionsSize === null ) { - if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { + $versionsSize = self::getVersionsSize($uid); + if ( $versionsSize === false || $versionsSize < 0 ) { $versionsSize = self::calculateSize($uid); } } // calculate available space for version history - $files_view = new \OC_FilesystemView('/'.$uid.'/files'); + $files_view = new \OC\Files\View('/'.$uid.'/files'); $rootInfo = $files_view->getFileInfo('/'); $free = $quota-$rootInfo['size']; // remaining free space for user if ( $free > 0 ) { $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions } else { $availableSpace = $free-$versionsSize; - } + } - // after every 1000s run reduce the number of all versions not only for the current file + // after every 1000s run reduce the number of all versions not only for the current file $random = rand(0, 1000); if ($random == 0) { $result = Storage::getAllVersions($uid); $versions_by_file = $result['by_file']; $all_versions = $result['all']; } else { - $all_versions = Storage::getVersions($filename); + $all_versions = Storage::getVersions($uid, $filename); $versions_by_file[$filename] = $all_versions; } - + $time = time(); - + // it is possible to expire versions from more than one file // iterate through all given files foreach ($versions_by_file as $filename => $versions) { $versions = array_reverse($versions); // newest version first - + $interval = 1; - $step = Storage::$max_versions_per_interval[$interval]['step']; + $step = Storage::$max_versions_per_interval[$interval]['step']; if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { $nextInterval = -1; } else { $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; } - + $firstVersion = reset($versions); $firstKey = key($versions); $prevTimestamp = $firstVersion['version']; $nextVersion = $firstVersion['version'] - $step; $remaining_versions[$firstKey] = $firstVersion; unset($versions[$firstKey]); - + foreach ($versions as $key => $version) { $newInterval = true; while ( $newInterval ) { @@ -405,11 +441,11 @@ class Storage { $prevTimestamp = $version['version']; } } - + // check if enough space is available after versions are rearranged. // if not we delete the oldest versions until we meet the size limit for versions $numOfVersions = count($all_versions); - $i = 0; + $i = 0; while ($availableSpace < 0) { if ($i = $numOfVersions-2) break; // keep at least the last version $versions_fileview->unlink($all_versions[$i]['path'].'.v'.$all_versions[$i]['version']); @@ -417,10 +453,10 @@ class Storage { $availableSpace += $all_versions[$i]['size']; $i++; } - + return $versionsSize; // finally return the new size of the version history } - + return false; } } diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php deleted file mode 100644 index f2873b8f7c2cfebeaf0b23e20e763f3d23fe455b..0000000000000000000000000000000000000000 --- a/apps/files_versions/settings.php +++ /dev/null @@ -1,9 +0,0 @@ -fetchPage(); diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php index 850ece89c98fc30277d22bbc785e57e8dd61da94..f7284439041e7762f50723fc87bbf5a7d77ae12b 100644 --- a/apps/files_versions/templates/history.php +++ b/apps/files_versions/templates/history.php @@ -5,28 +5,29 @@ if( isset( $_['message'] ) ) { - if( isset($_['path'] ) ) echo('File: '.$_['path'] ).'
'; - echo(''.$_['message'] ).'
'; + if( isset($_['path'] ) ) print_unescaped('File: '.OC_Util::sanitizeHTML($_['path'])).'
'; + print_unescaped(''.OC_Util::sanitizeHTML($_['message']) ).'
'; }else{ if( isset( $_['outcome_stat'] ) ) { - echo( '

'.$_['outcome_msg'] ).'


'; + print_unescaped( '

'.OC_Util::sanitizeHTML($_['outcome_msg']) ).'


'; } - echo( 'Versions of '.$_['path'] ).'
'; - echo('

'.$l->t('Revert a file to a previous version by clicking on its revert button').'


'); + print_unescaped( 'Versions of '.OC_Util::sanitizeHTML($_['path']) ).'
'; + print_unescaped('

'.OC_Util::sanitizeHTML($l->t('Revert a file to a previous version by clicking on its revert button')).'


'); foreach ( $_['versions'] as $v ) { - echo ' '; - echo OCP\Util::formatDate( doubleval($v['version']) ); - echo '
Revert

'; + p(' '); + p(OCP\Util::formatDate( doubleval($v['version']))); + print_unescaped(' Revert

'); if ( $v['cur'] ) { - echo ' (Current)'; + print_unescaped(' (Current)'); } - echo '

'; + print_unescaped('

'); } } diff --git a/apps/files_versions/templates/settings.php b/apps/files_versions/templates/settings.php deleted file mode 100644 index bfca8366f5da50d78ce670fdcf9d088f35ca306f..0000000000000000000000000000000000000000 --- a/apps/files_versions/templates/settings.php +++ /dev/null @@ -1,6 +0,0 @@ -
-
- t('Files Versioning');?> - />
-
-
diff --git a/apps/user_ldap/ajax/deleteConfiguration.php b/apps/user_ldap/ajax/deleteConfiguration.php index b7d633a049d275d7d88cd50e34600866f3b7d75d..ade57110d346b8986e724e5100d1f7e2b0064f1a 100644 --- a/apps/user_ldap/ajax/deleteConfiguration.php +++ b/apps/user_ldap/ajax/deleteConfiguration.php @@ -27,9 +27,9 @@ OCP\JSON::checkAppEnabled('user_ldap'); OCP\JSON::callCheck(); $prefix = $_POST['ldap_serverconfig_chooser']; -if(\OCA\user_ldap\lib\Helper::deleteServerConfiguration($prefix)){ +if(\OCA\user_ldap\lib\Helper::deleteServerConfiguration($prefix)) { OCP\JSON::success(); } else { $l=OC_L10N::get('user_ldap'); OCP\JSON::error(array('message' => $l->t('Failed to delete the server configuration'))); -} \ No newline at end of file +} diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php index f8038e31469c545fd91682c9d975bab39c833989..7ce1258a7967e7b8cd711f021b4f0fe4346b213d 100644 --- a/apps/user_ldap/ajax/testConfiguration.php +++ b/apps/user_ldap/ajax/testConfiguration.php @@ -32,10 +32,13 @@ $connection = new \OCA\user_ldap\lib\Connection('', null); if($connection->setConfiguration($_POST)) { //Configuration is okay if($connection->bind()) { - OCP\JSON::success(array('message' => $l->t('The configuration is valid and the connection could be established!'))); + OCP\JSON::success(array('message' + => $l->t('The configuration is valid and the connection could be established!'))); } else { - OCP\JSON::error(array('message' => $l->t('The configuration is valid, but the Bind failed. Please check the server settings and credentials.'))); + OCP\JSON::error(array('message' + => $l->t('The configuration is valid, but the Bind failed. Please check the server settings and credentials.'))); } } else { - OCP\JSON::error(array('message' => $l->t('The configuration is invalid. Please look in the ownCloud log for further details.'))); + OCP\JSON::error(array('message' + => $l->t('The configuration is invalid. Please look in the ownCloud log for further details.'))); } diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index dec87684c9e52d7d4a1d31a7a2c86935370fb826..89410b5ef07e821cae7046c55197c98bda73a409 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -51,5 +51,7 @@ $entry = array( OCP\Backgroundjob::addRegularTask('OCA\user_ldap\lib\Jobs', 'updateGroups'); if(OCP\App::isEnabled('user_webdavauth')) { - OCP\Util::writeLog('user_ldap', 'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour', OCP\Util::WARN); + OCP\Util::writeLog('user_ldap', + 'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour', + OCP\Util::WARN); } diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index 53269edfb34b179d426da751810b74efb681d156..148a72cecbb4449d27a248719a1f1e3d70de5609 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -2,12 +2,14 @@ user_ldap LDAP user and group backend - Authenticate users and groups by LDAP resp. Active Directoy. + Authenticate users and groups by LDAP respectively Active + Directory. - This app is not compatible to the WebDAV user backend. + This app is not compatible with the WebDAV user backend. + AGPL Dominik Schmidt and Arthur Schiwon - 4.91 + 4.93 true diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index f9681e38e6813c38dfa7f2178bc69dd9b03d3890..2fcbf1902ac54b9c65479047b4457ff10c2ee0aa 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -58,7 +58,9 @@ foreach($objects as $object) { try { $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn'])); } catch(Exception $e) { - \OCP\Util::writeLog('user_ldap', 'Could not update '.$object.' '.$dn['ldap_dn'].' in the mappings table. ', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'Could not update '.$object.' '.$dn['ldap_dn'].' in the mappings table. ', + \OCP\Util::WARN); } } @@ -87,4 +89,8 @@ if(!isset($connector)) { } //it is required, that connections do have ldap_configuration_active setting stored in the database $connector->getConfiguration(); -$connector->saveConfiguration(); \ No newline at end of file +$connector->saveConfiguration(); + +// we don't save it anymore, was a well-meant bad idea. Clean up database. +$query = OC_DB::prepare('DELETE FROM `*PREFIX*preferences` WHERE `appid` = ? AND `configkey` = ?'); +$query->execute(array('user_ldap' , 'homedir')); diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index 705e30728e00e9b7a903ac90292ef4270ba7603d..e619108dd639e5f36f97f73ae82d8819bdbb20b0 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.3.9.0 \ No newline at end of file +0.3.9.5 \ No newline at end of file diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 02ceecaea0bd3492678ce14394050f3289b49335..4fd4c636913cffa5ca04eca62d87e4d40ef88cf6 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -177,7 +177,8 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if($isMemberUid) { //we got uids, need to get their DNs to 'tranlsate' them to usernames $filter = $this->combineFilterWithAnd(array( - \OCP\Util::mb_str_replace('%uid', $member, $this->connection>ldapLoginFilter, 'UTF-8'), + \OCP\Util::mb_str_replace('%uid', $member, + $this->connection>ldapLoginFilter, 'UTF-8'), $this->getFilterPartForUserSearch($search) )); $ldap_users = $this->fetchListOfUsers($filter, 'dn'); @@ -188,7 +189,9 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { } else { //we got DNs, check if we need to filter by search or we can give back all of them if(!empty($search)) { - if(!$this->readAttribute($member, $this->connection->ldapUserDisplayName, $this->getFilterPartForUserSearch($search))) { + if(!$this->readAttribute($member, + $this->connection->ldapUserDisplayName, + $this->getFilterPartForUserSearch($search))) { continue; } } @@ -225,7 +228,8 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { return $ldap_groups; } - // if we'd pass -1 to LDAP search, we'd end up in a Protocol error. With a limit of 0, we get 0 results. So we pass null. + // if we'd pass -1 to LDAP search, we'd end up in a Protocol + // error. With a limit of 0, we get 0 results. So we pass null. if($limit <= 0) { $limit = null; } @@ -234,7 +238,8 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { $this->getFilterPartForGroupSearch($search) )); \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG); - $ldap_groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'), $limit, $offset); + $ldap_groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'), + $limit, $offset); $ldap_groups = $this->ownCloudGroupNames($ldap_groups); $this->connection->writeToCache($cachekey, $ldap_groups); @@ -282,7 +287,8 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { * compared with OC_USER_BACKEND_CREATE_USER etc. */ public function implementsActions($actions) { - //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now. + //always returns false, because possible actions are modifying + // actions. We do not write to LDAP, at least for now. return false; } -} \ No newline at end of file +} diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php index e4f27e25a7fab324d162cf5d6dfc1cab603a982d..abdecb164e8f29c1f4bd0487e556ba56743994f7 100644 --- a/apps/user_ldap/l10n/ca.php +++ b/apps/user_ldap/l10n/ca.php @@ -48,6 +48,7 @@ "Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexió només funciona amb aquesta opció, importeu el certificat SSL del servidor LDAP en el vostre servidor ownCloud.", "Not recommended, use for testing only." => "No recomanat, ús només per proves.", +"Cache Time-To-Live" => "Memòria de cau Time-To-Live", "in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.", "Directory Settings" => "Arranjaments de carpetes", "User Display Name Field" => "Camp per mostrar el nom d'usuari", @@ -63,7 +64,12 @@ "Group Search Attributes" => "Atributs de cerca de grup", "Group-Member association" => "Associació membres-grup", "Special Attributes" => "Atributs especials", +"Quota Field" => "Camp de quota", +"Quota Default" => "Quota per defecte", "in bytes" => "en bytes", +"Email Field" => "Camp de correu electrònic", +"User Home Folder Naming Rule" => "Norma per anomenar la carpeta arrel d'usuari", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixeu-ho buit pel nom d'usuari (per defecte). Altrament, especifiqueu un atribut LDAP/AD.", +"Test Configuration" => "Comprovació de la configuració", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php index 618e7a32457a6c00d56efa5e760d27f917b7dd1c..6217a6d4821ab8b27d01d03938134e5951beec78 100644 --- a/apps/user_ldap/l10n/de.php +++ b/apps/user_ldap/l10n/de.php @@ -1,8 +1,20 @@ "Löschen der Serverkonfiguration fehlgeschlagen", +"The configuration is valid and the connection could be established!" => "Die Konfiguration war erfolgreich, die Verbindung konnte hergestellt werden!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig aber die Verbindung ist fehlgeschlagen. Bitte überprüfen Sie die Servereinstellungen und die Anmeldeinformationen.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig, bitte sehen Sie für weitere Details im ownCloud Log nach", "Deletion failed" => "Löschen fehlgeschlagen", +"Take over settings from recent server configuration?" => "Einstellungen von letzter Konfiguration übernehmen?", "Keep settings?" => "Einstellungen beibehalten?", +"Cannot add server configuration" => "Serverkonfiguration konnte nicht hinzugefügt werden.", +"Connection test succeeded" => "Verbindungstest erfolgreich", +"Connection test failed" => "Verbindungstest fehlgeschlagen", +"Do you really want to delete the current Server Configuration?" => "Wollen Sie die aktuelle Serverkonfiguration wirklich löschen?", +"Confirm Deletion" => "Löschung bestätigen", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitte Deinen Systemadministator eine der beiden Anwendungen zu deaktivieren.", -"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitte Deinen Systemadministrator das Modul zu installieren.", +"Server configuration" => "Serverkonfiguration", +"Add Server Configuration" => "Serverkonfiguration hinzufügen", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kannst das Protokoll auslassen, außer wenn Du SSL benötigst. Beginne dann mit ldaps://", "Base DN" => "Basis-DN", @@ -21,22 +33,36 @@ "Group Filter" => "Gruppen-Filter", "Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", +"Connection Settings" => "Verbindungseinstellungen", +"Configuration Active" => "Konfiguration aktiv", +"When unchecked, this configuration will be skipped." => "Konfiguration wird übersprungen wenn deaktiviert", "Port" => "Port", +"Backup (Replica) Host" => "Backup Host (Kopie)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Gib einen optionalen Backup Host an. Es muss sich um eine Kopie des Haupt LDAP/AD Servers handeln.", +"Backup (Replica) Port" => "Backup Port", +"Disable Main Server" => "Hauptserver deaktivieren", +"When switched on, ownCloud will only connect to the replica server." => "Wenn aktiviert, wird ownCloud ausschließlich den Backupserver verwenden.", "Use TLS" => "Nutze TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Benutze es nicht zusammen mit LDAPS Verbindungen, es wird fehlschlagen.", "Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalte die SSL-Zertifikatsprüfung aus.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.", "Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", "in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", +"Directory Settings" => "Ordnereinstellungen", "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", "Base User Tree" => "Basis-Benutzerbaum", "One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", +"User Search Attributes" => "Benutzersucheigenschaften", +"Optional; one attribute per line" => "Optional; eine Eigenschaft pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", "Base Group Tree" => "Basis-Gruppenbaum", "One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", +"Group Search Attributes" => "Gruppensucheigenschaften", "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", +"Special Attributes" => "Spezielle Eigenschaften", "in bytes" => "in Bytes", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.", "Help" => "Hilfe" diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php index 7d3847f8a891c6be9f810b134be11ada73129d96..c88ed22b4fa61d798bd85a8b02745b24e234030d 100644 --- a/apps/user_ldap/l10n/de_DE.php +++ b/apps/user_ldap/l10n/de_DE.php @@ -1,20 +1,20 @@ "Das Löschen der Server-Konfiguration schlug fehl", -"The configuration is valid and the connection could be established!" => "Die Konfiguration ist valide und eine Verbindung konnte hergestellt werden!", -"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist valide, aber das Herstellen einer Verbindung schlug fehl. Bitte überprüfen Sie die Server-Einstellungen und Zertifikate.", -"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist nicht valide. Weitere Details können Sie im ownCloud-Log nachlesen.", +"The configuration is valid and the connection could be established!" => "Die Konfiguration ist gültig und die Verbindung konnte hergestellt werden!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig, aber das Herstellen der Verbindung schlug fehl. Bitte überprüfen Sie die Server-Einstellungen und Zertifikate.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig. Weitere Details können Sie im ownCloud-Log nachlesen.", "Deletion failed" => "Löschen fehlgeschlagen", -"Take over settings from recent server configuration?" => "Sollen die Einstellungen der letzten Server-Konfiguration übernommen werden?", +"Take over settings from recent server configuration?" => "Sollen die Einstellungen der letzten Serverkonfiguration übernommen werden?", "Keep settings?" => "Einstellungen behalten?", -"Cannot add server configuration" => "Das Hinzufügen der Server-Konfiguration schlug fehl", -"Connection test succeeded" => "Verbindungs-Test erfolgreich", -"Connection test failed" => "Verbindungs-Test fehlgeschlagen", -"Do you really want to delete the current Server Configuration?" => "Möchten Sie wirklich die Server-Konfiguration löschen?", +"Cannot add server configuration" => "Das Hinzufügen der Serverkonfiguration schlug fehl", +"Connection test succeeded" => "Verbindungstest erfolgreich", +"Connection test failed" => "Verbindungstest fehlgeschlagen", +"Do you really want to delete the current Server Configuration?" => "Möchten Sie die Serverkonfiguration wirklich löschen?", "Confirm Deletion" => "Löschung bestätigen", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitten Sie Ihren Systemadministator eine der beiden Anwendungen zu deaktivieren.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP ist nicht installiert, das Backend wird nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.", -"Server configuration" => "Server-Konfiguration", -"Add Server Configuration" => "Server-Konfiguration hinzufügen", +"Server configuration" => "Serverkonfiguration", +"Add Server Configuration" => "Serverkonfiguration hinzufügen", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://", "Base DN" => "Basis-DN", @@ -33,35 +33,36 @@ "Group Filter" => "Gruppen-Filter", "Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", -"Connection Settings" => "Verbindungs-Einstellungen", +"Connection Settings" => "Verbindungseinstellungen", "Configuration Active" => "Konfiguration aktiv", "When unchecked, this configuration will be skipped." => "Wenn nicht angehakt, wird diese Konfiguration übersprungen.", "Port" => "Port", "Backup (Replica) Host" => "Back-Up (Replikation) Host", -"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Optionaler Backup Host. Es muss ein Replikat des eigentlichen LDAP/AD Servers sein.", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Geben Sie einen optionalen Backup Host an. Es muss ein Replikat des Haupt- LDAP/AD Servers sein.", "Backup (Replica) Port" => "Back-Up (Replikation) Port", "Disable Main Server" => "Hauptserver deaktivieren", -"When switched on, ownCloud will only connect to the replica server." => "Wenn eingeschaltet wird sich ownCloud nur mit dem Replilat-Server verbinden.", +"When switched on, ownCloud will only connect to the replica server." => "Wenn eingeschaltet wird sich die ownCloud nur mit dem Replikat-Server verbinden.", "Use TLS" => "Nutze TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Benutzen Sie es nicht in Verbindung mit LDAPS Verbindungen, es wird fehlschlagen.", "Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.", "Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", "in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", -"Directory Settings" => "Verzeichnis-Einstellungen", +"Directory Settings" => "Verzeichniseinstellungen", "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", "Base User Tree" => "Basis-Benutzerbaum", "One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", "User Search Attributes" => "Benutzer-Suche Eigenschaften", -"Optional; one attribute per line" => "Optional; Ein Attribut pro Zeile", +"Optional; one attribute per line" => "Optional; ein Attribut pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", "Base Group Tree" => "Basis-Gruppenbaum", "One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", "Group Search Attributes" => "Gruppen-Suche Eigenschaften", "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", -"Special Attributes" => "besondere Eigenschaften", +"Special Attributes" => "Besondere Eigenschaften", "in bytes" => "in Bytes", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.", "Help" => "Hilfe" diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php index 7c0940dc09c6466fc406a25d63eee02ac103ac06..96ec818043720609a18b29cc0732afe1422ca3fe 100644 --- a/apps/user_ldap/l10n/el.php +++ b/apps/user_ldap/l10n/el.php @@ -1,6 +1,19 @@ "Αποτυχία διαγραφής ρυθμίσεων διακομιστή", +"The configuration is valid and the connection could be established!" => "Οι ρυθμίσεις είναι έγκυρες και η σύνδεση μπορεί να πραγματοποιηθεί!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Οι ρυθμίσεις είναι έγκυρες, αλλά απέτυχε η σύνδεση. Παρακαλώ ελέγξτε τις ρυθμίσεις του διακομιστή και τα διαπιστευτήρια.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Μη έγκυρες ρυθμίσεις. Παρακαλώ ελέγξτε τις καταγραφές του ownCloud για περισσότερες λεπτομέρειες.", "Deletion failed" => "Η διαγραφή απέτυχε", +"Keep settings?" => "Διατήρηση ρυθμίσεων;", +"Cannot add server configuration" => "Αδυναμία προσθήκης ρυθμίσεων διακομιστή", +"Connection test succeeded" => "Επιτυχημένη δοκιμαστική σύνδεση", +"Connection test failed" => "Αποτυχημένη δοκιμαστική σύνδεσης.", +"Do you really want to delete the current Server Configuration?" => "Θέλετε να διαγράψετε τις τρέχουσες ρυθμίσεις του διακομιστή;", +"Confirm Deletion" => "Επιβεβαίωση Διαγραφής", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Προσοχή: Οι εφαρμογές user_ldap και user_webdavauth είναι ασύμβατες. Μπορεί να αντιμετωπίσετε απρόβλεπτη συμπεριφορά. Παρακαλώ ζητήστε από τον διαχειριστή συστήματος να απενεργοποιήσει μία από αυτές.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Προσοχή: Το άρθρωμα PHP LDAP δεν είναι εγκατεστημένο και το σύστημα υποστήριξης δεν θα δουλέψει. Παρακαλώ ζητήστε από τον διαχειριστή συστήματος να το εγκαταστήσει.", +"Server configuration" => "Ρυθμίσεις Διακομιστή", +"Add Server Configuration" => "Προσθήκη Ρυθμίσεων Διακομιστή", "Host" => "Διακομιστής", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Μπορείτε να παραλείψετε το πρωτόκολλο, εκτός αν απαιτείται SSL. Σε αυτή την περίπτωση ξεκινήστε με ldaps://", "Base DN" => "Base DN", @@ -18,6 +31,7 @@ "Group Filter" => "Group Filter", "Defines the filter to apply, when retrieving groups." => "Καθορίζει το φίλτρο που θα ισχύει κατά την ανάκτηση ομάδων.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "χωρίς κάποια μεταβλητή, π.χ. \"objectClass=ΟμάδαPosix\".", +"Connection Settings" => "Ρυθμίσεις Σύνδεσης", "Port" => "Θύρα", "Use TLS" => "Χρήση TLS", "Case insensitve LDAP server (Windows)" => "LDAP server (Windows) με διάκριση πεζών-ΚΕΦΑΛΑΙΩΝ", @@ -25,6 +39,7 @@ "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Εάν η σύνδεση δουλεύει μόνο με αυτή την επιλογή, εισάγετε το LDAP SSL πιστοποιητικό του διακομιστή στον ownCloud server σας.", "Not recommended, use for testing only." => "Δεν προτείνεται, χρήση μόνο για δοκιμές.", "in seconds. A change empties the cache." => "σε δευτερόλεπτα. Μια αλλαγή αδειάζει την μνήμη cache.", +"Directory Settings" => "Ρυθμίσεις Καταλόγου", "User Display Name Field" => "Πεδίο Ονόματος Χρήστη", "The LDAP attribute to use to generate the user`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος χρήστη του ownCloud.", "Base User Tree" => "Base User Tree", diff --git a/apps/user_ldap/l10n/es_AR.php b/apps/user_ldap/l10n/es_AR.php index a87444a270cff8f7eab5c1cfff897537dab34722..b0e7ec12b21b94cfb535e23cc9767a8690dc5b4f 100644 --- a/apps/user_ldap/l10n/es_AR.php +++ b/apps/user_ldap/l10n/es_AR.php @@ -1,7 +1,10 @@ "Fallo al borrar la configuración del servidor", "The configuration is valid and the connection could be established!" => "La configuración es valida y la conexión pudo ser establecida.", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuración es válida, pero el enlace falló. Por favor, comprobá la configuración del servidor y las credenciales.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuración no es válida. Por favor, buscá en el log de ownCloud más detalles.", "Deletion failed" => "Error al borrar", +"Take over settings from recent server configuration?" => "Tomar los valores de la anterior configuración de servidor?", "Keep settings?" => "¿Mantener preferencias?", "Cannot add server configuration" => "No se pudo añadir la configuración del servidor", "Connection test succeeded" => "El este de conexión ha sido completado satisfactoriamente", @@ -32,9 +35,15 @@ "without any placeholder, e.g. \"objectClass=posixGroup\"." => "Sin ninguna plantilla, p. ej.: \"objectClass=posixGroup\".", "Connection Settings" => "Configuración de Conección", "Configuration Active" => "Configuración activa", +"When unchecked, this configuration will be skipped." => "Si no está seleccionada, esta configuración será omitida.", "Port" => "Puerto", +"Backup (Replica) Host" => "Host para copia de seguridad (réplica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dar un servidor de copia de seguridad opcional. Debe ser una réplica del servidor principal LDAP/AD.", +"Backup (Replica) Port" => "Puerto para copia de seguridad (réplica)", "Disable Main Server" => "Deshabilitar el Servidor Principal", +"When switched on, ownCloud will only connect to the replica server." => "Al comenzar, ownCloud se conectará únicamente al servidor réplica", "Use TLS" => "Usar TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "No usar adicionalmente para conexiones LDAPS, las mismas fallarán", "Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Desactivar la validación por certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importá el certificado SSL del servidor LDAP en tu servidor ownCloud.", @@ -45,10 +54,13 @@ "The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.", "Base User Tree" => "Árbol base de usuario", "One User Base DN per line" => "Una DN base de usuario por línea", +"User Search Attributes" => "Atributos de la búsqueda de usuario", +"Optional; one attribute per line" => "Opcional; un atributo por linea", "Group Display Name Field" => "Campo de nombre de grupo a mostrar", "The LDAP attribute to use to generate the groups`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de los grupos de ownCloud.", "Base Group Tree" => "Árbol base de grupo", "One Group Base DN per line" => "Una DN base de grupo por línea", +"Group Search Attributes" => "Atributos de búsqueda de grupo", "Group-Member association" => "Asociación Grupo-Miembro", "Special Attributes" => "Atributos Especiales", "in bytes" => "en bytes", diff --git a/apps/user_ldap/l10n/eu.php b/apps/user_ldap/l10n/eu.php index 97c23f86480891f7d4979c1c983afc7b19a35f06..5e9fd014c64f3a5875ea41d396007b1a078e0e67 100644 --- a/apps/user_ldap/l10n/eu.php +++ b/apps/user_ldap/l10n/eu.php @@ -1,7 +1,20 @@ "Zerbitzariaren konfigurazioa ezabatzeak huts egin du", +"The configuration is valid and the connection could be established!" => "Konfigurazioa egokia da eta konexioa ezarri daiteke!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurazioa ongi dago, baina Bind-ek huts egin du. Mesedez egiaztatu zerbitzariaren ezarpenak eta kredentzialak.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurazioa ez dago ongi. Mesedez ikusi ownCloud-en egunerokoa informazio gehiago eskuratzeko.", "Deletion failed" => "Ezabaketak huts egin du", +"Take over settings from recent server configuration?" => "oraintsuko zerbitzariaren konfigurazioaren ezarpenen ardura hartu?", +"Keep settings?" => "Mantendu ezarpenak?", +"Cannot add server configuration" => "Ezin da zerbitzariaren konfigurazioa gehitu", +"Connection test succeeded" => "Konexio froga ongi burutu da", +"Connection test failed" => "Konexio frogak huts egin du", +"Do you really want to delete the current Server Configuration?" => "Ziur zaude Zerbitzariaren Konfigurazioa ezabatu nahi duzula?", +"Confirm Deletion" => "Baieztatu Ezabatzea", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Abisua: user_ldap eta user_webdavauth aplikazioak bateraezinak dira. Portaera berezia izan dezakezu. Mesedez eskatu zure sistema kudeatzaileari bietako bat desgaitzeko.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Abisua: PHPk behar duen LDAP modulua ez dago instalaturik, motorrak ez du funtzionatuko. Mesedez eskatu zure sistema kudeatzaileari instala dezan.", +"Server configuration" => "Zerbitzariaren konfigurazioa", +"Add Server Configuration" => "Gehitu Zerbitzariaren Konfigurazioa", "Host" => "Hostalaria", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokoloa ez da beharrezkoa, SSL behar baldin ez baduzu. Honela bada hasi ldaps://", "Base DN" => "Oinarrizko DN", @@ -20,23 +33,43 @@ "Group Filter" => "Taldeen iragazkia", "Defines the filter to apply, when retrieving groups." => "Taldeak jasotzen direnean ezarriko den iragazkia zehazten du.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "txantiloirik gabe, adb. \"objectClass=posixGroup\".", +"Connection Settings" => "Konexio Ezarpenak", +"Configuration Active" => "Konfigurazio Aktiboa", +"When unchecked, this configuration will be skipped." => "Markatuta ez dagoenean, konfigurazio hau ez da kontutan hartuko.", "Port" => "Portua", +"Backup (Replica) Host" => "Babeskopia (Replica) Ostalaria", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Eman babeskopia ostalari gehigarri bat. LDAP/AD zerbitzari nagusiaren replica bat izan behar da.", +"Backup (Replica) Port" => "Babeskopia (Replica) Ataka", +"Disable Main Server" => "Desgaitu Zerbitzari Nagusia", +"When switched on, ownCloud will only connect to the replica server." => "Markatuta dagoenean, ownCloud bakarrik replica zerbitzarira konektatuko da.", "Use TLS" => "Erabili TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Ez erabili LDAPS konexioetarako, huts egingo du.", "Case insensitve LDAP server (Windows)" => "Maiuskulak eta minuskulak ezberditzen ez dituen LDAP zerbitzaria (windows)", "Turn off SSL certificate validation." => "Ezgaitu SSL ziurtagirien egiaztapena.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Konexioa aukera hau ezinbestekoa badu, inportatu LDAP zerbitzariaren SSL ziurtagiria zure ownCloud zerbitzarian.", "Not recommended, use for testing only." => "Ez da aholkatzen, erabili bakarrik frogak egiteko.", +"Cache Time-To-Live" => "Katxearen Bizi-Iraupena", "in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.", +"Directory Settings" => "Karpetaren Ezarpenak", "User Display Name Field" => "Erabiltzaileen bistaratzeko izena duen eremua", "The LDAP attribute to use to generate the user`s ownCloud name." => "ownCloud erabiltzailearen izena sortzeko erabiliko den LDAP atributua", "Base User Tree" => "Oinarrizko Erabiltzaile Zuhaitza", "One User Base DN per line" => "Erabiltzaile DN Oinarri bat lerroko", +"User Search Attributes" => "Erabili Bilaketa Atributuak ", +"Optional; one attribute per line" => "Aukerakoa; atributu bat lerro bakoitzeko", "Group Display Name Field" => "Taldeen bistaratzeko izena duen eremua", "The LDAP attribute to use to generate the groups`s ownCloud name." => "ownCloud taldearen izena sortzeko erabiliko den LDAP atributua", "Base Group Tree" => "Oinarrizko Talde Zuhaitza", "One Group Base DN per line" => "Talde DN Oinarri bat lerroko", +"Group Search Attributes" => "Taldekatu Bilaketa Atributuak ", "Group-Member association" => "Talde-Kide elkarketak", +"Special Attributes" => "Atributu Bereziak", +"Quota Field" => "Kuota Eremua", +"Quota Default" => "Kuota Lehenetsia", "in bytes" => "bytetan", +"Email Field" => "Eposta eremua", +"User Home Folder Naming Rule" => "Erabiltzailearen Karpeta Nagusia Izendatzeko Patroia", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Utzi hutsik erabiltzaile izenarako (lehentsia). Bestela zehaztu LDAP/AD atributua.", +"Test Configuration" => "Egiaztatu Konfigurazioa", "Help" => "Laguntza" ); diff --git a/apps/user_ldap/l10n/fa.php b/apps/user_ldap/l10n/fa.php index 7ddd7dad5c36f3375a8c2ca0d580909c14055308..7816ef7c6f724602d292b5bbe11084ca4c161e90 100644 --- a/apps/user_ldap/l10n/fa.php +++ b/apps/user_ldap/l10n/fa.php @@ -1,8 +1,17 @@ "عملیات حذف پیکربندی سرور ناموفق ماند", +"The configuration is valid and the connection could be established!" => "پیکربندی معتبر است و ارتباط می تواند برقرار شود", "Deletion failed" => "حذف کردن انجام نشد", "Keep settings?" => "آیا تنظیمات ذخیره شود ؟", +"Connection test succeeded" => "تست اتصال با موفقیت انجام گردید", +"Connection test failed" => "تست اتصال ناموفق بود", +"Do you really want to delete the current Server Configuration?" => "آیا واقعا می خواهید پیکربندی کنونی سرور را حذف کنید؟", +"Confirm Deletion" => "تایید حذف", +"Server configuration" => "پیکربندی سرور", +"Add Server Configuration" => "افزودن پیکربندی سرور", "Host" => "میزبانی", "Password" => "رمز عبور", "Port" => "درگاه", +"in bytes" => "در بایت", "Help" => "راه‌نما" ); diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php index 1c2a92f844a47629183aaed528b5effbc2ab6f03..bfbd6c78564b37b4e7a6f4ac58a21c757a1f8a8f 100644 --- a/apps/user_ldap/l10n/fi_FI.php +++ b/apps/user_ldap/l10n/fi_FI.php @@ -1,5 +1,10 @@ "Poisto epäonnistui", +"Keep settings?" => "Säilytetäänkö asetukset?", +"Cannot add server configuration" => "Palvelinasetusten lisäys epäonnistui", +"Connection test succeeded" => "Yhteystesti onnistui", +"Connection test failed" => "Yhteystesti epäonnistui", +"Confirm Deletion" => "Vahvista poisto", "Host" => "Isäntä", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Voit jättää protokollan määrittämättä, paitsi kun vaadit SSL:ää. Aloita silloin ldaps://", "Base DN" => "Oletus DN", @@ -17,13 +22,16 @@ "Group Filter" => "Ryhmien suodatus", "Defines the filter to apply, when retrieving groups." => "Määrittelee käytettävän suodattimen, kun ryhmiä haetaan. ", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilman paikanvaraustermiä, ts. \"objectClass=posixGroup\".", +"Connection Settings" => "Yhteysasetukset", "Port" => "Portti", +"Disable Main Server" => "Poista pääpalvelin käytöstä", "Use TLS" => "Käytä TLS:ää", "Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)", "Turn off SSL certificate validation." => "Poista käytöstä SSL-varmenteen vahvistus", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jos yhteys toimii vain tällä valinnalla, siirrä LDAP-palvelimen SSL-varmenne ownCloud-palvelimellesi.", "Not recommended, use for testing only." => "Ei suositella, käytä vain testausta varten.", "in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", +"Directory Settings" => "Hakemistoasetukset", "User Display Name Field" => "Käyttäjän näytettävän nimen kenttä", "The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP-attribuutti, jota käytetään käyttäjän ownCloud-käyttäjänimenä ", "Base User Tree" => "Oletuskäyttäjäpuu", diff --git a/apps/user_ldap/l10n/gl.php b/apps/user_ldap/l10n/gl.php index 36c1f7af11483e626e05e5094aa6e7767da4398c..deb6dbb5553a2e395c758588eaefb2eb8a0672c0 100644 --- a/apps/user_ldap/l10n/gl.php +++ b/apps/user_ldap/l10n/gl.php @@ -1,9 +1,24 @@ "Non foi posíbel eliminar a configuración do servidor", +"The configuration is valid and the connection could be established!" => "A configuración é correcta e pode estabelecerse a conexión.", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuración é correcta, mais a ligazón non. Comprobe a configuración do servidor e as credenciais.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuración non é correcta. Vexa o rexistro de ownCloud para máis detalles", "Deletion failed" => "Fallou o borrado", +"Take over settings from recent server configuration?" => "Tomar os recentes axustes de configuración do servidor?", +"Keep settings?" => "Manter os axustes?", +"Cannot add server configuration" => "Non é posíbel engadir a configuración do servidor", +"Connection test succeeded" => "A proba de conexión foi satisfactoria", +"Connection test failed" => "A proba de conexión fracasou", +"Do you really want to delete the current Server Configuration?" => "Confirma que quere eliminar a configuración actual do servidor?", +"Confirm Deletion" => "Confirmar a eliminación", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Aviso: Os aplicativos user_ldap e user_webdavauth son incompatíbeis. Pode acontecer un comportamento estraño. Consulte co administrador do sistema para desactivar un deles.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Aviso: O módulo PHP LDAP non está instalado, o servidor non funcionará. Consulte co administrador do sistema para instalalo.", +"Server configuration" => "Configuración do servidor", +"Add Server Configuration" => "Engadir a configuración do servidor", "Host" => "Servidor", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Pode omitir o protocolo agás que precise de SSL. Nese caso comece con ldaps://", "Base DN" => "DN base", +"One Base DN per line" => "Un DN base por liña", "You can specify Base DN for users and groups in the Advanced tab" => "Pode especificar a DN base para usuarios e grupos na lapela de «Avanzado»", "User DN" => "DN do usuario", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN do cliente do usuario co que hai que estabelecer unha conexión, p.ex uid=axente, dc=exemplo, dc=com. Para o acceso anónimo deixe o DN e o contrasinal baleiros.", @@ -18,21 +33,43 @@ "Group Filter" => "Filtro de grupo", "Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar cando se recompilan os grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ningunha marca de posición, como p.ex «objectClass=grupoPosix».", +"Connection Settings" => "Axustes da conexión", +"Configuration Active" => "Configuración activa", +"When unchecked, this configuration will be skipped." => "Se está sen marcar, omítese esta configuración.", "Port" => "Porto", +"Backup (Replica) Host" => "Servidor da copia de seguranza (Réplica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Indicar un servidor de copia de seguranza opcional. Debe ser unha réplica do servidor principal LDAP/AD.", +"Backup (Replica) Port" => "Porto da copia de seguranza (Réplica)", +"Disable Main Server" => "Desactivar o servidor principal", +"When switched on, ownCloud will only connect to the replica server." => "Cando está activado, ownCloud só se conectará ao servidor de réplica.", "Use TLS" => "Usar TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Non utilizalo ademais para conexións LDAPS xa que fallará.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP que non distingue entre maiúsculas e minúsculas (Windows)", "Turn off SSL certificate validation." => "Desactiva a validación do certificado SSL.", -"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexión só funciona con esta opción importa o certificado SSL do servidor LDAP no seu servidor ownCloud.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexión só funciona con esta opción importe o certificado SSL do servidor LDAP no seu servidor ownCloud.", "Not recommended, use for testing only." => "Non se recomenda. Só para probas.", +"Cache Time-To-Live" => "Tempo de persistencia da caché", "in seconds. A change empties the cache." => "en segundos. Calquera cambio baleira a caché.", +"Directory Settings" => "Axustes do directorio", "User Display Name Field" => "Campo de mostra do nome de usuario", "The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP a empregar para xerar o nome de usuario de ownCloud.", "Base User Tree" => "Base da árbore de usuarios", +"One User Base DN per line" => "Un DN base de usuario por liña", +"User Search Attributes" => "Atributos de busca do usuario", +"Optional; one attribute per line" => "Opcional; un atributo por liña", "Group Display Name Field" => "Campo de mostra do nome de grupo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP úsase para xerar os nomes dos grupos de ownCloud.", "Base Group Tree" => "Base da árbore de grupo", +"One Group Base DN per line" => "Un DN base de grupo por liña", +"Group Search Attributes" => "Atributos de busca do grupo", "Group-Member association" => "Asociación de grupos e membros", +"Special Attributes" => "Atributos especiais", +"Quota Field" => "Campo de cota", +"Quota Default" => "Cota predeterminada", "in bytes" => "en bytes", +"Email Field" => "Campo do correo", +"User Home Folder Naming Rule" => "Regra de nomeado do cartafol do usuario", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixar baleiro para o nome de usuario (predeterminado). Noutro caso, especifique un atributo LDAP/AD.", +"Test Configuration" => "Probar a configuración", "Help" => "Axuda" ); diff --git a/apps/user_ldap/l10n/he.php b/apps/user_ldap/l10n/he.php index 5c563b7b6f3cdcc987af135a07c74a44c12a551c..c9b0e282f1d26908fa3ab55e391f036f8083f1de 100644 --- a/apps/user_ldap/l10n/he.php +++ b/apps/user_ldap/l10n/he.php @@ -7,6 +7,7 @@ "User Login Filter" => "סנן כניסת משתמש", "User List Filter" => "סנן רשימת משתמשים", "Group Filter" => "סנן קבוצה", +"Port" => "פורט", "in seconds. A change empties the cache." => "בשניות. שינוי מרוקן את המטמון.", "in bytes" => "בבתים", "Help" => "עזרה" diff --git a/apps/user_ldap/l10n/hi.php b/apps/user_ldap/l10n/hi.php new file mode 100644 index 0000000000000000000000000000000000000000..60d4ea98e84abe1a2062f5e57fab5dc6f727d98d --- /dev/null +++ b/apps/user_ldap/l10n/hi.php @@ -0,0 +1,3 @@ + "सहयोग" +); diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php index 48a0823a5836c8e55e79f48f022b486eedcd452e..a82a64ab32f1387660ebfb3b93c38cf19f6db002 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -1,7 +1,20 @@ "Nem sikerült törölni a kiszolgáló konfigurációját", +"The configuration is valid and the connection could be established!" => "A konfiguráció érvényes, és a kapcsolat létrehozható!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A konfiguráció érvényes, de a kapcsolat nem hozható létre. Kérem ellenőrizze a kiszolgáló beállításait, és az elérési adatokat.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Érvénytelen konfiguráció. További információkért nézze meg az ownCloud naplófájlját.", "Deletion failed" => "A törlés nem sikerült", +"Take over settings from recent server configuration?" => "Vegyük át a beállításokat az előző konfigurációból?", +"Keep settings?" => "Tartsuk meg a beállításokat?", +"Cannot add server configuration" => "Az új kiszolgáló konfigurációja nem hozható létre", +"Connection test succeeded" => "A kapcsolatellenőrzés eredménye: sikerült", +"Connection test failed" => "A kapcsolatellenőrzés eredménye: nem sikerült", +"Do you really want to delete the current Server Configuration?" => "Tényleg törölni szeretné a kiszolgáló beállításait?", +"Confirm Deletion" => "A törlés megerősítése", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Figyelem: a user_ldap és user_webdavauth alkalmazások nem kompatibilisek. Együttes használatuk váratlan eredményekhez vezethet. Kérje meg a rendszergazdát, hogy a kettő közül kapcsolja ki az egyiket.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Figyelmeztetés: Az LDAP PHP modul nincs telepítve, ezért ez az alrendszer nem fog működni. Kérje meg a rendszergazdát, hogy telepítse!", +"Server configuration" => "A kiszolgálók beállításai", +"Add Server Configuration" => "Új kiszolgáló beállításának hozzáadása", "Host" => "Kiszolgáló", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "A protokoll előtag elhagyható, kivéve, ha SSL-t kíván használni. Ebben az esetben kezdje így: ldaps://", "Base DN" => "DN-gyökér", @@ -20,23 +33,43 @@ "Group Filter" => "A csoportok szűrője", "Defines the filter to apply, when retrieving groups." => "Ez a szűrő érvényes a csoportok listázásakor.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "itt ne használjunk változót, pl. \"objectClass=posixGroup\".", +"Connection Settings" => "Kapcsolati beállítások", +"Configuration Active" => "A beállítás aktív", +"When unchecked, this configuration will be skipped." => "Ha nincs kipipálva, ez a beállítás kihagyódik.", "Port" => "Port", +"Backup (Replica) Host" => "Másodkiszolgáló (replika)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Adjon meg egy opcionális másodkiszolgálót. Ez a fő LDAP/AD kiszolgáló szinkron másolata (replikája) kell legyen.", +"Backup (Replica) Port" => "A másodkiszolgáló (replika) portszáma", +"Disable Main Server" => "A fő szerver kihagyása", +"When switched on, ownCloud will only connect to the replica server." => "Ha ezt bekapcsoljuk, akkor az ownCloud csak a másodszerverekhez kapcsolódik.", "Use TLS" => "Használjunk TLS-t", +"Do not use it additionally for LDAPS connections, it will fail." => "LDAPS kapcsolatok esetén ne kapcsoljuk be, mert nem fog működni.", "Case insensitve LDAP server (Windows)" => "Az LDAP-kiszolgáló nem tesz különbséget a kis- és nagybetűk között (Windows)", "Turn off SSL certificate validation." => "Ne ellenőrizzük az SSL-tanúsítvány érvényességét", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát az ownCloud kiszolgálóra!", "Not recommended, use for testing only." => "Nem javasolt, csak tesztelésre érdemes használni.", +"Cache Time-To-Live" => "A gyorsítótár tárolási időtartama", "in seconds. A change empties the cache." => "másodpercben. A változtatás törli a cache tartalmát.", +"Directory Settings" => "Címtár beállítások", "User Display Name Field" => "A felhasználónév mezője", "The LDAP attribute to use to generate the user`s ownCloud name." => "Ebből az LDAP attribútumból képződik a felhasználó elnevezése, ami megjelenik az ownCloudban.", "Base User Tree" => "A felhasználói fa gyökere", "One User Base DN per line" => "Soronként egy felhasználói fa gyökerét adhatjuk meg", +"User Search Attributes" => "A felhasználók lekérdezett attribútumai", +"Optional; one attribute per line" => "Nem kötelező megadni, soronként egy attribútum", "Group Display Name Field" => "A csoport nevének mezője", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Ebből az LDAP attribútumból képződik a csoport elnevezése, ami megjelenik az ownCloudban.", "Base Group Tree" => "A csoportfa gyökere", "One Group Base DN per line" => "Soronként egy csoportfa gyökerét adhatjuk meg", +"Group Search Attributes" => "A csoportok lekérdezett attribútumai", "Group-Member association" => "A csoporttagság attribútuma", +"Special Attributes" => "Különleges attribútumok", +"Quota Field" => "Kvóta mező", +"Quota Default" => "Alapértelmezett kvóta", "in bytes" => "bájtban", +"Email Field" => "Email mező", +"User Home Folder Naming Rule" => "A home könyvtár elérési útvonala", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Hagyja üresen, ha a felhasználónevet kívánja használni. Ellenkező esetben adjon meg egy LDAP/AD attribútumot!", +"Test Configuration" => "A beállítások tesztelése", "Help" => "Súgó" ); diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php index c07892386d6a2f747c582afcbd0a8202ab8cca8f..5912789c85640cc78afcd098ec9ad1e52c8fd9c7 100644 --- a/apps/user_ldap/l10n/id.php +++ b/apps/user_ldap/l10n/id.php @@ -1,14 +1,69 @@ "Gagal menghapus konfigurasi server", +"The configuration is valid and the connection could be established!" => "Konfigurasi valid dan koneksi dapat dilakukan!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurasi valid, tetapi Bind gagal. Silakan cek pengaturan server dan keamanan.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurasi salah. Silakan lihat log ownCloud untuk lengkapnya.", "Deletion failed" => "penghapusan gagal", +"Take over settings from recent server configuration?" => "Ambil alih pengaturan dari konfigurasi server saat ini?", +"Keep settings?" => "Biarkan pengaturan?", +"Cannot add server configuration" => "Gagal menambah konfigurasi server", +"Connection test succeeded" => "Tes koneksi sukses", +"Connection test failed" => "Tes koneksi gagal", +"Do you really want to delete the current Server Configuration?" => "Anda ingin menghapus Konfigurasi Server saat ini?", +"Confirm Deletion" => "Konfirmasi Penghapusan", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Peringatan:/b> Aplikasi user_ldap dan user_webdavauth tidak kompatibel. Anda mungkin akan mengalami kejadian yang tidak diharapkan. Silakan minta administrator sistem untuk menonaktifkan salah satunya.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Peringatan: Modul LDAP PHP tidak terpasang, perangkat tidak akan bekerja. Silakan minta administrator sistem untuk memasangnya.", +"Server configuration" => "Konfigurasi server", +"Add Server Configuration" => "Tambah Konfigurasi Server", "Host" => "host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol dapat tidak ditulis, kecuali anda menggunakan SSL. Lalu jalankan dengan ldaps://", +"Base DN" => "Base DN", +"One Base DN per line" => "Satu Base DN per baris", +"You can specify Base DN for users and groups in the Advanced tab" => "Anda dapat menetapkan Base DN untuk pengguna dan grup dalam tab Lanjutan", +"User DN" => "User DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN dari klien pengguna yang dengannya tautan akan diterapkan, mis. uid=agen,dc=contoh,dc=com. Untuk akses anonim, biarkan DN dan kata sandi kosong.", "Password" => "kata kunci", +"For anonymous access, leave DN and Password empty." => "Untuk akses anonim, biarkan DN dan Kata sandi kosong.", "User Login Filter" => "gunakan saringan login", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definisikan filter untuk diterapkan, saat login dilakukan. %%uid menggantikan username saat login.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "gunakan pengganti %%uid, mis. \"uid=%%uid\"", +"User List Filter" => "Daftar Filter Pengguna", +"Defines the filter to apply, when retrieving users." => "Definisikan filter untuk diterapkan saat menerima pengguna.", +"without any placeholder, e.g. \"objectClass=person\"." => "tanpa pengganti apapun, mis. \"objectClass=seseorang\".", "Group Filter" => "saringan grup", +"Defines the filter to apply, when retrieving groups." => "Definisikan filter untuk diterapkan saat menerima grup.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "tanpa pengganti apapaun, mis. \"objectClass=posixGroup\".", +"Connection Settings" => "Pengaturan Koneksi", +"Configuration Active" => "Konfigurasi Aktif", +"When unchecked, this configuration will be skipped." => "Jika tidak dicentang, konfigurasi ini dilewati.", "Port" => "port", +"Backup (Replica) Host" => "Host Cadangan (Replika)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Berikan pilihan host cadangan. Harus merupakan replika dari server LDAP/AD utama.", +"Backup (Replica) Port" => "Port Cadangan (Replika)", +"Disable Main Server" => "Nonaktifkan Server Utama", +"When switched on, ownCloud will only connect to the replica server." => "Saat diaktifkan, ownCloud hanya akan terhubung ke server replika.", "Use TLS" => "gunakan TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Jangan gunakan utamanya untuk koneksi LDAPS, koneksi akan gagal.", +"Case insensitve LDAP server (Windows)" => "Server LDAP dengan kapitalisasi tidak sensitif (Windows)", "Turn off SSL certificate validation." => "matikan validasi sertivikat SSL", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jika koneksi hanya bekerja dengan opsi ini, impor sertifikat SSL server LDAP dari server ownCloud anda.", "Not recommended, use for testing only." => "tidak disarankan, gunakan hanya untuk pengujian.", "in seconds. A change empties the cache." => "dalam detik. perubahan mengosongkan cache", +"Directory Settings" => "Pengaturan Direktori", +"User Display Name Field" => "Bidang Tampilan Nama Pengguna", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP yang digunakan untuk menghasilkan nama pengguna ownCloud.", +"Base User Tree" => "Pohon Pengguna Dasar", +"One User Base DN per line" => "Satu Pengguna Base DN per baris", +"User Search Attributes" => "Atribut Pencarian Pengguna", +"Optional; one attribute per line" => "Pilihan; satu atribut per baris", +"Group Display Name Field" => "Bidang Tampilan Nama Grup", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP yang digunakan untuk menghasilkan nama grup ownCloud.", +"Base Group Tree" => "Pohon Grup Dasar", +"One Group Base DN per line" => "Satu Grup Base DN per baris", +"Group Search Attributes" => "Atribut Pencarian Grup", +"Group-Member association" => "asosiasi Anggota-Grup", +"Special Attributes" => "Atribut Khusus", "in bytes" => "dalam bytes", +"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Biarkan nama pengguna kosong (default). Atau tetapkan atribut LDAP/AD.", "Help" => "bantuan" ); diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php index 594529190d94cf28dbeea404d8461aa0e073a9dd..86887970dabc62131d1eda2e75536176ac3fb7f8 100644 --- a/apps/user_ldap/l10n/it.php +++ b/apps/user_ldap/l10n/it.php @@ -63,7 +63,11 @@ "Group Search Attributes" => "Attributi di ricerca gruppo", "Group-Member association" => "Associazione gruppo-utente ", "Special Attributes" => "Attributi speciali", +"Quota Field" => "Campo Quota", +"Quota Default" => "Quota predefinita", "in bytes" => "in byte", +"Email Field" => "Campo Email", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lascia vuoto per il nome utente (predefinito). Altrimenti, specifica un attributo LDAP/AD.", +"Test Configuration" => "Prova configurazione", "Help" => "Aiuto" ); diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php index 11ad6cc7a377ab93bc5ab2c3a11c163010e33c32..3ae7d2e639222326d4e24adcbbb9d434776845e4 100644 --- a/apps/user_ldap/l10n/ja_JP.php +++ b/apps/user_ldap/l10n/ja_JP.php @@ -48,6 +48,7 @@ "Turn off SSL certificate validation." => "SSL証明書の確認を無効にする。", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "接続がこのオプションでのみ動作する場合は、LDAPサーバのSSL証明書をownCloudサーバにインポートしてください。", "Not recommended, use for testing only." => "推奨しません、テスト目的でのみ利用してください。", +"Cache Time-To-Live" => "キャッシュのTTL", "in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。", "Directory Settings" => "ディレクトリ設定", "User Display Name Field" => "ユーザ表示名のフィールド", @@ -63,7 +64,12 @@ "Group Search Attributes" => "グループ検索属性", "Group-Member association" => "グループとメンバーの関連付け", "Special Attributes" => "特殊属性", +"Quota Field" => "クォータフィールド", +"Quota Default" => "クォータのデフォルト", "in bytes" => "バイト", +"Email Field" => "メールフィールド", +"User Home Folder Naming Rule" => "ユーザのホームフォルダ命名規則", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ユーザ名を空のままにしてください(デフォルト)。そうでない場合は、LDAPもしくはADの属性を指定してください。", +"Test Configuration" => "テスト設定", "Help" => "ヘルプ" ); diff --git a/apps/user_ldap/l10n/ka.php b/apps/user_ldap/l10n/ka.php new file mode 100644 index 0000000000000000000000000000000000000000..169926283e9a39b2a4de635e76e739143a1cdf3b --- /dev/null +++ b/apps/user_ldap/l10n/ka.php @@ -0,0 +1,4 @@ + "პაროლი", +"Help" => "შველა" +); diff --git a/apps/user_ldap/l10n/ko.php b/apps/user_ldap/l10n/ko.php index 419e2d0a690d837dfd13c63a4c341a3e344bd599..8aa9fe74b3d1f513c3c73de5c3b58f0930c2557e 100644 --- a/apps/user_ldap/l10n/ko.php +++ b/apps/user_ldap/l10n/ko.php @@ -1,5 +1,8 @@ "삭제 실패", +"Keep settings?" => "설정을 유지합니까?", +"Connection test succeeded" => "연결 시험 성공", +"Connection test failed" => "연결 시험 실패", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "경고: user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여 둘 중 하나만 사용하도록 하십시오.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "경고: PHP LDAP 모듈이 비활성화되어 있거나 설치되어 있지 않습니다. 백엔드를 사용할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", "Host" => "호스트", @@ -20,21 +23,29 @@ "Group Filter" => "그룹 필터", "Defines the filter to apply, when retrieving groups." => "그룹을 검색할 때 적용할 필터를 정의합니다.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "자리 비움자를 사용할 수 없습니다. 예제: \"objectClass=posixGroup\"", +"Connection Settings" => "연결 설정", +"Configuration Active" => "구성 활성화", "Port" => "포트", +"Backup (Replica) Host" => "백업 (복제) 포트", +"Backup (Replica) Port" => "백업 (복제) 포트", +"Disable Main Server" => "주 서버 비활성화", "Use TLS" => "TLS 사용", "Case insensitve LDAP server (Windows)" => "서버에서 대소문자를 구분하지 않음 (Windows)", "Turn off SSL certificate validation." => "SSL 인증서 유효성 검사를 해제합니다.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "이 옵션을 사용해야 연결할 수 있는 경우에는 LDAP 서버의 SSL 인증서를 ownCloud로 가져올 수 있습니다.", "Not recommended, use for testing only." => "추천하지 않음, 테스트로만 사용하십시오.", "in seconds. A change empties the cache." => "초. 항목 변경 시 캐시가 갱신됩니다.", +"Directory Settings" => "디렉토리 설정", "User Display Name Field" => "사용자의 표시 이름 필드", "The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP 속성은 사용자의 ownCloud 이름을 생성하기 위해 사용합니다.", "Base User Tree" => "기본 사용자 트리", "One User Base DN per line" => "사용자 DN을 한 줄에 하나씩 입력하십시오", +"User Search Attributes" => "사용자 검색 속성", "Group Display Name Field" => "그룹의 표시 이름 필드", "The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP 속성은 그룹의 ownCloud 이름을 생성하기 위해 사용합니다.", "Base Group Tree" => "기본 그룹 트리", "One Group Base DN per line" => "그룹 기본 DN을 한 줄에 하나씩 입력하십시오", +"Group Search Attributes" => "그룹 검색 속성", "Group-Member association" => "그룹-회원 연결", "in bytes" => "바이트", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "사용자 이름을 사용하려면 비워 두십시오(기본값). 기타 경우 LDAP/AD 속성을 지정하십시오.", diff --git a/apps/user_ldap/l10n/my_MM.php b/apps/user_ldap/l10n/my_MM.php new file mode 100644 index 0000000000000000000000000000000000000000..ee8d3dd26fa873eab8252b207db2e52f80d080e4 --- /dev/null +++ b/apps/user_ldap/l10n/my_MM.php @@ -0,0 +1,4 @@ + "စကားဝှက်", +"Help" => "အကူအညီ" +); diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php index 6879a4c4b9480489b5d0f2e09fd9a9566b18ec07..0eda263aa116bf7c8ec6dc6644518d798920b51e 100644 --- a/apps/user_ldap/l10n/nl.php +++ b/apps/user_ldap/l10n/nl.php @@ -43,6 +43,7 @@ "Disable Main Server" => "Deactiveren hoofdserver", "When switched on, ownCloud will only connect to the replica server." => "Wanneer ingeschakeld, zal ownCloud allen verbinden met de replicaserver.", "Use TLS" => "Gebruik TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Gebruik het niet voor LDAPS verbindingen, dat gaat niet lukken.", "Case insensitve LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)", "Turn off SSL certificate validation." => "Schakel SSL certificaat validatie uit.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Als de connectie alleen werkt met deze optie, importeer dan het LDAP server SSL certificaat naar je ownCloud server.", diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php index ef3f9140ef75365d2312e498d67ed46c424bbf40..776aa445e4eebbf8f32d15ffffd9d553d7b2209b 100644 --- a/apps/user_ldap/l10n/pl.php +++ b/apps/user_ldap/l10n/pl.php @@ -1,9 +1,24 @@ "Nie można usunąć konfiguracji serwera", +"The configuration is valid and the connection could be established!" => "Konfiguracja jest prawidłowa i można ustanowić połączenie!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfiguracja jest prawidłowa, ale Bind nie. Sprawdź ustawienia serwera i poświadczenia.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Konfiguracja jest nieprawidłowa. Proszę przejrzeć logi dziennika ownCloud ", "Deletion failed" => "Skasowanie nie powiodło się", +"Take over settings from recent server configuration?" => "Przejmij ustawienia z ostatnich konfiguracji serwera?", +"Keep settings?" => "Zachować ustawienia?", +"Cannot add server configuration" => "Nie można dodać konfiguracji serwera", +"Connection test succeeded" => "Test połączenia udany", +"Connection test failed" => "Test połączenia nie udany", +"Do you really want to delete the current Server Configuration?" => "Czy chcesz usunąć bieżącą konfigurację serwera?", +"Confirm Deletion" => "Potwierdź usunięcie", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Ostrzeżenie: Aplikacje user_ldap i user_webdavauth nie są kompatybilne. Mogą powodować nieoczekiwane zachowanie. Poproś administratora o wyłączenie jednej z nich.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Ostrzeżenie: Moduł PHP LDAP nie jest zainstalowany i nie będzie działał. Poproś administratora o włączenie go.", +"Server configuration" => "Konfiguracja servera", +"Add Server Configuration" => "Dodaj konfigurację servera", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Można pominąć protokół, z wyjątkiem wymaganego protokołu SSL. Następnie uruchom z ldaps://", "Base DN" => "Baza DN", +"One Base DN per line" => "Jedna baza DN na linię", "You can specify Base DN for users and groups in the Advanced tab" => "Bazę DN można określić dla użytkowników i grup w karcie Zaawansowane", "User DN" => "Użytkownik DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN użytkownika klienta, z którym powiązanie wykonuje się, np. uid=agent,dc=example,dc=com. Dla dostępu anonimowego pozostawić DN i hasło puste", @@ -18,21 +33,43 @@ "Group Filter" => "Grupa filtrów", "Defines the filter to apply, when retrieving groups." => "Definiuje filtry do zastosowania, podczas pobierania grup.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez żadnych symboli zastępczych np. \"objectClass=posixGroup\".", +"Connection Settings" => "Konfiguracja połączeń", +"Configuration Active" => "Konfiguracja archiwum", +"When unchecked, this configuration will be skipped." => "Gdy niezaznaczone, ta konfiguracja zostanie pominięta.", "Port" => "Port", +"Backup (Replica) Host" => "Kopia zapasowa (repliki) host", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dać opcjonalnie hosta kopii zapasowej . To musi być repliką głównego serwera LDAP/AD.", +"Backup (Replica) Port" => "Kopia zapasowa (repliki) Port", +"Disable Main Server" => "Wyłącz serwer główny", +"When switched on, ownCloud will only connect to the replica server." => "Po włączeniu, ownCloud tylko połączy się z serwerem repliki.", "Use TLS" => "Użyj TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Nie używaj go dodatkowo dla połączeń protokołu LDAPS, zakończy się niepowodzeniem.", "Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)", "Turn off SSL certificate validation." => "Wyłączyć sprawdzanie poprawności certyfikatu SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jeśli połączenie działa tylko z tą opcją, zaimportuj certyfikat SSL serwera LDAP w serwerze ownCloud.", "Not recommended, use for testing only." => "Niezalecane, użyj tylko testowo.", +"Cache Time-To-Live" => "Przechowuj czas życia", "in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.", +"Directory Settings" => "Ustawienia katalogów", "User Display Name Field" => "Pole wyświetlanej nazwy użytkownika", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy użytkownika ownCloud.", "Base User Tree" => "Drzewo bazy użytkowników", +"One User Base DN per line" => "Jeden użytkownik Bazy DN na linię", +"User Search Attributes" => "Szukaj atrybutów", +"Optional; one attribute per line" => "Opcjonalnie; jeden atrybut w wierszu", "Group Display Name Field" => "Pole wyświetlanej nazwy grupy", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy grup ownCloud.", "Base Group Tree" => "Drzewo bazy grup", +"One Group Base DN per line" => "Jedna grupa bazy DN na linię", +"Group Search Attributes" => "Grupa atrybutów wyszukaj", "Group-Member association" => "Członek grupy stowarzyszenia", +"Special Attributes" => "Specjalne atrybuty", +"Quota Field" => "Pole przydziału", +"Quota Default" => "Przydział domyślny", "in bytes" => "w bajtach", +"Email Field" => "Pole email", +"User Home Folder Naming Rule" => "Reguły nazewnictwa folderu domowego użytkownika", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pozostaw puste dla user name (domyślnie). W przeciwnym razie podaj atrybut LDAP/AD.", +"Test Configuration" => "Konfiguracja testowa", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/pt_BR.php b/apps/user_ldap/l10n/pt_BR.php index 514ceb7027ea5c16d6c5d77bd4f0f87cec1e65ba..e3d2da463cca9d227bc672810a26cd97cf18772a 100644 --- a/apps/user_ldap/l10n/pt_BR.php +++ b/apps/user_ldap/l10n/pt_BR.php @@ -1,8 +1,24 @@ "Falha ao deletar a configuração do servidor", +"The configuration is valid and the connection could be established!" => "A configuração é válida e a conexão foi estabelecida!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuração é válida, mas o Bind falhou. Confira as configurações do servidor e as credenciais.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuração é inválida. Leia o \"log\" do ownCloud para mais detalhes.", "Deletion failed" => "Remoção falhou", +"Take over settings from recent server configuration?" => "Tomar parámetros de recente configuração de servidor?", +"Keep settings?" => "Manter ajustes?", +"Cannot add server configuration" => "Não foi possível adicionar a configuração do servidor", +"Connection test succeeded" => "Teste de conexão bem sucedido", +"Connection test failed" => "Teste de conexão falhou", +"Do you really want to delete the current Server Configuration?" => "Você quer realmente deletar as atuais Configurações de Servidor?", +"Confirm Deletion" => "Confirmar Exclusão", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Aviso: Os aplicativos user_ldap e user_webdavauth são incompatíveis. Você deverá experienciar comportamento inesperado. Por favor, peça ao seu administrador do sistema para desabilitar um deles.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Aviso: O módulo PHP LDAP não está instalado, o backend não funcionará. Por favor, peça ao seu administrador do sistema para instalá-lo.", +"Server configuration" => "Configuração de servidor", +"Add Server Configuration" => "Adicionar configuração de servidor", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Você pode omitir o protocolo, exceto quando requerer SSL. Então inicie com ldaps://", "Base DN" => "DN Base", +"One Base DN per line" => "Uma base DN por linha", "You can specify Base DN for users and groups in the Advanced tab" => "Você pode especificar DN Base para usuários e grupos na guia Avançada", "User DN" => "DN Usuário", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN do cliente usuário com qual a ligação deverá ser feita, ex. uid=agent,dc=example,dc=com. Para acesso anônimo, deixe DN e Senha vazios.", @@ -17,20 +33,33 @@ "Group Filter" => "Filtro de Grupo", "Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar ao obter grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sem nenhum espaço reservado, ex. \"objectClass=posixGroup\"", +"Connection Settings" => "Configurações de conexão", +"Configuration Active" => "Configuração ativa", +"When unchecked, this configuration will be skipped." => "Quando assinalada, esta configuração será pulada.", "Port" => "Porta", +"Disable Main Server" => "Desativar Servidor Principal", +"When switched on, ownCloud will only connect to the replica server." => "Quando ativado, ownCloud somente conectar-se-á ao servidor réplica.", "Use TLS" => "Usar TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Não use adicionalmente para conexões LDAPS, pois falhará.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP sensível à caixa alta (Windows)", "Turn off SSL certificate validation." => "Desligar validação de certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexão só funciona com essa opção, importe o certificado SSL do servidor LDAP no seu servidor ownCloud.", "Not recommended, use for testing only." => "Não recomendado, use somente para testes.", "in seconds. A change empties the cache." => "em segundos. Uma mudança esvaziará o cache.", +"Directory Settings" => "Configurações de Diretório", "User Display Name Field" => "Campo Nome de Exibição de Usuário", "The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do usuário.", "Base User Tree" => "Árvore de Usuário Base", +"One User Base DN per line" => "Um usuário-base DN por linha", +"User Search Attributes" => "Atributos de busca de usuário", +"Optional; one attribute per line" => "Opcional; um atributo por linha", "Group Display Name Field" => "Campo Nome de Exibição de Grupo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do grupo.", "Base Group Tree" => "Árvore de Grupo Base", +"One Group Base DN per line" => "Um grupo-base DN por linha", +"Group Search Attributes" => "Atributos de busca de grupo", "Group-Member association" => "Associação Grupo-Membro", +"Special Attributes" => "Atributos Especiais", "in bytes" => "em bytes", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de usuário (padrão). Caso contrário, especifique um atributo LDAP/AD.", "Help" => "Ajuda" diff --git a/apps/user_ldap/l10n/pt_PT.php b/apps/user_ldap/l10n/pt_PT.php index 058e7ba25323abe9d78245223f73c023ea71f265..bfe6656b3b688df9c4aff407441a8688a09e5b10 100644 --- a/apps/user_ldap/l10n/pt_PT.php +++ b/apps/user_ldap/l10n/pt_PT.php @@ -43,6 +43,7 @@ "Disable Main Server" => "Desactivar servidor principal", "When switched on, ownCloud will only connect to the replica server." => "Se estiver ligado, o ownCloud vai somente ligar-se a este servidor de réplicas.", "Use TLS" => "Usar TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Não utilize para adicionar ligações LDAP, irá falhar!", "Case insensitve LDAP server (Windows)" => "Servidor LDAP (Windows) não sensível a maiúsculas.", "Turn off SSL certificate validation." => "Desligar a validação de certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a ligação apenas funcionar com está opção, importe o certificado SSL do servidor LDAP para o seu servidor do ownCloud.", diff --git a/apps/user_ldap/l10n/ru.php b/apps/user_ldap/l10n/ru.php index 4c4b9708667c34206bec63e5e119b38c4e02b1f8..0746e1e8929f33876af29823c6d528d77e626199 100644 --- a/apps/user_ldap/l10n/ru.php +++ b/apps/user_ldap/l10n/ru.php @@ -1,6 +1,7 @@ "Не удалось удалить конфигурацию сервера", "The configuration is valid and the connection could be established!" => "Конфигурация правильная и подключение может быть установлено!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Конфигурация верна, но операция подключения завершилась неудачно. Пожалуйста, проверьте настройки сервера и учетные данные.", "The configuration is invalid. Please look in the ownCloud log for further details." => "Конфигурация не верна. Пожалуйста, посмотрите в журнале ownCloud детали.", "Deletion failed" => "Удаление не удалось", "Take over settings from recent server configuration?" => "Принять настройки из последней конфигурации сервера?", @@ -11,11 +12,13 @@ "Do you really want to delete the current Server Configuration?" => "Вы действительно хотите удалить существующую конфигурацию сервера?", "Confirm Deletion" => "Подтверждение удаления", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Внимание:Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением. Пожалуйста, обратитесь к системному администратору, чтобы отключить одно из них.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Внимание: Модуль LDAP для PHP не установлен, бэкенд не будет работать. Пожалуйста, попросите вашего системного администратора его установить. ", "Server configuration" => "Конфигурация сервера", "Add Server Configuration" => "Добавить конфигурацию сервера", "Host" => "Сервер", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можно опустить протокол, за исключением того, когда вам требуется SSL. Тогда начните с ldaps :/ /", "Base DN" => "Базовый DN", +"One Base DN per line" => "По одному базовому DN в строке.", "You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп на вкладке \"Расширенное\"", "User DN" => "DN пользователя", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-клиента пользователя, с которым связывают должно быть заполнено, например, uid=агент, dc=пример, dc=com. Для анонимного доступа, оставьте DN и пароль пустыми.", @@ -32,26 +35,41 @@ "without any placeholder, e.g. \"objectClass=posixGroup\"." => "без заполнения, например \"objectClass=posixGroup\".", "Connection Settings" => "Настройки подключения", "Configuration Active" => "Конфигурация активна", +"When unchecked, this configuration will be skipped." => "Когда галочка снята, эта конфигурация будет пропущена.", "Port" => "Порт", +"Backup (Replica) Host" => "Адрес резервного сервера", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Укажите дополнительный резервный сервер. Он должен быть репликой главного LDAP/AD сервера.", +"Backup (Replica) Port" => "Порт резервного сервера", "Disable Main Server" => "Отключение главного сервера", +"When switched on, ownCloud will only connect to the replica server." => "Когда включено, ownCloud будет соединяться только с резервным сервером.", "Use TLS" => "Использовать TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Не используйте совместно с безопасными подключениями (LDAPS), это не сработает.", "Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру сервер LDAP (Windows)", "Turn off SSL certificate validation." => "Отключить проверку сертификата SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте на ваш сервер ownCloud сертификат SSL сервера LDAP.", "Not recommended, use for testing only." => "Не рекомендуется, используйте только для тестирования.", +"Cache Time-To-Live" => "Кэш времени жизни", "in seconds. A change empties the cache." => "в секундах. Изменение очистит кэш.", "Directory Settings" => "Настройки каталога", "User Display Name Field" => "Поле отображаемого имени пользователя", "The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP для генерации имени пользователя ownCloud.", "Base User Tree" => "База пользовательского дерева", +"One User Base DN per line" => "По одной базовому DN пользователей в строке.", "User Search Attributes" => "Поисковые атрибуты пользователя", "Optional; one attribute per line" => "Опционально; один атрибут на линию", "Group Display Name Field" => "Поле отображаемого имени группы", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP для генерации имени группы ownCloud.", "Base Group Tree" => "База группового дерева", +"One Group Base DN per line" => "По одной базовому DN групп в строке.", +"Group Search Attributes" => "Атрибуты поиска для группы", "Group-Member association" => "Ассоциация Группа-Участник", "Special Attributes" => "Специальные атрибуты", +"Quota Field" => "Поле квота", +"Quota Default" => "Квота по умолчанию", "in bytes" => "в байтах", +"Email Field" => "Поле адресса эллектронной почты", +"User Home Folder Naming Rule" => "Правило именования Домашней Папки Пользователя", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте имя пользователя пустым (по умолчанию). Иначе укажите атрибут LDAP/AD.", +"Test Configuration" => "Тестовая конфигурация", "Help" => "Помощь" ); diff --git a/apps/user_ldap/l10n/sk_SK.php b/apps/user_ldap/l10n/sk_SK.php index af8ff0307a7584661ae575b71029dd4ad42c5700..cb55762e64f6beeea256f7a145d1dc5fdeed53dd 100644 --- a/apps/user_ldap/l10n/sk_SK.php +++ b/apps/user_ldap/l10n/sk_SK.php @@ -11,8 +11,8 @@ "Connection test failed" => "Test pripojenia zlyhal", "Do you really want to delete the current Server Configuration?" => "Naozaj chcete zmazať súčasné nastavenie servera?", "Confirm Deletion" => "Potvrdiť vymazanie", -"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Upozornenie: Aplikácie user_ldap a user_webdavauth nie sú kompatibilné. Môže nastávať neočakávané správanie. Požiadajte správcu systému aby jednu z nich zakázal.", -"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Upozornenie: nie je nainštalovaný LDAP modul pre PHP, backend vrstva nebude fungovať. Požádejte správcu systému aby ho nainštaloval.", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Upozornenie: Aplikácie user_ldap a user_webdavauth nie sú kompatibilné. Môže nastávať neočakávané správanie. Požiadajte administrátora systému aby jednu z nich zakázal.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Upozornenie: nie je nainštalovaný LDAP modul pre PHP, backend vrstva nebude fungovať. Požádejte administrátora systému aby ho nainštaloval.", "Server configuration" => "Nastavenia servera", "Add Server Configuration" => "Pridať nastavenia servera.", "Host" => "Hostiteľ", @@ -43,10 +43,12 @@ "Disable Main Server" => "Zakázať hlavný server", "When switched on, ownCloud will only connect to the replica server." => "Pri zapnutí sa ownCloud pripojí len k záložnému serveru.", "Use TLS" => "Použi TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Nepoužívajte pre pripojenie LDAPS, zlyhá.", "Case insensitve LDAP server (Windows)" => "LDAP server nerozlišuje veľkosť znakov (Windows)", "Turn off SSL certificate validation." => "Vypnúť overovanie SSL certifikátu.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ak pripojenie pracuje len s touto možnosťou, tak importujte SSL certifikát LDAP serveru do vášho servera ownCloud.", "Not recommended, use for testing only." => "Nie je doporučované, len pre testovacie účely.", +"Cache Time-To-Live" => "Životnosť objektov v cache", "in seconds. A change empties the cache." => "v sekundách. Zmena vyprázdni vyrovnávaciu pamäť.", "Directory Settings" => "Nastavenie priečinka", "User Display Name Field" => "Pole pre zobrazenia mena používateľa", @@ -60,9 +62,14 @@ "Base Group Tree" => "Základný skupinový strom", "One Group Base DN per line" => "Jedna skupinová základná DN na riadok", "Group Search Attributes" => "Atribúty vyhľadávania skupín", -"Group-Member association" => "Asociácia člena skupiny", +"Group-Member association" => "Priradenie člena skupiny", "Special Attributes" => "Špeciálne atribúty", +"Quota Field" => "Pole kvóty", +"Quota Default" => "Predvolená kvóta", "in bytes" => "v bajtoch", +"Email Field" => "Pole email", +"User Home Folder Naming Rule" => "Pravidlo pre nastavenie mena používateľského priečinka dát", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Nechajte prázdne pre používateľské meno (predvolené). Inak uveďte atribút LDAP/AD.", +"Test Configuration" => "Test nastavenia", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php index c100fc94afe97f79f7d9883fed8f4389156c4f8c..702912f9c682fe479ee26d6128caafee87e52a0f 100644 --- a/apps/user_ldap/l10n/sv.php +++ b/apps/user_ldap/l10n/sv.php @@ -43,6 +43,7 @@ "Disable Main Server" => "Inaktivera huvudserver", "When switched on, ownCloud will only connect to the replica server." => "När denna är påkopplad kommer ownCloud att koppla upp till replika-servern, endast.", "Use TLS" => "Använd TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Använd inte för LDAPS-anslutningar, det kommer inte att fungera.", "Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)", "Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Om anslutningen bara fungerar med det här alternativet, importera LDAP-serverns SSL-certifikat i din ownCloud-server.", diff --git a/apps/user_ldap/l10n/tr.php b/apps/user_ldap/l10n/tr.php index 1bed9e246c924af9031d228ae725d191a4ecfb6c..7bcabb0448a3c8f81dab7e05e39fbae020dd3d62 100644 --- a/apps/user_ldap/l10n/tr.php +++ b/apps/user_ldap/l10n/tr.php @@ -1,16 +1,22 @@ "Silme başarısız oldu", -"Host" => "Konak", -"Base DN" => "Base DN", -"User DN" => "User DN", +"Keep settings?" => "Ayarları kalsınmı?", +"Connection test succeeded" => "Bağlantı testi başarılı oldu", +"Connection test failed" => "Bağlantı testi başarısız oldu", +"Confirm Deletion" => "Silmeyi onayla", +"Host" => "Sunucu", +"Base DN" => "Ana DN", +"User DN" => "Kullanıcı DN", "Password" => "Parola", "For anonymous access, leave DN and Password empty." => "Anonim erişim için DN ve Parola alanlarını boş bırakın.", -"User Login Filter" => "Kullanıcı Oturum Açma Süzgeci", +"User Login Filter" => "Kullanıcı Oturum Filtresi", "use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid yer tutucusunu kullanın, örneğin \"uid=%%uid\"", -"User List Filter" => "Kullanıcı Liste Süzgeci", +"User List Filter" => "Kullanıcı Liste Filtresi", "without any placeholder, e.g. \"objectClass=person\"." => "bir yer tutucusu olmadan, örneğin \"objectClass=person\"", "Group Filter" => "Grup Süzgeci", +"Connection Settings" => "Bağlantı ayarları", "Port" => "Port", +"Disable Main Server" => "Ana sunucuyu devredışı birak", "Use TLS" => "TLS kullan", "Turn off SSL certificate validation." => "SSL sertifika doğrulamasını kapat.", "Not recommended, use for testing only." => "Önerilmez, sadece test için kullanın.", diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php index 643a7495890b89e6ef74ac4c75ef6d04e0e337e5..3b85e095ff01ac2a1f574f23d2b95640b196dacc 100644 --- a/apps/user_ldap/l10n/uk.php +++ b/apps/user_ldap/l10n/uk.php @@ -33,24 +33,36 @@ "Group Filter" => "Фільтр Груп", "Defines the filter to apply, when retrieving groups." => "Визначає фільтр, який застосовується при отриманні груп.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "без будь-якого заповнювача, наприклад: \"objectClass=posixGroup\".", +"Connection Settings" => "Налаштування З'єднання", "Configuration Active" => "Налаштування Активне", "When unchecked, this configuration will be skipped." => "Якщо \"галочка\" знята, ця конфігурація буде пропущена.", "Port" => "Порт", +"Backup (Replica) Host" => "Сервер для резервних копій", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Вкажіть додатковий резервний сервер. Він повинен бути копією головного LDAP/AD сервера.", +"Backup (Replica) Port" => "Порт сервера для резервних копій", +"Disable Main Server" => "Вимкнути Головний Сервер", +"When switched on, ownCloud will only connect to the replica server." => "Коли увімкнуто, ownCloud буде приєднуватись лише до сервера з резервними копіями.", "Use TLS" => "Використовуйте TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Не використовуйте це додатково для під'єднання до LDAP, бо виконано не буде.", "Case insensitve LDAP server (Windows)" => "Нечутливий до регістру LDAP сервер (Windows)", "Turn off SSL certificate validation." => "Вимкнути перевірку SSL сертифіката.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Якщо з'єднання працює лише з цією опцією, імпортуйте SSL сертифікат LDAP сервера у ваший ownCloud сервер.", "Not recommended, use for testing only." => "Не рекомендується, використовуйте лише для тестів.", "in seconds. A change empties the cache." => "в секундах. Зміна очищує кеш.", +"Directory Settings" => "Налаштування Каталога", "User Display Name Field" => "Поле, яке відображає Ім'я Користувача", "The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен користувачів ownCloud.", "Base User Tree" => "Основне Дерево Користувачів", "One User Base DN per line" => "Один Користувач Base DN на одній строчці", +"User Search Attributes" => "Пошукові Атрибути Користувача", +"Optional; one attribute per line" => "Додатково; один атрибут на строчку", "Group Display Name Field" => "Поле, яке відображає Ім'я Групи", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен груп ownCloud.", "Base Group Tree" => "Основне Дерево Груп", "One Group Base DN per line" => "Одна Група Base DN на одній строчці", +"Group Search Attributes" => "Пошукові Атрибути Групи", "Group-Member association" => "Асоціація Група-Член", +"Special Attributes" => "Спеціальні Атрибути", "in bytes" => "в байтах", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Залиште порожнім для імені користувача (за замовчанням). Інакше, вкажіть атрибут LDAP/AD.", "Help" => "Допомога" diff --git a/apps/user_ldap/l10n/ur_PK.php b/apps/user_ldap/l10n/ur_PK.php new file mode 100644 index 0000000000000000000000000000000000000000..4c606a138081f80fed2752ffeaba5a345108481a --- /dev/null +++ b/apps/user_ldap/l10n/ur_PK.php @@ -0,0 +1,4 @@ + "پاسورڈ", +"Help" => "مدد" +); diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php index 46054e4a4e21b3c07ca4f76b6595bb49b37f8562..4bbb977f36362ff85c5375811920f57e19b753ea 100644 --- a/apps/user_ldap/l10n/vi.php +++ b/apps/user_ldap/l10n/vi.php @@ -17,20 +17,30 @@ "Group Filter" => "Bộ lọc nhóm", "Defines the filter to apply, when retrieving groups." => "Xác định các bộ lọc để áp dụng, khi nhóm sử dụng.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "mà không giữ chỗ nào, ví dụ như \"objectClass = osixGroup\".", +"Connection Settings" => "Connection Settings", "Port" => "Cổng", +"Backup (Replica) Port" => "Cổng sao lưu (Replica)", +"Disable Main Server" => "Tắt máy chủ chính", +"When switched on, ownCloud will only connect to the replica server." => "When switched on, ownCloud will only connect to the replica server.", "Use TLS" => "Sử dụng TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Do not use it additionally for LDAPS connections, it will fail.", "Case insensitve LDAP server (Windows)" => "Trường hợp insensitve LDAP máy chủ (Windows)", "Turn off SSL certificate validation." => "Tắt xác thực chứng nhận SSL", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Nếu kết nối chỉ hoạt động với tùy chọn này, vui lòng import LDAP certificate SSL trong máy chủ ownCloud của bạn.", "Not recommended, use for testing only." => "Không khuyến khích, Chỉ sử dụng để thử nghiệm.", "in seconds. A change empties the cache." => "trong vài giây. Một sự thay đổi bộ nhớ cache.", +"Directory Settings" => "Directory Settings", "User Display Name Field" => "Hiển thị tên người sử dụng", "The LDAP attribute to use to generate the user`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo tên người dùng ownCloud.", "Base User Tree" => "Cây người dùng cơ bản", +"User Search Attributes" => "User Search Attributes", +"Optional; one attribute per line" => "Optional; one attribute per line", "Group Display Name Field" => "Hiển thị tên nhóm", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo các nhóm ownCloud.", "Base Group Tree" => "Cây nhóm cơ bản", +"Group Search Attributes" => "Group Search Attributes", "Group-Member association" => "Nhóm thành viên Cộng đồng", +"Special Attributes" => "Special Attributes", "in bytes" => "Theo Byte", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Để trống tên người dùng (mặc định). Nếu không chỉ định thuộc tính LDAP/AD", "Help" => "Giúp đỡ" diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 68cbe4a5e759fed0364111ef8c213b0524580a84..05249b8f16380f0583ed2e62e31199fc40c46046 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -4,7 +4,7 @@ * ownCloud – LDAP Access * * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -48,7 +48,9 @@ abstract class Access { */ public function readAttribute($dn, $attr, $filter = 'objectClass=*') { if(!$this->checkConnection()) { - \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'No LDAP Connector assigned, access impossible for readAttribute.', + \OCP\Util::WARN); return false; } $cr = $this->connection->getConnectionResource(); @@ -57,8 +59,8 @@ abstract class Access { \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG); return false; } - $rr = @ldap_read($cr, $dn, $filter, array($attr)); $dn = $this->DNasBaseParameter($dn); + $rr = @ldap_read($cr, $dn, $filter, array($attr)); if(!is_resource($rr)) { \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG); //in case an error occurs , e.g. object does not exist @@ -123,7 +125,8 @@ abstract class Access { return $result; } - //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! + //OID sometimes gives back DNs with whitespace after the comma + // a la "uid=foo, cn=bar, dn=..." We need to tackle this! $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); //make comparisons and everything work @@ -218,7 +221,8 @@ abstract class Access { * @param $ldapname optional, the display name of the object * @returns string with with the name to use in ownCloud, false on DN outside of search DN * - * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure + * returns the internal ownCloud name for the given LDAP DN of the + * group, false on DN outside of search DN or failure */ public function dn2groupname($dn, $ldapname = null) { //To avoid bypassing the base DN settings under certain circumstances @@ -293,6 +297,10 @@ abstract class Access { $query->execute(array($dn, $uuid)); return $component; } + } else { + //If the UUID can't be detected something is foul. + \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$dn.'. Skipping.', \OCP\Util::INFO); + return false; } if(is_null($ldapname)) { @@ -303,21 +311,24 @@ abstract class Access { } $ldapname = $ldapname[0]; } - $ldapname = $this->sanitizeUsername($ldapname); + $intname = $isUser ? $this->sanitizeUsername($uuid) : $this->sanitizeUsername($ldapname); //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups - if(($isUser && !\OCP\User::userExists($ldapname, 'OCA\\user_ldap\\USER_LDAP')) || (!$isUser && !\OC_Group::groupExists($ldapname))) { - if($this->mapComponent($dn, $ldapname, $isUser)) { - return $ldapname; + //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check + $originalTTL = $this->connection->ldapCacheTTL; + $this->connection->setConfiguration(array('ldapCacheTTL' => 0)); + if(($isUser && !\OCP\User::userExists($intname)) + || (!$isUser && !\OC_Group::groupExists($intname))) { + if($this->mapComponent($dn, $intname, $isUser)) { + $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL)); + return $intname; } } + $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL)); - //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. - $oc_name = $this->alternateOwnCloudName($ldapname, $dn); - if(($isUser && !\OCP\User::userExists($oc_name)) || (!$isUser && !\OC_Group::groupExists($oc_name))) { - if($this->mapComponent($dn, $oc_name, $isUser)) { - return $oc_name; - } + $altname = $this->createAltInternalOwnCloudName($intname, $isUser); + if($this->mapComponent($dn, $altname, $isUser)) { + return $altname; } //if everything else did not help.. @@ -400,18 +411,92 @@ abstract class Access { } /** - * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * @brief creates a unique name for internal ownCloud use for users. Don't call it directly. * @param $name the display name of the object - * @param $dn the dn of the object - * @returns string with with the name to use in ownCloud + * @returns string with with the name to use in ownCloud or false if unsuccessful * - * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * Instead of using this method directly, call + * createAltInternalOwnCloudName($name, true) */ - private function alternateOwnCloudName($name, $dn) { - $ufn = ldap_dn2ufn($dn); - $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); - $name = $this->sanitizeUsername($name); - return $name; + private function _createAltInternalOwnCloudNameForUsers($name) { + $attempts = 0; + //while loop is just a precaution. If a name is not generated within + //20 attempts, something else is very wrong. Avoids infinite loop. + while($attempts < 20){ + $altName = $name . '_' . uniqid(); + if(\OCP\User::userExists($altName)) { + return $altName; + } + $attempts++; + } + return false; + } + + /** + * @brief creates a unique name for internal ownCloud use for groups. Don't call it directly. + * @param $name the display name of the object + * @returns string with with the name to use in ownCloud or false if unsuccessful. + * + * Instead of using this method directly, call + * createAltInternalOwnCloudName($name, false) + * + * Group names are also used as display names, so we do a sequential + * numbering, e.g. Developers_42 when there are 41 other groups called + * "Developers" + */ + private function _createAltInternalOwnCloudNameForGroups($name) { + $query = \OCP\DB::prepare(' + SELECT `owncloud_name` + FROM `'.$this->getMapTable(false).'` + WHERE `owncloud_name` LIKE ? + '); + + $usedNames = array(); + $res = $query->execute(array($name.'_%')); + while($row = $res->fetchRow()) { + $usedNames[] = $row['owncloud_name']; + } + if(!($usedNames) || count($usedNames) == 0) { + $lastNo = 1; //will become name_2 + } else { + natsort($usedNames); + $lastname = array_pop($usedNames); + $lastNo = intval(substr($lastname, strrpos($lastname, '_') + 1)); + } + $altName = $name.'_'.strval($lastNo+1); + unset($usedNames); + + $attempts = 1; + while($attempts < 21){ + //Pro forma check to be really sure it is unique + //while loop is just a precaution. If a name is not generated within + //20 attempts, something else is very wrong. Avoids infinite loop. + if(!\OC_Group::groupExists($altName)) { + return $altName; + } + $altName = $name . '_' . $lastNo + $attempts; + $attempts++; + } + return false; + } + + /** + * @brief creates a unique name for internal ownCloud use. + * @param $name the display name of the object + * @param $isUser boolean, whether name should be created for a user (true) or a group (false) + * @returns string with with the name to use in ownCloud or false if unsuccessful + */ + private function createAltInternalOwnCloudName($name, $isUser) { + $originalTTL = $this->connection->ldapCacheTTL; + $this->connection->setConfiguration(array('ldapCacheTTL' => 0)); + if($isUser) { + $altName = $this->_createAltInternalOwnCloudNameForUsers($name); + } else { + $altName = $this->_createAltInternalOwnCloudNameForGroups($name); + } + $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL)); + + return $altName; } /** @@ -539,7 +624,8 @@ abstract class Access { * @brief executes an LDAP search * @param $filter the LDAP filter for the search * @param $base an array containing the LDAP subtree(s) that shall be searched - * @param $attr optional, when a certain attribute shall be filtered out + * @param $attr optional, array, one or more attributes that shall be + * retrieved. Results will according to the order in the array. * @returns array with the search result * * Executes an LDAP search @@ -565,10 +651,20 @@ abstract class Access { $sr = ldap_search($linkResources, $base, $filter, $attr); $error = ldap_errno($link_resource); if(!is_array($sr) || $error > 0) { - \OCP\Util::writeLog('user_ldap', 'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource), \OCP\Util::ERROR); + \OCP\Util::writeLog('user_ldap', + 'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource), + \OCP\Util::ERROR); \OCP\Util::writeLog('user_ldap', 'Attempt for Paging? '.print_r($pagedSearchOK, true), \OCP\Util::ERROR); return array(); } + + // Do the server-side sorting + foreach(array_reverse($attr) as $sortAttr){ + foreach($sr as $searchResource) { + ldap_sort($link_resource, $searchResource, $sortAttr); + } + } + $findings = array(); foreach($sr as $key => $res) { $findings = array_merge($findings, ldap_get_entries($link_resource, $res )); @@ -587,7 +683,9 @@ abstract class Access { if($skipHandling) { return; } - //if count is bigger, then the server does not support paged search. Instead, he did a normal search. We set a flag here, so the callee knows how to deal with it. + // if count is bigger, then the server does not support + // paged search. Instead, he did a normal search. We set a + // flag here, so the callee knows how to deal with it. if($findings['count'] <= $limit) { $this->pagedSearchedSuccessful = true; } @@ -621,7 +719,9 @@ abstract class Access { $key = mb_strtolower($key, 'UTF-8'); if(isset($item[$key])) { if($key != 'dn') { - $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; + $selection[$i][$key] = $this->resemblesDN($key) ? + $this->sanitizeDN($item[$key][0]) + : $item[$key][0]; } else { $selection[$i][$key] = $this->sanitizeDN($item[$key]); } @@ -725,7 +825,9 @@ abstract class Access { * @return string the final filter part to use in LDAP searches */ public function getFilterPartForUserSearch($search) { - return $this->getFilterPartForSearch($search, $this->connection->ldapAttributesForUserSearch, $this->connection->ldapUserDisplayName); + return $this->getFilterPartForSearch($search, + $this->connection->ldapAttributesForUserSearch, + $this->connection->ldapUserDisplayName); } /** @@ -734,7 +836,9 @@ abstract class Access { * @return string the final filter part to use in LDAP searches */ public function getFilterPartForGroupSearch($search) { - return $this->getFilterPartForSearch($search, $this->connection->ldapAttributesForGroupSearch, $this->connection->ldapGroupDisplayName); + return $this->getFilterPartForSearch($search, + $this->connection->ldapAttributesForGroupSearch, + $this->connection->ldapGroupDisplayName); } /** @@ -793,13 +897,15 @@ abstract class Access { foreach($testAttributes as $attribute) { \OCP\Util::writeLog('user_ldap', 'Testing '.$attribute.' as UUID attr', \OCP\Util::DEBUG); - $value = $this->readAttribute($dn, $attribute); - if(is_array($value) && isset($value[0]) && !empty($value[0])) { + $value = $this->readAttribute($dn, $attribute); + if(is_array($value) && isset($value[0]) && !empty($value[0])) { \OCP\Util::writeLog('user_ldap', 'Setting '.$attribute.' as UUID attr', \OCP\Util::DEBUG); $this->connection->ldapUuidAttribute = $attribute; return true; - } - \OCP\Util::writeLog('user_ldap', 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value, true), \OCP\Util::DEBUG); + } + \OCP\Util::writeLog('user_ldap', + 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value, true), + \OCP\Util::DEBUG); } return false; @@ -807,7 +913,9 @@ abstract class Access { public function getUUID($dn) { if($this->detectUuidAttribute($dn)) { - \OCP\Util::writeLog('user_ldap', 'UUID Checking \ UUID for '.$dn.' using '. $this->connection->ldapUuidAttribute, \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', + 'UUID Checking \ UUID for '.$dn.' using '. $this->connection->ldapUuidAttribute, + \OCP\Util::DEBUG); $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); if(!is_array($uuid) && $this->connection->ldapOverrideUuidAttribute) { $this->detectUuidAttribute($dn, true); @@ -946,13 +1054,18 @@ abstract class Access { $pagedSearchOK = false; if($this->connection->hasPagedResultSupport && !is_null($limit)) { $offset = intval($offset); //can be null - \OCP\Util::writeLog('user_ldap', 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true).' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true) + .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, + \OCP\Util::INFO); //get the cookie from the search for the previous search, required by LDAP foreach($bases as $base) { $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset); if(empty($cookie) && ($offset > 0)) { - //no cookie known, although the offset is not 0. Maybe cache run out. We need to start all over *sigh* (btw, Dear Reader, did you need LDAP paged searching was designed by MSFT?) + // no cookie known, although the offset is not 0. Maybe cache run out. We need + // to start all over *sigh* (btw, Dear Reader, did you need LDAP paged + // searching was designed by MSFT?) $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit; //a bit recursive, $offset of 0 is the exit \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO); @@ -968,13 +1081,16 @@ abstract class Access { if($offset > 0) { \OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO); } - $pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(), $limit, false, $cookie); + $pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(), + $limit, false, $cookie); if(!$pagedSearchOK) { return false; } \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::INFO); } else { - \OCP\Util::writeLog('user_ldap', 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, + \OCP\Util::INFO); } } diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index f92779b1cada27d8e7012c80e212da1ce9f73aa7..6643428afe4082429ad92fc0b1d22eb10cae07f6 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -76,7 +76,8 @@ class Connection { $this->configPrefix = $configPrefix; $this->configID = $configID; $this->cache = \OC_Cache::getGlobalCache(); - $this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result') && function_exists('ldap_control_paged_result_response')); + $this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result') + && function_exists('ldap_control_paged_result_response')); } public function __destruct() { @@ -192,7 +193,7 @@ class Connection { private function getValue($varname) { static $defaults; - if(is_null($defaults)){ + if(is_null($defaults)) { $defaults = $this->getDefaults(); } return \OCP\Config::getAppValue($this->configID, @@ -235,7 +236,7 @@ class Connection { $this->config['turnOffCertCheck'] = $this->$v('ldap_turn_off_cert_check'); $this->config['ldapUserDisplayName'] - = mb_strtolower($this->$v('ldap_display_name'),'UTF-8'); + = mb_strtolower($this->$v('ldap_display_name'), 'UTF-8'); $this->config['ldapUserFilter'] = $this->$v('ldap_userlist_filter'); $this->config['ldapGroupFilter'] = $this->$v('ldap_group_filter'); @@ -274,9 +275,36 @@ class Connection { * @return returns an array that maps internal variable names to database fields */ private function getConfigTranslationArray() { - static $array = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_backup_host'=>'ldapBackupHost', 'ldap_backup_port'=>'ldapBackupPort', 'ldap_override_main_server' => 'ldapOverrideMainServer', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', - - 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL', 'home_folder_naming_rule' => 'homeFolderNamingRule', 'ldap_turn_off_cert_check' => 'turnOffCertCheck', 'ldap_configuration_active' => 'ldapConfigurationActive', 'ldap_attributes_for_user_search' => 'ldapAttributesForUserSearch', 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch'); + static $array = array( + 'ldap_host'=>'ldapHost', + 'ldap_port'=>'ldapPort', + 'ldap_backup_host'=>'ldapBackupHost', + 'ldap_backup_port'=>'ldapBackupPort', + 'ldap_override_main_server' => 'ldapOverrideMainServer', + 'ldap_dn'=>'ldapAgentName', + 'ldap_agent_password'=>'ldapAgentPassword', + 'ldap_base'=>'ldapBase', + 'ldap_base_users'=>'ldapBaseUsers', + 'ldap_base_groups'=>'ldapBaseGroups', + 'ldap_userlist_filter'=>'ldapUserFilter', + 'ldap_login_filter'=>'ldapLoginFilter', + 'ldap_group_filter'=>'ldapGroupFilter', + 'ldap_display_name'=>'ldapUserDisplayName', + 'ldap_group_display_name'=>'ldapGroupDisplayName', + + 'ldap_tls'=>'ldapTLS', + 'ldap_nocase'=>'ldapNoCase', + 'ldap_quota_def'=>'ldapQuotaDefault', + 'ldap_quota_attr'=>'ldapQuotaAttribute', + 'ldap_email_attr'=>'ldapEmailAttribute', + 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', + 'ldap_cache_ttl'=>'ldapCacheTTL', + 'home_folder_naming_rule' => 'homeFolderNamingRule', + 'ldap_turn_off_cert_check' => 'turnOffCertCheck', + 'ldap_configuration_active' => 'ldapConfigurationActive', + 'ldap_attributes_for_user_search' => 'ldapAttributesForUserSearch', + 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch' + ); return $array; } @@ -294,6 +322,12 @@ class Connection { $params = $this->getConfigTranslationArray(); foreach($config as $parameter => $value) { + if(($parameter == 'homeFolderNamingRule' + || (isset($params[$parameter]) + && $params[$parameter] == 'homeFolderNamingRule')) + && !empty($value)) { + $value = 'attr:'.$value; + } if(isset($this->config[$parameter])) { $this->config[$parameter] = $value; if(is_array($setParameters)) { @@ -323,15 +357,12 @@ class Connection { case 'ldapAgentPassword': $value = base64_encode($value); break; - case 'homeFolderNamingRule': - $value = empty($value) ? 'opt:username' : 'attr:'.$value; - break; case 'ldapBase': case 'ldapBaseUsers': case 'ldapBaseGroups': case 'ldapAttributesForUserSearch': case 'ldapAttributesForGroupSearch': - if(is_array($value)){ + if(is_array($value)) { $value = implode("\n", $value); } break; @@ -360,10 +391,10 @@ class Connection { $config = array(); foreach($trans as $dbKey => $classKey) { if($classKey == 'homeFolderNamingRule') { - if(strpos($this->config[$classKey], 'opt') === 0) { - $config[$dbKey] = ''; - } else { + if(strpos($this->config[$classKey], 'attr:') === 0) { $config[$dbKey] = substr($this->config[$classKey], 5); + } else { + $config[$dbKey] = ''; } continue; } else if((strpos($classKey, 'ldapBase') !== false) @@ -382,7 +413,8 @@ class Connection { * @returns true if configuration seems OK, false otherwise */ private function validateConfiguration() { - //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message + // first step: "soft" checks: settings that are not really + // necessary, but advisable. If left empty, give an info message if(empty($this->config['ldapBaseUsers'])) { \OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO); $this->config['ldapBaseUsers'] = $this->config['ldapBase']; @@ -392,11 +424,16 @@ class Connection { $this->config['ldapBaseGroups'] = $this->config['ldapBase']; } if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { - \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'No group filter is specified, LDAP group feature will not be used.', + \OCP\Util::INFO); } - if(!in_array($this->config['ldapUuidAttribute'], array('auto', 'entryuuid', 'nsuniqueid', 'objectguid')) && (!is_null($this->configID))) { + if(!in_array($this->config['ldapUuidAttribute'], array('auto', 'entryuuid', 'nsuniqueid', 'objectguid')) + && (!is_null($this->configID))) { \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', 'auto'); - \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'Illegal value for the UUID Attribute, reset to autodetect.', + \OCP\Util::INFO); } if(empty($this->config['ldapBackupPort'])) { //force default @@ -412,7 +449,9 @@ class Connection { if((strpos($this->config['ldapHost'], 'ldaps') === 0) && $this->config['ldapTLS']) { $this->config['ldapTLS'] = false; - \OCP\Util::writeLog('user_ldap', 'LDAPS (already using secure connection) and TLS do not work together. Switched off TLS.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'LDAPS (already using secure connection) and TLS do not work together. Switched off TLS.', + \OCP\Util::INFO); } @@ -429,20 +468,28 @@ class Connection { } if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword'])) || (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) { - \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', + \OCP\Util::WARN); $configurationOK = false; } //TODO: check if ldapAgentName is in DN form - if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) { + if(empty($this->config['ldapBase']) + && (empty($this->config['ldapBaseUsers']) + && empty($this->config['ldapBaseGroups']))) { \OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN); $configurationOK = false; } if(empty($this->config['ldapUserDisplayName'])) { - \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'No user display name attribute specified, won`t connect.', + \OCP\Util::WARN); $configurationOK = false; } if(empty($this->config['ldapGroupDisplayName'])) { - \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'No group display name attribute specified, won`t connect.', + \OCP\Util::WARN); $configurationOK = false; } if(empty($this->config['ldapLoginFilter'])) { @@ -450,7 +497,9 @@ class Connection { $configurationOK = false; } if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) { - \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'Login filter does not contain %uid place holder, won`t connect.', + \OCP\Util::WARN); \OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG); $configurationOK = false; } @@ -488,7 +537,7 @@ class Connection { 'ldap_cache_ttl' => 600, 'ldap_uuid_attribute' => 'auto', 'ldap_override_uuid_attribute' => 0, - 'home_folder_naming_rule' => 'opt:username', + 'home_folder_naming_rule' => '', 'ldap_turn_off_cert_check' => 0, 'ldap_configuration_active' => 1, 'ldap_attributes_for_user_search' => '', @@ -514,13 +563,17 @@ class Connection { if(!$this->ldapConnectionRes) { if(!function_exists('ldap_connect')) { $phpLDAPinstalled = false; - \OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', \OCP\Util::ERROR); + \OCP\Util::writeLog('user_ldap', + 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', + \OCP\Util::ERROR); return false; } if($this->config['turnOffCertCheck']) { if(putenv('LDAPTLS_REQCERT=never')) { - \OCP\Util::writeLog('user_ldap', 'Turned off SSL certificate validation successfully.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', + 'Turned off SSL certificate validation successfully.', + \OCP\Util::WARN); } else { \OCP\Util::writeLog('user_ldap', 'Could not turn off SSL certificate validation.', \OCP\Util::WARN); } @@ -578,7 +631,9 @@ class Connection { } $ldapLogin = @ldap_bind($cr, $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); if(!$ldapLogin) { - \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($cr) . ': ' . ldap_error($cr), \OCP\Util::ERROR); + \OCP\Util::writeLog('user_ldap', + 'Bind failed: ' . ldap_errno($cr) . ': ' . ldap_error($cr), + \OCP\Util::ERROR); $this->ldapConnectionRes = null; return false; } diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php index 29ce998dae700c5b21edf2b6ed550152e6a82a7c..308da3ef724237c280083ad1f0811e60c7bb3c8e 100644 --- a/apps/user_ldap/lib/helper.php +++ b/apps/user_ldap/lib/helper.php @@ -54,7 +54,7 @@ class Helper { WHERE `configkey` LIKE ? '; if($activeConfigurations) { - $query .= ' AND `configvalue` = 1'; + $query .= ' AND `configvalue` = \'1\''; } $query = \OCP\DB::prepare($query); @@ -86,8 +86,8 @@ class Helper { DELETE FROM `*PREFIX*appconfig` WHERE `configkey` LIKE ? - AND `appid` = "user_ldap" - AND `configkey` NOT IN ("enabled", "installed_version", "types", "bgjUpdateGroupsLastRun") + AND `appid` = \'user_ldap\' + AND `configkey` NOT IN (\'enabled\', \'installed_version\', \'types\', \'bgjUpdateGroupsLastRun\') '); $res = $query->execute(array($prefix.'%')); @@ -102,4 +102,3 @@ class Helper { return true; } } - diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php index b265a8339efa9aed93a5fd438b9462892d6587b1..094d11db3d5d55681d968f077da5573dfe78ba89 100644 --- a/apps/user_ldap/lib/jobs.php +++ b/apps/user_ldap/lib/jobs.php @@ -42,7 +42,9 @@ class Jobs { $actualGroups = self::getGroupBE()->getGroups(); if(empty($actualGroups) && empty($knownGroups)) { - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', + \OCP\Util::INFO); \OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); return; } @@ -75,19 +77,25 @@ class Jobs { $hasChanged = false; foreach(array_diff($knownUsers, $actualUsers) as $removedUser) { \OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group)); - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".', + \OCP\Util::INFO); $hasChanged = true; } foreach(array_diff($actualUsers, $knownUsers) as $addedUser) { \OCP\Util::emitHook('OC_User', 'post_addFromGroup', array('uid' => $addedUser, 'gid' => $group)); - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".', + \OCP\Util::INFO); $hasChanged = true; } if($hasChanged) { $query->execute(array(serialize($actualUsers), $group)); } } - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with known Groups.', \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – FINISHED dealing with known Groups.', + \OCP\Util::DEBUG); } static private function handleCreatedGroups($createdGroups) { @@ -98,11 +106,15 @@ class Jobs { VALUES (?, ?) '); foreach($createdGroups as $createdGroup) { - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.', + \OCP\Util::INFO); $users = serialize(self::getGroupBE()->usersInGroup($createdGroup)); $query->execute(array($createdGroup, $users)); } - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with created Groups.', \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – FINISHED dealing with created Groups.', + \OCP\Util::DEBUG); } static private function handleRemovedGroups($removedGroups) { @@ -113,10 +125,14 @@ class Jobs { WHERE `owncloudname` = ? '); foreach($removedGroups as $removedGroup) { - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.', + \OCP\Util::INFO); $query->execute(array($removedGroup)); } - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with removed groups.', \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', + 'bgJ "updateGroups" – FINISHED dealing with removed groups.', + \OCP\Util::DEBUG); } static private function getConnector() { @@ -154,4 +170,4 @@ class Jobs { return self::$groupsFromDB; } -} \ No newline at end of file +} diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index d5d2f648b38c100cd3d1acd5f25dd78e070e43d3..c55a718a82a9b515edccfe51354808f319eb28a4 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -52,7 +52,7 @@ foreach($prefixes as $prefix) { if(count($prefixes) == 0) { $scoHtml .= ''; } -$tmpl->assign('serverConfigurationOptions', $scoHtml, false); +$tmpl->assign('serverConfigurationOptions', $scoHtml); // assign default values if(!isset($ldap)) { diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index c6f1834e0131100b85203f0d5e20b2d5eb44f675..cd004cec4b3e8f00507429d6ef5857b24b7f2547 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -5,61 +5,85 @@
  • Advanced
  • '.$l->t('Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'

    '; + print_unescaped('

    '.$l->t('Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'

    '); } if(!function_exists('ldap_connect')) { - echo '

    '.$l->t('Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.').'

    '; + print_unescaped('

    '.$l->t('Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.').'

    '); } ?>
    -

    + + - +

    -

    -

    -

    -

    -


    t('use %%uid placeholder, e.g. "uid=%%uid"');?>

    -


    t('without any placeholder, e.g. "objectClass=person".');?>

    -


    t('without any placeholder, e.g. "objectClass=posixGroup".');?>

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + +
    t('use %%uid placeholder, e.g. "uid=%%uid"'));?>

    +

    + +
    t('without any placeholder, e.g. "objectClass=person".'));?>

    +

    + +
    t('without any placeholder, e.g. "objectClass=posixGroup".'));?>

    -

    t('Connection Settings');?>

    +

    t('Connection Settings'));?>

    -

    -

    -

    -

    -

    -

    -

    >

    -


    t('Not recommended, use for testing only.');?>

    -

    +

    +

    +

    +

    +

    +

    +

    >

    +


    t('Not recommended, use for testing only.'));?>

    +

    -

    t('Directory Settings');?>

    +

    t('Directory Settings'));?>

    -

    -

    -

    -

    -

    -

    -

    +

    +

    +

    +

    +

    +

    +

    -

    t('Special Attributes');?>

    +

    t('Special Attributes'));?>

    -

    -

    -

    -

    +

    +

    +

    +

    - t('Help');?> + t('Help'));?>
    diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 6aa8cd9b83cc54b85e630415552e96127ae24152..44a1947859805c5935b06e68d6cb53aa8ac8ccff 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -112,7 +112,8 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { return $ldap_users; } - // if we'd pass -1 to LDAP search, we'd end up in a Protocol error. With a limit of 0, we get 0 results. So we pass null. + // if we'd pass -1 to LDAP search, we'd end up in a Protocol + // error. With a limit of 0, we get 0 results. So we pass null. if($limit <= 0) { $limit = null; } @@ -121,9 +122,12 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { $this->getFilterPartForUserSearch($search) )); - \OCP\Util::writeLog('user_ldap', 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter, \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', + 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter, + \OCP\Util::DEBUG); //do the search and translate results to owncloud names - $ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'), $limit, $offset); + $ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'), + $limit, $offset); $ldap_users = $this->ownCloudUserNames($ldap_users); \OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG); @@ -171,40 +175,39 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { } /** - * @brief determine the user's home directory - * @param string $uid the owncloud username + * @brief get the user's home directory + * @param string $uid the username * @return boolean */ - private function determineHomeDir($uid) { + public function getHome($uid) { + $cacheKey = 'getHome'.$uid; + if($this->connection->isCached($cacheKey)) { + return $this->connection->getFromCache($cacheKey); + } if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) { $attr = substr($this->connection->homeFolderNamingRule, strlen('attr:')); $homedir = $this->readAttribute($this->username2dn($uid), $attr); - if($homedir) { - $homedir = \OCP\Config::getSystemValue( "datadirectory", \OC::$SERVERROOT."/data" ) . '/' . $homedir[0]; - \OCP\Config::setUserValue($uid, 'user_ldap', 'homedir', $homedir); + if($homedir && isset($homedir[0])) { + $path = $homedir[0]; + //if attribute's value is an absolute path take this, otherwise append it to data dir + //check for / at the beginning or pattern c:\ resp. c:/ + if( + '/' == $path[0] + || (3 < strlen($path) && ctype_alpha($path[0]) + && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2])) + ) { + $homedir = $path; + } else { + $homedir = \OCP\Config::getSystemValue('datadirectory', + \OC::$SERVERROOT.'/data' ) . '/' . $homedir[0]; + } + $this->connection->writeToCache($cacheKey, $homedir); return $homedir; } } - //fallback and default: username - $homedir = \OCP\Config::getSystemValue( "datadirectory", \OC::$SERVERROOT."/data" ) . '/' . $uid; - \OCP\Config::setUserValue($uid, 'user_ldap', 'homedir', $homedir); - return $homedir; - } - - /** - * @brief get the user's home directory - * @param string $uid the username - * @return boolean - */ - public function getHome($uid) { - if($this->userExists($uid)) { - $homedir = \OCP\Config::getUserValue($uid, 'user_ldap', 'homedir', false); - if(!$homedir) { - $homedir = $this->determineHomeDir($uid); - } - return $homedir; - } + //false will apply default behaviour as defined and done by OC_User + $this->connection->writeToCache($cacheKey, false); return false; } @@ -224,7 +227,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { $this->connection->ldapUserDisplayName); if($displayName && (count($displayName) > 0)) { - $this->connection->writeToCache($cacheKey, $displayName); + $this->connection->writeToCache($cacheKey, $displayName[0]); return $displayName[0]; } @@ -261,7 +264,16 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { * compared with OC_USER_BACKEND_CREATE_USER etc. */ public function implementsActions($actions) { - return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_GET_HOME) & $actions); + return (bool)((OC_USER_BACKEND_CHECK_PASSWORD + | OC_USER_BACKEND_GET_HOME + | OC_USER_BACKEND_GET_DISPLAYNAME) + & $actions); } -} \ No newline at end of file + /** + * @return bool + */ + public function hasUserListings() { + return true; + } +} diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php index a94be3354fcb75fe083f0441a1b961dac8286de5..6a75bae3815f0cbdb1b4b416cc9dacc58192d44d 100644 --- a/apps/user_ldap/user_proxy.php +++ b/apps/user_ldap/user_proxy.php @@ -183,4 +183,12 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface { public function deleteUser($uid) { return false; } + + /** + * @return bool + */ + public function hasUserListings() { + return $this->refBackend->hasUserListings(); + } + } \ No newline at end of file diff --git a/apps/user_webdavauth/appinfo/info.xml b/apps/user_webdavauth/appinfo/info.xml index f62f03577e8bdf682f4018bce195b0b44049009c..76b314e48aa4a440dc1f19f85558c27167fd8ffd 100755 --- a/apps/user_webdavauth/appinfo/info.xml +++ b/apps/user_webdavauth/appinfo/info.xml @@ -7,7 +7,7 @@ This app is not compatible to the LDAP user and group backend. AGPL Frank Karlitschek - 4.91 + 4.93 true diff --git a/apps/user_webdavauth/l10n/de.php b/apps/user_webdavauth/l10n/de.php index f893bddc71ce599a7d16c97609c703427e57a150..c86ff44e55cb398bb682fc943b011f42bcb82fee 100644 --- a/apps/user_webdavauth/l10n/de.php +++ b/apps/user_webdavauth/l10n/de.php @@ -1,5 +1,5 @@ "WebDAV Authentifikation", "URL: http://" => "URL: http://", -"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud wird die Benutzer-Anmeldedaten an diese URL schicken. Dieses Plugin prüft die Anmeldedaten auf ihre Gültigkeit und interpretiert die HTTP Statusfehler 401 und 403 als ungültige, sowie alle Anderen als gültige Anmeldedaten." +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sendet die Benutzerdaten an diese URL. Dieses Plugin prüft die Antwort und wird die Statuscodes 401 und 403 als ungültige Daten und alle anderen Antworten als gültige Daten interpretieren." ); diff --git a/apps/user_webdavauth/l10n/de_DE.php b/apps/user_webdavauth/l10n/de_DE.php index 8f67575fc0fd6ae8665f1f4800823fff606a5b3f..bd5d328e477cacf71815a0bd0ccddbc6691ec9c3 100644 --- a/apps/user_webdavauth/l10n/de_DE.php +++ b/apps/user_webdavauth/l10n/de_DE.php @@ -1,5 +1,5 @@ "WebDAV Authentifizierung", "URL: http://" => "URL: http://", -"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sendet die Benutzerdaten an diese URL. Dieses Plugin prüft die Antwort und wird die Statuscodes 401 und 403 als ungültige Daten interpretieren und alle anderen Antworten als gültige Daten." +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sendet die Benutzerdaten an diese URL. Dieses Plugin prüft die Antwort und wird die Statuscodes 401 und 403 als ungültige Daten und alle anderen Antworten als gültige Daten interpretieren." ); diff --git a/apps/user_webdavauth/l10n/et_EE.php b/apps/user_webdavauth/l10n/et_EE.php index 9bd32954b058d9ad5c43d6604e9ba18e4bd559f3..f4f7486035866c4b1693b4210d662ca23d8bde9c 100644 --- a/apps/user_webdavauth/l10n/et_EE.php +++ b/apps/user_webdavauth/l10n/et_EE.php @@ -1,3 +1,4 @@ "WebDAV URL: http://" +"WebDAV Authentication" => "WebDAV autentimine", +"URL: http://" => "URL: http://" ); diff --git a/apps/user_webdavauth/l10n/fi_FI.php b/apps/user_webdavauth/l10n/fi_FI.php index 070a0ffdaff78ed22f6c63607a398b64baff4451..6c67c78c8127238b3620faf4320ea366bab43d8b 100644 --- a/apps/user_webdavauth/l10n/fi_FI.php +++ b/apps/user_webdavauth/l10n/fi_FI.php @@ -1,3 +1,4 @@ "WebDAV-osoite: http://" +"WebDAV Authentication" => "WebDAV-todennus", +"URL: http://" => "Osoite: http://" ); diff --git a/apps/user_webdavauth/l10n/gl.php b/apps/user_webdavauth/l10n/gl.php index a6b8355c07433c653ed89d68217bcc9133750b81..f63a7cb0ce85d357f1fd909b03547bf983f6de45 100644 --- a/apps/user_webdavauth/l10n/gl.php +++ b/apps/user_webdavauth/l10n/gl.php @@ -1,5 +1,5 @@ "Autenticación WebDAV", "URL: http://" => "URL: http://", -"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enviará as credenciais do usuario a esta URL. Este conector comproba a resposta e interpretará os códigos de estado 401 e 403 como credenciais non válidas, e todas as outras respostas como credenciais válidas." +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enviará as credenciais do usuario a este URL. Este engadido comproba a resposta e interpretará os códigos de estado HTTP 401 e 403 como credenciais incorrectas, e todas as outras respostas como credenciais correctas." ); diff --git a/apps/user_webdavauth/l10n/ru.php b/apps/user_webdavauth/l10n/ru.php index 245a5101341d2a51e59b2cc0575b907f85eb3945..f12982fc406104f67ba64e6ea0c4694ff0e64bc7 100644 --- a/apps/user_webdavauth/l10n/ru.php +++ b/apps/user_webdavauth/l10n/ru.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Идентификация WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud отправит пользовательские данные на этот URL. Затем плагин проверит ответ, в случае HTTP ответа 401 или 403 данные будут считаться неверными, при любых других ответах - верными." ); diff --git a/apps/user_webdavauth/l10n/tr.php b/apps/user_webdavauth/l10n/tr.php index 245a5101341d2a51e59b2cc0575b907f85eb3945..4a2f6d2403b5781c4a8bdc51a6cb4c61e27ba82f 100644 --- a/apps/user_webdavauth/l10n/tr.php +++ b/apps/user_webdavauth/l10n/tr.php @@ -1,3 +1,4 @@ "WebDAV Kimlik doğrulaması", "URL: http://" => "URL: http://" ); diff --git a/apps/user_webdavauth/l10n/uk.php b/apps/user_webdavauth/l10n/uk.php index 245a5101341d2a51e59b2cc0575b907f85eb3945..66887df54b51295cea65e22c7de385efe8ae44ff 100644 --- a/apps/user_webdavauth/l10n/uk.php +++ b/apps/user_webdavauth/l10n/uk.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Аутентифікація WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud надішле облікові дані на цей URL. Цей плагін перевірить відповідь і буде інтерпретувати HTTP коди 401 і 403 як повідомлення про недійсні повноваження, а решту відповідей як дійсні облікові дані." ); diff --git a/apps/user_webdavauth/l10n/vi.php b/apps/user_webdavauth/l10n/vi.php index 9bd32954b058d9ad5c43d6604e9ba18e4bd559f3..ee2aa0891259b8874343f01577a6c50509c08b8f 100644 --- a/apps/user_webdavauth/l10n/vi.php +++ b/apps/user_webdavauth/l10n/vi.php @@ -1,3 +1,5 @@ "WebDAV URL: http://" +"WebDAV Authentication" => "Xác thực WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sẽ gửi chứng thư người dùng tới URL này. Tính năng này kiểm tra trả lời và sẽ hiểu mã 401 và 403 của giao thức HTTP là chứng thư không hợp lệ, và mọi trả lời khác được coi là hợp lệ." ); diff --git a/apps/user_webdavauth/settings.php b/apps/user_webdavauth/settings.php index 7eabb0d48cc545c67bd5199f0957853d9941b561..ae9cb7e4c921074524c6213ad533ad28d7a09fb5 100755 --- a/apps/user_webdavauth/settings.php +++ b/apps/user_webdavauth/settings.php @@ -26,7 +26,7 @@ OC_Util::checkAdminUser(); if($_POST) { // CSRF check OCP\JSON::callCheck(); - + if(isset($_POST['webdav_url'])) { OC_CONFIG::setValue('user_webdavauth_url', strip_tags($_POST['webdav_url'])); } diff --git a/apps/user_webdavauth/templates/settings.php b/apps/user_webdavauth/templates/settings.php index 45f4d81aecf844dbfb8ea11660db8d7437df14b0..ec6524ee4f79a9029b4441b465f1cee8270d6db5 100755 --- a/apps/user_webdavauth/templates/settings.php +++ b/apps/user_webdavauth/templates/settings.php @@ -1,9 +1,9 @@
    - t('WebDAV Authentication');?> -

    - + t('WebDAV Authentication'));?> +

    + -
    t('ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials.'); ?> +
    t('ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials.')); ?>

    diff --git a/apps/user_webdavauth/user_webdavauth.php b/apps/user_webdavauth/user_webdavauth.php index 1459781a3b4034d7229cc4ac1f414c48d2b66ecd..6417e45434dc4c3f23c1c12438f59a38a8001e76 100755 --- a/apps/user_webdavauth/user_webdavauth.php +++ b/apps/user_webdavauth/user_webdavauth.php @@ -28,12 +28,6 @@ class OC_USER_WEBDAVAUTH extends OC_User_Backend { $this->webdavauth_url = OC_Config::getValue( "user_webdavauth_url" ); } - public function createUser() { - // Can't create user - OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to create users from web frontend using WebDAV user backend', 3); - return false; - } - public function deleteUser($uid) { // Can't delete user OC_Log::write('OC_USER_WEBDAVAUTH', 'Not possible to delete users from web frontend using WebDAV user backend', 3); @@ -71,6 +65,12 @@ class OC_USER_WEBDAVAUTH extends OC_User_Backend { return true; } + /** + * @return bool + */ + public function hasUserListings() { + return false; + } /* * we don´t know the users so all we can do it return an empty array here diff --git a/autotest.cmd b/autotest.cmd index 053860db5473f73d9ad36fab0c5126ecb9058827..a511faef9c603b81cb3980900fff70e82d5053a6 100644 --- a/autotest.cmd +++ b/autotest.cmd @@ -4,14 +4,14 @@ :: @author Thomas Müller :: @author Tobias Ramforth (translated into Windows batch file) :: -:: @copyright 2012 Thomas Müller thomas.mueller@tmit.eu +:: @copyright 2012, 2013 Thomas Müller thomas.mueller@tmit.eu :: @echo off set DATADIR=data-autotest set BASEDIR=%~dp0 -:: create autoconfig for sqlite, mysql and postgresql +:: create autoconfig for sqlite, mysql, postgresql and mssql echo ^ .\tests\autoconfig-sqlite.php echo $AUTOCONFIG ^= array ^( >> .\tests\autoconfig-sqlite.php echo 'installed' ^=^> false^, >> .\tests\autoconfig-sqlite.php @@ -50,16 +50,35 @@ echo 'dbhost' ^=^> 'localhost'^, >> .\tests\autoconfig-pgsql.php echo 'dbpass' ^=^> 'owncloud'^, >> .\tests\autoconfig-pgsql.php echo ^)^; >> .\tests\autoconfig-pgsql.php +echo ^ .\tests\autoconfig-mssql.php +echo $AUTOCONFIG ^= array ^( >> .\tests\autoconfig-mssql.php +echo 'installed' ^=^> false^, >> .\tests\autoconfig-mssql.php +echo 'dbtype' ^=^> 'mssql'^, >> .\tests\autoconfig-mssql.php +echo 'dbtableprefix' ^=^> 'oc_'^, >> .\tests\autoconfig-mssql.php +echo 'adminlogin' ^=^> 'admin'^, >> .\tests\autoconfig-mssql.php +echo 'adminpass' ^=^> 'admin'^, >> .\tests\autoconfig-mssql.php +echo 'directory' ^=^> '%BASEDIR%%DATADIR%'^, >> .\tests\autoconfig-mssql.php +echo 'dbuser' ^=^> 'oc_autotest'^, >> .\tests\autoconfig-mssql.php +echo 'dbname' ^=^> 'oc_autotest'^, >> .\tests\autoconfig-mssql.php +echo 'dbhost' ^=^> 'localhost\sqlexpress'^, >> .\tests\autoconfig-mssql.php +echo 'dbpass' ^=^> 'owncloud'^, >> .\tests\autoconfig-mssql.php +echo ^)^; >> .\tests\autoconfig-mssql.php + echo localhost:5432:*:oc_autotest:owncloud > %APPDATA%\postgresql\pgpass.conf :: :: start test execution :: -::call:execute_tests "sqlite" -call:execute_tests "mysql" -::call:execute_tests "mssql" -::call:execute_tests "ora" -::call:execute_tests "pgsql" +if [%1] == [] ( + echo "Running on all database backends" + call:execute_tests "sqlite" + call:execute_tests "mysql" + call:execute_tests "mssql" + ::call:execute_tests "ora" + call:execute_tests "pgsql" +) else ( + call:execute_tests "%1" +) goto:eof @@ -83,6 +102,9 @@ goto:eof if "%~1" == "mysql" mysql -u oc_autotest -powncloud -e "DROP DATABASE oc_autotest" if "%~1" == "pgsql" dropdb -h localhost -p 5432 -U oc_autotest -w oc_autotest + + :: we assume a sqlexpress installation + if "%~1" == "mssql" sqlcmd -S localhost\sqlexpress -U oc_autotest -P owncloud -Q "IF EXISTS (SELECT name FROM sys.databases WHERE name=N'oc_autotest') DROP DATABASE [oc_autotest]" :: copy autoconfig copy /y %BASEDIR%\tests\autoconfig-%~1.php %BASEDIR%\config\autoconfig.php @@ -96,9 +118,8 @@ goto:eof rmdir /s /q coverage-html-%~1 md coverage-html-%~1 php -f enable_all.php - ::phpunit --log-junit autotest-results-%~1.xml --coverage-clover autotest-clover-%~1.xml --coverage-html coverage-html-%~1 - ::phpunit --bootstrap bootstrap.php --configuration phpunit.xml - php win32-phpunit.php --bootstrap bootstrap.php --configuration phpunit.xml --log-junit autotest-results-%~1.xml --coverage-clover autotest-clover-%~1.xml --coverage-html coverage-html-%~1 + + php win32-phpunit.php --bootstrap bootstrap.php --configuration phpunit-autotest.xml --log-junit autotest-results-%~1.xml --coverage-clover autotest-clover-%~1.xml --coverage-html coverage-html-%~1 echo "Done with testing %~1 ..." cd %BASEDIR% goto:eof @@ -114,4 +135,10 @@ goto:eof :: - to enable dropdb I decided to add following line to pg_hba.conf (this is not the safest way but I don't care for the testing machine): :: local all all trust :: +:: NOTES on mssql: +:: we assume the usage of a local installed sqlexpress +:: create a user 'oc_autotest' with password 'owncloud' and assign the server role 'dbcreator' +:: make sure the sqlserver is configured to allow sql authentication +:: + diff --git a/config/config.sample.php b/config/config.sample.php index cfef3d5117dd51220f9ba0ea7d5ac384a48e96d0..ec61ceefd6cd27167c5ba1a472f68caac5c4d1e9 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -114,6 +114,9 @@ $CONFIG = array( /* How long should ownCloud keep deleted files in the trash bin, default value: 180 days */ 'trashbin_retention_obligation' => 180, +/* allow user to change his display name, if it is supported by the back-end */ +'allow_user_to_change_display_name' => true, + /* Check 3rdparty apps for malicious code fragments */ "appcodechecker" => "", @@ -133,7 +136,7 @@ $CONFIG = array( "remember_login_cookie_lifetime" => 60*60*24*15, /* Custom CSP policy, changing this will overwrite the standard policy */ -"custom_csp_policy" => "default-src \'self\'; script-src \'self\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; frame-src *; img-src *; font-src \'self\' data:", +"custom_csp_policy" => "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src *; font-src 'self' data:", /* The directory where the user data is stored, default to data in the owncloud * directory. The sqlite database is also stored here, when sqlite is used. diff --git a/core/ajax/share.php b/core/ajax/share.php index 6704a00c5a2c5345debf862a51715af80fbd58f1..9201b48cb954af5849b0b19334a376b37486a444 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -34,7 +34,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $shareWith = null; } - $token = OCP\Share::shareItem($_POST['itemType'], $_POST['itemSource'], $shareType, $shareWith, $_POST['permissions']); + $token = OCP\Share::shareItem( + $_POST['itemType'], + $_POST['itemSource'], + $shareType, + $shareWith, + $_POST['permissions'] + ); if (is_string($token)) { OC_JSON::success(array('data' => array('token' => $token))); @@ -59,7 +65,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; case 'setPermissions': if (isset($_POST['shareType']) && isset($_POST['shareWith']) && isset($_POST['permissions'])) { - $return = OCP\Share::setPermissions($_POST['itemType'], $_POST['itemSource'], $_POST['shareType'], $_POST['shareWith'], $_POST['permissions']); + $return = OCP\Share::setPermissions( + $_POST['itemType'], + $_POST['itemSource'], + $_POST['shareType'], + $_POST['shareWith'], + $_POST['permissions'] + ); ($return) ? OC_JSON::success() : OC_JSON::error(); } break; @@ -86,9 +98,11 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo if ($type === 'dir') $subject = (string)$l->t('User %s shared a folder with you', $displayName); - $text = (string)$l->t('User %s shared the file "%s" with you. It is available for download here: %s', array($displayName, $file, $link)); + $text = (string)$l->t('User %s shared the file "%s" with you. It is available for download here: %s', + array($displayName, $file, $link)); if ($type === 'dir') - $text = (string)$l->t('User %s shared the folder "%s" with you. It is available for download here: %s', array($displayName, $file, $link)); + $text = (string)$l->t('User %s shared the folder "%s" with you. It is available for download here: %s', + array($displayName, $file, $link)); $default_from = OCP\Util::getDefaultEmailAddress('sharing-noreply'); @@ -112,14 +126,29 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } break; case 'getItem': - if (isset($_GET['itemType']) && isset($_GET['itemSource']) && isset($_GET['checkReshare']) && isset($_GET['checkShares'])) { + if (isset($_GET['itemType']) + && isset($_GET['itemSource']) + && isset($_GET['checkReshare']) + && isset($_GET['checkShares'])) { if ($_GET['checkReshare'] == 'true') { - $reshare = OCP\Share::getItemSharedWithBySource($_GET['itemType'], $_GET['itemSource'], OCP\Share::FORMAT_NONE, null, true); + $reshare = OCP\Share::getItemSharedWithBySource( + $_GET['itemType'], + $_GET['itemSource'], + OCP\Share::FORMAT_NONE, + null, + true + ); } else { $reshare = false; } if ($_GET['checkShares'] == 'true') { - $shares = OCP\Share::getItemShared($_GET['itemType'], $_GET['itemSource'], OCP\Share::FORMAT_NONE, null, true); + $shares = OCP\Share::getItemShared( + $_GET['itemType'], + $_GET['itemSource'], + OCP\Share::FORMAT_NONE, + null, + true + ); } else { $shares = false; } @@ -156,8 +185,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $users = array(); $limit = 0; $offset = 0; - while ($count < 4 && count($users) == $limit) { - $limit = 4 - $count; + while ($count < 15 && count($users) == $limit) { + $limit = 15 - $count; if ($sharePolicy == 'groups_only') { $users = OC_Group::DisplayNamesInGroups($groups, $_GET['search'], $limit, $offset); } else { @@ -165,21 +194,34 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } $offset += $limit; foreach ($users as $uid => $displayName) { - if ((!isset($_GET['itemShares']) || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) || !in_array($uid, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) && $uid != OC_User::getUser()) { - $shareWith[] = array('label' => $displayName, 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, 'shareWith' => $uid)); + if ((!isset($_GET['itemShares']) + || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) + || !in_array($uid, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) + && $uid != OC_User::getUser()) { + $shareWith[] = array( + 'label' => $displayName, + 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, + 'shareWith' => $uid) + ); $count++; } } } $count = 0; foreach ($groups as $group) { - if ($count < 4) { + if ($count < 15) { if (stripos($group, $_GET['search']) !== false && (!isset($_GET['itemShares']) || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) || !in_array($group, $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]))) { - $shareWith[] = array('label' => $group.' (group)', 'value' => array('shareType' => OCP\Share::SHARE_TYPE_GROUP, 'shareWith' => $group)); + $shareWith[] = array( + 'label' => $group.' (group)', + 'value' => array( + 'shareType' => OCP\Share::SHARE_TYPE_GROUP, + 'shareWith' => $group + ) + ); $count++; } } else { diff --git a/core/ajax/translations.php b/core/ajax/translations.php index e22cbad4708f09979e41273c6eef1bbda2d4f5ba..c9c642077982b0671a39ba156f932e1176248b3c 100644 --- a/core/ajax/translations.php +++ b/core/ajax/translations.php @@ -21,7 +21,9 @@ * */ -$app = $_POST["app"]; +$app = isset($_POST["app"]) ? $_POST["app"] : ""; + +$app = OC_App::cleanAppId($app); $l = OC_L10N::get( $app ); diff --git a/core/ajax/update.php b/core/ajax/update.php index 20ab045c89243b09f433dac2900caba7acb57b17..b112cf6266b59d41698e0f8655adcbd8ee69f578 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -4,6 +4,7 @@ $RUNTIME_NOAPPS = true; require_once '../../lib/base.php'; if (OC::checkUpgrade(false)) { + \OC_DB::enableCaching(false); $updateEventSource = new OC_EventSource(); $watcher = new UpdateWatcher($updateEventSource); OC_Hook::connect('update', 'success', $watcher, 'success'); @@ -64,4 +65,4 @@ class UpdateWatcher { $this->eventSource->close(); } -} \ No newline at end of file +} diff --git a/core/css/share.css b/core/css/share.css index e806d25982e2b9ad8731103584255d07484e1857..2d6849b4bb1361b1978906716ce0974fd7d1c643 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -24,6 +24,18 @@ #shareWithList li { padding-top:.1em; } + + #shareWithList li:first-child { + white-space:normal; + } + + #shareWithList .cruds { + margin-left:-10px; + } + +#shareWithList .unshare img, #shareWithList .showCruds img { + vertical-align:text-bottom; /* properly align icons */ +} #dropdown label { font-weight:400; @@ -43,6 +55,7 @@ float:right; opacity:.5; padding:.3em 0 0 .3em !important; + margin-top:-5px; } #link { @@ -75,4 +88,12 @@ a.showCruds:hover,a.unshare:hover { opacity:1; - } \ No newline at end of file + } + +.reshare { white-space:normal; } /* fix shared by text going out of box */ + +.ui-autocomplete { /* limit dropdown height to 4 1/2 entries */ + max-height:103px; + overflow-y:auto; + overflow-x:hidden; +} diff --git a/core/css/styles.css b/core/css/styles.css index 556ca6b82bbfb04e70468e630f83b264974fae42..61243237bc1df523c5857f23ecee2e63f463902c 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -16,7 +16,10 @@ body { background:#fefefe; font:normal .8em/1.6em "Lucida Grande", Arial, Verdan /* HEADERS */ -#body-user #header, #body-settings #header { position:fixed; top:0; left:0; right:0; z-index:100; height:45px; line-height:2.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } +#body-user #header, #body-settings #header { + position:fixed; top:0; left:0; right:0; z-index:100; height:45px; line-height:2.5em; + background:#1d2d44 url('../img/noise.png') repeat; + -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } #body-login #header { margin: -2em auto 0; text-align:center; height:10em; padding:1em 0 .5em; -moz-box-shadow:0 0 1em rgba(0, 0, 0, .5); -webkit-box-shadow:0 0 1em rgba(0, 0, 0, .5); box-shadow:0 0 1em rgba(0, 0, 0, .5); background:#1d2d44; /* Old browsers */ @@ -33,8 +36,9 @@ filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endC .header-right > * { vertical-align:middle; } /* INPUTS */ -input[type="text"], input[type="password"] { cursor:text; } -input, textarea, select, button, .button, #quota, div.jp-progress, .pager li a { +input[type="text"], input[type="password"], input[type="number"] { cursor:text; } +input[type="text"], input[type="password"], input[type="search"], input[type="number"], +textarea, select, button, .button, #quota, div.jp-progress, .pager li a { width:10em; margin:.3em; padding:.6em .5em .4em; font-size:1em; font-family:Arial, Verdana, sans-serif; background:#fff; color:#333; border:1px solid #ddd; outline:none; @@ -42,10 +46,11 @@ input, textarea, select, button, .button, #quota, div.jp-progress, .pager li a { -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } input[type="hidden"] { height:0; width:0; } -input[type="text"], input[type="password"], input[type="search"], textarea { background:#f8f8f8; color:#555; cursor:text; } -input[type="text"], input[type="password"], input[type="search"] { -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; } +input[type="text"], input[type="password"], input[type="search"], input[type="number"], textarea { background:#f8f8f8; color:#555; cursor:text; } +input[type="text"], input[type="password"], input[type="search"], input[type="number"] { -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; } input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active, input[type="password"]:hover, input[type="password"]:focus, input[type="password"]:active, +input[type="number"]:hover, input[type="number"]:focus, input[type="number"]:active, .searchbox input[type="search"]:hover, .searchbox input[type="search"]:focus, .searchbox input[type="search"]:active, textarea:hover, textarea:focus, textarea:active { background-color:#fff; color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } input[type="checkbox"] { margin:0; padding:0; height:auto; width:auto; } @@ -134,6 +139,7 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b #body-login div.buttons { text-align:center; } #body-login p.info { width:22em; text-align:center; margin:2em auto; color:#777; text-shadow:#fff 0 1px 0; } #body-login p.info a { font-weight:bold; color:#777; } +#body-login #submit.login { margin-right:7px; } /* quick fix for log in button not being aligned with input fields, should be properly fixed by input field width later */ #login { min-height:30em; margin:2em auto 0; border-bottom:1px solid #f8f8f8; background:#eee; } #login form { width:22em; margin:2em auto 2em; padding:0; } @@ -151,10 +157,11 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b #adminlogin, #adminpass, #user, #password { width:11.7em!important; padding-left:1.8em; } #adminlogin+label+img, #adminpass-icon, #user+label+img, #password-icon { position:absolute; left:1.25em; top:1.65em; - opacity:.3; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; } #adminpass-icon, #password-icon { top:1.1em; } input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } +input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } /* Nicely grouping input field sets */ .grouptop input { @@ -163,7 +170,7 @@ input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } } .groupmiddle input { margin-top:0; margin-bottom:0; - border-top:0; border-radius:0; + border-top:0; border-bottom:0; border-radius:0; box-shadow:0 1px 1px #fff,0 1px 0 #ddd inset; } .groupbottom input { @@ -177,21 +184,28 @@ input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } #login .groupmiddle label, #login .groupbottom label { top:.65em; } p.infield { position:relative; } label.infield { cursor:text !important; top:1.05em; left:.85em; } -#login form label.infield { position:absolute; font-size:19px; color:#aaa; white-space:nowrap; padding-left:1.4em; } +#login form label.infield { /* labels are ellipsized when too long, keep them short */ + position:absolute; width:90%; padding-left:1.4em; + font-size:19px; color:#aaa; + white-space:nowrap; overflow:hidden; text-overflow:ellipsis; +} #login #databaseField .infield { padding-left:0; } #login form input[type="checkbox"]+label { position:relative; margin:0; font-size:1em; text-shadow:#fff 0 1px 0; } #login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; } /* Show password toggle */ -#show { - position:absolute; right:1em; top:.8em; float:right; - display:none; -} -#show + label { - position:absolute!important; height:14px; width:24px; right:1em; top:1.25em!important; - background-image:url("../img/actions/toggle.png"); background-repeat:no-repeat; opacity:.3; +#show { position:absolute; right:1em; top:.8em; float:right; } +#show, #personal-show { display:none; } +#show + label { right:1em; top:1.25em!important; } +#show:checked + label, #personal-show:checked + label { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } +#show + label, #personal-show + label { + position:absolute!important; height:14px; width:24px; + background-image:url("../img/actions/toggle.png"); background-repeat:no-repeat; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; } -#show:checked + label { opacity:.8; } +#pass2, input[name="personal-password-clone"] { padding:0.6em 2.5em 0.4em 0.4em; width:8em;} +#personal-show + label { margin-top:1em; margin-left:-3em; } +#passwordbutton { margin-left:0.5em; } /* Database selector */ #login form #selectDbType { text-align:center; } @@ -222,30 +236,40 @@ fieldset.warning a { color:#b94a48 !important; font-weight:bold; } position:fixed; top:3.5em; float:left; width:64px; padding:0; z-index:75; height:100%; background:#383c43 url('../img/noise.png') repeat; border-right:1px #333 solid; -moz-box-shadow:0 0 7px #000; -webkit-box-shadow:0 0 7px #000; box-shadow:0 0 7px #000; - overflow-x:scroll; + overflow:hidden; } +#navigation:hover { overflow-y:auto; } #navigation a { display:block; padding:8px 0 4px; text-decoration:none; font-size:10px; text-align:center; - color:#fff; text-shadow:#000 0 -1px 0; opacity:.5; + color:#fff; text-shadow:#000 0 -1px 0; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; // ellipsize long app names } - #navigation a:hover, #navigation a:focus { opacity:.8; } - #navigation a.active { opacity:1; } + #navigation a:hover, #navigation a:focus { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } + #navigation a.active { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } #navigation .icon { display:block; width:32px; height:32px; margin:0 16px 0; } #navigation li:first-child a { padding-top:16px; } + + +/* USER MENU */ #settings { float:right; margin-top:7px; color:#bbb; text-shadow:0 -1px 0 #000; } #expand { padding:15px; cursor:pointer; font-weight:bold; } #expand:hover, #expand:focus, #expand:active { color:#fff; } -#expand img { opacity:.7; margin-bottom:-2px; } -#expand:hover img, #expand:focus img, #expand:active img { opacity:1; } +#expand img { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; margin-bottom:-2px; } +#expand:hover img, #expand:focus img, #expand:active img { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } #expanddiv { position:absolute; right:0; top:45px; z-index:76; display:none; - background-color:#444; border-bottom-left-radius:7px; box-shadow: 0 0 20px rgb(29,45,68); + background:#383c43 url('../img/noise.png') repeat; + border-bottom-left-radius:7px; border-bottom:1px #333 solid; border-left:1px #333 solid; + -moz-box-shadow:0 0 7px rgb(29,45,68); -webkit-box-shadow:0 0 7px rgb(29,45,68); box-shadow:0 0 7px rgb(29,45,68); } - #expanddiv a { display:block; color:#fff; text-shadow:0 -1px 0 #000; padding:0 8px; opacity:.7; } + #expanddiv a { + display:block; color:#fff; text-shadow:0 -1px 0 #000; padding:0 8px; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70);opacity:.7; + } #expanddiv a img { margin-bottom:-3px; } - #expanddiv a:hover, #expanddiv a:focus, #expanddiv a:active { opacity:1; } + #expanddiv a:hover, #expanddiv a:focus, #expanddiv a:active { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } /* VARIOUS REUSABLE SELECTORS */ @@ -319,9 +343,11 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin .arrow.left { left:-13px; bottom:1.2em; -webkit-transform:rotate(270deg); -moz-transform:rotate(270deg); -o-transform:rotate(270deg); -ms-transform:rotate(270deg); transform:rotate(270deg); } .arrow.up { top:-8px; right:2em; } .arrow.down { -webkit-transform:rotate(180deg); -moz-transform:rotate(180deg); -o-transform:rotate(180deg); -ms-transform:rotate(180deg); transform:rotate(180deg); } +.help-includes {overflow: hidden; width: 100%; height: 100%; -moz-box-sizing: border-box; box-sizing: border-box; padding-top: 2.8em; } +.help-iframe {width: 100%; height: 100%; margin: 0;padding: 0; border: 0; overflow: auto;} /* ---- BREADCRUMB ---- */ -div.crumb { float:left; display:block; background:url('../img/breadcrumb.svg') no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em; } +div.crumb { float:left; display:block; background:url('../img/breadcrumb.svg') no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em; -moz-box-sizing:border-box; box-sizing:border-box; } div.crumb:first-child { padding:10px 20px 10px 5px; } div.crumb.last { font-weight:bold; background:none; padding-right:10px; } div.crumb a{ padding: 0.9em 0 0.7em 0; } diff --git a/core/img/actions/caret-dark.png b/core/img/actions/caret-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7e1e6980298b86b6eb5bbf9008ea7dfb67699f Binary files /dev/null and b/core/img/actions/caret-dark.png differ diff --git a/core/img/actions/caret-dark.svg b/core/img/actions/caret-dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..abb1dc192d228813753b156c4e370059a3408767 --- /dev/null +++ b/core/img/actions/caret-dark.svg @@ -0,0 +1,102 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/core/img/actions/close.png b/core/img/actions/close.png new file mode 100644 index 0000000000000000000000000000000000000000..bc0c782882deaa4f9ecf1676592ddba0cc9aacbc Binary files /dev/null and b/core/img/actions/close.png differ diff --git a/core/img/actions/close.svg b/core/img/actions/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..6a6d98e34ad84113dedfa7b5984ffaa8128622d9 --- /dev/null +++ b/core/img/actions/close.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/delete-hover.png b/core/img/actions/delete-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..08b15510d926eaddb2c59558120a8d0166c58486 Binary files /dev/null and b/core/img/actions/delete-hover.png differ diff --git a/core/img/actions/delete-hover.svg b/core/img/actions/delete-hover.svg new file mode 100644 index 0000000000000000000000000000000000000000..63cacd5e38e584b0afaee2265b273ab3a8063755 --- /dev/null +++ b/core/img/actions/delete-hover.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/triangle-n.png b/core/img/actions/triangle-n.png index b0d7183caabd7e536b1b543c731f119aa78fdbac..14825f701146398259292881410727962c776ad8 100644 Binary files a/core/img/actions/triangle-n.png and b/core/img/actions/triangle-n.png differ diff --git a/core/img/actions/triangle-n.svg b/core/img/actions/triangle-n.svg index 35658631111caa6694338f4bff7dbac7189a88cf..e8d70fa8ce3387c7e8f7146f247c599f1c03bd2f 100644 --- a/core/img/actions/triangle-n.svg +++ b/core/img/actions/triangle-n.svg @@ -57,10 +57,10 @@ showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" - inkscape:window-width="1920" - inkscape:window-height="1025" - inkscape:window-x="-2" - inkscape:window-y="-3" + inkscape:window-width="1280" + inkscape:window-height="773" + inkscape:window-x="0" + inkscape:window-y="-1" inkscape:window-maximized="1" /> @@ -79,14 +79,10 @@ inkscape:label="Layer 1" inkscape:groupmode="layer"> - + sodipodi:nodetypes="cccc" /> diff --git a/core/img/actions/triangle-s.png b/core/img/actions/triangle-s.png index 53590a2197bd73dd8987a77f59a2a87f9111b14f..f36faef2b8ad8c706943eafbed40748ca791e5a0 100644 Binary files a/core/img/actions/triangle-s.png and b/core/img/actions/triangle-s.png differ diff --git a/core/img/actions/triangle-s.svg b/core/img/actions/triangle-s.svg index f899300bbca0e7750ba729ad7ac3fced18b2ce1d..396c61e01e22bdf981f039d22840b496673eee2d 100644 --- a/core/img/actions/triangle-s.svg +++ b/core/img/actions/triangle-s.svg @@ -14,7 +14,7 @@ height="16px" id="svg6077" version="1.1" - inkscape:version="0.48.2 r9819" + inkscape:version="0.48.3.1 r9886" sodipodi:docname="triangle-s.svg" inkscape:export-filename="/home/tol/tanghus-owncloud/core/img/actions/triangle-s.png" inkscape:export-xdpi="90" @@ -57,10 +57,10 @@ showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" - inkscape:window-width="1600" - inkscape:window-height="845" - inkscape:window-x="-2" - inkscape:window-y="-3" + inkscape:window-width="1280" + inkscape:window-height="773" + inkscape:window-x="0" + inkscape:window-y="-1" inkscape:window-maximized="1" /> @@ -70,7 +70,7 @@ image/svg+xml - + @@ -79,14 +79,10 @@ inkscape:label="Layer 1" inkscape:groupmode="layer"> - + sodipodi:nodetypes="cccc" /> diff --git a/core/img/actions/view-close.png b/core/img/actions/view-close.png new file mode 100644 index 0000000000000000000000000000000000000000..80339d78229a0047c048fb1560cdd11f86aebbec Binary files /dev/null and b/core/img/actions/view-close.png differ diff --git a/core/img/actions/view-close.svg b/core/img/actions/view-close.svg new file mode 100644 index 0000000000000000000000000000000000000000..45d66976084e8859b414ca9ea418610f23805dc9 --- /dev/null +++ b/core/img/actions/view-close.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/view-next.png b/core/img/actions/view-next.png new file mode 100644 index 0000000000000000000000000000000000000000..b76bea06713c6bd66eb4913a70f8ce88b55d5829 Binary files /dev/null and b/core/img/actions/view-next.png differ diff --git a/core/img/actions/view-next.svg b/core/img/actions/view-next.svg new file mode 100644 index 0000000000000000000000000000000000000000..d5642f1a11cb50d4bd0c81bad1d1a732e4c45a0a --- /dev/null +++ b/core/img/actions/view-next.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/view-pause.png b/core/img/actions/view-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..64264ff9281a2114a7c662b93c96018ba5da9777 Binary files /dev/null and b/core/img/actions/view-pause.png differ diff --git a/core/img/actions/view-pause.svg b/core/img/actions/view-pause.svg new file mode 100644 index 0000000000000000000000000000000000000000..0edc6f14e28059dce9fed6cea3c1235ab77bcffc --- /dev/null +++ b/core/img/actions/view-pause.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/view-play.png b/core/img/actions/view-play.png new file mode 100644 index 0000000000000000000000000000000000000000..0080d45b5cdc57d396bba4ade8c42b36ea197edd Binary files /dev/null and b/core/img/actions/view-play.png differ diff --git a/core/img/actions/view-play.svg b/core/img/actions/view-play.svg new file mode 100644 index 0000000000000000000000000000000000000000..0bdc63bf7e1d07e21af60d0e8982c35059750be0 --- /dev/null +++ b/core/img/actions/view-play.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/actions/view-previous.png b/core/img/actions/view-previous.png new file mode 100644 index 0000000000000000000000000000000000000000..82943c23a59ca65b54c2882c4a2ea8901fb42998 Binary files /dev/null and b/core/img/actions/view-previous.png differ diff --git a/core/img/actions/view-previous.svg b/core/img/actions/view-previous.svg new file mode 100644 index 0000000000000000000000000000000000000000..df1f49511d070184f4f5d98311b8e2d60190d5d8 --- /dev/null +++ b/core/img/actions/view-previous.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/img/appstore.png b/core/img/appstore.png new file mode 100644 index 0000000000000000000000000000000000000000..009b2b51b98ae5ccaa835aa56a350142dfb2e827 Binary files /dev/null and b/core/img/appstore.png differ diff --git a/core/img/desktopapp.png b/core/img/desktopapp.png new file mode 100644 index 0000000000000000000000000000000000000000..182ddd2cf18891878210352e9ac0f34d7d89409d Binary files /dev/null and b/core/img/desktopapp.png differ diff --git a/core/img/desktopapp.svg b/core/img/desktopapp.svg new file mode 100644 index 0000000000000000000000000000000000000000..93d91e461a613db6c49ce5d498e0e9971492ac98 --- /dev/null +++ b/core/img/desktopapp.svg @@ -0,0 +1,100 @@ + +image/svg+xml + + +Desktop app +Windows, OS X, Linux + \ No newline at end of file diff --git a/core/img/filetypes/application-msexcel.png b/core/img/filetypes/application-msexcel.png index abcd93689a08ec9bdbf0984927e8da06c043c7cd..b977d7e52e2446ea01201c5c7209ac3a05f12c9f 100644 Binary files a/core/img/filetypes/application-msexcel.png and b/core/img/filetypes/application-msexcel.png differ diff --git a/core/img/filetypes/application-mspowerpoint.png b/core/img/filetypes/application-mspowerpoint.png index b4aaad9a45c9abbee2d47611a6963101b64a8023..c4eff0387d5888c638ba09473ba6d2369f7b56f0 100644 Binary files a/core/img/filetypes/application-mspowerpoint.png and b/core/img/filetypes/application-mspowerpoint.png differ diff --git a/core/img/filetypes/application-msword.png b/core/img/filetypes/application-msword.png index e8b230c59cb66c6ce473c43cf2b7253e22129d75..ae8ecbf47672a874c0958d0d113a56162c2bd364 100644 Binary files a/core/img/filetypes/application-msword.png and b/core/img/filetypes/application-msword.png differ diff --git a/core/img/filetypes/application.png b/core/img/filetypes/application.png new file mode 100644 index 0000000000000000000000000000000000000000..1dee9e366094e87db68c606d0522d72d4b939818 Binary files /dev/null and b/core/img/filetypes/application.png differ diff --git a/core/img/filetypes/ms-excel.png b/core/img/filetypes/ms-excel.png index abcd93689a08ec9bdbf0984927e8da06c043c7cd..b977d7e52e2446ea01201c5c7209ac3a05f12c9f 100644 Binary files a/core/img/filetypes/ms-excel.png and b/core/img/filetypes/ms-excel.png differ diff --git a/core/img/filetypes/ms-powerpoint.png b/core/img/filetypes/ms-powerpoint.png index b4aaad9a45c9abbee2d47611a6963101b64a8023..c4eff0387d5888c638ba09473ba6d2369f7b56f0 100644 Binary files a/core/img/filetypes/ms-powerpoint.png and b/core/img/filetypes/ms-powerpoint.png differ diff --git a/core/img/googleplay.png b/core/img/googleplay.png new file mode 100644 index 0000000000000000000000000000000000000000..2d9ad6296080509464d3d412c8b3e93254626c3f Binary files /dev/null and b/core/img/googleplay.png differ diff --git a/core/img/icon-error.png b/core/img/icon-error.png deleted file mode 100644 index edeaceb16980c6af896cfb1b5f46447f3e91b5d1..0000000000000000000000000000000000000000 Binary files a/core/img/icon-error.png and /dev/null differ diff --git a/core/img/icon-error.svg b/core/img/icon-error.svg deleted file mode 100644 index bba639bfd7e564de5fec8374f1c91c4b068198c5..0000000000000000000000000000000000000000 --- a/core/img/icon-error.svg +++ /dev/null @@ -1,813 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/img/icon-sync.png b/core/img/icon-sync.png deleted file mode 100644 index c38ca87a237bd0be449e41c845c8f7add91b230c..0000000000000000000000000000000000000000 Binary files a/core/img/icon-sync.png and /dev/null differ diff --git a/core/img/icon-sync.svg b/core/img/icon-sync.svg deleted file mode 100644 index 0806572f2c717fea53640cd450f8dc74982f02a9..0000000000000000000000000000000000000000 --- a/core/img/icon-sync.svg +++ /dev/null @@ -1,815 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/img/icon.png b/core/img/icon.png deleted file mode 100644 index e87191f1c0cedd918056b7330b9966e1f65cca3f..0000000000000000000000000000000000000000 Binary files a/core/img/icon.png and /dev/null differ diff --git a/core/img/icon.svg b/core/img/icon.svg deleted file mode 100644 index 6f91abe6e48cd02529552f8b7158ae286b3be41a..0000000000000000000000000000000000000000 --- a/core/img/icon.svg +++ /dev/null @@ -1,821 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/img/logo-inverted.png b/core/img/logo-inverted.png deleted file mode 100644 index 265a8871b45b14b046fd3cf20531d9a3694fb445..0000000000000000000000000000000000000000 Binary files a/core/img/logo-inverted.png and /dev/null differ diff --git a/core/img/logo-inverted.svg b/core/img/logo-inverted.svg deleted file mode 100644 index 9ac167cc41f0a19ec81ad892773433ebf33a220b..0000000000000000000000000000000000000000 --- a/core/img/logo-inverted.svg +++ /dev/null @@ -1,762 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/img/logo-square.png b/core/img/logo-square.png deleted file mode 100644 index b836de8f3be81ab3cb2673e3db1c94d4a3966e5e..0000000000000000000000000000000000000000 Binary files a/core/img/logo-square.png and /dev/null differ diff --git a/core/img/logo-sticker.jpg b/core/img/logo-sticker.jpg deleted file mode 100644 index ad2bf63ca37aa064d4f7c05c6e293bcafcb5e702..0000000000000000000000000000000000000000 Binary files a/core/img/logo-sticker.jpg and /dev/null differ diff --git a/core/img/logo-sticker.svg b/core/img/logo-sticker.svg deleted file mode 100644 index e48f7a78c7d91cc0cd5c25c2ed082be598af4ee3..0000000000000000000000000000000000000000 --- a/core/img/logo-sticker.svg +++ /dev/null @@ -1,764 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/img/logo-wide.png b/core/img/logo-wide.png index 702f1d97e5b7bdf6da6a10eea3ebfda32e594be0..b0c90984e44bb4b8244339c62c609749f7db6db5 100644 Binary files a/core/img/logo-wide.png and b/core/img/logo-wide.png differ diff --git a/core/img/logo-wide.svg b/core/img/logo-wide.svg index 37fc0007479055e1324f465ee53f01cd6fd31a16..cf8eace5204242c4ddd230f1c268cd5f0dd327a6 100644 --- a/core/img/logo-wide.svg +++ b/core/img/logo-wide.svg @@ -19,8 +19,11 @@ viewBox="0 0 147.33262 32" enable-background="new 0 0 595.275 311.111" xml:space="preserve" - inkscape:version="0.48.2 r9819" - sodipodi:docname="logo-wide.svg">image/svg+xml + fit-margin-bottom="0"> \ No newline at end of file + \ No newline at end of file diff --git a/core/img/noise.png b/core/img/noise.png index 8fdda17b5e36b5a1aacc09652bc93126a1ccb8fc..271dd5ebcfbdc858f83f0f7ea18a59bb1980d74f 100644 Binary files a/core/img/noise.png and b/core/img/noise.png differ diff --git a/core/img/places/calendar-dark.png b/core/img/places/calendar-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e372104a28482c72ff9e2113f5b77e36a7a9ea88 Binary files /dev/null and b/core/img/places/calendar-dark.png differ diff --git a/core/img/places/calendar-dark.svg b/core/img/places/calendar-dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..6f7cb8e74d7dfeb65e12a8c7524418bee7035ed9 --- /dev/null +++ b/core/img/places/calendar-dark.svg @@ -0,0 +1,75 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/core/img/places/contacts-dark.png b/core/img/places/contacts-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..a08339d1d3d88413910fe07e95beb32536983983 Binary files /dev/null and b/core/img/places/contacts-dark.png differ diff --git a/core/img/places/contacts-dark.svg b/core/img/places/contacts-dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..df364911c519e617ecdee7dca235072513107705 --- /dev/null +++ b/core/img/places/contacts-dark.svg @@ -0,0 +1,73 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/core/js/compatibility.js b/core/js/compatibility.js new file mode 100644 index 0000000000000000000000000000000000000000..0cfeefab87128329f43dceb35c473fc806c70ca4 --- /dev/null +++ b/core/js/compatibility.js @@ -0,0 +1,32 @@ + +//https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} + +//https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim +if(!String.prototype.trim) { + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g,''); + }; +} \ No newline at end of file diff --git a/core/js/config.php b/core/js/config.php index 9069175ed6fa58d542ee165fbb4fe3dbad42ef9e..0aaa44822876b339d4b47c73ad8f6145069b6d0e 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -29,8 +29,33 @@ $array = array( "oc_current_user" => "\"".OC_User::getUser(). "\"", "oc_requesttoken" => "\"".OC_Util::callRegister(). "\"", "datepickerFormatDate" => json_encode($l->l('jsdate', 'jsdate')), - "dayNames" => json_encode(array((string)$l->t('Sunday'), (string)$l->t('Monday'), (string)$l->t('Tuesday'), (string)$l->t('Wednesday'), (string)$l->t('Thursday'), (string)$l->t('Friday'), (string)$l->t('Saturday'))), - "monthNames" => json_encode(array((string)$l->t('January'), (string)$l->t('February'), (string)$l->t('March'), (string)$l->t('April'), (string)$l->t('May'), (string)$l->t('June'), (string)$l->t('July'), (string)$l->t('August'), (string)$l->t('September'), (string)$l->t('October'), (string)$l->t('November'), (string)$l->t('December'))), + "dayNames" => json_encode( + array( + (string)$l->t('Sunday'), + (string)$l->t('Monday'), + (string)$l->t('Tuesday'), + (string)$l->t('Wednesday'), + (string)$l->t('Thursday'), + (string)$l->t('Friday'), + (string)$l->t('Saturday') + ) + ), + "monthNames" => json_encode( + array( + (string)$l->t('January'), + (string)$l->t('February'), + (string)$l->t('March'), + (string)$l->t('April'), + (string)$l->t('May'), + (string)$l->t('June'), + (string)$l->t('July'), + (string)$l->t('August'), + (string)$l->t('September'), + (string)$l->t('October'), + (string)$l->t('November'), + (string)$l->t('December') + ) + ), "firstDay" => json_encode($l->l('firstday', 'firstday')) , ); @@ -38,4 +63,3 @@ $array = array( foreach ($array as $setting => $value) { echo("var ". $setting ."=".$value.";\n"); } -?> \ No newline at end of file diff --git a/core/js/eventsource.js b/core/js/eventsource.js index f783ade7ae916cd79401a4fa98bd9b4cbe25b45d..ce8c8387c8efef5eab85542807ca2993a0a998a2 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -87,8 +87,10 @@ OC.EventSource.prototype={ useFallBack:false, fallBackCallBack:function(type,data){ if(type){ - for(var i=0;i
    '); - popup = $('#appsettings_popup'); + popup = $('#appsettings_popup'); popup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft'); } if(popup.is(':visible')) { @@ -317,35 +317,44 @@ OC.addStyle.loaded=[]; OC.addScript.loaded=[]; OC.Notification={ - getDefaultNotificationFunction: null, - setDefault: function(callback) { - OC.Notification.getDefaultNotificationFunction = callback; - }, - hide: function(callback) { - $("#notification").text(''); - $('#notification').fadeOut('400', function(){ - if (OC.Notification.isHidden()) { - if (OC.Notification.getDefaultNotificationFunction) { - OC.Notification.getDefaultNotificationFunction.call(); - } - } - if (callback) { - callback.call(); - } - }); - }, - showHtml: function(html) { - var notification = $('#notification'); - notification.hide(); - notification.html(html); - notification.fadeIn().css("display","inline"); - }, - show: function(text) { - var notification = $('#notification'); - notification.hide(); - notification.text(text); - notification.fadeIn().css("display","inline"); - }, + queuedNotifications: [], + getDefaultNotificationFunction: null, + setDefault: function(callback) { + OC.Notification.getDefaultNotificationFunction = callback; + }, + hide: function(callback) { + $('#notification').fadeOut('400', function(){ + if (OC.Notification.isHidden()) { + if (OC.Notification.getDefaultNotificationFunction) { + OC.Notification.getDefaultNotificationFunction.call(); + } + } + if (callback) { + callback.call(); + } + $('#notification').empty(); + if(OC.Notification.queuedNotifications.length > 0){ + OC.Notification.showHtml(OC.Notification.queuedNotifications[0]); + OC.Notification.queuedNotifications.shift(); + } + }); + }, + showHtml: function(html) { + if(($('#notification').filter('span.undo').length == 1) || OC.Notification.isHidden()){ + $('#notification').html(html); + $('#notification').fadeIn().css("display","inline"); + }else{ + OC.Notification.queuedNotifications.push(html); + } + }, + show: function(text) { + if(($('#notification').filter('span.undo').length == 1) || OC.Notification.isHidden()){ + $('#notification').html(html); + $('#notification').fadeIn().css("display","inline"); + }else{ + OC.Notification.queuedNotifications.push($(text).html()); + } + }, isHidden: function() { return ($("#notification").text() === ''); } @@ -548,7 +557,7 @@ function replaceSVG(){ */ function object(o) { function F() {} - F.prototype = o; + F.prototype = o; return new F(); } @@ -584,6 +593,7 @@ function fillWindow(selector) { } $(document).ready(function(){ + sessionHeartBeat(); if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg replaceSVG(); @@ -628,7 +638,8 @@ $(document).ready(function(){ }); // 'show password' checkbox - $('#password').showPassword(); + $('#password').showPassword(); + $('#adminpass').showPassword(); $('#pass2').showPassword(); //use infield labels @@ -662,14 +673,14 @@ $(document).ready(function(){ } }); $('#settings #expand').click(function(event) { - $('#settings #expanddiv').slideToggle(); + $('#settings #expanddiv').slideToggle(200); event.stopPropagation(); }); $('#settings #expanddiv').click(function(event){ event.stopPropagation(); }); - $(window).click(function(){//hide the settings menu when clicking outside it - $('#settings #expanddiv').slideUp(); + $(document).click(function(){//hide the settings menu when clicking outside it + $('#settings #expanddiv').slideUp(200); }); // all the tipsy stuff needs to be here (in reverse order) to work @@ -814,3 +825,17 @@ OC.set=function(name, value) { } context[tail]=value; }; + + +/** + * Calls the server periodically every 15 mins to ensure that session doesnt + * time out + */ +function sessionHeartBeat(){ + OC.Router.registerLoadedCallback(function(){ + var url = OC.Router.generate('heartbeat'); + setInterval(function(){ + $.post(url); + }, 900000); + }); +} diff --git a/core/js/multiselect.js b/core/js/multiselect.js index 623c6e0f7e1c02a71a781bdf1341b426f5050f23..bc4223feb64735cffe364eb953b17d076e544cae 100644 --- a/core/js/multiselect.js +++ b/core/js/multiselect.js @@ -266,8 +266,9 @@ } list.append(list.find('li.creator')); var pos=button.position(); - if($(document).height() > (button.offset().top+button.outerHeight() + list.children().length * button.height()) - || $(document).height()/2 > pos.top + if(($(document).height() > (button.offset().top+button.outerHeight() + list.children().length * button.height()) + && $(document).height() - button.offset().top > (button.offset().top+button.outerHeight() + list.children().length * button.height())) + || $(document).height()/2 > button.offset().top ) { list.css({ top:pos.top+button.outerHeight()-5, diff --git a/core/js/router.js b/core/js/router.js index 3562785b3420ef01963ff39b74eccb20f5caa57c..b94721673a73c879156bf3f45b9e4d27524b5738 100644 --- a/core/js/router.js +++ b/core/js/router.js @@ -1,11 +1,11 @@ -OC.router_base_url = OC.webroot + '/index.php/', +OC.router_base_url = OC.webroot + '/index.php', OC.Router = { // register your ajax requests to load after the loading of the routes // has finished. otherwise you face problems with race conditions registerLoadedCallback: function(callback){ this.routes_request.done(callback); }, - routes_request: $.ajax(OC.router_base_url + 'core/routes.json', { + routes_request: $.ajax(OC.router_base_url + '/core/routes.json', { dataType: 'json', success: function(jsondata) { if (jsondata.status === 'success') { diff --git a/core/js/setup.js b/core/js/setup.js index 2656cac2f45be916b0be82d31c5e28208dea08b1..76812b2997969e28dc8bc3725b56ab2aaf7a9138 100644 --- a/core/js/setup.js +++ b/core/js/setup.js @@ -5,6 +5,7 @@ $(document).ready(function() { mysql:!!$('#hasMySQL').val(), postgresql:!!$('#hasPostgreSQL').val(), oracle:!!$('#hasOracle').val(), + mssql:!!$('#hasMSSQL').val() }; $('#selectDbType').buttonset(); @@ -41,6 +42,12 @@ $(document).ready(function() { $('#dbhost').show(250); $('#dbhostlabel').show(250); }); + + $('#mssql').click(function() { + $('#use_other_db').slideDown(250); + $('#dbhost').show(250); + $('#dbhostlabel').show(250); + }); $('input[checked]').trigger('click'); diff --git a/core/js/share.js b/core/js/share.js index 58cb787b6d1038585748c2b5fccfd9d19ed18d29..34f24da4df746eeb788a104936888776210a2c96 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -24,9 +24,9 @@ OC.Share={ var file = $('tr').filterAttr('data-file', OC.basename(item)); if (file.length > 0) { var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); - action.find('img').attr('src', image); + var img = action.find('img').attr('src', image); action.addClass('permanent'); - action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); + action.html(' '+t('core', 'Shared')).prepend(img); } var dir = $('#dir').val(); if (dir.length > 1) { @@ -40,7 +40,7 @@ OC.Share={ if (img.attr('src') != OC.imagePath('core', 'actions/public')) { img.attr('src', image); action.addClass('permanent'); - action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); + action.html(' '+t('core', 'Shared')).prepend(img); } } last = path; @@ -84,13 +84,13 @@ OC.Share={ $('a.share[data-item="'+itemSource+'"]').css('background', 'url('+image+') no-repeat center'); } else { var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); - action.find('img').attr('src', image); + var img = action.find('img').attr('src', image); if (shares) { action.addClass('permanent'); - action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); + action.html(' '+t('core', 'Shared')).prepend(img); } else { action.removeClass('permanent'); - action.html(action.html().replace(t('core', 'Shared'), t('core', 'Share'))); + action.html(' '+t('core', 'Share')).prepend(img); } } if (shares) { @@ -186,8 +186,8 @@ OC.Share={ html += ''; html += ''; html += ''; } html += '
    '; @@ -213,7 +213,7 @@ OC.Share={ } }); } - $('#shareWith').autocomplete({minLength: 2, source: function(search, response) { + $('#shareWith').autocomplete({minLength: 1, source: function(search, response) { // if (cache[search.term]) { // response(cache[search.term]); // } else { @@ -309,12 +309,12 @@ OC.Share={ if (permissions & OC.PERMISSION_SHARE) { shareChecked = 'checked="checked"'; } - var html = '
  • '; + var html = '
  • '; html += ''; if(shareWith.length > 14){ - html += shareWithDisplayName.substr(0,11) + '...'; + html += escapeHTML(shareWithDisplayName.substr(0,11) + '...'); }else{ - html += shareWithDisplayName; + html += escapeHTML(shareWithDisplayName); } if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) { if (editChecked == '') { diff --git a/core/js/singleselect.js b/core/js/singleselect.js new file mode 100644 index 0000000000000000000000000000000000000000..1a018b7414872b3a740e7adf26d63d5ea7f1fc0f --- /dev/null +++ b/core/js/singleselect.js @@ -0,0 +1,76 @@ +(function ($) { + $.fn.singleSelect = function () { + return this.each(function (i, select) { + var input = $(''); + select = $(select); + input.css('position', 'absolute'); + input.css(select.offset()); + input.css({ + 'box-sizing': 'border-box', + '-moz-box-sizing': 'border-box', + 'margin': 0, + 'width': (select.width() - 5) + 'px', + 'height': (select.outerHeight() - 2) + 'px', + 'border': 'none', + 'box-shadow': 'none', + 'margin-top': '1px', + 'margin-left': '1px', + 'z-index': 1000 + }); + input.hide(); + $('body').append(input); + + select.on('change', function (event) { + var value = $(this).val(), + newAttr = $('option:selected', $(this)).attr('data-new'); + if (!(typeof newAttr !== 'undefined' && newAttr !== false)) { + input.hide(); + select.data('previous', value); + } else { + event.stopImmediatePropagation(); + input.show(); + select.css('background-color', 'white'); + input.focus(); + } + }); + + $(select).data('previous', $(select).val()); + + input.on('change', function () { + var value = $(this).val(); + if (value) { + select.children().attr('selected', null); + var existingOption = select.children().filter(function (i, option) { + return ($(option).val() == value); + }); + if (existingOption.length) { + existingOption.attr('selected', 'selected'); + } else { + var option = $('