diff --git a/NEWS b/NEWS index 39374cc501..32e10cc2a0 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ Bug fixes - [sogo-tool] fixed "manage-acl unsubscribe" command (#4591) - [web] fixed handling of collapsed/expanded mail accounts (#4541) - [web] fixed handling of duplicate recipients (#4597) + - [web] fixed folder export when XSRF validation is enabled (#4502) + - [web] don't encode filename extension when exporting folders 4.0.4 (2018-10-23) ------------------ diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index cd8aabfcb4..2ca2a962be 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -94,12 +94,11 @@ + (void) initialize } response = [context response]; - [response setHeader: @"application/octet-stream; charset=utf-8" + [response setHeader: @"application/directory; charset=utf-8" forKey: @"content-type"]; filename = [NSString stringWithFormat: @"%@.ldif", - [sourceFolder displayName]]; - disposition = [NSString stringWithFormat: @"attachment; filename=\"%@\"", - [filename asQPSubjectString: @"utf-8"]]; + [[sourceFolder displayName] asQPSubjectString: @"utf-8"]]; + disposition = [NSString stringWithFormat: @"attachment; filename=\"%@\"", filename]; [response setHeader: disposition forKey: @"Content-Disposition"]; [response setContent: [content dataUsingEncoding: NSUTF8StringEncoding]]; diff --git a/UI/SOGoUI/SOGoDirectAction.m b/UI/SOGoUI/SOGoDirectAction.m index 99cca44fed..394a1ee3d5 100644 --- a/UI/SOGoUI/SOGoDirectAction.m +++ b/UI/SOGoUI/SOGoDirectAction.m @@ -253,8 +253,12 @@ - (NSString *) urlForResourceFilename: (NSString *) filename ![auth isKindOfClass: [SOGoWebAuthenticator class]]) return [super performActionNamed: _actionName]; - // We grab the X-XSRF-TOKEN header + // We grab the X-XSRF-TOKEN from the header or the URL token = [[context request] headerForKey: @"X-XSRF-TOKEN"]; + if (![token length]) + { + token = [[context request] formValueForKey: @"X-XSRF-TOKEN"]; + } // We compare it with our session key value = [[context request] diff --git a/UI/SOGoUI/UIxComponent.m b/UI/SOGoUI/UIxComponent.m index 1d6c4e28ba..c959b4e3ba 100644 --- a/UI/SOGoUI/UIxComponent.m +++ b/UI/SOGoUI/UIxComponent.m @@ -796,8 +796,12 @@ - (BOOL) isUIxDebugEnabled return [super performActionNamed: _actionName]; } - // We grab the X-XSRF-TOKEN header + // We grab the X-XSRF-TOKEN from the header or the URL token = [[context request] headerForKey: @"X-XSRF-TOKEN"]; + if (![token length]) + { + token = [[context request] formValueForKey: @"X-XSRF-TOKEN"]; + } // We compare it with our session key value = [[context request] diff --git a/UI/Scheduler/UIxCalFolderActions.m b/UI/Scheduler/UIxCalFolderActions.m index 1f9682bc8e..86a9e057be 100644 --- a/UI/Scheduler/UIxCalFolderActions.m +++ b/UI/Scheduler/UIxCalFolderActions.m @@ -54,7 +54,7 @@ - (WOResponse *) exportAction [response setHeader: @"text/calendar; charset=utf-8" forKey: @"content-type"]; disposition = [NSString stringWithFormat: @"attachment; filename=\"%@.ics\"", - [folderICS displayName]]; + [[folderICS displayName] asQPSubjectString: @"utf-8"]]; [response setHeader: disposition forKey: @"Content-Disposition"]; return response; diff --git a/UI/WebServerResources/Gruntfile.js b/UI/WebServerResources/Gruntfile.js index f04015302e..0c6a46bab9 100644 --- a/UI/WebServerResources/Gruntfile.js +++ b/UI/WebServerResources/Gruntfile.js @@ -18,7 +18,7 @@ module.exports = function(grunt) { }; var custom_vendor_files = { 'js/vendor/angular-file-upload.min.js': ['bower_components/angular-file-upload/dist/angular-file-upload.js', 'js/Common/angular-file-upload.trump.js'], - 'js/vendor/FileSaver.min.js': ['bower_components/file-saver.js/FileSaver.js'] + 'js/vendor/FileSaver.min.js': ['bower_components/FileSaver/dist/FileSaver.js'] }; require('time-grunt')(grunt); diff --git a/UI/WebServerResources/bower.json b/UI/WebServerResources/bower.json index 948a10004f..6dfacd758f 100644 --- a/UI/WebServerResources/bower.json +++ b/UI/WebServerResources/bower.json @@ -11,7 +11,7 @@ "angular-ui-router": "1.0.20", "angular-file-upload": "latest", "angular-material": "1.1.10", - "file-saver.js": "latest", + "FileSaver": "latest", "lodash": "latest", "ng-sortable": "1.3.7", "breakpoint-sass": ">=2.4.2" diff --git a/UI/WebServerResources/js/Common/Resource.service.js b/UI/WebServerResources/js/Common/Resource.service.js index 8e65b04f5b..db43749e12 100644 --- a/UI/WebServerResources/js/Common/Resource.service.js +++ b/UI/WebServerResources/js/Common/Resource.service.js @@ -11,11 +11,12 @@ * @param {String} path - the base path of the external resource * @param {Object} options - extra attributes to be associated to the object */ - function Resource($http, $q, $window, path, activeUser, options) { + function Resource($http, $q, $window, $cookies, path, activeUser, options) { angular.extend(this, { _http: $http, _q: $q, _window: $window, + _cookies: $cookies, _path: path, _activeUser: activeUser }); @@ -29,9 +30,9 @@ * @desc The factory we'll use to register with Angular. * @return a new Resource object */ - Resource.$factory = ['$http', '$q', '$window', function($http, $q, $window) { + Resource.$factory = ['$http', '$q', '$window', '$cookies', function($http, $q, $window, $cookies) { return function(path, activeUser, options) { - return new Resource($http, $q, $window, path, activeUser, options); + return new Resource($http, $q, $window, $cookies, path, activeUser, options); }; }]; @@ -54,7 +55,7 @@ if (uid) path.splice(path.length - 1, 1, escape(uid)); - return new Resource(this._http, this._q, this._window, '/' + path.join('/'), this._activeUser); + return new Resource(this._http, this._q, this._window, this._cookies, '/' + path.join('/'), this._activeUser); }; /** @@ -234,10 +235,14 @@ }; Resource.prototype.open = function(id, action) { - var path = [this._path]; + var path = [this._path], xsrfToken; + xsrfToken = this._cookies.get('XSRF-TOKEN'); if (id) path.push(id); if (action) path.push(action); path = _.compact(_.flatten(path)).join('/'); + if (xsrfToken) { + path += '?X-XSRF-TOKEN=' + xsrfToken; + } this._window.location.href = path; };