| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 | 
							- getHeaders = [];
 
- getHeadersByCollection = {};
 
- var contentDisposition = Npm.require('content-disposition');
 
- FS.HTTP.Handlers = {};
 
- /**
 
-  * @method FS.HTTP.Handlers.Del
 
-  * @public
 
-  * @returns {any} response
 
-  *
 
-  * HTTP DEL request handler
 
-  */
 
- FS.HTTP.Handlers.Del = function httpDelHandler(ref) {
 
-   var self = this;
 
-   var opts = FS.Utility.extend({}, self.query || {}, self.params || {});
 
-   // If DELETE request, validate with 'remove' allow/deny, delete the file, and return
 
-   FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId);
 
-   /*
 
-    * From the DELETE spec:
 
-    * A successful response SHOULD be 200 (OK) if the response includes an
 
-    * entity describing the status, 202 (Accepted) if the action has not
 
-    * yet been enacted, or 204 (No Content) if the action has been enacted
 
-    * but the response does not include an entity.
 
-    */
 
-   self.setStatusCode(200);
 
-   return {
 
-     deleted: !!ref.file.remove()
 
-   };
 
- };
 
- /**
 
-  * @method FS.HTTP.Handlers.GetList
 
-  * @public
 
-  * @returns {Object} response
 
-  *
 
-  * HTTP GET file list request handler
 
-  */
 
- FS.HTTP.Handlers.GetList = function httpGetListHandler() {
 
-   // Not Yet Implemented
 
-   // Need to check publications and return file list based on
 
-   // what user is allowed to see
 
- };
 
- /*
 
-   requestRange will parse the range set in request header - if not possible it
 
-   will throw fitting errors and autofill range for both partial and full ranges
 
-   throws error or returns the object:
 
-   {
 
-     start
 
-     end
 
-     length
 
-     unit
 
-     partial
 
-   }
 
- */
 
- var requestRange = function(req, fileSize) {
 
-   if (req) {
 
-     if (req.headers) {
 
-       var rangeString = req.headers.range;
 
-       // Make sure range is a string
 
-       if (rangeString === ''+rangeString) {
 
-         // range will be in the format "bytes=0-32767"
 
-         var parts = rangeString.split('=');
 
-         var unit = parts[0];
 
-         // Make sure parts consists of two strings and range is of type "byte"
 
-         if (parts.length == 2 && unit == 'bytes') {
 
-           // Parse the range
 
-           var range = parts[1].split('-');
 
-           var start = Number(range[0]);
 
-           var end = Number(range[1]);
 
-           // Fix invalid ranges?
 
-           if (range[0] != start) start = 0;
 
-           if (range[1] != end || !end) end = fileSize - 1;
 
-           // Make sure range consists of a start and end point of numbers and start is less than end
 
-           if (start < end) {
 
-             var partSize = 0 - start + end + 1;
 
-             // Return the parsed range
 
-             return {
 
-               start: start,
 
-               end: end,
 
-               length: partSize,
 
-               size: fileSize,
 
-               unit: unit,
 
-               partial: (partSize < fileSize)
 
-             };
 
-           } else {
 
-             throw new Meteor.Error(416, "Requested Range Not Satisfiable");
 
-           }
 
-         } else {
 
-           // The first part should be bytes
 
-           throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable");
 
-         }
 
-       } else {
 
-         // No range found
 
-       }
 
-     } else {
 
-       // throw new Error('No request headers set for _parseRange function');
 
-     }
 
-   } else {
 
-     throw new Error('No request object passed to _parseRange function');
 
-   }
 
-   return {
 
-     start: 0,
 
-     end: fileSize - 1,
 
-     length: fileSize,
 
-     size: fileSize,
 
-     unit: 'bytes',
 
-     partial: false
 
-   };
 
- };
 
- /**
 
-  * @method FS.HTTP.Handlers.Get
 
-  * @public
 
-  * @returns {any} response
 
-  *
 
-  * HTTP GET request handler
 
-  */
 
- FS.HTTP.Handlers.Get = function httpGetHandler(ref) {
 
-   var self = this;
 
-   // Once we have the file, we can test allow/deny validators
 
-   // XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access?
 
-   FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/);
 
-   var storeName = ref.storeName;
 
-   // If no storeName was specified, use the first defined storeName
 
-   if (typeof storeName !== "string") {
 
-     // No store handed, we default to primary store
 
-     storeName = ref.collection.primaryStore.name;
 
-   }
 
-   // Get the storage reference
 
-   var storage = ref.collection.storesLookup[storeName];
 
-   if (!storage) {
 
-     throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"');
 
-   }
 
-   // Get the file
 
-   var copyInfo = ref.file.copies[storeName];
 
-   if (!copyInfo) {
 
-     throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store');
 
-   }
 
-   // Set the content type for file
 
-   if (typeof copyInfo.type === "string") {
 
-     self.setContentType(copyInfo.type);
 
-   } else {
 
-     self.setContentType('application/octet-stream');
 
-   }
 
-   // Add 'Content-Disposition' header if requested a download/attachment URL
 
-   if (typeof ref.download !== "undefined") {
 
-     var filename = ref.filename || copyInfo.name;
 
-     self.addHeader('Content-Disposition', contentDisposition(filename));
 
-   } else {
 
-     self.addHeader('Content-Disposition', 'inline');
 
-   }
 
-   // Get the contents range from request
 
-   var range = requestRange(self.request, copyInfo.size);
 
-   // Some browsers cope better if the content-range header is
 
-   // still included even for the full file being returned.
 
-   self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
 
-   // If a chunk/range was requested instead of the whole file, serve that'
 
-   if (range.partial) {
 
-     self.setStatusCode(206, 'Partial Content');
 
-   } else {
 
-     self.setStatusCode(200, 'OK');
 
-   }
 
-   // Add any other global custom headers and collection-specific custom headers
 
-   FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) {
 
-     self.addHeader(header[0], header[1]);
 
-   });
 
-   // Inform clients about length (or chunk length in case of ranges)
 
-   self.addHeader('Content-Length', range.length);
 
-   // Last modified header (updatedAt from file info)
 
-   self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString());
 
-   // Inform clients that we accept ranges for resumable chunked downloads
 
-   self.addHeader('Accept-Ranges', range.unit);
 
-   if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
 
-   var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end});
 
-   readStream.on('error', function(err) {
 
-     // Send proper error message on get error
 
-     if (err.message && err.statusCode) {
 
-       self.Error(new Meteor.Error(err.statusCode, err.message));
 
-     } else {
 
-       self.Error(new Meteor.Error(503, 'Service unavailable'));
 
-     }
 
-   });
 
-   readStream.pipe(self.createWriteStream());
 
- };
 
- // File with unicode or other encodings filename can upload to server susscessfully,
 
- // but when download, the  HTTP header "Content-Disposition" cannot accept 
 
- // characters other than ASCII, the filename should be converted to binary or URI encoded.
 
- // https://github.com/wekan/wekan/issues/784
 
- const originalHandler = FS.HTTP.Handlers.Get;
 
- FS.HTTP.Handlers.Get = function (ref) {
 
-   try {
 
-       var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
 
-       if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
 
-           ref.filename =  encodeURIComponent(ref.filename);
 
-       } else if(userAgent.indexOf('firefox') >= 0) {
 
-           ref.filename = new Buffer(ref.filename).toString('binary');
 
-       } else {
 
-           /* safari*/
 
-           ref.filename = new Buffer(ref.filename).toString('binary');
 
-       }   
 
-    } catch (ex){
 
-         ref.filename = ref.filename;
 
-    } 
 
-    return originalHandler.call(this, ref);
 
- };
 
- /**
 
-  * @method FS.HTTP.Handlers.PutInsert
 
-  * @public
 
-  * @returns {Object} response object with _id property
 
-  *
 
-  * HTTP PUT file insert request handler
 
-  */
 
- FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) {
 
-   var self = this;
 
-   var opts = FS.Utility.extend({}, self.query || {}, self.params || {});
 
-   FS.debug && console.log("HTTP PUT (insert) handler");
 
-   // Create the nice FS.File
 
-   var fileObj = new FS.File();
 
-   // Set its name
 
-   fileObj.name(opts.filename || null);
 
-   // Attach the readstream as the file's data
 
-   fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
 
-   // Validate with insert allow/deny
 
-   FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId);
 
-   // Insert file into collection, triggering readStream storage
 
-   ref.collection.insert(fileObj);
 
-   // Send response
 
-   self.setStatusCode(200);
 
-   // Return the new file id
 
-   return {_id: fileObj._id};
 
- };
 
- /**
 
-  * @method FS.HTTP.Handlers.PutUpdate
 
-  * @public
 
-  * @returns {Object} response object with _id and chunk properties
 
-  *
 
-  * HTTP PUT file update chunk request handler
 
-  */
 
- FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) {
 
-   var self = this;
 
-   var opts = FS.Utility.extend({}, self.query || {}, self.params || {});
 
-   var chunk = parseInt(opts.chunk, 10);
 
-   if (isNaN(chunk)) chunk = 0;
 
-   FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk);
 
-   // Validate with insert allow/deny; also mounts and retrieves the file
 
-   FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId);
 
-   self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) );
 
-   // Send response
 
-   self.setStatusCode(200);
 
-   return { _id: ref.file._id, chunk: chunk };
 
- };
 
 
  |