├── .gitignore ├── README.md ├── core ├── apiValidators │ ├── ApiValidator.js │ ├── ApiValidatorBase.js │ ├── DataChannelsValidator.js │ └── FileSystemApiValidator.js ├── controllers │ ├── IController.js │ └── P2PController.js ├── data │ ├── BlockCache.js │ └── FSio.js ├── dataStructures │ ├── AvailabilityMap.js │ ├── AvailabilityMapBase.js │ ├── Block.js │ ├── BlockMap.js │ ├── DoublyLinkedList.js │ ├── LRU.js │ └── Queue.compressed.js ├── protocol │ ├── BinaryProtocol.js │ └── ProtocolMessages.js ├── stats │ └── StatsCalculator.js ├── test │ ├── data │ │ ├── FS.io.test.js │ │ └── FS.io.test.jstd │ └── dataStructures │ │ ├── DoublyLinkedList.test.js │ │ ├── DoublyLinkedList.test.jstd │ │ ├── LRU.test.js │ │ └── LRU.test.jstd ├── tracker │ ├── ITracker.js │ ├── SimpleTracker.js │ ├── Swarm.js │ └── util.js ├── transport │ ├── AbstractPeerConnection.js │ ├── PeerConnectionImpl.js │ ├── WebSocketServer.js │ ├── WebSocketServerNoop.js │ └── WsConnection.js └── util │ ├── base64.js │ ├── buildify-peer5.js │ ├── crypto.js │ ├── lang_ext.js │ ├── logger.js │ ├── namespaces.js │ ├── norequire.js │ ├── radio.js │ ├── session-manager.js │ └── uuid.js ├── package.json ├── serverConfig.json └── sharefest ├── LICENSE ├── clientConfig.js ├── public ├── beta.html ├── css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── main.css │ └── main_new.css ├── download.html ├── faq.html ├── favicon-peer5.ico ├── favicon.ico ├── img │ ├── 1.jpg │ ├── back-dark.jpg │ ├── back.png │ ├── cnet.png │ ├── github_icon.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── google_io.png │ ├── google_io_color.png │ ├── grey.png │ ├── io-live-on-air-2013.png │ ├── logo.png │ ├── logo.svg │ ├── symbol.png │ ├── symbol.svg │ ├── tf.png │ └── twitter_icon.png ├── index.html ├── js │ ├── dnd.js │ ├── files.js │ ├── jquery.js │ ├── sfClient.js │ └── ui.js ├── press.html ├── privacy.html └── terms.html ├── run-prod.sh ├── server.js └── server ├── lib ├── client-includes.js └── router.js └── secret ├── GlobalSignIntermediate.crt ├── private.key ├── readme └── self.crt /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Farewell 2 | It's been 5 years since [commercial browsers started supporting WebRTC ](). Peer5 is no longer able to keep Sharefest functional and operating, it's just too much out of our main focus - video streaming. We loved creating Sharefest - the first WebRTC file sharing production application, all the hacks that involved making it work on early Chrome and Firefox, going through first Interoperability of browsers, and now lately with Safari support, WebRTC is almost ubiquitous - it's been a hell of a ride! Thanks everyone that supported the project over the years and special thanks to the Chrome and Firefox teams for being super attentive and tolerant to all our bug reports, feature requests and plain questions about the technology. farewell! 3 | 4 | ![Sharefest](https://raw.github.com/Peer5/ShareFest/master/sharefest/public/img/logo.png) 5 | 6 | One-To-Many sharing application. Serverless. 7 | Eliminates the need to fully upload your file to services such as Dropbox or Google Drive. 8 | Put your file and start sharing immediately with anyone that enters the page. 9 | Pure javascript-based. No plugins needed thanks to HTML5 WebRTC Data Channel API - http://webrtc.org 10 | 11 | How does it work 12 | ================ 13 | http://sharefest.me/faq 14 | http://www.youtube.com/watch?v=p2HzZkd2A40&feature=youtu.be&t=15m30s 15 | 16 | Sharefest operates on a mesh network similar to Bittorrent network. 17 | The main difference is that currently the peers are coordinated using an intelligent server. 18 | This coordinator controls which parts are sent from A to B and who shall talk with whom. 19 | Peer5 Coordinator (or any other solution) is used to accomplish this. 20 | Each peer will connect to few other peers in order to maximize the distribution of the file. 21 | Supporting Chrome (>26) and Firefox (>19) 22 | 23 | First version includes a simple page that one user will drag a file onto to 24 | share, and other users will enter the first user's url and start downloading the file. 25 | 26 | Hosted version: http://sharefest.me 27 | 28 | TODO: 29 | ============ 30 | * see issues - https://github.com/Peer5/ShareFest/issues 31 | 32 | Quick setup 33 | ============== 34 | 1. Install nodejs 35 | 2. [Download](https://github.com/Peer5/ShareFest/archive/master.zip) this repo, or `git clone https://github.com/Peer5/ShareFest.git` 36 | 3. `cd ShareFest` 37 | 4. `npm install --dedupe` to install dependencies. 38 | 5. `npm start` to start the server 39 | 6. http://localhost:13337 should work 40 | 41 | Environment Variables 42 | ============== 43 | NODE_ENV: development or production 44 | REQUIRE_HTTPS: 1 redirect to HTTPS when http GET request is coming 45 | 46 | About 47 | ============== 48 | Sharefest started by Peer5 at the SV DevFest 2012 hackathon (San Jose). 49 | It was soon open sourced to GitHub and now being developed by Peer5 and a community of great WebRTC hackers. 50 | 51 | License 52 | ============== 53 | Apache 2.0 - see LICENSE file 54 | -------------------------------------------------------------------------------- /core/apiValidators/ApiValidator.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | peer5.core.apiValidators.ApiValidator = Object.subClass({ 4 | //just so we'll know what is the name of the class we are (nameSpace) in if we need 5 | name:'peer5.core.apiValidators.ApiValidator', 6 | 7 | /* 8 | * ctor will be called when instantiating a class with for example here new peer5.client.MediaElementWrapper(x,y,z); 9 | * to call super function we do this._super(); 10 | * Param: gets an object of validators classes to run .validate function - these classes inherits from ApiValidatorBase 11 | * */ 12 | ctor:function (validators) { 13 | var browserDetails = this.detectBrowser(); 14 | this.browserName = browserDetails[0].toLowerCase(); 15 | this.browserVersion = browserDetails[1]; 16 | this.validators = []; 17 | 18 | //fill in validators array 19 | for(var i = 0 ; i < validators.length ; i++){ 20 | var validator = validators[i] 21 | this.validators.push(new validator(this.browserName,this.browserVersion)); 22 | } 23 | }, 24 | 25 | detectBrowser:function () { 26 | var N = navigator.appName, ua = navigator.userAgent, tem; 27 | var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); 28 | if (M && (tem = ua.match(/version\/([\.\d]+)/i)) != null) M[2] = tem[1]; 29 | M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?']; 30 | return M; 31 | }, 32 | 33 | validate:function () { 34 | var result = true; 35 | for(var i = 0 ; i < this.validators.length ; i++){ 36 | var validator = this.validators[i]; 37 | result = result && validator.validate(); 38 | } 39 | 40 | return result; 41 | 42 | } 43 | }) 44 | })(); 45 | 46 | -------------------------------------------------------------------------------- /core/apiValidators/ApiValidatorBase.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | peer5.core.apiValidators.ApiValidatorBase = Object.subClass({ 4 | 5 | //juset so we'll know what is the name of the class we are (nameSpace) in if we need 6 | name:'peer5.core.apiValidators.ApiValidatorBase', 7 | 8 | /* 9 | * ctor will be called when instantiating a class with for example here new peer5.client.MediaElementWrapper(x,y,z); 10 | * to call super function we do this._super(); 11 | * Param: gets an object of validators classes to run .validate function 12 | * */ 13 | ctor:function (validators) { 14 | 15 | }, 16 | 17 | detectBrowser:function () { 18 | var N = navigator.appName, ua = navigator.userAgent, tem; 19 | var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); 20 | if (M && (tem = ua.match(/version\/([\.\d]+)/i)) != null) M[2] = tem[1]; 21 | M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?']; 22 | return M; 23 | }, 24 | 25 | getMajorVersionNumber:function (fullVersion) { 26 | var arr = fullVersion.split('.'); 27 | var number = parseInt(arr[0]); 28 | return number; 29 | }, 30 | 31 | validate:function () { 32 | //must be implemented by inheritors 33 | throw 'unimplemented method'; 34 | } 35 | 36 | }) 37 | 38 | })(); 39 | 40 | -------------------------------------------------------------------------------- /core/apiValidators/DataChannelsValidator.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | peer5.core.apiValidators.DataChannelsApiValidator = peer5.core.apiValidators.ApiValidatorBase.subClass({ 4 | 5 | //juset so we'll know what is the name of the class we are (nameSpace) in if we need 6 | name:'peer5.core.apiValidators.DataChannelsApiValidator', 7 | 8 | /* 9 | * ctor will be called when instantiating a class with for example here new peer5.client.MediaElementWrapper(x,y,z); 10 | * to call super function we do this._super(); 11 | * 12 | * */ 13 | ctor:function (browserName, browserVersion) { 14 | this.browserName = browserName 15 | this.browserVersion = browserVersion 16 | 17 | this.browserVersionSupprot = { 18 | chrome:26, //just so it'll work with current version of chrome and websockets 19 | firefox:19, 20 | msie:11, 21 | opera:12, 22 | safari:1000 23 | } 24 | }, 25 | 26 | /*getMajorVersionNumber:function (fullVersion) { 27 | var arr = fullVersion.split('.'); 28 | var number = parseInt(arr[0]); 29 | return number; 30 | },*/ 31 | 32 | 33 | validate:function () { 34 | var dc = true; 35 | var pc; 36 | 37 | try { 38 | var RTCPeerConnection 39 | if (window.webkitRTCPeerConnection) { 40 | RTCPeerConnection = window.webkitRTCPeerConnection; 41 | var SERVER = "stun:stun.l.google.com:19302"; 42 | 43 | servers = {iceServers:[ 44 | {url:"stun:" + SERVER} 45 | ]}; 46 | 47 | pc = new RTCPeerConnection( 48 | servers, { optional:[ 49 | { RtpDataChannels:true } 50 | ]}); 51 | pc.createDataChannel('test_data_channels', { reliable:false }) 52 | } else { 53 | //we are in firefox 54 | if (window.mozRTCPeerConnection) { 55 | RTCPeerConnection = window.mozRTCPeerConnection; 56 | pc = new RTCPeerConnection(); 57 | var dcNotExist = !!!pc.createDataChannel 58 | if(dcNotExist){ 59 | //data channel is not working 60 | dc = false; 61 | } 62 | 63 | } else { 64 | //no support in webrtc at all 65 | dc = false; 66 | } 67 | } 68 | 69 | 70 | } 71 | catch 72 | (e) { 73 | dc = false; 74 | } 75 | 76 | if (pc) { 77 | pc.close(); 78 | delete pc; 79 | } 80 | 81 | if (!dc) { 82 | radio('browserUnsupported').broadcast(this.browserName, this.browserVersion, 'data channels'); 83 | } 84 | return (/*(this.getMajorVersionNumber(this.browserVersion) >= this.browserVersionSupprot[this.browserName] ) &&*/ dc); 85 | 86 | 87 | } 88 | }) 89 | 90 | }) 91 | (); 92 | 93 | -------------------------------------------------------------------------------- /core/apiValidators/FileSystemApiValidator.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | peer5.core.apiValidators.FileSystemApiValidator = peer5.core.apiValidators.ApiValidatorBase.subClass({ 4 | 5 | //juset so we'll know what is the name of the class we are (nameSpace) in if we need 6 | name:'peer5.core.apiValidators.FileSystemApiValidator', 7 | 8 | /* 9 | * ctor will be called when instantiating a class with for example here new peer5.client.MediaElementWrapper(x,y,z); 10 | * to call super function we do this._super(); 11 | * 12 | * */ 13 | ctor:function (browserName, browserVersion) { 14 | this.browserName = browserName 15 | this.browserVersion = browserVersion 16 | 17 | this.browserVersionSupprot = { 18 | chrome:26, //just so it'll work with current version of chrome and websockets 19 | firefox:19, 20 | msie:11, 21 | opera:12, 22 | safari:1000 23 | } 24 | }, 25 | 26 | validate:function () { 27 | if(peer5.config.USE_FS == false){ 28 | return true; 29 | } 30 | if (window.webkitRequestFileSystem) { 31 | //test func 32 | peer5.core.data.FSio.requestQuota(100, function (succ) { 33 | if (!succ) { 34 | peer5.info('changing USE_FS to false') 35 | peer5.config.USE_FS = false; 36 | } 37 | }) 38 | }else{ 39 | //no fileSystem 40 | peer5.config.USE_FS = false; 41 | 42 | } 43 | 44 | return true; 45 | } 46 | }) 47 | 48 | }) 49 | (); 50 | 51 | -------------------------------------------------------------------------------- /core/controllers/IController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | peer5.core.controllers.IController = Object.subClass({ 3 | download:function (id, startBlock, endBlock) { 4 | throw 'unimplemented method'; 5 | }, 6 | stop:function () { 7 | throw 'unimplemented method'; 8 | }, 9 | init:function (resourceId) { 10 | throw 'unimplemented method'; 11 | } 12 | }); 13 | })(); 14 | 15 | -------------------------------------------------------------------------------- /core/data/BlockCache.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | peer5.core.data.BlockMaps = Object.subClass({ 3 | ctor:function () { 4 | this._blockMaps = {}; 5 | this._keys = {}; 6 | }, 7 | 8 | getRealKeyName: function(key) { 9 | return this._keys[key]; 10 | }, 11 | 12 | alias:function(newKey, key) { 13 | this._keys[newKey] = key; 14 | }, 15 | 16 | add:function (key, blockMap) { 17 | this._keys[key] = key; 18 | this._blockMaps[key] = blockMap; 19 | }, 20 | 21 | get:function (key) { 22 | key = this.getRealKeyName(key); 23 | return this._blockMaps[key]; 24 | }, 25 | 26 | remove:function (key) { 27 | key = this.getRealKeyName(key); 28 | delete this._blockMaps[key]; 29 | }, 30 | 31 | forEach:function(cb) { 32 | for (var k in this._blockMaps) { 33 | cb(k,this._blockMaps[k]); 34 | } 35 | } 36 | }); 37 | 38 | peer5.core.data.BlockCache = new peer5.core.data.BlockMaps(); 39 | 40 | })(); -------------------------------------------------------------------------------- /core/data/FSio.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | peer5.core.data.FSio = Object.subClass({ 3 | ctor:function () { 4 | this.writeQueue = new Queue(); 5 | this.registerEvents(); 6 | this.pendingObjectUrlCb = {}; 7 | this.finishWriteCbs = []; 8 | // this.removeAll(function(){window.webkitRequestFileSystem(window.TEMPORARY, peer5.config.FS_SIZE, this.onInitFs, this.errorHandler);}); 9 | // window.webkitRequestFileSystem(window.TEMPORARY, peer5.config.FS_SIZE, this.onInitFs, this.errorHandler); 10 | }, 11 | 12 | createResource:function(resourceId,cb,single){ //single:true - single file, :false a directory 13 | peer5.info("Adding resource " + resourceId + " to the filesystem."); 14 | var thi$ = this; 15 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId,{create:true},function(fileEntry){ 16 | if(cb) cb(true); 17 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 18 | }, 19 | 20 | removeResource:function(resourceId,cb){ //implementation supports single file only 21 | peer5.log("Removing resource " + resourceId + " from the filesystem."); 22 | var thi$ = this; 23 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId,{create:false},function(fileEntry){ 24 | fileEntry.remove(function(){ 25 | if(cb) cb(true); 26 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}) 27 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}) 28 | }, 29 | 30 | renameResource:function(oldResourceId,newResourceId,cb){ 31 | peer5.info("changing resource name from " + oldResourceId + " to " + newResourceId); 32 | var thi$ = this; 33 | this.fs.root.getDirectory(peer5.config.FS_ROOT_DIR,{create:false},function(dirEntry){ 34 | dirEntry.getFile(oldResourceId,{create:false},function(fileEntry){ 35 | fileEntry.moveTo(dirEntry,newResourceId,function(succ){ 36 | if(succ) peer5.info("succesfully renamed"); 37 | if(cb) cb(succ); 38 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 39 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 40 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 41 | }, 42 | 43 | write:function(resourceId,data,position, cb){ //implementation supports single file only 44 | peer5.debug("Writing to resource " + resourceId); 45 | if(this._writeAvailable()){ 46 | this._write(resourceId,data,position,cb); 47 | } 48 | this._addWriteCommand(resourceId,data,position,cb); 49 | }, 50 | 51 | //supports only a TBD:max size, (reading more than 100MB at a time crashes the browser) 52 | //cb(success,data), data is Uint8Array 53 | read:function(resourceId,startPosition,endPosition,cb){ 54 | var thi$ = this; 55 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId, {}, function(fileEntry) { 56 | 57 | // Get a File object representing the file, 58 | // then use FileReader to read its contents. 59 | fileEntry.file(function(file) { 60 | var reader = new FileReader(); 61 | 62 | reader.onloadend = function(evt) { 63 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 64 | if(cb) cb(true,new Uint8Array(evt.target.result)); 65 | }; 66 | } 67 | 68 | var blob = file.slice(startPosition,endPosition); 69 | reader.readAsArrayBuffer(blob); 70 | 71 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 72 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 73 | }, 74 | 75 | //cb(succ,details) 76 | //details = {size:#} 77 | getResourceDetails:function(resourceId,cb){ 78 | var thi$ = this; 79 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId, {}, function(fileEntry) { 80 | // Get a File object representing the file, 81 | fileEntry.file(function(file) { 82 | cb(true,{size:file.size}); 83 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 84 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 85 | }, 86 | 87 | //cb(success,objectUrl) 88 | createObjectURL:function(resourceId,cb){ 89 | var thi$ = this; 90 | if(this.writeQueue.getLength() > 0) 91 | this.pendingObjectUrlCb[resourceId] = cb; 92 | else{ 93 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId, {}, function(fileEntry) { 94 | peer5.log("queue size: " + thi$.writeQueue.getLength()); 95 | if(cb) cb(true,fileEntry.toURL()); 96 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 97 | } 98 | }, 99 | 100 | notifyFinishWrite:function(cb){ 101 | if(this.writeQueue.getLength() <= 0) 102 | cb(); 103 | this.finishWriteCbs.push(cb); 104 | }, 105 | 106 | isExist:function(resourceId,cb){ 107 | peer5.log("Checking if resource " + resourceId + " exists in the filesystem."); 108 | var thi$ = this; 109 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId,{create:false},function(fileEntry){ 110 | if(cb) cb(true); 111 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 112 | }, 113 | 114 | listFiles:function(cb){ 115 | var thi$ = this; 116 | var dirReader = this.fs.root.createReader(); 117 | dirReader.readEntries(function(entries) { 118 | if (!entries.length) { 119 | peer5.debug("Filesystem is empty") 120 | } else { 121 | peer5.debug("Filesystem files: " + entries); 122 | } 123 | cb(true,entries); 124 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 125 | }, 126 | 127 | removeAll:function(cb){ 128 | peer5.warn("removing all files in filesystem"); 129 | var thi$ = this; 130 | var dirReader = this.fs.root.createReader(); 131 | dirReader.readEntries(function(entries) { 132 | for (var i = 0, entry; entry = entries[i]; ++i) { 133 | if (entry.isDirectory) { 134 | entry.removeRecursively(function() {if(cb) cb(true);}, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 135 | } else { 136 | entry.remove(function() {if(cb) cb(true);}, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 137 | } 138 | } 139 | peer5.debug('Directory emptied.'); 140 | },function(e){thi$.errorHandler(e);if(cb) cb(false);} ); 141 | }, 142 | 143 | removeRootDir:function(cb){ 144 | peer5.warn("removing all files in filesystem under " + peer5.config.FS_ROOT_DIR); 145 | var thi$ = this; 146 | var dirReader = this.fs.root.createReader(); 147 | dirReader.readEntries(function(entries) { 148 | for (var i = 0, entry; entry = entries[i]; ++i) { 149 | if (entry.isDirectory && entry.name + '/' == peer5.config.FS_ROOT_DIR) { 150 | entry.removeRecursively(function() { 151 | thi$.fs.root.getDirectory(peer5.config.FS_ROOT_DIR,{create:true},function(dirEntry){ 152 | cb(true); 153 | },thi$.errorHandler) 154 | }, function(e){thi$.errorHandler(e);if(cb) cb(false);}); 155 | } 156 | } 157 | peer5.debug('Directory emptied.'); 158 | },function(e){thi$.errorHandler(e);if(cb) cb(false);} ); 159 | }, 160 | 161 | //cb(succ,freespace) 162 | requestQuota:function(size,cb){ 163 | peer5.info("requesting quota size = " + size); 164 | var thi$ = this; 165 | var requestSize = 1.1*size; //taking 10% overhead 166 | window.webkitRequestFileSystem(window.TEMPORARY, requestSize, function(fs){ 167 | thi$.onInitFs(fs); 168 | thi$.queryQuota(function(succ,usage,quota){ 169 | cb(true,quota-usage); 170 | }) 171 | },function(e){thi$.errorHandler(e);if(cb) cb(false);}); 172 | }, 173 | 174 | //cb(succ,usage,quota) 175 | queryQuota:function(cb){ 176 | navigator.webkitTemporaryStorage.queryUsageAndQuota(function(usage, quota) { 177 | peer5.info('Using: ' + (usage / quota) * 100 + '% of temporary storage'); 178 | if(cb) 179 | cb(true,usage,quota); 180 | }, function(e) { 181 | peer5.error('Error', e); 182 | if(cb) 183 | cb(false); 184 | }); 185 | }, 186 | 187 | _writeAvailable:function(){ 188 | return (this.writeQueue.isEmpty()) 189 | }, 190 | 191 | _write:function(resourceId,data,position,cb){ 192 | var thi$ = this; 193 | this.fs.root.getFile(peer5.config.FS_ROOT_DIR + resourceId,{create:false},function(fileEntry){ 194 | fileEntry.createWriter(function (fileWriter) { 195 | if(position > fileWriter.length){ 196 | peer5.debug("truncating: filewriter length = " + fileWriter.length + " position = " + position) 197 | fileWriter.truncate(position); 198 | thi$._write(resourceId,data,position,cb); //after truncate a new fileWriter need to be created 199 | }else{ 200 | fileWriter.onwriteend = function (evt) { 201 | if(evt.currentTarget.error) 202 | thi$.errorHandler(evt.currentTarget.error); 203 | thi$.writeQueue.dequeue(); //dequeue the finished command 204 | peer5.debug("onwriteend: writeQueue length = " + thi$.writeQueue.getLength()); 205 | if(cb) cb(true); 206 | if(!thi$.writeQueue.isEmpty()){ 207 | var writeCommand = thi$.writeQueue.peek(); //getting the next command 208 | thi$._write(writeCommand.resourceId,writeCommand.data,writeCommand.position,writeCommand.cb); 209 | }else{ 210 | peer5.debug("finished writing all the commands in command queue"); 211 | peer5.debug("writeQueue is empty, pendingObjectUrlCb = " + thi$.pendingObjectUrlCb[resourceId]); 212 | if(thi$.pendingObjectUrlCb[resourceId]){ 213 | thi$.createObjectURL(resourceId,thi$.pendingObjectUrlCb[resourceId]); 214 | delete thi$.pendingObjectUrlCb[resourceId]; 215 | } 216 | for(var i=0;i> 9 | this.list = new peer5.core.dataStructures.DoublyLinkedList(); //keyX <=> keyY <=> keyZ 10 | this.deleteCB = deleteCB; 11 | }, 12 | 13 | set:function(key,value){ 14 | peer5.debug("LRU.set with key: " + key); 15 | if(this.dict[key]) //already have this key 16 | this._delete(key); //deleting the old element matching this key - without cb since it causes a loop of set/delete 17 | this.list.insert(key); 18 | this.dict[key] = {value:value,p:this.list.tail}; //the most recent element 19 | if(this.list.length > this.max){ 20 | //delete the lru element 21 | var lruElem = this.list.head; 22 | this._delete(lruElem.value,this.deleteCB); //value of the list elements are the keys in dictionary 23 | } 24 | }, 25 | 26 | get:function(key){ 27 | peer5.debug("LRU.get with key: " + key); 28 | if(this.dict[key]){ 29 | var listElem = this.dict[key].p; 30 | if(listElem == this.list.tail) //already most recent 31 | return this.dict[key].value; 32 | this.list.delete(listElem); 33 | this.list.insert(key); //(most recent) 34 | this.dict[key].p = this.list.tail; 35 | return this.dict[key].value; 36 | }else{ 37 | return false; 38 | } 39 | }, 40 | /** @private functions*/ 41 | _delete:function(key,cb){ 42 | peer5.debug("removing key " + key + " from lru"); 43 | var delElem = this.dict[key]; 44 | this.list.delete(delElem.p); 45 | delete this.dict[key]; 46 | if(cb) 47 | cb(key,delElem.value); 48 | } 49 | }) 50 | })(); 51 | -------------------------------------------------------------------------------- /core/dataStructures/Queue.compressed.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Queue.js 4 | 5 | A function to represent a queue 6 | 7 | Created by Stephen Morley - http://code.stephenmorley.org/ - and released under 8 | the terms of the CC0 1.0 Universal legal code: 9 | 10 | http://creativecommons.org/publicdomain/zero/1.0/legalcode 11 | 12 | */ 13 | 14 | function Queue(){ 15 | var _1=[]; 16 | var _2=0; 17 | this.getLength=function(){ 18 | return (_1.length-_2); 19 | }; 20 | this.isEmpty=function(){ 21 | return (_1.length==0); 22 | }; 23 | this.enqueue=function(_3){ 24 | _1.push(_3); 25 | }; 26 | this.dequeue=function(){ 27 | if(_1.length==0){ 28 | return undefined; 29 | } 30 | var _4=_1[_2]; 31 | if(++_2*2>=_1.length){ 32 | _1=_1.slice(_2); 33 | _2=0; 34 | } 35 | return _4; 36 | }; 37 | this.peek=function(){ 38 | return (_1.length>0?_1[_2]:undefined); 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /core/protocol/ProtocolMessages.js: -------------------------------------------------------------------------------- 1 | 2 | (function (exports) { 3 | 4 | exports.P2P_DATA = 0x11; 5 | exports.P2P_REQUEST = 0x12; 6 | exports.P2P_CANCEL = 0x13; 7 | exports.P2P_HAVE = 0x14; 8 | 9 | exports.REPORT = 0x21; 10 | exports.FILE_INFO = 0x22; 11 | exports.MATCH = 0x23; 12 | exports.JOIN = 0x24; 13 | exports.SWARM_HEALTH = 0x29; 14 | exports.SWARM_ERROR = 0x30; 15 | exports.SDP = 0x31; 16 | exports.COMPLETED_DOWNLOAD = 0x32; 17 | 18 | // attributes (ordered) 19 | // swarmId - 4bytes - only the intial 4 bytes will be encoded. We have a slight chance for collision, but even if there is, the pollution detection will prevent serious problems. 20 | // chunkId - 4bytes (UInt32) means that we have a limitation of 2^32 chunks per swarm -> ~4TB for each file. 21 | // payload data - chunk 22 | // length: chunk size + swarmId length under 1200 bytes constraint 23 | // constraints: 24 | // peer will send only hash varified chunks 25 | function Data(swarmId, chunkId, payload) { 26 | this.tag = exports.P2P_DATA; 27 | this.swarmId = swarmId; 28 | this.chunkId = chunkId; 29 | this.payload = payload; 30 | } 31 | 32 | // swarmId (4bytes) 33 | // chunkIds - optional - array of ids each 4bytes 34 | // Example of 2 chunks encoding: 0x010203040A0B0C0D. 0x01-0x04 are the first encoded chunk, 0x0A-0x0D are the second encoded chunk) 35 | function Request(swarmId, chunkIds) { 36 | this.tag = exports.P2P_REQUEST 37 | this.swarmId = swarmId; 38 | if (!chunkIds) chunkIds = []; 39 | this.chunkIds = chunkIds; 40 | } 41 | 42 | //Cancel (0x13) 43 | //attributes (ordered) 44 | //swarmId (4 bytes) 45 | //chunkIds/all (optional) if empty (not included) then all chunks are to be canceled. If included, chunks are encoded in 4bytes-per-chunk - for example 0x010203040A0B0C0D 46 | function Cancel(swarmId, chunkIds) { 47 | this.tag = exports.P2P_CANCEL; 48 | this.swarmId = swarmId; 49 | if (!chunkIds) 50 | chunkIds = []; 51 | this.all = (chunkIds.length == 0) 52 | this.chunkIds = chunkIds; 53 | } 54 | 55 | /** 56 | * 57 | * @param swarmId 58 | * @param seeder true if seeder 59 | * @param complete true if availabilityMap, false if update 60 | * @param blockIds in list of chunks, or availabilityMap 61 | * @constructor 62 | */ 63 | function Have(swarmId, seeder, availabilityMap, blockIds) { 64 | this.tag = exports.P2P_HAVE; 65 | this.swarmId = swarmId; 66 | this.seeder = seeder; 67 | this.blockIds = []; 68 | if (!seeder) { 69 | if (availabilityMap) { 70 | this.complete = true 71 | this.availabilityMap = availabilityMap; 72 | } 73 | else { 74 | this.complete = false 75 | this.blockIds = blockIds; 76 | } 77 | } 78 | } 79 | 80 | // swarmId full (20bytes) 81 | // last requested blockId (not random) (uint) 82 | // transport statistics (for P2P total, HTTP) 83 | // total bytes dl/up 84 | // dl/up speed (max,min,avg) 85 | // rejected connections (NAT problems, etc.) 86 | // num of connections (byte) 87 | // connections (special parsing) 88 | // info gathered on those connections? 89 | //#bytes recv, sent, dl/up speed (max,min,avg) 90 | // chunk drop % (expiration) 91 | //#blocks failed hash verification 92 | // latency MS (best so far) 93 | // browser - user agent 94 | // vendor (byte) 95 | // major (byte) 96 | // ?minor (byte) 97 | // availability block map (variable length) 98 | function Report(swarmId, last_requested_block_id, total_bytes_up_P2P, total_bytes_down_P2P, total_bytes_down_HTTP,total_waste_P2P,total_waste_HTTP, speed_up, speed_down, connections, ua, availabilityMap,numOfBlocksHave,fileSize,completedDownload) { 99 | this.tag = exports.REPORT; 100 | this.swarmId = swarmId; 101 | this.lastRequestedBlockId = last_requested_block_id; 102 | this.totalBytesUpP2P = total_bytes_up_P2P; 103 | this.totalBytesDownP2P = total_bytes_down_P2P; 104 | this.totalBytesDownHTTP = total_bytes_down_HTTP; 105 | this.totalWasteP2P = total_waste_P2P; 106 | this.totalWasteHTTP = total_waste_HTTP; 107 | this.speedUp = speed_up; 108 | this.speedDown = speed_down 109 | this.connections = connections; 110 | this.ua = ua; 111 | this.availabilityMap = availabilityMap; 112 | this.numOfBlocksHave = numOfBlocksHave; 113 | this.fileSize = fileSize; 114 | this.completedDownload = completedDownload; 115 | } 116 | 117 | function Join(swarmId) { 118 | this.tag = exports.JOIN; 119 | this.swarmId = swarmId; 120 | } 121 | 122 | /** 123 | * Represents metadata needed to manage the swarm 124 | * @param swarmId uniqueId or null if needs to create new 125 | * @param size the size of the swarm in bytes 126 | * @param hashes list of digests for each block 127 | * @param blockSize size of a single block in bytes 128 | * @param origin optional identification of the swarm creator (i.e. customerId, name of uploader, company) 129 | * @constructor 130 | */ 131 | function FileInfo(swarmId, size, hashes, blockSize, origin, name, lastModified, type) { 132 | this.tag = exports.FILE_INFO; 133 | this.swarmId = swarmId; 134 | this.size = size; 135 | this.hashes = hashes; 136 | this.blockSize = blockSize; 137 | this.origin = origin; 138 | this.name = name; 139 | this.lastModified = lastModified; 140 | this.type = type; 141 | } 142 | 143 | exports.SWARM_NOT_FOUND = 0; 144 | exports.SWARM_ONLY_FIREFOX = 11; 145 | exports.SWARM_ONLY_CHROME = 12; 146 | 147 | function SwarmError(swarmId,error) { 148 | this.tag = exports.SWARM_ERROR; 149 | this.error = error; 150 | } 151 | 152 | function Match(swarmId,peerId,availabilityMap){ 153 | this.tag = exports.MATCH; //protocol tag 154 | this.swarmId = swarmId; //the swarm that consists the two peers 155 | this.peerId = peerId; //the matched peerid 156 | this.availabilityMap = availabilityMap; //bitarray consisting available blocks 157 | } 158 | 159 | function Connection(totalBytesDown, totalBytesUp, speedDown, speedUp, chunksExpired, chunksRequested, latency, connected, connectingDuration, failure) { 160 | this.totalBytesDown = totalBytesDown; // total bytes dl 161 | this.totalBytesUp = totalBytesUp; // total bytes up 162 | this.speedDown = speedDown; // dl speed kbs max, min, avg 163 | this.speedUp = speedUp;// up speed kbs max, min, avg 164 | this.chunksRequested = chunksRequested; 165 | this.chunksExpired = chunksExpired; // number of chunks expired (loss) 166 | this.latency = latency; // best latency 167 | this.connected = connected; //is the connection connected/not connected 168 | this.connectingDuration = connectingDuration; 169 | this.failure = failure; //was there a problem 170 | } 171 | 172 | function Sdp(originId,destId,sdpMessage,port,type){ 173 | this.tag = exports.SDP; 174 | this.originId = originId; 175 | this.destId = destId; 176 | this.sdpMessage = sdpMessage; 177 | //deprecated: just to support Firefox 21- 178 | this.port = port; 179 | this.type = type; 180 | } 181 | 182 | function SwarmHealth(swarmId , numOfSeedersInSwarm , NumOfPeersInSwarm, totalCompletedDownloads){ 183 | this.tag = exports.SWARM_HEALTH; 184 | this.swarmId = swarmId; 185 | this.numOfSeedersInSwarm = numOfSeedersInSwarm; 186 | this.NumOfPeersInSwarm = NumOfPeersInSwarm; 187 | this.totalCompletedDownloads = totalCompletedDownloads; 188 | 189 | } 190 | 191 | exports.Have = Have; 192 | exports.Cancel = Cancel; 193 | exports.Request = Request; 194 | exports.Data = Data; 195 | exports.Report = Report; 196 | exports.Connection = Connection; 197 | exports.FileInfo = FileInfo; 198 | exports.Match = Match; 199 | exports.Join = Join; 200 | exports.Sdp = Sdp; 201 | exports.SwarmHealth = SwarmHealth; 202 | exports.SwarmError = SwarmError; 203 | }) 204 | (typeof exports === 'undefined' ? peer5.core.protocol : exports); -------------------------------------------------------------------------------- /core/stats/StatsCalculator.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var CONST = 'const'; // placeHolder to add static class constants 4 | //const UPLOAD_CAP = 100000; //100KB/s 5 | 6 | /* 7 | * here we are extending Object , because we dont really want any special inheritance 8 | * if we want to inherit from class A we should write A.subClass 9 | * */ 10 | peer5.core.stats.StatsCalculator = Object.subClass({ 11 | 12 | //just so we'll know what is the name of the class we are (nameSpace) in if we need 13 | name:'peer5.core.stats.StatsCalculator', 14 | 15 | /* 16 | * ctor will be called when instantiating a class with for example here new peer5.client.MediaElementWrapper(x,y,z); 17 | * to call super function we do this._super(); 18 | * 19 | * */ 20 | ctor:function (fileSize, fileName, url) { 21 | 22 | this.Url = url; 23 | this.name = fileName; 24 | this.size = fileSize //in bytes 25 | this.transferFinished = false; 26 | this.Report_Created_Timestamp = Date.now(); 27 | this.Report_Timestamp = 0; 28 | this.Stats_Timestamp = Date.now(); 29 | this.Total_Recv = 0; 30 | this.Total_Sent = 0; 31 | this.Total_Sent_P2P = 0; 32 | this.Total_Recv_P2P = 0; 33 | this.Total_Recv_HTTP = 0; 34 | this.Total_Waste_P2P = 0; 35 | this.Total_Waste_HTTP = 0; 36 | this.Total_loaded_FS = 0; 37 | this.numOfSenders = 0; 38 | this.Total_Avg_Download = 0; 39 | 40 | //the totals from last report 41 | this.Prev_Report_Timestamp = Date.now(); 42 | this.Prev_Total_Recv = 0; 43 | this.Prev_Total_Sent = 0; 44 | this.Prev_Total_Sent_P2P = 0; 45 | this.Prev_Total_Recv_P2P = 0; 46 | this.Prev_Total_Waste_P2P = 0; 47 | this.Prev_Total_Waste_HTTP = 0; 48 | this.Prev_Total_Recv_HTTP = 0; 49 | this.Prev_Total_loaded_FS = 0; 50 | this.Avg_Sent = 0; 51 | this.Avg_Recv = 0; 52 | this.Avg_Sent_P2P = 0; 53 | this.Avg_Recv_P2P = 0; 54 | this.Avg_Loaded_FS = 0; 55 | this.Avg_Waste_P2P = 0; 56 | this.Avg_Waste_HTTP = 0; 57 | this.Avg_Recv_HTTP = 0; 58 | this.Size_Uploaded_To_Memory = 0; 59 | this.Total_Avg_Download = 0; 60 | this.numOfHttpCompletedChunks = 0; 61 | this.numOfHttpWasteChunks = 0; 62 | this.numOfP2PCompletedChunks = 0; 63 | this.numOfP2PWasteChunks = 0; 64 | this.statsTimestamp = 0; //starts at 0 the handle the special case of first download 65 | this.startTime = Date.now(); 66 | 67 | this.registerEvents(); 68 | //first to init the values. 69 | this.calc_avg(); 70 | 71 | 72 | }, 73 | 74 | addP2PRecv:function () { 75 | this.Total_Recv_P2P += peer5.config.CHUNK_SIZE; 76 | }, 77 | addP2PWaste:function () { 78 | this.numOfP2PWasteChunks++; 79 | this.Total_Waste_P2P += peer5.config.CHUNK_SIZE; 80 | }, 81 | addP2PSent:function () { 82 | this.Total_Sent_P2P += peer5.config.CHUNK_SIZE; 83 | }, 84 | addHTTP:function () { 85 | this.Total_Recv_HTTP += peer5.config.CHUNK_SIZE; 86 | }, 87 | 88 | calc_avg:function (forceCalc) { 89 | //delta: the time passed from last report 90 | var delta = Date.now() - this.statsTimestamp; //the time delta 91 | 92 | //we have to ignore the first calc because statsTimestamp is init to 0 and this will harm the calc 93 | if (this.statsTimestamp == 0) { 94 | //first update to set with initial values (0,NAN, infinity years left 95 | radio('peer5_state_updated').broadcast(this); 96 | this.statsTimestamp += delta; 97 | return; 98 | } 99 | 100 | //currently we forceCalc only when a calc_avg is called from sendReport 101 | if (delta < 1000 && !forceCalc) return; 102 | 103 | this.statsTimestamp += delta; 104 | 105 | //we want the calculations to be in bytes - therefore we will change delta to be in seconds 106 | delta /= 1000; 107 | 108 | this.Total_Recv = this.Total_Recv_HTTP + this.Total_Recv_P2P + this.Total_loaded_FS; 109 | this.Total_Sent = this.Total_Sent_P2P; //+this.Total_Sent_HTTP; 110 | 111 | this.Avg_Recv = (this.Total_Recv - this.Prev_Total_Recv) / delta 112 | this.Avg_Sent = (this.Total_Sent - this.Prev_Total_Sent) / delta 113 | this.Avg_Loaded_FS = (this.Total_loaded_FS - this.Prev_Total_loaded_FS) / delta; 114 | this.Avg_Sent_P2P = (this.Total_Sent_P2P - this.Prev_Total_Sent_P2P) / delta; 115 | this.Avg_Recv_P2P = (this.Total_Recv_P2P - this.Prev_Total_Recv_P2P) / delta; 116 | this.Avg_Waste_P2P = (this.Total_Waste_P2P - this.Prev_Total_Waste_P2P) / delta; 117 | this.Avg_Waste_HTTP = (this.Total_Waste_HTTP - this.Prev_Total_Waste_HTTP) / delta; 118 | this.Avg_Recv_HTTP = (this.Total_Recv_HTTP - this.Prev_Total_Recv_HTTP) / delta; 119 | this.Avg_Recv_WS = (this.Total_Recv_WS - this.Prev_Total_Recv_WS) / delta; 120 | this.Avg_Sent_WS = (this.Total_Sent_WS - this.Prev_Total_Sent_WS) / delta; 121 | this.Offloading = this.Total_Recv_P2P / (this.Total_Recv_HTTP + this.Total_Recv_P2P) 122 | 123 | this.Prev_Total_Recv = this.Total_Recv; 124 | this.Prev_Total_Sent = this.Total_Sent; 125 | this.Prev_Total_Sent_P2P = this.Total_Sent_P2P; 126 | this.Prev_Total_Recv_P2P = this.Total_Recv_P2P; 127 | this.Prev_Total_Waste_P2P = this.Total_Waste_P2P; 128 | this.Prev_Total_Waste_HTTP = this.Total_Waste_HTTP; 129 | this.Prev_Total_Recv_HTTP = this.Total_Recv_HTTP; 130 | this.Prev_Total_loaded_FS = this.Total_loaded_FS; 131 | 132 | radio('peer5_state_updated').broadcast(this); 133 | }, 134 | 135 | 136 | registerEvents:function () { 137 | var thi$ = this; 138 | 139 | radio('peer5_received_fs_chunk').subscribe([function(chunkId,swarmId){ 140 | this.Total_loaded_FS += peer5.config.CHUNK_SIZE; 141 | },this]); 142 | 143 | radio('peer5_received_http_chunk').subscribe(function (chunkId, swarmId) { 144 | //thi$.Total_Recv_HTTP += peer5.config.CHUNK_SIZE; 145 | //thi$.calc_avg(false); 146 | }); 147 | 148 | radio('peer5_pending_http_chunk').subscribe(function (chunkId, swarmId) { 149 | 150 | }); 151 | 152 | radio('peer5_waste_http_chunk').subscribe(function (chunkId, swarmId, originId) { 153 | thi$.Total_Waste_P2P += peer5.config.CHUNK_SIZE; 154 | }); 155 | 156 | radio('peer5_new_p2p_chunk').subscribe(function (chunkId, swarmId, originId) { 157 | thi$.Total_Recv_P2P += peer5.config.CHUNK_SIZE; 158 | //thi$.calc_avg(false); 159 | }); 160 | 161 | radio('peer5_waste_p2p_chunk').subscribe(function (chunkId, swarmId, originId) { 162 | thi$.numOfP2PWasteChunks++; 163 | thi$.Total_Waste_P2P += peer5.config.CHUNK_SIZE; 164 | }); 165 | 166 | radio('peer5_p2p_pending_chunk').subscribe(function (chunkId, swarmId, originId) { 167 | 168 | }); 169 | 170 | radio('chunkAddedToBlockMap').subscribe(function () { 171 | thi$.Size_Uploaded_To_Memory += peer5.config.CHUNK_SIZE; 172 | }); 173 | 174 | 175 | radio('chunkSentEvent').subscribe([function (blockMap) { 176 | thi$.Total_Sent_P2P += peer5.config.CHUNK_SIZE; 177 | }]); 178 | 179 | radio('xhrBytesReceived').subscribe([function (size) { 180 | thi$.Total_Recv_HTTP += size; 181 | }]); 182 | 183 | radio('transferFinishedEvent').subscribe([function (blockMap) { 184 | thi$.transferFinished = true; 185 | thi$.Total_Avg_Download = thi$.size / ((Date.now() - thi$.startTime)/1000); 186 | //thi$.calc_avg(true); 187 | }]); 188 | 189 | } 190 | }) 191 | })(); 192 | 193 | 194 | -------------------------------------------------------------------------------- /core/test/data/FS.io.test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | describe("FSio tests", function () { 3 | 4 | it('some init stuff', function() { 5 | peer5.setLogLevel(5); 6 | }); 7 | 8 | it('create and remove a resource', function () { 9 | var resourceId = "test.txt"; 10 | var createFlag = null; 11 | var removeFlag = null; 12 | peer5.core.data.FSio.createResource(resourceId,function(succ){createFlag = succ}); 13 | waitsFor(function(){return createFlag!=null},'FS.io.createResource didnt finish on time',1000); 14 | runs(function(){ 15 | console.log("createResource finished and was successful - " + createFlag); 16 | expect(createFlag).toEqual(true); 17 | peer5.core.data.FSio.removeResource(resourceId,function(succ){removeFlag = succ}); 18 | }) 19 | waitsFor(function(){return removeFlag!=null},'FS.io.removeResource didnt finish on time',1000); 20 | runs(function(){ 21 | console.log("removeResource finished and was successful - " + removeFlag); 22 | expect(removeFlag).toEqual(true); 23 | }); 24 | }); 25 | 26 | it('write 1 chunk of data', function () { 27 | var resourceId = "test.txt"; 28 | var createFlag = null; 29 | var removeFlag = null; 30 | var writeFlag = null; 31 | peer5.core.data.FSio.createResource(resourceId,function(succ){createFlag = succ}); 32 | waitsFor(function(){return createFlag!=null},'FSio.createResource didnt finish on time',1000); 33 | runs(function(){ 34 | // console.log("createResource finished and was successful - " + createFlag); 35 | expect(createFlag).toEqual(true); 36 | peer5.core.data.FSio.write(resourceId,new Blob(["lorem ipsum"]),0,function(succ){writeFlag = succ}) 37 | }) 38 | waitsFor(function(){return writeFlag!=null},'FSio.write didnt finish on time',1000); 39 | 40 | runs(function(){ 41 | console.log("write finished and was successful - " + writeFlag); 42 | expect(writeFlag).toEqual(true); 43 | peer5.core.data.FSio.removeResource(resourceId,function(succ){removeFlag = succ}); 44 | }) 45 | waitsFor(function(){return removeFlag!=null},'FSio.removeResource didnt finish on time',1000); 46 | 47 | runs(function(){ 48 | // console.log("removeResource finished and was successful - " + createFlag); 49 | expect(removeFlag).toEqual(true); 50 | }); 51 | }); 52 | 53 | it('write several chunks of data', function () { 54 | var resourceId = "test.txt"; 55 | var createFlag = null; 56 | var removeFlag = null; 57 | var writeFlag = 0; 58 | var numOfChunks = 10; 59 | var data = new Blob([new Uint8Array(512000)]); 60 | peer5.core.data.FSio.createResource(resourceId,function(succ){createFlag = succ}); 61 | waitsFor(function(){return createFlag!=null},'FSio.createResource didnt finish on time',1000); 62 | runs(function(){ 63 | // console.log("createResource finished and was successful - " + createFlag); 64 | expect(createFlag).toEqual(true); 65 | for(var i=0;i 0) { 25 | var peers = swarm.getRandomK(this.MAX_PEERS_MATCH); 26 | //create Match message 27 | //TODO: loop thru peers and send one by one 28 | if (peers && peers.length > 0) { 29 | sender.send(peerId, new protocol.Match(swarm.id, peers)); 30 | } 31 | } 32 | swarm.addPeer(peerId); 33 | } else { 34 | sender.send(peerId, new protocol.FileInfo()); 35 | } 36 | }, 37 | 38 | leave:function (peerId, sender) { 39 | swarms.removePeer(peerId); 40 | }, 41 | 42 | validateToken:function (token, domain) { 43 | return true; 44 | }, 45 | 46 | report:function(peerId,swarmId,sender){ 47 | var swarm = swarms.getSwarm(swarmId); 48 | if (swarm) { 49 | //match only when swarm has peers in it already 50 | if (swarm.count > 0) { 51 | var peers = swarm.getRandomK(this.MAX_PEERS_MATCH); 52 | for(var i=peers.length;i>=0;--i){ 53 | if(peers[i]==peerId) 54 | peers.splice(i,1); 55 | } 56 | //create Match message 57 | if (peers && peers.length > 0 && peers[0]!=peerId) { 58 | sender.send(peerId, new protocol.Match(swarm.id, peers, null, null, null)); 59 | } 60 | } 61 | swarm.addPeer(peerId); 62 | } 63 | } 64 | 65 | }); 66 | 67 | var instance = new exports.SimpleTracker(); 68 | exports.instance = instance; 69 | -------------------------------------------------------------------------------- /core/tracker/Swarm.js: -------------------------------------------------------------------------------- 1 | var util = require('./util.js'); 2 | // an in memory placeholder for the rooms 3 | var swarms = {}; 4 | 5 | function Swarm(id, metadata) { 6 | this.id = id; 7 | this.count = 0; 8 | this.peers = {}; 9 | this.metadata = metadata; 10 | } 11 | 12 | Swarm.prototype = { 13 | getCount:function() { 14 | return this.count; 15 | }, 16 | 17 | getMetadata:function () { 18 | return this.metadata; 19 | }, 20 | 21 | getRandomK:function (k) { 22 | return util.getRandomK(Object.keys(this.peers), k); 23 | }, 24 | 25 | addPeer:function (id) { 26 | if (id) { 27 | this.count++; 28 | this.peers[id] = "placeholder"; 29 | } 30 | }, 31 | 32 | removePeer:function (id) { 33 | delete this.peers[id]; 34 | this.count--; 35 | } 36 | }; 37 | 38 | exports.getSwarm = function getSwarm(id) { 39 | return swarms [id]; 40 | } 41 | 42 | exports.addSwarm = function (id, metadata) { 43 | return swarms[id] = new Swarm(id, metadata); 44 | } 45 | 46 | exports.removePeer = function(id) { 47 | for (var swarmId in swarms) { 48 | var swarm = swarms[swarmId]; 49 | swarm.removePeer(id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/tracker/util.js: -------------------------------------------------------------------------------- 1 | 2 | exports.shuffle = function (array) { 3 | var tmp, current, top = array.length; 4 | 5 | if (top) while (--top) { 6 | current = Math.floor(Math.random() * (top + 1)); 7 | tmp = array[current]; 8 | array[current] = array[top]; 9 | array[top] = tmp; 10 | } 11 | 12 | return array; 13 | } 14 | 15 | exports.getRandomK = function (array, k) { 16 | var shuffled = exports.shuffle(array); 17 | return shuffled.slice(0, k); 18 | }; 19 | -------------------------------------------------------------------------------- /core/transport/AbstractPeerConnection.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | peer5.core.transport.AbstractPeerConnection = Object.subClass({ 3 | name:'peer5.core.transport.AbstractPeerConnection', 4 | 5 | ctor:function (originId, targetId, initiator) { 6 | /** @private properties */ 7 | this.peerConnection; 8 | this.dataChannel; 9 | this.label = initiator ? (originId + targetId) : (targetId + originId); 10 | this.originId = originId; 11 | this.targetId = targetId; 12 | this.createAnswerConstraints = {}; 13 | this.createOfferConstraints = {}; 14 | this.startTime; 15 | /** @public properties*/ 16 | this.ready = false; 17 | this.requestDropped = 0; 18 | this.numOfRequestedChunks = 0; 19 | this.numOfExpiredChunks = 0; 20 | this.numOfPendingChunks = 0; 21 | this.maxNumOfPendingChunks = peer5.config.MAX_PENDING_CHUNKS / 2; 22 | this.minLatencyForChunk = null; 23 | this.maxLatencyForChunk = null; 24 | this.avgLatencyForChunk = null; 25 | this.connectingDuration = null; 26 | this.numOfChunksArrived = 0; 27 | this.failure = false; 28 | }, 29 | 30 | /** @public methods*/ 31 | setupCall:function () { 32 | throw 'unimplemented method'; 33 | }, 34 | handleMessage:function (message){ 35 | throw 'unimplemented method'; 36 | }, 37 | send:function (binaryMessage){ 38 | throw 'unimplemented method'; 39 | } 40 | }) 41 | })(); 42 | 43 | -------------------------------------------------------------------------------- /core/transport/WebSocketServer.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); 2 | var binary = require('../protocol/BinaryProtocol.js'); 3 | var protocol = require('../protocol/ProtocolMessages.js'); 4 | var sessionManager = require('../util/session-manager.js'); 5 | 6 | module.exports.instance = new function () { 7 | this.start = function (tracker, app, port, clientTimeout) { 8 | var thi$ = this; 9 | sessionManager.monitor(clientTimeout); 10 | var WsServer = require('ws').Server; 11 | var options = (port) ? {port:port} : {server:app}; // if port is specified server is not used 12 | peer5.log('Starting WebSocketServer'); 13 | var expireFunction = function (expiredId) { 14 | peer5.log('expired ' + expiredId); 15 | var socket = thi$.sockets[expiredId]; 16 | if (socket) socket.close(); 17 | }; 18 | var wss = new WsServer(options); 19 | wss.on('connection', function (socket) { 20 | var queryData = url.parse(socket.upgradeReq.url, true).query; 21 | var ip = socket._socket.remoteAddress; 22 | // ip = '37.26.146.175'; //uncomment to test 23 | var domain = url.parse(socket.upgradeReq.headers.origin).hostname; 24 | var host = socket.upgradeReq.headers.host; 25 | if (!domain) { 26 | peer5.error("couldn't grab a domain from the websocket"); 27 | return; 28 | } 29 | 30 | socket.id = queryData.id; 31 | socket.token = queryData.token; 32 | 33 | peer5.log('connection established, socket: ' + socket + ' from ' + ip + ' in domain ' + domain + ' with host ' + host + ' and got id ' + socket.id); 34 | 35 | socket.on('message', function (message) { 36 | var browserName = (socket.upgradeReq.headers['user-agent'] || 'Unknown_Browser'); 37 | if (browserName.indexOf('Firefox') > -1) { 38 | browserName = protocol.SWARM_ONLY_FIREFOX; // hack to ease send of errors later 39 | } 40 | else if (browserName.indexOf('Chrome') > -1) { 41 | browserName = protocol.SWARM_ONLY_CHROME; // hack to ease send of errors later 42 | } 43 | sessionManager.notify(socket.id, expireFunction); 44 | if (typeof message != 'string') { 45 | var decoded = binary.decode(message); 46 | if (!decoded) { peer5.warn('decoded message was empty');} 47 | for (var i = 0; i < decoded.length; ++i) { 48 | var entry = decoded[i]; 49 | switch (entry.tag) { 50 | case protocol.JOIN: 51 | tracker.join(socket.id, entry.swarmId, browserName, thi$, socket.ip, socket.token); 52 | break; 53 | case protocol.FILE_INFO: 54 | entry.originBrowser = browserName; 55 | tracker.createSwarm(socket.id, entry, thi$); 56 | break; 57 | case protocol.REPORT: 58 | entry.originBrowser = browserName; 59 | tracker.report(socket.id, entry, thi$, socket.ip, socket.token); 60 | break; 61 | case protocol.SDP: 62 | thi$.send(entry.destId,entry); 63 | break; 64 | 65 | case protocol.COMPLETED_DOWNLOAD: 66 | tracker.onDownloadCompleted(entry.swarmId); 67 | break; 68 | } 69 | } 70 | } else { 71 | peer5.warn("received a string message: string/JSON messages aren't supported"); 72 | } 73 | }); 74 | socket.on('close', function () { 75 | thi$.onclose(socket.id); 76 | }); 77 | 78 | //Mechanism for removal of sockets in case that a peer reconnects with the same id, 79 | //and a new instance with that same id is created before the old one is deleted. 80 | var oldSocket = thi$.sockets[socket.id]; 81 | if (oldSocket) { 82 | peer5.warn('Already got socket id ' + socket.id + ' closing the old one'); 83 | oldSocket.id = "todelete"; //prevent removal 84 | oldSocket.close(); 85 | } 86 | thi$.sockets[socket.id] = socket; 87 | }); 88 | this.sockets = {}; 89 | this.sockets.find = function (id) { 90 | return thi$.sockets[id]; 91 | } 92 | this.onclose = function (socketId) { 93 | if (socketId in this.sockets) { //safe to call close twice 94 | peer5.log('peer with Id ' + socketId + " was closed"); 95 | tracker.leave(socketId, this); 96 | delete this.sockets[socketId]; 97 | } 98 | }; 99 | this.errorHandler = function (error) { 100 | if (error) { 101 | peer5.error('server socket error' + JSON.stringify(error)); 102 | } 103 | }; 104 | this.send = function (socketId, commandObject) { 105 | var s = this.sockets.find(socketId); 106 | if (s){ 107 | if(s.readyState == 1) { 108 | s.send(binary.encode([commandObject]), {binary:true, mask:false},this.errorHandler); 109 | }else{ 110 | peer5.warn("socketId: " + socketId + " isn't initialized, readyState=" + s.readyState); 111 | } 112 | } 113 | }; 114 | }; 115 | }; 116 | -------------------------------------------------------------------------------- /core/transport/WebSocketServerNoop.js: -------------------------------------------------------------------------------- 1 | module.exports.sender = new function() { 2 | this.send = function(peerID, commandObject) { 3 | } 4 | 5 | this.receive = function(peerID, key, commandObject) { 6 | } 7 | 8 | this.sendFileInfo = function (peerID, fileInfo) { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/transport/WsConnection.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | peer5.core.transport.WsConnection = Object.subClass({ 3 | name:'peer5.core.transport.WsConnection', 4 | ctor:function (wsSeverUrl, clientId) { 5 | this.state = { 6 | connectionOpenTime:0 7 | }; 8 | this.clientId = clientId; 9 | this.wsSeverUrl = wsSeverUrl; 10 | this.lastPongTimeStamp; // indicating when was the last server interaction. 11 | this.socketInitating = true; 12 | this.socket; 13 | this.initiateWebSocket(this.wsSeverUrl, this.clientId); 14 | }, 15 | 16 | initiateWebSocket:function (wsSeverUrl, clientId) { 17 | var thi$ = this; 18 | this.start = Date.now(); 19 | //TODO: check if id and token are needed 20 | this.socket = new WebSocket(wsSeverUrl + '?id=' + clientId + '&token=' + peer5.config.TOKEN); 21 | peer5.log('trying to connect to a new websocket'); 22 | this.socket.sessionid = clientId; 23 | this.socket.binaryType = "arraybuffer"; 24 | 25 | this.socket.onclose = function (e) { 26 | ga('send', 'event', 'errors', 'websocketClosed', ''); 27 | peer5.warn('WebSocket closed with error'); 28 | peer5.warn(e); 29 | thi$.socketInitating = true; 30 | thi$.socket = null; 31 | 32 | peer5.info('Peer ' + thi$.sessionid + " is trying to reconnect"); 33 | setTimeout(function () { 34 | thi$.initiateWebSocket(thi$.wsSeverUrl, thi$.clientId); 35 | }, peer5.config.SOCKET_RECONNECTION_INTERVAL); 36 | }; 37 | 38 | this.socket.onerror = function (error) { 39 | peer5.error('peer with Id ' + thi$.sessionid + " had socket error : "); 40 | peer5.error(error); 41 | }; 42 | 43 | this.socket.onopen = function () { 44 | //clearTimeout(timeout); 45 | var now = Date.now(); 46 | thi$.state.connectionOpenTime = now - thi$.start; 47 | thi$.socketInitating = false; 48 | thi$.lastPongTimeStamp = now; 49 | peer5.log("websocket took: " + thi$.state.connectionOpenTime + " to open"); 50 | thi$.socketInit = true; 51 | thi$.registerServerNotifications(); 52 | radio('webSocketInit').broadcast(); 53 | }; 54 | 55 | radio('websocketsSendData').subscribe([function(message){ 56 | this.sendData(message); 57 | },this]); 58 | }, 59 | 60 | registerServerNotifications:function () { 61 | var thi$ = this; 62 | 63 | this.socket.onmessage = function (message) { 64 | thi$.lastPongTimeStamp = Date.now(); 65 | if (typeof message.data != 'string') { 66 | var unpackedDataArray = peer5.core.protocol.BinaryProtocol.decode(new Uint8Array(message.data)); 67 | for (var i = 0; i < unpackedDataArray.length; ++i) { 68 | switch (unpackedDataArray[i].tag) { 69 | case peer5.core.protocol.MATCH: 70 | radio('receivedMatchEvent').broadcast(unpackedDataArray[i]); 71 | break; 72 | case peer5.core.protocol.FILE_INFO: 73 | radio('receivedFileInfo').broadcast(unpackedDataArray[i]); 74 | break; 75 | case peer5.core.protocol.SDP: 76 | radio('receivedSDP').broadcast(unpackedDataArray[i]); 77 | break; 78 | case peer5.core.protocol.SWARM_HEALTH: 79 | radio('swarmHealth').broadcast(unpackedDataArray[i]); 80 | break; 81 | case peer5.core.protocol.SWARM_ERROR: 82 | radio('swarmError').broadcast(unpackedDataArray[i]); 83 | break; 84 | } 85 | } 86 | } else { 87 | peer5.warn("received a string message from server - not supported"); 88 | } 89 | }; 90 | }, 91 | 92 | socketReadyToSend:function () { 93 | return (this.socket && this.socketInit && this.socket.readyState == this.socket.OPEN); 94 | }, 95 | 96 | sendMessage:function(protocolMessage) { 97 | var packedData = peer5.core.protocol.BinaryProtocol.encode([protocolMessage]); 98 | this.sendData(packedData); 99 | }, 100 | 101 | sendData:function (packedData) { 102 | if (this.socketReadyToSend()) { 103 | peer5.log('sending data on websockets, at time: ' + (Date.now())); 104 | this.socket.send(packedData); 105 | } else { 106 | peer5.warn('cant send data - socket is not defined'); 107 | } 108 | } 109 | }) 110 | })(); -------------------------------------------------------------------------------- /core/util/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Reference - Daniel Guerrero function 3 | * We use it until Binary DataChannels will be out 4 | */ 5 | (function (exports) { 6 | //NOTE! we use '-' and not '+' 7 | //I did it so we can put '-' delimited strings 8 | var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/='; 9 | 10 | var Base64Binary = { 11 | decode:function (input) { 12 | //get last chars to see if are valid 13 | var padding1 = encodings.indexOf(input.charAt(input.length - 1)); 14 | var padding2 = encodings.indexOf(input.charAt(input.length - 2)); 15 | 16 | var bytes = (input.length / 4) * 3; 17 | if (padding1 == 64) bytes--; //padding chars, so skip 18 | if (padding2 == 64) bytes--; //padding chars, so skip 19 | 20 | var chr1, chr2, chr3; 21 | var enc1, enc2, enc3, enc4; 22 | var i = 0; 23 | var j = 0; 24 | 25 | var arr = new Uint8Array(bytes); 26 | // input = input.replace(/[^A-Za-z0-9\-\/\=]/g, ""); 27 | 28 | for (i = 0; i < bytes; i += 3) { 29 | //get the 3 octets in 4 ascii chars 30 | enc1 = encodings.indexOf(input.charAt(j++)); 31 | enc2 = encodings.indexOf(input.charAt(j++)); 32 | enc3 = encodings.indexOf(input.charAt(j++)); 33 | enc4 = encodings.indexOf(input.charAt(j++)); 34 | 35 | chr1 = (enc1 << 2) | (enc2 >> 4); 36 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 37 | chr3 = ((enc3 & 3) << 6) | enc4; 38 | 39 | arr[i] = chr1; 40 | if (enc3 != 64) arr[i + 1] = chr2; 41 | if (enc4 != 64) arr[i + 2] = chr3; 42 | } 43 | 44 | return arr; 45 | }, 46 | 47 | 48 | // Converts an ArrayBuffer directly to base64, without any intermediate 'convert to string then 49 | // use window.btoa' step. According to my tests, this appears to be a faster approach: 50 | // http://jsperf.com/encoding-xhr-image-data/5 51 | encode:function (arrayBuffer) { 52 | var base64 = ''; 53 | 54 | var bytes = new Uint8Array(arrayBuffer); 55 | var byteLength = bytes.byteLength; 56 | var byteRemainder = byteLength % 3; 57 | var mainLength = byteLength - byteRemainder; 58 | 59 | var a, b, c, d; 60 | var chunk; 61 | 62 | // Main loop deals with bytes in chunks of 3 63 | for (var i = 0; i < mainLength; i = i + 3) { 64 | // Combine the three bytes into a single integer 65 | chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; 66 | 67 | // Use bitmasks to extract 6-bit segments from the triplet 68 | a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 69 | b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 70 | c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 71 | d = chunk & 63; // 63 = 2^6 - 1 72 | 73 | // Convert the raw binary segments to the appropriate ASCII encoding 74 | base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] 75 | } 76 | 77 | // Deal with the remaining bytes and padding 78 | if (byteRemainder == 1) { 79 | chunk = bytes[mainLength]; 80 | 81 | a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 82 | 83 | // Set the 4 least significant bits to zero 84 | b = (chunk & 3) << 4; // 3 = 2^2 - 1 85 | 86 | base64 += encodings[a] + encodings[b] + '=='; 87 | } else if (byteRemainder == 2) { 88 | chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]; 89 | 90 | a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 91 | b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 92 | 93 | // Set the 2 least significant bits to zero 94 | c = (chunk & 15) << 2; // 15 = 2^4 - 1 95 | 96 | base64 += encodings[a] + encodings[b] + encodings[c] + '=' 97 | } 98 | 99 | return base64 100 | } 101 | }; 102 | 103 | exports.base64 = Base64Binary; 104 | })(typeof exports === 'undefined' ? peer5.core.util : exports); 105 | 106 | -------------------------------------------------------------------------------- /core/util/buildify-peer5.js: -------------------------------------------------------------------------------- 1 | //TODO: upload back to github and use with npm 2 | var fs = require('fs'), 3 | path = require('path'), 4 | _ = require('underscore'), 5 | uglifyJS = require('uglify-js'); 6 | 7 | 8 | /** 9 | * @param {String} [dir] Starting directory absolute path. Default: current working dir 10 | * @param {Object} [options] 11 | * @param {Object} [options.interpolate] Underscore template settings. Default to mustache {{var}} style interpolation tags. 12 | * @param {String} [options.encoding] File encoding ('utf-8') 13 | * @param {String} [options.eol] End of line character ('\n') 14 | * @param {Boolean} [options.quiet] Whether to silence console output 15 | */ 16 | function Builder(dir, options) { 17 | dir = dir || process.cwd(); 18 | 19 | this.options = _.extend({ 20 | encoding: 'utf-8', 21 | eol: '\n', 22 | interpolate: /\{\{(.+?)\}\}/g 23 | }, options); 24 | 25 | _.templateSettings.interpolate = this.options.interpolate; 26 | 27 | //The current directory 28 | this.setDir(dir); 29 | 30 | //The content being acted on 31 | this.content = ''; 32 | }; 33 | 34 | /** 35 | * Set the current working directory 36 | * 37 | * @param {String} absolutePath Absolute directory path 38 | */ 39 | Builder.prototype.setDir = function(absolutePath) { 40 | this.dir = path.normalize(absolutePath); 41 | 42 | return this; 43 | }; 44 | 45 | /** 46 | * Change the directory, relateive to the current working directory 47 | * 48 | * @param {String} relativePath Directory path, relative to the current directory 49 | */ 50 | Builder.prototype.changeDir = function(relativePath) { 51 | this.setDir(this.dir + '/' + relativePath); 52 | 53 | return this; 54 | }; 55 | 56 | /** 57 | * Set the content to work with 58 | * 59 | * @param {String} content 60 | */ 61 | Builder.prototype.setContent = function(content) { 62 | this.content = content; 63 | 64 | return this; 65 | }; 66 | 67 | /** 68 | * Returns the content. Note: this method breaks the chain 69 | * 70 | * @return {String} 71 | */ 72 | Builder.prototype.getContent = function() { 73 | return this.content; 74 | } 75 | 76 | /** 77 | * Load file contents 78 | * 79 | * @param {String} file File path relative to current directory 80 | */ 81 | Builder.prototype.load = function(file) { 82 | file = path.normalize(this.dir + '/' + file); 83 | 84 | this.content = fs.readFileSync(file, this.options.encoding); 85 | 86 | return this; 87 | }; 88 | 89 | /** 90 | * Concatenate file contents 91 | * 92 | * @param {String|String[]} files File path(s) relative to current directory 93 | * @param {String} [eol] Join character. Default: '\n' 94 | */ 95 | Builder.prototype.concat = function(files, eol) { 96 | eol = (_.isUndefined(eol)) ? this.options.eol : eol; 97 | 98 | if (!_.isArray(files)) files = [files]; 99 | 100 | var dir = this.dir, 101 | encoding = this.options.encoding; 102 | 103 | var contents = files.map(function(file) { 104 | file = path.normalize(dir + '/' + file); 105 | 106 | return fs.readFileSync(file, encoding); 107 | }); 108 | 109 | if (this.content) contents.unshift(this.content); 110 | 111 | this.content = contents.join(eol); 112 | 113 | return this; 114 | }; 115 | 116 | /** 117 | * Wrap the contents in a template 118 | * 119 | * @param {String} templatePath Template file path, relative to current directory. Should have a {{body}} tag where content will go. 120 | * @param {Object} [templateData] Data to pass to template 121 | */ 122 | Builder.prototype.wrap = function(templatePath, templateData) { 123 | templatePath = path.normalize(this.dir + '/' + templatePath); 124 | templateData = templateData || {}; 125 | 126 | var data = _.extend(templateData, { 127 | body: this.content 128 | }); 129 | 130 | var templateStr = fs.readFileSync(templatePath, this.options.encoding); 131 | 132 | this.content = _.template(templateStr, data); 133 | 134 | return this; 135 | }; 136 | 137 | /** 138 | * Perform a function to manipulate or use the content, function must return the content 139 | * 140 | * @param {Function} fn Function that takes the current content and returns it after an operation 141 | */ 142 | Builder.prototype.perform = function(fn) { 143 | this.content = fn(this.content); 144 | 145 | return this; 146 | }; 147 | 148 | /** 149 | * Uglifies the content 150 | */ 151 | Builder.prototype.uglify = function() { 152 | this.content = uglifyJS.minify(this.content, {fromString: true}).code; 153 | return this; 154 | }; 155 | 156 | /** 157 | * Reset/clear the contents 158 | */ 159 | Builder.prototype.clear = function() { 160 | this.content = ''; 161 | 162 | return this; 163 | }; 164 | 165 | 166 | /** 167 | * Factory method which creates a new builder 168 | * 169 | * @param {Object} [options] Constructor options 170 | */ 171 | module.exports = function(dir, options) { 172 | return new Builder(dir, options); 173 | }; 174 | -------------------------------------------------------------------------------- /core/util/crypto.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.0.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2012 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | (function(exports) 8 | { 9 | 10 | 11 | var CryptoJS=CryptoJS||function(v,p){var d={},u=d.lib={},r=function(){},f=u.Base={extend:function(a){r.prototype=this;var b=new r;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 12 | s=u.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=p?b:4*a.length},toString:function(a){return(a||y).stringify(this)},concat:function(a){var b=this.words,c=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var n=0;n>>2]|=(c[n>>>2]>>>24-8*(n%4)&255)<<24-8*((j+n)%4);else if(65535>>2]=c[n>>>2];else b.push.apply(b,c);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<< 13 | 32-8*(b%4);a.length=v.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],c=0;c>>2]>>>24-8*(j%4)&255;c.push((n>>>4).toString(16));c.push((n&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],j=0;j>>3]|=parseInt(a.substr(j, 14 | 2),16)<<24-4*(j%8);return new s.init(c,b/2)}},e=x.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var c=[],j=0;j>>2]>>>24-8*(j%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],j=0;j>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new s.init(c,b)}},q=x.Utf8={stringify:function(a){try{return decodeURIComponent(escape(e.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return e.parse(unescape(encodeURIComponent(a)))}}, 15 | t=u.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new s.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=q.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,c=b.words,j=b.sigBytes,n=this.blockSize,e=j/(4*n),e=a?v.ceil(e):v.max((e|0)-this._minBufferSize,0);a=e*n;j=v.min(4*a,j);if(a){for(var f=0;ft;t++){s[e+5*q]=(t+1)*(t+2)/2%64;var w=(2*e+3*q)%5,e=q%5,q=w}for(e=0;5>e;e++)for(q=0;5>q;q++)x[e+5*q]=q+5*((2*e+3*q)%5);e=1;for(q=0;24>q;q++){for(var a=w=t=0;7>a;a++){if(e&1){var b=(1<b?w^=1<e;e++)c[e]=f.create();d=d.SHA3=r.extend({cfg:r.cfg.extend({outputLength:512}),_doReset:function(){for(var a=this._state= 20 | [],b=0;25>b;b++)a[b]=new f.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(a,b){for(var e=this._state,f=this.blockSize/2,h=0;h>>24)&16711935|(l<<24|l>>>8)&4278255360,m=(m<<8|m>>>24)&16711935|(m<<24|m>>>8)&4278255360,g=e[h];g.high^=m;g.low^=l}for(f=0;24>f;f++){for(h=0;5>h;h++){for(var d=l=0,k=0;5>k;k++)g=e[h+5*k],l^=g.high,d^=g.low;g=c[h];g.high=l;g.low=d}for(h=0;5>h;h++){g=c[(h+4)%5];l=c[(h+1)%5];m=l.high;k=l.low;l=g.high^ 21 | (m<<1|k>>>31);d=g.low^(k<<1|m>>>31);for(k=0;5>k;k++)g=e[h+5*k],g.high^=l,g.low^=d}for(m=1;25>m;m++)g=e[m],h=g.high,g=g.low,k=s[m],32>k?(l=h<>>32-k,d=g<>>32-k):(l=g<>>64-k,d=h<>>64-k),g=c[x[m]],g.high=l,g.low=d;g=c[0];h=e[0];g.high=h.high;g.low=h.low;for(h=0;5>h;h++)for(k=0;5>k;k++)m=h+5*k,g=e[m],l=c[m],m=c[(h+1)%5+5*k],d=c[(h+2)%5+5*k],g.high=l.high^~m.high&d.high,g.low=l.low^~m.low&d.low;g=e[0];h=y[f];g.high^=h.high;g.low^=h.low}},_doFinalize:function(){var a=this._data, 22 | b=a.words,c=8*a.sigBytes,e=32*this.blockSize;b[c>>>5]|=1<<24-c%32;b[(v.ceil((c+1)/e)*e>>>5)-1]|=128;a.sigBytes=4*b.length;this._process();for(var a=this._state,b=this.cfg.outputLength/8,c=b/8,e=[],h=0;h>>24)&16711935|(f<<24|f>>>8)&4278255360,d=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360;e.push(d);e.push(f)}return new u.init(e,b)},clone:function(){for(var a=r.clone.call(this),b=a._state=this._state.slice(0),c=0;25>c;c++)b[c]=b[c].clone();return a}}); 23 | p.SHA3=r._createHelper(d);p.HmacSHA3=r._createHmacHelper(d)})(Math); 24 | 25 | exports.CryptoJS = CryptoJS; 26 | }) (typeof exports === 'undefined' ? peer5 : exports); -------------------------------------------------------------------------------- /core/util/lang_ext.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // The following code (inheritance) is taken from the book - Secrets of the JavaScript Ninja (John Resig). 4 | var initializing = false; 5 | 6 | // Determine if functions can be serialized 7 | var fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/; 8 | 9 | // Create a new Class that inherits from this class 10 | Object.subClass = function(prop) { 11 | var _super = this.prototype; 12 | 13 | // Instantiate a base class (but only create the instance, don't run the init constructor) 14 | initializing = true; 15 | var proto = new this(); 16 | initializing = false; 17 | 18 | var name; 19 | // Copy the properties over onto the new prototype 20 | for (name in prop) { 21 | // Check if we're overwriting an existing function 22 | var isAnExistingFunction = 23 | typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]); 24 | 25 | proto[name] = isAnExistingFunction ? 26 | (function(name, fn) { 27 | return function() { 28 | var tmp = this._super; 29 | 30 | // Add a new ._super() method that is the same method but on the super-class. 31 | this._super = _super[name]; 32 | 33 | // The method only need to be bound temporarily, so we remove it when we're done executing. 34 | var ret = fn.apply(this, arguments); 35 | this._super = tmp; 36 | 37 | return ret; 38 | }; 39 | })(name, prop[name]) : 40 | prop[name]; 41 | } 42 | 43 | // The dummy class constructor 44 | function Class() { 45 | // All construction is actually done in the init method 46 | if (!initializing && this.ctor) this.ctor.apply(this, arguments); 47 | } 48 | 49 | // Handle static members 50 | for (name in this){ 51 | if (this.hasOwnProperty(name) && typeof(this[name]) != 'function') 52 | Class[name] = this[name]; 53 | } 54 | 55 | // Populate our constructed prototype object 56 | Class.prototype = proto; 57 | 58 | // Enforce the constructor to be what we expect 59 | Class.constructor = Class; 60 | 61 | // And make this class extensible 62 | Class.subClass = arguments.callee; 63 | 64 | // Add behavior ability 65 | Class.addBehavior = function (behaviorAbstractClass, behaviorOverrides) { 66 | behaviorOverrides = behaviorOverrides || {}; 67 | if (behaviorAbstractClass) { 68 | override(proto, behaviorOverrides, new behaviorAbstractClass()); 69 | } else { 70 | throw 'behaviorAbstractClass must be a vaild behavior class'; 71 | } 72 | }; 73 | 74 | return Class; 75 | }; 76 | })(); 77 | // NO JQUERY 78 | // 79 | //jQuery.extend({ 80 | // xmlToString: function(xmlObj) { 81 | // if (this.browser.msie) { 82 | // return xmlObj.xml; 83 | // } else { 84 | // return (new XMLSerializer()).serializeToString(xmlObj); 85 | // } 86 | // } 87 | //}); 88 | 89 | -------------------------------------------------------------------------------- /core/util/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple log wrapper 3 | * Currently with no log framework 4 | * 5 | * NOTE: uses (hack) global peer5 var, and no exports 6 | */ 7 | (function () { 8 | if (typeof peer5 === 'undefined') { 9 | peer5 = {}; 10 | } 11 | var flat = false; 12 | var logLevel = 2; // by default we allow warnings and error 13 | var friendly = ['0', 'ERROR', "WARNING", "info", "log", "debug"]; 14 | 15 | var internal_log = function (level, object, title) { 16 | if (level > logLevel) return; // our current log level is not verbose enough 17 | // just a plain object to hold the log message which prints out nice on the console 18 | function p5log(object, title) { 19 | this.title = title; 20 | this.severity = friendly[level]; 21 | var d = new Date(); 22 | this.time = d.toLocaleTimeString(); 23 | this.content = object; 24 | if (level < 3) { 25 | var stack = new Error().stack; 26 | this.stack = (stack)?new Error().stack.replace("Error\n", ""):''; 27 | } 28 | } 29 | 30 | p5log.prototype.toString = function () { 31 | var arr; 32 | if (this.content.stack) { //good for errors 33 | arr = [this.title, this.severity, this.time, this.content, this.content.stack]; 34 | } else { 35 | arr = [this.title, this.severity, this.time, JSON.stringify(this.content) , this.stack]; 36 | } 37 | return arr.join('\t'); 38 | }; 39 | 40 | object = (flat) ? new p5log(object).toString() : new p5log(object); 41 | // logFunction[level](object); 42 | switch (level) { 43 | case 1: 44 | console.error(object); 45 | break; 46 | case 2: 47 | console.warn(object); 48 | break; 49 | case 3: 50 | console.info(object); 51 | break; 52 | case 4: 53 | case 5: 54 | console.log(object); 55 | //console.debug(object.stack); 56 | //console.debug('--------------------------------------------------------------------------------------------------------'); 57 | break; 58 | } 59 | 60 | 61 | // if (window.console && window.console.log) { 62 | // console.log(); 63 | // } 64 | }; 65 | 66 | peer5.setLogFlat = function (b) { 67 | flat = b 68 | }; 69 | 70 | peer5.setLogLevel = function (newLevel) { 71 | logLevel = newLevel; 72 | }; 73 | 74 | peer5.isVerbose = function () { 75 | return (logLevel > 2); 76 | }; 77 | 78 | peer5.isDebug = function () { 79 | return (logLevel == 5); 80 | }; 81 | 82 | peer5.debug = function (object, title) { 83 | internal_log(5, object, title) 84 | }; 85 | peer5.log = function (object) { 86 | internal_log(4, object) 87 | }; 88 | peer5.info = function (object) { 89 | internal_log(3, object) 90 | }; 91 | peer5.warn = function (object) { 92 | internal_log(2, object) 93 | }; 94 | peer5.error = function (object) { 95 | internal_log(1, object) 96 | }; 97 | })(); -------------------------------------------------------------------------------- /core/util/namespaces.js: -------------------------------------------------------------------------------- 1 | var peer5 = { 2 | core:{ 3 | apiValidators:{}, 4 | dataStructures:{}, 5 | data:{}, 6 | controllers:{}, 7 | protocol:{}, 8 | transport:{}, 9 | util:{}, 10 | stats:{} 11 | } 12 | 13 | }; -------------------------------------------------------------------------------- /core/util/norequire.js: -------------------------------------------------------------------------------- 1 | // help compatibility with node.js require 2 | // let's do var x = require('./x.js') || peer5.z.y.x 3 | // without checking if require is defined 4 | function require() { return null; } -------------------------------------------------------------------------------- /core/util/radio.js: -------------------------------------------------------------------------------- 1 | /** 2 | Radio.js - Chainable, Dependency Free Publish/Subscribe for Javascript 3 | http://radio.uxder.com 4 | Author: Scott Murphy 2011 5 | twitter: @hellocreation, github: uxder 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation 9 | files (the "Software"), to deal in the Software without 10 | restriction, including without limitation the rights to use, 11 | copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | (function (name, global, definition) { 29 | if (typeof module !== 'undefined') module.exports = definition(name, global); 30 | else if (typeof define === 'function' && typeof define.amd === 'object') define(definition); 31 | else global[name] = definition(name, global); 32 | })('radio', this, function (name, global) { 33 | 34 | "use strict"; 35 | 36 | /** 37 | * Main Wrapper for radio.$ and create a function radio to accept the channelName 38 | * @param {String} channelName topic of event 39 | */ 40 | function radio(channelName) { 41 | arguments.length ? radio.$.channel(channelName) : radio.$.reset(); 42 | return radio.$; 43 | } 44 | 45 | radio.$ = { 46 | version: '0.2', 47 | channelName: "", 48 | channels: [], 49 | 50 | /** 51 | * Reset global state, by removing all channels 52 | * @example 53 | * radio() 54 | */ 55 | reset: function() { 56 | radio.$.channelName = ""; 57 | radio.$.channels = []; 58 | }, 59 | 60 | /** 61 | * Broadcast (publish) 62 | * Iterate through all listeners (callbacks) in current channel and pass arguments to subscribers 63 | * @param arguments data to be sent to listeners 64 | * @example 65 | * //basic usage 66 | * radio('channel1').broadcast('my message'); 67 | * //send an unlimited number of parameters 68 | * radio('channel2').broadcast(param1, param2, param3 ... ); 69 | */ 70 | broadcast: function() { 71 | var i, c = this.channels[this.channelName], 72 | l = c.length, 73 | subscriber, callback, context; 74 | //iterate through current channel and run each subscriber 75 | for (i = 0; i < l; i++) { 76 | subscriber = c[i]; 77 | //if subscriber was an array, set the callback and context. 78 | if ((typeof(subscriber) === 'object') && (subscriber.length)) { 79 | callback = subscriber[0]; 80 | //if user set the context, set it to the context otherwise, it is a globally scoped function 81 | context = subscriber[1] || global; 82 | } 83 | callback.apply(context, arguments); 84 | } 85 | return this; 86 | }, 87 | 88 | /** 89 | * Create the channel if it doesn't exist and set the current channel/event name 90 | * @param {String} name the name of the channel 91 | * @example 92 | * radio('channel1'); 93 | */ 94 | channel: function(name) { 95 | var c = this.channels; 96 | //create a new channel if it doesn't exists 97 | if (!c[name]) c[name] = []; 98 | this.channelName = name; 99 | return this; 100 | }, 101 | 102 | /** 103 | * Add Subscriber to channel 104 | * Take the arguments and add it to the this.channels array. 105 | * @param {Function|Array} arguments list of callbacks or arrays[callback, context] separated by commas 106 | * @example 107 | * //basic usage 108 | * var callback = function() {}; 109 | * radio('channel1').subscribe(callback); 110 | * 111 | * //subscribe an endless amount of callbacks 112 | * radio('channel1').subscribe(callback, callback2, callback3 ...); 113 | * 114 | * //adding callbacks with context 115 | * radio('channel1').subscribe([callback, context],[callback1, context], callback3); 116 | * 117 | * //subscribe by chaining 118 | * radio('channel1').subscribe(callback).radio('channel2').subscribe(callback).subscribe(callback2); 119 | */ 120 | subscribe: function() { 121 | var a = arguments, 122 | c = this.channels[this.channelName], 123 | i, l = a.length, 124 | p, ai = []; 125 | 126 | //run through each arguments and subscribe it to the channel 127 | for (i = 0; i < l; i++) { 128 | ai = a[i]; 129 | //if the user sent just a function, wrap the fucntion in an array [function] 130 | p = (typeof(ai) === "function") ? [ai] : ai; 131 | if ((typeof(p) === 'object') && (p.length)) c.push(p); 132 | } 133 | return this; 134 | }, 135 | 136 | /** 137 | * Remove subscriber from channel 138 | * Take arguments with functions and unsubscribe it if there is a match against existing subscribers. 139 | * @param {Function} arguments callbacks separated by commas 140 | * @example 141 | * //basic usage 142 | * radio('channel1').unsubscribe(callback); 143 | * //you can unsubscribe as many callbacks as you want 144 | * radio('channel1').unsubscribe(callback, callback2, callback3 ...); 145 | * //removing callbacks with context is the same 146 | * radio('channel1').subscribe([callback, context]).unsubscribe(callback); 147 | */ 148 | unsubscribe: function() { 149 | var a = arguments, 150 | i, j, c = this.channels[this.channelName], 151 | l = a.length, 152 | cl = c.length, 153 | offset = 0, 154 | jo; 155 | //loop through each argument 156 | for (i = 0; i < l; i++) { 157 | //need to reset vars that change as the channel array items are removed 158 | offset = 0; 159 | cl = c.length; 160 | //loop through the channel 161 | for (j = 0; j < cl; j++) { 162 | jo = j - offset; 163 | //if there is a match with the argument and the channel function, unsubscribe it from the channel array 164 | if (c[jo][0] === a[i]) { 165 | //unsubscribe matched item from the channel array 166 | c.splice(jo, 1); 167 | offset++; 168 | } 169 | } 170 | } 171 | return this; 172 | } 173 | }; 174 | 175 | return radio; 176 | }); -------------------------------------------------------------------------------- /core/util/session-manager.js: -------------------------------------------------------------------------------- 1 | var oldSessions = {}; 2 | var newSessions = {}; 3 | var started = false; 4 | exports.monitor = function (interval) { 5 | if (!started) { 6 | started = true; 7 | } else { 8 | peer5.warn("sessionManager.monitor already called"); 9 | } 10 | 11 | function expire() { 12 | peer5.debug('sessionManager is expiring ' + Object.keys(oldSessions).length + '/' + (Object.keys(oldSessions).length + Object.keys(newSessions).length)); 13 | Object.keys(oldSessions).forEach(function (k) { 14 | oldSessions[k](k); // spawn the function if session expired 15 | }); 16 | oldSessions = newSessions; 17 | newSessions = {}; 18 | } 19 | 20 | setInterval(expire, interval); 21 | } 22 | 23 | /** 24 | * notify the manager that a client is still connected 25 | * @param id 26 | */ 27 | exports.notify = function (id, expire_function) { 28 | peer5.debug(id + ' notified to sessionManager'); 29 | if (!(id in newSessions)) { // otherwise already safe 30 | delete oldSessions[id]; 31 | newSessions[id] = expire_function; // we can put timestamp here... 32 | } 33 | } -------------------------------------------------------------------------------- /core/util/uuid.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | exports.shortId = function () { 3 | return 'xxxxxxxx'.replace(/[xy]/g, function (c) { 4 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 5 | return v.toString(16); 6 | }); 7 | }; 8 | 9 | exports.generate_uuid = function () { 10 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 11 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 12 | return v.toString(16); 13 | }); 14 | }; 15 | })(typeof exports === 'undefined' ? peer5.core.util : exports); 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sharefest", 3 | "description": "", 4 | "version": "1.0.0", 5 | "author": "", 6 | "dependencies": { 7 | "express": ">=3.0", 8 | "underscore": "", 9 | "uglify-js": "=2.2.5", 10 | "ws": ">=0.4", 11 | "ua-parser":"=0.3.1" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "start": "node sharefest/server.js" 16 | }, 17 | "engines": { 18 | "node": "0.6.x" 19 | }, 20 | "subdomain": "sharefest" 21 | } 22 | -------------------------------------------------------------------------------- /serverConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockSize": 1024000, 3 | "clientTimeout": 20000, 4 | "logLevel": 4, 5 | "trackerPath":"../core/tracker/SimpleTracker.js" 6 | } 7 | -------------------------------------------------------------------------------- /sharefest/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Peer5 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /sharefest/clientConfig.js: -------------------------------------------------------------------------------- 1 | peer5.config = { 2 | LOG_LEVEL:2, 3 | MAX_PENDING_CHUNKS:200, //max number of chunks pending per peer 4 | MOZ_MAX_PENDING_CHUNKS:8, //max number of chunks pending per peer for mozilla 5 | CHUNK_SIZE:800, 6 | CHUNK_EXPIRATION_TIMEOUT:1500, 7 | REPORT_INTERVAL:10000, 8 | STAT_CALC_INTERVAL:1000, 9 | MONITOR_INTERVAL:1000, 10 | STUN_SERVERS:['stun.l.google.com:19302'], 11 | TURN_SERVERS:[], 12 | TURN_CREDENTIALS:[], 13 | P2P_PREFETCH_THRESHOLD:100, 14 | PC_FAIL_TIMEOUT:15000, 15 | PC_RESEND_INTERVAL:1000, 16 | SOCKET_RECONNECTION_INTERVAL:2000, 17 | 18 | ALLOWED_FILE_SIZE:250*1024*1024, //in bytes 250MB 19 | USE_FS:true, 20 | CACHE_SIZE:50000000, //in bytes 21 | FS_ROOT_DIR:'peer5/', 22 | FS_SIZE: 4294967296 //in bytes 23 | }; 24 | -------------------------------------------------------------------------------- /sharefest/public/beta.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Sharefest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 34 | 35 | 36 | 37 | 38 | 62 | 63 |
64 | 65 | 66 |

Sharefest for websites

67 | 68 | 69 |
Embed Sharefest into your website to empower your downloads. 75 |
76 |
77 | Sharefest for websites will automatically create a peer-to-peer network and download the file from the server as well as from nearby peers. 78 | 79 | Your site and downloads will be faster, use less bandwidth and handle unexpected bursts of traffic easily. 80 | 81 | 82 |
83 |
84 | Apply now for our private beta: 85 |
86 |

87 | 88 |

89 | 90 |
91 | 92 |

93 | 94 | 95 | 96 | 97 | 98 | 99 |
100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /sharefest/public/css/main.css: -------------------------------------------------------------------------------- 1 | .bg { 2 | /*background: url('http://subtlepatterns.com/patterns/random_grey_variations.png');*/ 3 | background: url('/img/grey.png'); 4 | } 5 | 6 | .bgTerms { 7 | background: #D3D9DE; 8 | } 9 | 10 | .navbar-fixed-top { 11 | margin-bottom: 20px; 12 | position: absolute; 13 | top: 0; 14 | height: auto; 15 | } 16 | 17 | .container { 18 | position: absolute; 19 | display: block; 20 | margin: auto; 21 | width: 98%; 22 | height: 97%; 23 | top: 0; 24 | bottom: 0; 25 | left: 0; 26 | right: 0; 27 | } 28 | 29 | .terms { 30 | margin-top: 50px; 31 | margin-left: 20px; 32 | } 33 | 34 | .paragraph { 35 | margin-left: 30px; 36 | font-size: 14px; 37 | font-family: Oswald; 38 | color: #666; 39 | 40 | } 41 | 42 | .headLine { 43 | font-family: Oswald; 44 | margin-left: 20px; 45 | color: #000; 46 | } 47 | 48 | .navbar-inner .container { 49 | position: absolute; 50 | display: block; 51 | margin: auto; 52 | width: 98%; 53 | height: 97%; 54 | background: transparent; 55 | top: 0; 56 | bottom: 0; 57 | left: 0; 58 | right: 0; 59 | border: 0; 60 | padding-left: 10px; 61 | } 62 | 63 | /*.dragdrop {*/ 64 | /*line-height: 52px;*/ 65 | /*text-align: center;*/ 66 | /*font-weight: bold;*/ 67 | /*color: #666;*/ 68 | /*font-size: 18px;*/ 69 | /*}*/ 70 | 71 | .dragdrop:hover { 72 | /*width:85%;*/ 73 | border: 3px solid #FFFFFF; 74 | /*background-color: #000;*/ 75 | transition: background-color 1000ms ease; 76 | transition-delay: 0.5s; 77 | background-color: rgba(255, 250, 250, 0.85); 78 | } 79 | 80 | .dragdrop:hover * { 81 | 82 | /*font-size: 20px;*/ 83 | /*zoom: 120%;*/ 84 | } 85 | 86 | .dragdrop { 87 | 88 | /*transition: all 10000ms ease-in-out;*/ 89 | /*-webkit-transform: rotate(-10deg);*/ 90 | /*-moz-transform: rotate(-10deg);*/ 91 | /*transition-delay: 15s;*/ 92 | 93 | -moz-transition: width 1s, height 1s, background-color 1s, -moz-transform 1s; 94 | -webkit-transition: width 1s, height 1s, background-color 1s, -webkit-transform 1s; 95 | -o-transition: width 1s, height 1s, background-color 1s, -o-transform 1s; 96 | transition: width 1s, height 1s, background-color 1s, transform 1s; 97 | 98 | width: 81%; 99 | height: 70%; 100 | border: 2px solid #DDD; 101 | line-height: 30px; 102 | text-align: center; 103 | font-weight: bold; 104 | /*background-color: #FFFFFF;*/ 105 | background-color: rgba(255, 255, 255, 0.65); 106 | /*opacity: 0.75;*/ 107 | position: absolute; 108 | display: block; 109 | top: 4%; 110 | left: 0; 111 | bottom: 0; 112 | right: 0; 113 | margin: auto; 114 | cursor: cell; 115 | } 116 | 117 | .termsHref{ 118 | font-size: 11px; 119 | color:#BBBBBB; 120 | 121 | } 122 | 123 | .termsHref:hover{ 124 | font-size: 11px; 125 | color:#999999; 126 | 127 | } 128 | .footerMenu{ 129 | text-align: left; 130 | /*line-height: 20px;*/ 131 | 132 | width: 100%; 133 | /*margin-top: 10px;*/ 134 | /*border-top: 1px solid #e2e2e2;*/ 135 | } 136 | .footer * { 137 | vertical-align: bottom; 138 | } 139 | 140 | 141 | .footer { 142 | position: absolute; 143 | bottom: 1%; 144 | left: 10%; 145 | clear: both; 146 | color: #DDDDDD; 147 | /*font-family: cursive;*/ 148 | /**/ 149 | /*position: absolute;*/ 150 | /*bottom: 1%;*/ 151 | /*right:1%;*/ 152 | /*clear:both;*/ 153 | /*color: #DDDDDD;*/ 154 | } 155 | 156 | .textDrag { 157 | width: 100%; 158 | position: absolute; 159 | top: 0; 160 | bottom: 0; 161 | margin: auto; 162 | height: 200px; 163 | } 164 | 165 | .addthis_toolbox { 166 | /*position: absolute;*/ 167 | /*top:41%;*/ 168 | /*left:61%;*/ 169 | display: inline-table; 170 | margin-top: 8px; 171 | } 172 | 173 | -------------------------------------------------------------------------------- /sharefest/public/css/main_new.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: rgba(20, 20, 20, 1); 3 | color: #f5f5f5; 4 | } 5 | h3 { 6 | font-size: 20px; 7 | line-height: 0px; 8 | padding-top: 10px; 9 | } 10 | 11 | ul { 12 | margin: 0.75em 0; 13 | padding: 0 1em; 14 | list-style: none; 15 | } 16 | 17 | .bg { 18 | /*background: url('http://subtlepatterns.com/patterns/random_grey_variations.png');*/ 19 | /*background: url('/img/grey.png');*/ 20 | /*background: url('http://farm9.staticflickr.com/8514/8478065248_d23f9241ee_h.jpg');*/ 21 | /*background: url('http://farm3.staticflickr.com/2607/4016909843_185db132df_b.jpg');*/ 22 | /*background: url('http://farm9.staticflickr.com/8358/8394528614_413e2c5636_o.jpg');*/ 23 | background: url('/img/back-dark.jpg') fixed; 24 | background-size: cover; 25 | 26 | /*background: url('http://farm5.staticflickr.com/4135/4755887601_5a0b7d00b3_b.jpg');*/ 27 | /*background: url('http://farm5.staticflickr.com/4061/4270706290_8e2c0017bc_o.jpg');*/ 28 | } 29 | 30 | .bgTerms { 31 | background: #D3D9DE; 32 | } 33 | 34 | .navbar-fixed-top { 35 | margin-bottom: 20px; 36 | position: fixed; 37 | top: 0; 38 | height: auto; 39 | } 40 | 41 | .container { 42 | position: absolute; 43 | display: block; 44 | margin: auto; 45 | width: 800px; 46 | height: 97%; 47 | } 48 | 49 | .static-container { 50 | padding-top: 50px; 51 | padding-left: 20px; 52 | font-size: 1.2em; 53 | line-height: 22px; 54 | } 55 | 56 | .static-container h3 { 57 | /*color: #ff4500;*/ 58 | padding-bottom: 5px; 59 | padding-top: 20px; 60 | 61 | } 62 | 63 | .terms { 64 | margin-top: 50px; 65 | margin-left: 20px; 66 | } 67 | 68 | .paragraph { 69 | margin-left: 30px; 70 | font-size: 14px; 71 | font-family: Oswald; 72 | color: #666; 73 | 74 | } 75 | 76 | .headLine { 77 | font-family: Oswald; 78 | margin-left: 20px; 79 | color: #000; 80 | } 81 | 82 | .navbar-inner .container { 83 | position: absolute; 84 | display: block; 85 | margin: auto; 86 | width: 98%; 87 | height: 97%; 88 | background: transparent; 89 | top: 0; 90 | bottom: 0; 91 | left: 0; 92 | right: 0; 93 | border: 0; 94 | padding-left: 10px; 95 | } 96 | 97 | /*.dragdrop {*/ 98 | /*line-height: 52px;*/ 99 | /*text-align: center;*/ 100 | /*font-weight: bold;*/ 101 | /*color: #666;*/ 102 | /*font-size: 18px;*/ 103 | /*}*/ 104 | 105 | .dragdrop:hover { 106 | /*width:85%;*/ 107 | border: 3px solid #FFFFFF; 108 | /*background-color: #000;*/ 109 | transition: background-color 1000ms ease; 110 | transition-delay: 0.5s; 111 | background-color: rgba(255, 250, 250, 0.85); 112 | } 113 | 114 | .dragdrop:hover * { 115 | 116 | /*font-size: 20px;*/ 117 | /*zoom: 120%;*/ 118 | } 119 | 120 | .dragdrop { 121 | 122 | /*transition: all 10000ms ease-in-out;*/ 123 | /*-webkit-transform: rotate(-10deg);*/ 124 | /*-moz-transform: rotate(-10deg);*/ 125 | /*transition-delay: 15s;*/ 126 | 127 | -moz-transition: width 1s, height 1s, background-color 1s, -moz-transform 1s; 128 | -webkit-transition: width 1s, height 1s, background-color 1s, -webkit-transform 1s; 129 | -o-transition: width 1s, height 1s, background-color 1s, -o-transform 1s; 130 | transition: width 1s, height 1s, background-color 1s, transform 1s; 131 | 132 | width: 81%; 133 | height: 70%; 134 | border: 2px solid #DDD; 135 | line-height: 30px; 136 | text-align: center; 137 | font-weight: bold; 138 | /*background-color: #FFFFFF;*/ 139 | background-color: rgba(255, 255, 255, 0.65); 140 | /*opacity: 0.75;*/ 141 | position: absolute; 142 | display: block; 143 | top: 4%; 144 | left: 0; 145 | bottom: 0; 146 | right: 0; 147 | margin: auto; 148 | cursor: cell; 149 | } 150 | 151 | .termsHref { 152 | font-size: 11px; 153 | color: #BBBBBB; 154 | 155 | } 156 | 157 | .termsHref:hover { 158 | font-size: 11px; 159 | color: #999999; 160 | 161 | } 162 | 163 | .footerMenu { 164 | vertical-align: bottom; 165 | text-align: left; 166 | /*line-height: 20px;*/ 167 | 168 | width: 100%; 169 | /*margin-top: 10px;*/ 170 | /*border-top: 1px solid #e2e2e2;*/ 171 | } 172 | 173 | .footer * { 174 | vertical-align: bottom; 175 | } 176 | 177 | .label-upper-desc { 178 | font-family: courier; 179 | color: #CC3333; 180 | font-size: 0.9em; 181 | top: -3px; 182 | left: 2px; 183 | position: relative; 184 | } 185 | 186 | .right_floater { 187 | position: absolute; 188 | line-height: 30px; 189 | top: 20%; 190 | left: 839px; 191 | margin: auto; 192 | width: 350px; 193 | color: whitesmoke; 194 | vertical-align: bottom; 195 | } 196 | 197 | .beta_floater { 198 | position: absolute; 199 | line-height: 22px; 200 | top: 7%; 201 | left: 5%; 202 | margin: auto; 203 | width: 750px; 204 | color: whitesmoke; 205 | vertical-align: bottom; 206 | } 207 | 208 | #explainwhat { 209 | line-height: 20px; 210 | padding-left: 5px; 211 | /*background-color: rgba(20, 20, 20, 0.55);*/ 212 | } 213 | 214 | .footer { 215 | position: absolute; 216 | bottom: 7%; 217 | left: 10%; 218 | clear: both; 219 | color: #DDDDDD; 220 | /*font-family: cursive;*/ 221 | /**/ 222 | /*position: absolute;*/ 223 | /*bottom: 1%;*/ 224 | /*right:1%;*/ 225 | /*clear:both;*/ 226 | /*color: #DDDDDD;*/ 227 | } 228 | 229 | .textDrag { 230 | width: 100%; 231 | position: absolute; 232 | top: 0; 233 | bottom: 0; 234 | margin: auto; 235 | height: 200px; 236 | color: #333333; 237 | } 238 | 239 | .addthis_toolbox { 240 | /*position: absolute;*/ 241 | /*top:41%;*/ 242 | /*left:61%;*/ 243 | display: inline-table; 244 | margin-top: 8px; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /sharefest/public/download.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 26 | 27 | 28 | 29 | 30 |
THE CONTENT FILLING
31 |
32 |
33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /sharefest/public/faq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sharefest FAQ 5 | 6 | 7 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 68 | 69 |
70 |

What is this site?

71 |

72 | Sharefest let's you send files, small or big, to other people. 73 |

74 | 75 |

How do I use Sharefest

76 |

77 | You add files by clicking on the box or by dragging files into the box. 78 | Then you should see a link. This is a unique url for your files or "room". 79 | You can send this room URL through social networks, mail or just manually by copy&paste. 80 |

81 | 82 |

What is room?

83 | A room is a place on Sharefest with a unique URL, that people 84 | can enter and start downloading specific files. This files are 85 | currently added by the room creator. 86 | A room has a swarm of peers that are connected in a mesh network 87 | For example room "156a7b33b0a9f2dbdc8cd274790b7855" can be found on http://www.sharefest.me/156a7b33b0a9f2dbdc8cd274790b7855 88 |

89 | 90 |

Can't I use Gmail for that?

91 |

92 | Sometimes. Gmail is limited for 25MB, and is not designed primarily for sending files. 93 | You also need the recipients email addresses. 94 |

95 | 96 |

Why is it different from services such as Dropbox and Yousendit?

97 |

98 | Sharefest does not use cloud storage. These services work by uploading and storing 99 | the files in a remote storage, owned by these companies. Then recipients can download 100 | it from the servers. In Sharefest, the files are downloaded from other browsers, peer-2-peer. 101 |

102 | 103 |

Sound like BitTorrent. Why should I use Sharefest?

104 |

BitTorrent is great, but requires a special software to be downloaded.
105 | We wanted to build a web-based platform for filesharing that everyone with a modern browser 106 | could use.
107 | Our goal is that everyone could just enter a Sharefest URL and start downloading, even our parents :) 108 |

109 | 110 |

Do you use Flash or any other plugin for the P2P

111 |

112 | No - Only HTML+JS+CSS. 113 |

114 |

How is it even possible? I thought P2P cannot happen unless a plugin is used

115 |

116 | This was true up until recently.
Now there is a new HTML5 standard called WebRTC 117 | and it has an API to transfer arbitrary data - Data Channels API (or RTCDataChannels API). 118 |

119 | 120 |

Who can see my files?

121 | 122 |

What encryption is used?

123 | 124 |

What browsers are supported?

125 |

126 | Currently Google Chrome and Mozilla Firefox. 127 |

128 | 129 |

do I have to open my ports for that?

130 |

131 | Usually no. Sharefest use standard WebRTC NAT traversal protocol - STUN. 132 |

133 | 134 |

135 | What is a seeder? 136 |

137 |

138 | A peer (user) that have the complete file and serve it to other peers. 139 |

140 | 141 |

142 | When can I close the tab? 143 |

144 |

145 | The notification bar on the top of the box helps you to determine that. 146 | If you are the only seeder of the file, you want to make sure the file is available. 147 | Once more people seed, it is safe to close (you will see a green bar). 148 |

149 | 150 |

What are swarm and mesh network?

151 |

152 | 153 |

154 | 155 |

Why is this “a firefox swarm”/”chrome swarm” ?

156 |

Right now, Google Chrome and Mozilla Firefox cannot send data between each other. Sad but true

157 | 158 |

Why is the in alpha? When Sharefest is going to be beta and stable?

159 |

160 | I drag a file to sharefest but I don’t get a link 161 |

162 | 163 |

164 | I’m kicking back and relaxing but I don’t get the file 165 |

166 | 167 |

I have some ideas how to improve Sharefest, who should I talk to?

168 |

How can I help?

169 |

Who is behind this project?

170 |

Why do you have such an ugly logo?

171 |

Because we are not designers. You are welcome to submit a new one. 172 | We do love our icon though

173 | 174 |

Why do I get a "Not enough disk space to download this file" message?

175 |

To support the delivery of large files Sharefest uses HTML5 FileSystem API for storing the file temporarily in your browser. 176 | This API is supported only by Chrome and it won't work in incognito mode. 177 | It only works if you have enough space on your disk for us to use. More precisely Chrome let us use up to 10% of your free disk space. This means that if you'd like to download or share a big file you'll have to have 10x free space than the file size. 178 | Would you like to know more?. 179 |

180 | 181 |

Why do I get a "Could not add this file" message?

182 |

To support the delivery of large files Sharefest uses HTML5 FileSystem API for storing the file temporarily in your browser. 183 | This API is supported only by Chrome and it won't work in incognito mode. 184 | It only works if you have enough space on your disk for us to use. More precisely Chrome let us use up to 10% of your free disk space. This means that if you'd like to download or share a big file you'll have to have 10x free space than the file size. 185 | Would you like to know more?. 186 |

187 | 188 |

What is this weird icon?

189 |

No one knows. Some think it's a happy, festing star.
190 | Other claim it's actually a smashed rubber on the wall. 191 |

192 |



193 |



194 |



195 |



196 |



197 |



198 |
199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /sharefest/public/favicon-peer5.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/favicon-peer5.ico -------------------------------------------------------------------------------- /sharefest/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/favicon.ico -------------------------------------------------------------------------------- /sharefest/public/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/1.jpg -------------------------------------------------------------------------------- /sharefest/public/img/back-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/back-dark.jpg -------------------------------------------------------------------------------- /sharefest/public/img/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/back.png -------------------------------------------------------------------------------- /sharefest/public/img/cnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/cnet.png -------------------------------------------------------------------------------- /sharefest/public/img/github_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/github_icon.png -------------------------------------------------------------------------------- /sharefest/public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /sharefest/public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /sharefest/public/img/google_io.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/google_io.png -------------------------------------------------------------------------------- /sharefest/public/img/google_io_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/google_io_color.png -------------------------------------------------------------------------------- /sharefest/public/img/grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/grey.png -------------------------------------------------------------------------------- /sharefest/public/img/io-live-on-air-2013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/io-live-on-air-2013.png -------------------------------------------------------------------------------- /sharefest/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/logo.png -------------------------------------------------------------------------------- /sharefest/public/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 59 | 68 | Shar 80 | fest 92 | 110 | 121 | 122 | -------------------------------------------------------------------------------- /sharefest/public/img/symbol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/symbol.png -------------------------------------------------------------------------------- /sharefest/public/img/symbol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 57 | 75 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /sharefest/public/img/tf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/tf.png -------------------------------------------------------------------------------- /sharefest/public/img/twitter_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peer5/ShareFest/2aaef15a2698bbea50123691822bfb172c01e49d/sharefest/public/img/twitter_icon.png -------------------------------------------------------------------------------- /sharefest/public/js/dnd.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("dragover", function (e) { 2 | e = e || event; 3 | e.dataTransfer.dropEffect = 'none'; 4 | e.preventDefault(); 5 | }, false); 6 | window.addEventListener("drop", function (e) { 7 | e = e || event; 8 | e.dataTransfer.dropEffect = 'none'; 9 | e.preventDefault(); 10 | }, false); 11 | 12 | $(document).ready(function () { 13 | var dropbox = document.getElementById("dropbox") 14 | 15 | // init event handlers 16 | dropbox.addEventListener("dragenter", dragEnter, false); 17 | dropbox.addEventListener("dragexit", dragExit, false); 18 | dropbox.addEventListener("dragover", dragOver, false); 19 | dropbox.addEventListener("drop", drop, false); 20 | 21 | // init the widgets 22 | // $("#progressbar").progressbar(); 23 | }); 24 | 25 | 26 | function disableDrag() { 27 | var dropbox = document.getElementById("dropbox") 28 | 29 | dropbox.removeEventListener("dragenter", dragEnter); 30 | dropbox.removeEventListener("dragexit", dragExit); 31 | dropbox.removeEventListener("dragover", dragOver); 32 | dropbox.removeEventListener("drop", drop); 33 | dropbox.setAttribute("style","cursor: auto;"); 34 | } 35 | 36 | function dragEnter(evt) { 37 | evt.stopPropagation(); 38 | evt.preventDefault(); 39 | $('.dragdrop')[0].style.opacity = 0.9; 40 | $('.dragdrop')[0].style.borderStyle = 'dashed'; 41 | $('.dragdrop')[0].style.borderWidth = '5px'; 42 | } 43 | 44 | function dragExit(evt) { 45 | evt.stopPropagation(); 46 | evt.preventDefault(); 47 | $('.dragdrop')[0].style.opacity = 0.8; 48 | $('.dragdrop')[0].style.borderStyle = 'solid'; 49 | $('.dragdrop')[0].style.borderWidth = '2px'; 50 | } 51 | 52 | function dragOver(evt) { 53 | evt.stopPropagation(); 54 | evt.preventDefault(); 55 | } 56 | 57 | function drop(evt) { 58 | evt.stopPropagation(); 59 | evt.preventDefault(); 60 | 61 | var files = evt.dataTransfer.files; 62 | console.log(files); 63 | var count = files.length; 64 | 65 | // Only call the handler if 1 or more files was dropped. 66 | addFiles(files); 67 | 68 | if (count > 0) 69 | handleFiles(files); 70 | else { 71 | $('.dragdrop')[0].style.borderStyle = 'solid'; 72 | $('.dragdrop')[0].style.borderWidth = '2px'; 73 | } 74 | } 75 | 76 | 77 | function handleFiles(files) { 78 | 79 | // var file = files[0]; 80 | // 81 | // var reader = new FileReader(); 82 | // 83 | // // init the reader event handlers 84 | // reader.onprogress = handleReaderProgress; 85 | // reader.onloadend = handleReaderLoadEnd; 86 | // 87 | // // begin the read operation 88 | // reader.readAsDataURL(file); 89 | } 90 | 91 | function handleReaderProgress(evt) { 92 | if (evt.lengthComputable) { 93 | var loaded = (evt.loaded / evt.total); 94 | 95 | // $("#progressbar").progressbar({ value: loaded * 100 }); 96 | } 97 | } 98 | 99 | function handleReaderLoadEnd(evt) { 100 | // $("#progressbar").progressbar({ value: 100 }); 101 | 102 | var img = document.getElementById("preview"); 103 | img.src = evt.target.result; 104 | } -------------------------------------------------------------------------------- /sharefest/public/js/files.js: -------------------------------------------------------------------------------- 1 | function sendFileInfo(fileInfo) { 2 | var xhr = new XMLHttpRequest(); 3 | xhr.open('post', '/new'); 4 | xhr.setRequestHeader("Content-Type", "application/json"); 5 | xhr.onload = function () { 6 | //TODO: check statuses 200/403/... 7 | radio('SwarmCreatedEvent').broadcast(xhr.response); 8 | } 9 | xhr.send(JSON.stringify(fileInfo)); 10 | 11 | 12 | }; 13 | function addFiles(files) { 14 | //TODO: fix for multiple files 15 | if (files.length > 1){ 16 | //ToDo: use bootstrap alerts 17 | alert('Currently only a single file can be transfered'); 18 | ga('send', 'event','alerts', 'addFile', 'multipleFiles', files.length); 19 | } 20 | userState.isSeeder = true; 21 | var file = files[0]; // FileList object 22 | ga('send', 'event', 'files', 'addFile', 'fileSize', file.size); 23 | sharefestClient.prepareToReadFile(file.name, file.size); 24 | if(peer5.config.USE_FS) 25 | radio('filesystemInitiated').subscribe([function(){ //(event called from within prepareToReadFile()) 26 | if(!peer5.config.USE_FS){ 27 | peer5.warn("There was an error initiating the filesystem"); 28 | //TODO: notify the user 29 | } 30 | readFile(file); 31 | },this]); 32 | else{ 33 | readFile(file); 34 | } 35 | } 36 | 37 | function readFile(file){ 38 | if (!peer5.config.USE_FS && file.size > peer5.config.ALLOWED_FILE_SIZE) { 39 | var maxFileSize = peer5.config.ALLOWED_FILE_SIZE/(1024*1024); 40 | //TODO: use bootstrap alerts 41 | showErrorAlert('Could not add the file due to disk limitations. why?',true); 42 | // alert('Currently only files under ' + maxFileSize + 'MB are allowed'); 43 | ga('send', 'event', 'alerts', 'addFile', 'fileTooBig', file.size); 44 | return; 45 | } 46 | 47 | var reader = new FileReader(); 48 | //Reading the file in slices: 49 | var sliceId = 0; 50 | //read a slice the size not bigger than CACHE_SIZE,100MB since ~100MB is the limit for read size of file api (in chrome). 51 | var chunksPerSlice = Math.floor(Math.min(1024000,peer5.config.CACHE_SIZE,100000000)/peer5.config.CHUNK_SIZE); 52 | //var swID; 53 | var sliceSize = chunksPerSlice * peer5.config.CHUNK_SIZE; 54 | var blob; 55 | $('#box-text').text('Preparing file...'); 56 | startNonsense(); 57 | 58 | reader.onloadend = function (evt) { 59 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 60 | sharefestClient.addChunks(file.name, evt.target.result,function(){ 61 | sliceId++; 62 | if ((sliceId + 1) * sliceSize < file.size) { 63 | blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize); 64 | reader.readAsArrayBuffer(blob); 65 | } else if (sliceId * sliceSize < file.size) { 66 | blob = file.slice(sliceId * sliceSize, file.size); 67 | reader.readAsArrayBuffer(blob); 68 | } else { 69 | finishedLoadingFile(file); 70 | } 71 | }); 72 | } 73 | }; 74 | 75 | blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize); 76 | reader.readAsArrayBuffer(blob); 77 | } 78 | 79 | function finishedLoadingFile(file){ 80 | stopNonsense(); 81 | finishedLoadingFileUi([file]); 82 | 83 | //TODO: calc and add hashes 84 | 85 | //ToDo: add origin instead of 'sharefest' 86 | var fileInfo = new peer5.core.protocol.FileInfo(null, file.size, null, null, 'sharefest', 87 | file.name, file.lastModifiedDate, file.type); 88 | sharefestClient.upload(fileInfo); 89 | } 90 | 91 | function friendlyFileInfo(fileInfo) { 92 | return fileInfo.name + ' (' + bytesToSize(fileInfo.size) + ') '; 93 | } 94 | 95 | function listFiles(files) { 96 | var str = ''; 97 | for (var i = 0; i < files.length; i++) { 98 | var entry = files[i]; 99 | str += entry.name + ' (' + bytesToSize(entry.size) + ') '; 100 | } 101 | return str; 102 | } 103 | 104 | function bytesToSize(bytes) { 105 | var sizes = ['b', 'KB', 'MB', 'GB', 'TB']; 106 | if (bytes == 0) return '0.00KB'; 107 | var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); 108 | return (bytes / Math.pow(1024, i)).toFixed(2) + '' + sizes[i]; 109 | }; -------------------------------------------------------------------------------- /sharefest/public/js/sfClient.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | sfclient = function () { 3 | this.pendingSwarms = []; 4 | this.clientId; 5 | this.initiateClient(); 6 | this.registerEvents(); 7 | this.chunkRead = 0; 8 | this.BW_INTERVAL = 500; 9 | this.lastDownCycleTime = Date.now(); 10 | this.lastUpCycleTime; 11 | this.totalUpSinceLastCycle = 0 12 | this.lastCycleUpdateSizeInBytes = 0; 13 | this.firstTime = true; 14 | this.startTime; 15 | this.totalAvarageBw; 16 | this.lastReportTime = 0; 17 | this.lastStatCalcTime = 0; 18 | peer5.setLogLevel(peer5.config.LOG_LEVEL); 19 | 20 | //monitor the sendQueues 21 | this.cron_interval_id = window.setInterval(this.cron, peer5.config.MONITOR_INTERVAL, this); 22 | }; 23 | 24 | sfclient.prototype = { 25 | updateMetadata:function (metadata) { 26 | if (!this.originator) { 27 | peer5.core.data.BlockCache.add(metadata.swarmId, new peer5.core.dataStructures.BlockMap(metadata.size, metadata.swarmId)); 28 | } else { 29 | peer5.core.data.BlockCache.alias(metadata.swarmId, metadata.name); 30 | } 31 | if(!this.statsCalculator){ 32 | this.statsCalculator = new peer5.core.stats.StatsCalculator(metadata.size, metadata.name, ''); 33 | } 34 | 35 | var blockMap = peer5.core.data.BlockCache.get(metadata.swarmId); 36 | blockMap.addMetadata(metadata); 37 | if (!peer5.config.USE_FS) 38 | radio('resourceInit').broadcast(metadata); 39 | else if (this.originator){ 40 | blockMap.changeResourceId(metadata.swarmId,function(succ){ 41 | if(succ){ 42 | radio('resourceInit').broadcast(metadata); 43 | }else{ 44 | peer5.warn("couldn't change resource Id"); 45 | } 46 | }); 47 | } 48 | // var thi$ = this; 49 | // var resourceId = metadata.name; 50 | // if(!this.originator){ 51 | // if (peer5.config.USE_FS) { 52 | //// peer5.core.data.FSio.isExist(resourceId,function(succ){ 53 | //// if(succ){ 54 | //// //file exists 55 | //// console.log("Resource " + resourceId + " exists already in the filesystem."); 56 | //// blockMap.fs = true; 57 | //// blockMap.initiateFromLocalData(0); 58 | //// }else{ 59 | //// console.log("Resource " + resourceId + " doesn't exist in the filesystem."); 60 | //// peer5.core.data.FSio.createResource(resourceId,function(succ){ 61 | //// if(succ){ 62 | //// blockMap.fs = true; 63 | //// }else{ 64 | //// blockMap.fs = false; 65 | //// } 66 | //// radio('resourceInit').broadcast(metadata); 67 | //// }); 68 | //// } 69 | //// }) 70 | // } 71 | // }else{ 72 | // radio('resourceInit').broadcast(metadata); 73 | // } 74 | }, 75 | 76 | addChunks:function (fileName, binarySlice, cb) { 77 | var blockMap = peer5.core.data.BlockCache.get(fileName); 78 | this.numOfChunksInSlice = Math.ceil(binarySlice.byteLength / peer5.config.CHUNK_SIZE); 79 | for (var i = 0; i < this.numOfChunksInSlice; i++) { 80 | var start = i * peer5.config.CHUNK_SIZE; 81 | var newChunk = new Uint8Array(binarySlice.slice(start, Math.min(start + peer5.config.CHUNK_SIZE, binarySlice.byteLength))); 82 | var blockId = blockMap.setChunk(this.chunkRead, newChunk); 83 | blockMap.verifyBlock(blockId); 84 | radio('chunkAddedToBlockMap').broadcast(); 85 | this.chunkRead++; 86 | } 87 | if (this.chunkRead == this.numOfChunksInFile) { 88 | this.hasEntireFile = true; 89 | } 90 | if (peer5.config.USE_FS) 91 | peer5.core.data.FSio.notifyFinishWrite(cb); 92 | else 93 | cb() 94 | }, 95 | 96 | cron:function (self) { 97 | self.sendReport(); 98 | self.calculateStats(); 99 | }, 100 | 101 | calculateStats:function () { 102 | 103 | var currentTime = Date.now(); 104 | 105 | if (currentTime - this.lastStatCalcTime < peer5.config.STAT_CALC_INTERVAL) return; 106 | this.lastStatCalcTime = currentTime; 107 | if (this.statsCalculator) { 108 | this.statsCalculator.calc_avg(false); 109 | } 110 | 111 | }, 112 | 113 | prepareToReadFile:function (fileName, fileSize) { 114 | this.originator = true; 115 | this.statsCalculator = new peer5.core.stats.StatsCalculator(fileSize, fileName, ''); 116 | peer5.core.data.BlockCache.add(fileName, new peer5.core.dataStructures.BlockMap(fileSize, fileName)); 117 | var blockMap = peer5.core.data.BlockCache.get(fileName); 118 | // blockMap.addMetadata({name:fileName}); 119 | // if (peer5.config.USE_FS){ 120 | // peer5.core.data.FSio.createResource(fileName,function(succ){ 121 | // if(succ){ 122 | // blockMap.fs = true; 123 | // }else{ 124 | // blockMap.fs = false; 125 | // } 126 | // }); 127 | // } 128 | }, 129 | 130 | join:function (swarmId) { 131 | if (this.ws.socketReadyToSend()) { 132 | this.ws.sendMessage(new peer5.core.protocol.Join(swarmId)); 133 | } else { 134 | this.pendingSwarms.push(swarmId); 135 | } 136 | }, 137 | 138 | sendReport:function (completedDownload) { 139 | var compDown = completedDownload ? true : null; 140 | var thi$ = this; 141 | var currentTime = Date.now(); 142 | if (currentTime - thi$.lastReportTime < peer5.config.REPORT_INTERVAL && !completedDownload) return; 143 | thi$.lastReportTime = currentTime; 144 | peer5.core.data.BlockCache.forEach(function (blockMapId, blockMap) { 145 | if (blockMap.metadata && blockMap.metadata.swarmId) { 146 | var reportMessage = new peer5.core.protocol.Report( 147 | blockMap.metadata.swarmId, null, 148 | null, 149 | null, 150 | null, 151 | null, 152 | null, 153 | null, null, 154 | null, 155 | null, blockMap.availabilityMap.serialize(), blockMap.availabilityMap.numOfOnBits, blockMap.fileSize,compDown); 156 | var encodedReportMessage = peer5.core.protocol.BinaryProtocol.encode([reportMessage]); 157 | thi$.ws.sendData(encodedReportMessage); 158 | } 159 | }); 160 | }, 161 | 162 | isOrigin:function () { 163 | if (this.originator) { 164 | return this.originator 165 | } else { 166 | return false; 167 | } 168 | }, 169 | 170 | initiateClient:function () { 171 | var ws_url = location.protocol.replace('http', 'ws') + '//'; 172 | ws_url += location.host; 173 | 174 | this.clientId = peer5.core.util.generate_uuid(); 175 | this.ws = new peer5.core.transport.WsConnection(ws_url, this.clientId); 176 | }, 177 | 178 | receiveRequestMessage:function (requestMessage, originatorId) { 179 | peer5.log("received a request for " + requestMessage.chunkIds.length + " chunks"); 180 | for (var i = 0; i < requestMessage.chunkIds.length; ++i) { 181 | this.sendData(originatorId, "swarmId", requestMessage.chunkIds[i]); 182 | } 183 | }, 184 | 185 | upload:function (fileInfo) { 186 | var encodedMsg = peer5.core.protocol.BinaryProtocol.encode([fileInfo]); 187 | this.ws.sendData(encodedMsg); 188 | }, 189 | 190 | registerEvents:function () { 191 | var thi$ = this; 192 | 193 | //after I know all the metadata about the resource, and verified for filesystem quotas 194 | radio('resourceInit').subscribe([function (fileInfo) { 195 | if (fileInfo) { 196 | if(!peer5.config.USE_FS && fileInfo.size > peer5.config.ALLOWED_FILE_SIZE){ 197 | var maxFileSize = peer5.config.ALLOWED_FILE_SIZE/(1024*1024); 198 | //TODO: use bootstrap alerts 199 | //alert("Oops not enough disk space, file size limited to " + maxFileSize + "MB. You can't download this file."); 200 | radio('fileTooBigToDownload').broadcast(); 201 | return; 202 | } 203 | this.controller = new peer5.core.controllers.P2PController(this.clientId, true); 204 | this.controller.init(fileInfo.swarmId, true); 205 | } else { //don't have fileInfo yet, resource hasn't really initiated 206 | 207 | } 208 | 209 | }, this]); 210 | 211 | radio('transferFinishedEvent').subscribe([function (blockMap) { 212 | if (!this.originator){ 213 | 214 | thi$.sendReport(true); 215 | blockMap.saveLocally(); 216 | ga('send', 'event', 'transfer', 'downloadFinished', 'fileSize', blockMap.fileSize); 217 | } 218 | }, this]); 219 | 220 | radio('swarmError').subscribe([function (errorObj) { 221 | switch (errorObj.error) { 222 | case peer5.core.protocol.SWARM_NOT_FOUND: 223 | radio('roomNotFound').broadcast(); 224 | peer5.log('empty room'); 225 | break; 226 | case peer5.core.protocol.SWARM_ONLY_CHROME: 227 | radio('roomOnlyChrome').broadcast(); 228 | break; 229 | case peer5.core.protocol.SWARM_ONLY_FIREFOX: 230 | radio('roomOnlyFirefox').broadcast(); 231 | break; 232 | } 233 | }, this]); 234 | 235 | //websockets events 236 | radio('receivedFileInfo').subscribe([function (fileInfo) { 237 | if (fileInfo.swarmId) { 238 | if (peer5.core.data.BlockCache.get(fileInfo.swarmId) && peer5.core.data.BlockCache.get(fileInfo.swarmId).metadata) 239 | peer5.log("I allready have metadata of swarm " + fileInfo.swarmId); 240 | else { 241 | this.updateMetadata(fileInfo); 242 | radio('receivedNewFileInfo').broadcast(fileInfo); 243 | } 244 | } else { 245 | radio('roomNotFound').broadcast(); 246 | peer5.log('empty room'); 247 | } 248 | }, this]); 249 | 250 | radio('socketConnected').subscribe([function () { 251 | this.clientId = this.ws.socket.socket.sessionid; 252 | console.log('got an id: ' + this.clientId); 253 | }, this]); 254 | 255 | radio('webSocketInit').subscribe([function () { 256 | for (var swarmId in this.pendingSwarms) { 257 | this.ws.sendMessage(new peer5.core.protocol.Join(this.pendingSwarms[swarmId])); 258 | } 259 | }, this]); 260 | } 261 | }; 262 | })(); 263 | -------------------------------------------------------------------------------- /sharefest/public/press.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sharefest - Press 5 | 6 | 7 | 27 | 28 | 29 | Fork me on GitHub 33 | 34 | 60 | 61 |
62 |

In the news...

63 |

http://download.cnet.com/8301-2007_4-57591025-12/new-firefox-22-enables-browser-based-file-sharing/

https://news.ycombinator.com/item?id=5824114

https://news.ycombinator.com/item?id=5401872

http://news.xploituga.com/2013/06/26/sharefest-a-solucao-p2p-para-usar-com-o-firefox-e-chrome/

http://www.chriskranky.com/webrtc-is-not-a-feature/

http://lwn.net/Articles/556486/rss

http://www.xn--apaados-6za.es/tenemos-que-apanar/internet-tutoriales-y-trucos/451-firefoz-22-intercambio-archivos-basado-navegador.html

http://radar.oreilly.com/2013/06/four-short-links-6-june-2013.html

http://www.moongift.jp/2013/06/20130622-2/

http://alanquayle.com/2013/06/webrtc-meet-up-innovations/

http://www.arfy.fr/dotclear/index.php?post/2013/06/19/sharefest-me-partage-P2P-en-WebSocket-d-un-fichier-via-une-demo-WebRTC

http://www.metafilter.com/129000/ShareFest

http://ecampus.nmit.ac.nz/moodle/mod/forum/discuss.php?d=47594

http://www.xakep.ru/post/60738/

http://www.tecnovideoblog.it/curiosita/mozilla-firefox-22-con-sharefest-condivisione-istantanea-di-file-via-browser.html#.Uc2mqfZ7lak.twitter

http://www.youtube.com/watch?feature=player_embedded&v=EHh4hV1krqU

http://cpplover.blogspot.co.il/2013/07/blog-post_3.html

http://www.jamiiforums.com/tech-gadgets-and-science-forum/478269-sharefest-njia-mbadala-ya-ku-share-ma-file.html

http://torrentfreak.com/the-easiest-way-in-the-world-to-share-files-p2p-and-how-it-works-130706/

https://news.ycombinator.com/item?id=5998630

http://www.reddit.com/r/technology/comments/1hqwl2/the_easiest_way_in_the_world_to_share_files_p2p/

http://vr-zone.com/articles/sharefest-is-the-easiest-p2p-service-on-the-web/43934.html

http://www.pc.co.il/?p=122396

http://www.blogy.co.il/2013/07/06/%D7%A9%D7%99%D7%AA%D7%95%D7%A3-%D7%A7%D7%91%D7%A6%D7%99%D7%9D-%D7%91%D7%90%D7%99%D7%A0%D7%98%D7%A8%D7%A0%D7%98/

http://lwn.net/Articles/556232/#Comments

http://www-es.appy-geek.com/Web/ArticleWeb.aspx?regionid=8&articleid=10736181

http://www.xatakaon.com/p2p-y-descargas/sharefest-p2p-desde-tu-navegador-con-soporte-para-webrtc

http://www.enriquedans.com/2013/07/sharefest-simplicidad-absoluta.html

http://bright.nl/gemakkelijk-peer-peer-delen-browser

http://eibar.org/blogak/teknosexua/nola-bidali-fitxategi-handi-bat

http://www.fixedbyvonnie.com/share-files-with-sharefest/#.UeRSKUGbels

http://www.baixaki.com.br/download/sharefest.htm

http://mimcomunicacio.wordpress.com/2013/07/17/sharefest-per-compartir-arxius-para-compartir-archivos/

http://blog.avanzaentucarrera.com/consejos/hay-que-organizarse-ii-guarda-y-comparte-tus-archivos-en-la-nube/

http://roget.biz/envoyer-un-fichier-de-taille-illimitee-sans-passer-par-un-serveur-central

http://inf0mag.blogspot.fr/2013/07/sharefest-partager-fichiers-peer2peer.html#more

http://www.duken.nl/simpel-peer-to-peer-delen-via-je-webbrowser/

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /sharefest/public/privacy.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Sharefest - Privacy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 33 | 34 | 35 | 36 | 37 | 61 | 62 |
63 |

Sharefest Privacy Policy:

64 | 65 | 66 |

What information does Sharefest collects and how does it use it? 67 |

68 | 69 |

70 | We do collect some limited information that your browser routinely makes available whenever you visit a website. 71 | We collect this data to improve the overall quality of the online experience. This information includes your 72 | Internet Protocol address, the geographic location of the IP address the user is using to access the Internet, 73 | browser type, browser language and the date and time of your query. 74 | 75 | We do not track which files you share using Sharefest. 76 |

77 | 78 |

79 | In addition, we may use aggregated information for the purposes of tracking the usage of our services and in 80 | order to help us develop and improve the service and the software. 81 |

82 | 83 | 84 |

Does Sharefest use user’s local storage? 85 |

86 | 87 |

88 | Local storage (such as IndexDB and FileSystem API) are sandboxed storage spaces which the browser has access to. Sharefest 89 | may temporarily use visitor's local storage to enable file sharing and saving of content. 90 | This allow us to handle big files which are too big for the RAM and enable resumable downloads. As mentioned, this 91 | space is temporary and Sharefest does not have any access to the user’s files. Sharefest cannot delete, modify 92 | or search files on the user’s hard drive. 93 |

94 | 95 | 96 |

Does Sharefest use user’s cookies?

97 | 98 |

99 | The use of cookies is standard on the Internet and many web sites use them. Sharefest does not use cookies 100 | directly, but may include the usage of cookies through 3rd party services that are embedded within the site. 101 |

102 | 103 | 104 |

Which data does Sharefest store about the files I share?

105 | 106 | 107 |

108 | With every upload the following data is stored in addition to the actual file: 109 | 110 |

111 | 112 |

113 | 1) SHA3 checksum of the shared file. This DOES NOT include the file data itself. By having the checksum, users are 114 | able to verify if the file is not tempered. This checksum is not sufficient to reproduce the data itself. 115 | 116 |

117 | 118 |

119 | 2) Timestamp of the initiate share 120 | 121 |

122 | 123 |

124 | 3) File meta data which may be useful for other users, such as file name, file type, creation data and more. This 125 | DOES NOT include the file data itself. 126 | 127 |

128 | 129 |

Changes to this Privacy Policy 130 |

131 | 132 |

133 | Sharefest may amend this policy from time to time. If we make any substantial changes in the way we use your 134 | personal information we will notify you by posting a prominent announcement on our pages. 135 | 136 |

137 | 138 |
139 | 140 | 141 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /sharefest/run-prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Starts sharefest server "daemon" 3 | # Using forever (npm install forever -g) 4 | 5 | # run in production mode (port 443 and 80) 6 | export NODE_ENV=production 7 | 8 | # enforce https 9 | export REQUIRE_HTTPS=1 10 | 11 | # configure logs 12 | NOW=$(date +"%F") 13 | 14 | # stop existing if found 15 | forever stop server.js 16 | 17 | # 18 | forever start -e err.log -o out.log -l $NOW.log --append server.js -------------------------------------------------------------------------------- /sharefest/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var ws = require('../core/transport/WebSocketServer.js'); 4 | 5 | var config = require('../serverConfig.json'); 6 | var router = require('./server/lib/router.js'); 7 | var tracker = require(config.trackerPath); 8 | var server; 9 | var https = require('https'); 10 | var fs = require('fs'); 11 | 12 | var allowCrossDomain = function (req, res, next) { 13 | res.header("Access-Control-Allow-Origin", "*"); 14 | res.header('Access-Control-Allow-Headers', 'Range'); 15 | next(); 16 | } 17 | 18 | if (process.env.REQUIRE_HTTPS) { 19 | app.use(function (req, res, next) { 20 | if (!req.secure) { 21 | return res.redirect('https://' + req.get('Host') + req.url); 22 | } 23 | next(); 24 | }); 25 | } 26 | 27 | app.use(express.json()); 28 | app.use(express.compress()); 29 | app.use(allowCrossDomain); 30 | app.use(express.static(__dirname + '/public')); 31 | 32 | var server; 33 | var wsPort; 34 | 35 | app.configure('development', function () { 36 | app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); 37 | console.log('listening to port 13337'); 38 | server = app.listen(13337); 39 | ws.instance.start(tracker.instance, server, null, config.clientTimeout); 40 | // signaling.start(server); 41 | console.log('here I am'); 42 | }); 43 | 44 | app.configure('production', function () { 45 | var options = { 46 | // Important: the following crt and key files are insecure 47 | // replace the following files with your own keys 48 | key:fs.readFileSync('secret/private.key'), 49 | ca:[fs.readFileSync('secret/AddTrustExternalCARoot.crt'), 50 | fs.readFileSync('secret/SSLcomAddTrustSSLCA.crt')], 51 | cert:fs.readFileSync('secret/www_sharefest_me.crt') 52 | }; 53 | app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); 54 | // wsPort = process.env.WS_PORT || 443; 55 | 56 | peer5.log('listening to port 80'); 57 | server80 = app.listen(80); 58 | 59 | peer5.log('listening to port 443'); 60 | server = https.createServer(options, app).listen(443); 61 | 62 | ws.instance.start(tracker.instance, server, null, config.clientTimeout); 63 | // signaling.start(server); 64 | }); 65 | 66 | router.configure(app, __dirname); -------------------------------------------------------------------------------- /sharefest/server/lib/client-includes.js: -------------------------------------------------------------------------------- 1 | exports.FULL = 100; 2 | exports.NONE = 0; 3 | 4 | // the order has importance! 5 | exports[exports.FULL] = [ 6 | 'core/util/namespaces.js', 7 | 'sharefest/clientConfig.js', 8 | 'core/util/logger.js', 9 | 'core/util/lang_ext.js', 10 | 'core/util/norequire.js', 11 | 'core/util/radio.js', 12 | 'core/util/uuid.js', 13 | 'core/util/base64.js', 14 | 'core/dataStructures/BlockMap.js', 15 | 'core/dataStructures/Block.js', 16 | 'core/dataStructures/AvailabilityMapBase.js', 17 | 'core/dataStructures/Queue.compressed.js', 18 | 'core/dataStructures/DoublyLinkedList.js', 19 | 'core/dataStructures/LRU.js', 20 | 'core/data/BlockCache.js', 21 | 'core/data/FSio.js', 22 | 'core/dataStructures/AvailabilityMap.js', 23 | 'core/apiValidators/ApiValidatorBase.js', 24 | 'core/apiValidators/ApiValidator.js', 25 | 'core/apiValidators/DataChannelsValidator.js', 26 | 'core/apiValidators/FileSystemApiValidator.js', 27 | 'core/transport/AbstractPeerConnection.js', 28 | 'core/transport/PeerConnectionImpl.js', 29 | 'core/transport/WsConnection.js', 30 | 'core/protocol/ProtocolMessages.js', 31 | 'core/protocol/BinaryProtocol.js', 32 | 'core/controllers/IController.js', 33 | 'core/controllers/P2PController.js', 34 | 'core/stats/StatsCalculator.js', 35 | 'sharefest/public/js/sfClient.js' 36 | ]; 37 | 38 | -------------------------------------------------------------------------------- /sharefest/server/lib/router.js: -------------------------------------------------------------------------------- 1 | var config = require('../../../serverConfig.json'); 2 | var modes = require('./client-includes.js'); 3 | var buildify = require('../../../core/util/buildify-peer5.js'); //our own version of buildify 4 | var url = require('url'); 5 | var tracker = require('../../' + config.trackerPath); 6 | 7 | 8 | exports.configure = function (app, rootdir) { 9 | 10 | //https redirect 11 | // app.get('*', function (req, res, next) { 12 | // if (!req.secure) { 13 | // return res.redirect('https://' + req.get('Host') + req.url); 14 | // } 15 | // next(); 16 | // }); 17 | 18 | //buildify 19 | app.get('/client.js', function (req, res) { 20 | var ua = req.headers['user-agent']; 21 | var referer = req.headers.referer; 22 | var domain = 'localhost'; 23 | if (referer) { 24 | try { 25 | domain = url.parse(req.headers.referer).hostname; 26 | } catch (e) { 27 | peer5.error(e); 28 | peer5.error('printing request headers'); 29 | peer5.error(req.headers); 30 | } 31 | 32 | } 33 | 34 | console.log('/client.js requested. Parsing user agent ' + ua); 35 | 36 | var debug = false; 37 | try { 38 | debug = (domain == 'localhost') || url.parse(req.headers.referer).query == 'debuggee'; 39 | } catch (err) {} 40 | 41 | //get info from IP 42 | var ip = null; 43 | ip = req.ip; 44 | console.log(ip + " has requested a connection"); 45 | 46 | var mode = req.query["mode"] || modes.FULL; //default: full client 47 | var files = []; 48 | if (modes[mode]) { 49 | files = (modes[mode]); 50 | } 51 | 52 | var js = buildify().concat(files); 53 | js = js.perform(function (content) { 54 | return content.replace(/peer5.config.BLOCK_SIZE/g, config.blockSize); 55 | }); 56 | // 57 | // var port = req.query["port"] || process.env.WS_PORT; 58 | // if (port) { 59 | // js = js.perform(function (content) { 60 | // return content.replace(/peer5.config.WS_PORT/g, "\'" + port + "\'"); 61 | // }); 62 | // } 63 | // var server = req.query["server"] || process.env.WS_SERVER; 64 | // if (server) { 65 | // js = js.perform(function (content) { 66 | // return content.replace(/peer5.config.WS_SERVER/g, "\'" + server + "\'"); 67 | // }); 68 | // } 69 | 70 | if (!debug) { 71 | js = js.uglify(); 72 | } 73 | 74 | res.setHeader('Content-Type', 'text/javascript'); 75 | res.send(200, js.content); 76 | }); 77 | 78 | //TODO: add to ws 79 | app.post('/new', function (req, res) { 80 | peer5.info('request for a new swarm'); 81 | var fileInfo = req.body; 82 | peer5.info(fileInfo); 83 | var swarmId = tracker.instance.createSwarm(fileInfo); 84 | res.send(200, swarmId); 85 | }); 86 | 87 | app.get('/B', function (req, res) { 88 | res.sendfile(rootdir + '/public/new.html'); 89 | }); 90 | 91 | app.get('/browser', function (req, res) { 92 | res.sendfile(rootdir + '/public/browser.html'); 93 | }); 94 | 95 | app.get('/faq', function (req, res) { 96 | res.sendfile(rootdir + '/public/faq.html'); 97 | }); 98 | 99 | 100 | app.get('/press', function (req, res) { 101 | res.sendfile(rootdir + '/public/press.html'); 102 | }); 103 | 104 | app.get('/download', function (req, res) { 105 | res.sendfile(rootdir + '/public/download.html'); 106 | }); 107 | 108 | app.get('/demo', function (req, res) { 109 | res.redirect("https://www.sharefest.me/ec90ce95"); 110 | }); 111 | 112 | app.get('/about', function (req, res) { 113 | res.redirect("https://github.com/peer5/sharefest#about"); 114 | }); 115 | 116 | app.get('/contact', function (req, res) { 117 | res.redirect("mailto://sharefest@peer5.com"); 118 | }); 119 | 120 | app.get('/issues', function (req, res) { 121 | res.redirect("https://github.com/peer5/sharefest/issues?page=1&state=open"); 122 | }); 123 | 124 | app.get('/:id', function (req, res) { 125 | //todo: bind the room info to the page and output 126 | res.sendfile(rootdir + '/public/index.html'); 127 | }); 128 | } 129 | ; 130 | -------------------------------------------------------------------------------- /sharefest/server/secret/GlobalSignIntermediate.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEWjCCA0KgAwIBAgILBAAAAAABL07hQUMwDQYJKoZIhvcNAQEFBQAwVzELMAkG 3 | A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv 4 | b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw 5 | MDBaFw0yMjA0MTMxMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i 6 | YWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0 7 | aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxo83A 8 | 3zNAJuveWteUZtQBY8wzRIng4rjCRw2PrWmGHKhzQgvxcvstrLURcoMi9lbnLsVn 9 | cZ0AHDK84+0uCEWp5vrdyIyDBcFvS9AmSgv2G0XATX6TvA0nhO0wo+nGJibdLR/Y 10 | i8POGdBb/Aif5NjiNeSgaKb2DaN0YEKyl4IkjkGk8i5eto6nbtlsfw07JDVq0Ktb 11 | aveXAgA/UaanbnPKdw12fJu2MBoanPcfKHsOi0cf538FjMbJyLvP6dx6QS6hhtrU 12 | ObLiE0CmqDr6D1MeT+xumAkbypp3s1WFhekuFrWdXlTxSnpsObpuFwY0s7JC4ffz 13 | nJoLEUTeaniOsRNPAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T 14 | AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlq36sFu5g2QqdsIcimnaQtz+/SgwRwYD 15 | VR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh 16 | bHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9j 17 | cmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAvMC0GCCsG 18 | AQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYDVR0j 19 | BBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEBADrn 20 | /K6vBUOAJ3VBX6jwKI8fj4N+sri6rnUxJ4il5blOBEPSregTAKPbGQEwnmw8Un9c 21 | 3qtnw4QEVFGZnmMvvdW3wNXaAw5J0+Gzkk/fkk59riJqzti8/Hyua7aK6kVikBHT 22 | C3GnXgYi/0046rk6bs1nGgJ/S/O/DnlvvtUpMllZHZYIm3CP9x5cRntO0J20U8gS 23 | AhsNuzLrWVO5PhtWjRXI8UI/d/4f5W2eZh+r2rKDV7QMItKGvNoy18DtcIV8k6rw 24 | l9w5EdLYieuNkKO2UCXLbNmmw2/7iFS45JJwh855O/DeNr8DBAA9+e+eqWek9IY+ 25 | I5e4KnHi7f5piGe/Jlw= 26 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /sharefest/server/secret/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA8LylxlTK+RoSwl0OhwVMH88HEUXtMlrQrWrGq6Cwn8W2IYWT 3 | ycmGBKJ+YPhUkAR72CQ5EKjwY14limMGvcpah0cAX5VO86MTUxN0mJsgdayTCaUA 4 | HB7/G6R2E8blemou0xkOpjrGlu9Ez02nRISiC7nIRxhWXJwBo3JwYN+FBjehYCKB 5 | G1Gu/j28e86CMVzmaVLLGZvjB5LEt/GgL6jZ7MtL9L3oZjHFV9PWSaXkRg4ocwfU 6 | 4QmShOr2sFhHUxWFJ1hmTjdgjlcsaYkzo0fijz/CIncHjZLcSQsV7CeU58Nfmsac 7 | IVHbyxJzM0oK25HnO2Gfep8YOR0Gx+CGAJM4NwIDAQABAoIBAQDi1k86T8Gl8VyF 8 | ylua/HHbcx9MtmAn14zUK02igNqM4iaY9Z3pQLvIeDivNK+xuJfddVxUB2vjpd2n 9 | xWGfyiF9YHjImA754cqeuDFgbqOqXf960gAFf2uQZ2pffkXTEGSowu6b0bw4kqJ6 10 | 18fp9zQd6mP+Ib+WqeWdVoLvtQ5KaKL3aPS638sc62cq4O3lJTGjivsYJy6Sm/uF 11 | 74c38g8TrIForTU382CHeVu7U3TtPIT2Vpp4V5iDVoburFY4C/jmsB//kUAqF0qD 12 | xaFtes+aP26xoNWwz3mmFlpYDnY/3Um/lBe7MrSB+eMjtu+NDWKG/fEMiGa4qwLR 13 | cFHI7H4hAoGBAP0jq4rpi224Bm3Rg/4xxogk579hXm2pB4lGQsWHqAd5NFQ1P0ZB 14 | wVDhLNHUgU9ao5V991VfDQgoAwxuMO8cjqZrxGOAGdIyLjkTsrQaExvag45XxZFU 15 | ZNJGGLbMH+rMpUSOEFISRh3jlnyrD9Z13m2VS7llnlLOHSXmkJDV3XLnAoGBAPN1 16 | GOtb0g/BxI7kGJ/45Fath7OI+K07PxLkhdRDCYkyWDyr0C09y40ljbQvPFnq02bV 17 | KYAr2IuYK6Xm0m+YsD851Kl9l1/GarAoDLmrI/+y4Lh+oOYmhSGKEAXcio4CnG0e 18 | nxoaFFp0MNZAneqbSFTDp164O2m8GIi9Yd6igLYxAoGBAIZzgLSakRTornl70+ia 19 | lYFQYMr0s4v5QGh4KhTylZvKk7G6Sm5NlJkZ0I1umnQEh2hqbIkqoChZ4CylBlaq 20 | S1Y3XSsgKhbL0mM8hClGxCmCKWfcVLJ5KcfJtWy6BKXYpltAPYjp7dYIQLDcxt+4 21 | 4ThBGORTBgGjOjYcPWLARQu3AoGABzmtcKa7+D2+8KKyXGUNmcjyoxBomPOOLqhb 22 | lG4c5VZ6KerLddxEoz6/Et7oAp8mgQHIhoZGmfX9LaJJEojMikPUHOeeS3hfCDVY 23 | E1aR7e/IHZGcgtgX0WQAj7v43LgVHmPvQt4NRpX2MRWf2iubnPBlkjkTglLTw5Mj 24 | SXE3MrECgYEA4hqfs675Uhno4GS6FKbAciZh5YZ6LuSihI513ApV175cupozX3A9 25 | 6+dWVfU9PJcJijxUuHL5pxnz8Di47Y3koLk7CaxgHCOE4qW3xL10J0EIcRZbneJ6 26 | /LRSpav0Rpe6CEFEgIGIeXKUi1I9InApwXTnLJFj2hRINt7SjJCVXl8= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /sharefest/server/secret/readme: -------------------------------------------------------------------------------- 1 | This directory is used for TLS encrypted communication with the server 2 | For real world usage, you should put REAL certificates here 3 | The existing keys here are self signed and are to be used only for debugging -------------------------------------------------------------------------------- /sharefest/server/secret/self.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDYjCCAkoCCQDYCHvQcKtmIDANBgkqhkiG9w0BAQUFADBzMQswCQYDVQQGEwJh 3 | YTEPMA0GA1UECAwGYWFhYWFhMQswCQYDVQQHDAJhYTEOMAwGA1UECgwFYWFhYWEx 4 | DDAKBgNVBAsMA2FhYTEOMAwGA1UEAwwFYWFhYWExGDAWBgkqhkiG9w0BCQEWCWFh 5 | YUBhYS5hYTAeFw0xMzA4MTMxMjI4MDNaFw0xNDA4MTMxMjI4MDNaMHMxCzAJBgNV 6 | BAYTAmFhMQ8wDQYDVQQIDAZhYWFhYWExCzAJBgNVBAcMAmFhMQ4wDAYDVQQKDAVh 7 | YWFhYTEMMAoGA1UECwwDYWFhMQ4wDAYDVQQDDAVhYWFhYTEYMBYGCSqGSIb3DQEJ 8 | ARYJYWFhQGFhLmFhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8Lyl 9 | xlTK+RoSwl0OhwVMH88HEUXtMlrQrWrGq6Cwn8W2IYWTycmGBKJ+YPhUkAR72CQ5 10 | EKjwY14limMGvcpah0cAX5VO86MTUxN0mJsgdayTCaUAHB7/G6R2E8blemou0xkO 11 | pjrGlu9Ez02nRISiC7nIRxhWXJwBo3JwYN+FBjehYCKBG1Gu/j28e86CMVzmaVLL 12 | GZvjB5LEt/GgL6jZ7MtL9L3oZjHFV9PWSaXkRg4ocwfU4QmShOr2sFhHUxWFJ1hm 13 | TjdgjlcsaYkzo0fijz/CIncHjZLcSQsV7CeU58NfmsacIVHbyxJzM0oK25HnO2Gf 14 | ep8YOR0Gx+CGAJM4NwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAA0/v/H76ZI66b 15 | XcJ+rFNGwhWlQfcIlxnSZ5qSThbbi8vQLrz5D9q2FLzUCAHne+h587nqwMAscATc 16 | 1aMGo3lUtZDZpV/Y/POQNY1QwwIlfi4Dzm4jRDg2a4sgHtkGzIZ1ftyTebMqgwc5 17 | mXoBQPyEyqOQnd5S1XbeB0feIpGeYY7pKkwGNGLMH2J8MFZoDO720gEYd82wHDWd 18 | x8pDSK4erG6hVfKlLTq7xhSDxW3xOQvIWJ75CpiDjn25VXlS35SQFAp2jVbs45O6 19 | DTDXm4VzytuA6KysUSQtrDXMV+DLkEWMCgTMNKKWSPDd+3hH8TKGHpvpSKCtnnYr 20 | uqNMpaqK 21 | -----END CERTIFICATE----- --------------------------------------------------------------------------------