PageRenderTime 72ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/src/content/js/connection/dataSocket.js

https://gitlab.com/web-optimize/fireftp
JavaScript | 508 lines | 414 code | 92 blank | 2 comment | 116 complexity | cfbf24418bf453f5e38f528df9d6c0e8 MD5 | raw file
  1. function ftpDataSocketMozilla(controlHost, controlPort, security, proxy, host, port, compress, id, observer, cert, asciiMode) {
  2. this.transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"].getService(Components.interfaces.nsISocketTransportService);
  3. this.proxyService = Components.classes["@mozilla.org/network/protocol-proxy-service;1"].getService (Components.interfaces.nsIProtocolProxyService);
  4. this.dnsService = Components.classes["@mozilla.org/network/dns-service;1"].getService (Components.interfaces.nsIDNSService);
  5. this.eventTarget = Components.classes["@mozilla.org/thread-manager;1"].getService ().currentThread;
  6. this.security = security || false;
  7. this.host = (security ? controlHost : (host || ""));
  8. this.port = port || -1;
  9. this.proxyType = proxy ? proxy.proxyType : "";
  10. this.proxyHost = proxy ? proxy.proxyHost : "";
  11. this.proxyPort = proxy ? proxy.proxyPort : -1;
  12. this.useCompression = compress;
  13. this.dataListener = new dataListener();
  14. this.progressEventSink = new progressEventSink();
  15. this.id = id;
  16. this.observer = observer;
  17. this.asciiMode = asciiMode;
  18. if (security) {
  19. try {
  20. this.certOverride = Components.classes["@mozilla.org/security/certoverride;1"].getService(Components.interfaces.nsICertOverrideService);
  21. var hashAlg = {}; var fingerprint = {}; var overrideBits = {}; var isTemporary = {};
  22. var ok = this.certOverride.getValidityOverride(controlHost, controlPort, hashAlg, fingerprint, overrideBits, isTemporary);
  23. this.certOverride.rememberValidityOverride(this.host, port, cert, overrideBits.value, true);
  24. } catch (ex) {
  25. this.observer.onDebug(ex);
  26. }
  27. }
  28. }
  29. ftpDataSocketMozilla.prototype = {
  30. dataTransport : null,
  31. dataInstream : null,
  32. dataOutstream : null,
  33. fileInstream : null,
  34. serverSocket : null,
  35. listData : "",
  36. finished : true,
  37. exception : false,
  38. emptyFile : false, // XXX empty files are (still) special cases
  39. connect : function(write, localPath, fileTotalBytes, filePartialBytes, activeTransport) {
  40. try {
  41. if (activeTransport) {
  42. this.dataTransport = activeTransport;
  43. } else {
  44. var proxyInfo = this.proxyType == "" ? null : this.proxyService.newProxyInfo(this.proxyType, this.proxyHost, this.proxyPort,
  45. Components.interfaces.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST, 30, null);
  46. if (this.security) {
  47. this.dataTransport = this.transportService.createTransport(["ssl"], 1, this.host, this.port, proxyInfo);
  48. } else {
  49. this.dataTransport = this.transportService.createTransport(null, 0, this.host, this.port, proxyInfo);
  50. }
  51. }
  52. this.finished = false;
  53. if (write) { // upload
  54. this.dataOutstream = this.dataTransport.openOutputStream(0, 0, -1);
  55. var file;
  56. try {
  57. file = localFile.init(localPath);
  58. this.fileInstream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance();
  59. this.fileInstream.QueryInterface(Components.interfaces.nsIFileInputStream);
  60. this.fileInstream.init(file, 0x01, 0644, 0);
  61. this.fileInstream.QueryInterface(Components.interfaces.nsISeekableStream);
  62. this.fileInstream.seek(0, filePartialBytes); // append or not to append
  63. } catch (ex) {
  64. this.observer.onDebug(ex);
  65. this.observer.onError(gStrbundle.getFormattedString("failedUpload", [localPath]));
  66. this.kill();
  67. return;
  68. }
  69. var binaryOutstream = Components.classes["@mozilla.org/binaryoutputstream;1"].createInstance(Components.interfaces.nsIBinaryOutputStream);
  70. binaryOutstream.setOutputStream(this.dataOutstream);
  71. this.dataInstream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
  72. this.dataInstream.setInputStream(this.fileInstream);
  73. this.progressEventSink.parent = this;
  74. this.progressEventSink.localPath = localPath;
  75. this.progressEventSink.sendPrevSent = 0;
  76. this.progressEventSink.timeStart = new Date();
  77. this.progressEventSink.bytesTotal = file.fileSize;
  78. this.progressEventSink.bytesUploaded = this.useCompression ? 0 : filePartialBytes;
  79. this.progressEventSink.bytesPartial = filePartialBytes;
  80. this.progressEventSink.dataInstream = this.dataInstream;
  81. this.progressEventSink.dataOutstream = binaryOutstream;
  82. this.progressEventSink.fileInstream = this.fileInstream;
  83. this.progressEventSink.asciiMode = this.asciiMode;
  84. this.emptyFile = !file.fileSize;
  85. this.progressEventSink.asciiCarryover = false;
  86. this.dataTransport.setEventSink(this.progressEventSink, this.eventTarget);
  87. if (this.useCompression && file.fileSize) { // never as elegant as downloading :(
  88. this.progressEventSink.compressStream = true;
  89. var streamConverter = Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=deflate"].createInstance(Components.interfaces.nsIStreamConverter);
  90. streamConverter.asyncConvertData("uncompressed", "deflate", this.progressEventSink, null);
  91. var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance(Components.interfaces.nsIInputStreamPump);
  92. pump.init(this.dataInstream, -1, -1, 0, 0, false);
  93. pump.asyncRead(streamConverter, null);
  94. } else {
  95. var dataBuffer = this.dataInstream.readBytes(this.dataInstream.available() < 4096 ? this.dataInstream.available() : 4096);
  96. if (this.asciiMode && dataBuffer.length && dataBuffer.charAt(dataBuffer.length - 1) == '\r') {
  97. this.progressEventSink.asciiCarryover = true;
  98. }
  99. var diff = dataBuffer.length;
  100. if (this.asciiMode) {
  101. dataBuffer = dataBuffer.replace(/(^|[^\r])\n/g, "$1\r\n");
  102. }
  103. this.progressEventSink.bytesTotal += dataBuffer.length - diff;
  104. this.progressEventSink.dataOutstream.writeBytes(dataBuffer, dataBuffer.length);
  105. }
  106. } else { // download
  107. this.listData = "";
  108. var dataStream = this.dataTransport.openInputStream(0, 0, 0);
  109. var streamConverter;
  110. this.dataInstream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
  111. if (this.useCompression) {
  112. streamConverter = Components.classes["@mozilla.org/streamconv;1?from=deflate&to=uncompressed"].createInstance(Components.interfaces.nsIStreamConverter);
  113. streamConverter.asyncConvertData("deflate", "uncompressed", this.dataListener, null);
  114. } else {
  115. this.dataInstream.setInputStream(dataStream);
  116. }
  117. this.dataListener.parent = this;
  118. this.dataListener.localPath = localPath;
  119. this.dataListener.dataInstream = this.dataInstream;
  120. this.dataListener.data = "";
  121. this.dataListener.file = "";
  122. this.dataListener.fileOutstream = "";
  123. this.dataListener.binaryOutstream = "";
  124. this.dataListener.bytesTotal = fileTotalBytes || 0;
  125. this.dataListener.bytesDownloaded = filePartialBytes || 0;
  126. this.dataListener.bytesPartial = filePartialBytes || 0;
  127. this.dataListener.timeStart = new Date();
  128. this.dataListener.dataBuffer = "";
  129. this.dataListener.isNotList = localPath != null;
  130. this.dataListener.useCompression = this.useCompression;
  131. this.dataListener.asciiMode = this.asciiMode;
  132. this.dataListener.asciiCarryover = false;
  133. var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance(Components.interfaces.nsIInputStreamPump);
  134. pump.init(dataStream, -1, -1, 0, 0, false);
  135. pump.asyncRead(this.useCompression ? streamConverter : this.dataListener, null);
  136. }
  137. } catch(ex) {
  138. this.observer.onDebug(ex);
  139. this.observer.onError(gStrbundle.getString("errorDataConn"));
  140. return;
  141. }
  142. },
  143. createServerSocket : function(activeInfo) {
  144. try {
  145. var ipAddress = this.dnsService.resolve(this.dnsService.myHostName, false).getNextAddrAsString();
  146. var re = /\x2e/g;
  147. this.serverSocket = Components.classes["@mozilla.org/network/server-socket;1"].createInstance(Components.interfaces.nsIServerSocket);
  148. var self = this;
  149. var serverListener = {
  150. onSocketAccepted : function(serv, transport) {
  151. if (activeInfo.cmd == "LIST") {
  152. self.connect(false, null, 0, 0, transport);
  153. } else if (activeInfo.cmd == "RETR") {
  154. self.connect(false, activeInfo.localPath, activeInfo.totalBytes, 0, transport);
  155. } else if (activeInfo.cmd == "REST") {
  156. self.connect(false, activeInfo.localPath, activeInfo.totalBytes, activeInfo.partialBytes, transport);
  157. } else if (activeInfo.cmd == "STOR") {
  158. self.connect(true, activeInfo.localPath, 0, 0, transport);
  159. } else if (activeInfo.cmd == "APPE") {
  160. self.connect(true, activeInfo.localPath, 0, activeInfo.partialBytes, transport);
  161. }
  162. },
  163. onStopListening : function(serv, status) { }
  164. };
  165. this.serverSocket.init(this.port, false, -1);
  166. this.serverSocket.asyncListen(serverListener);
  167. if (activeInfo.ipType == "IPv4" && ipAddress.indexOf(':') == -1) {
  168. return ipAddress.replace(re, ",") + "," + parseInt(this.serverSocket.port / 256) + "," + this.serverSocket.port % 256;
  169. } else {
  170. return (ipAddress.indexOf(':') != -1 ? "|2|" : "|1|") + ipAddress + "|" + this.serverSocket.port + "|";
  171. }
  172. } catch (ex) {
  173. this.observer.onDebug(ex);
  174. this.observer.onError(gStrbundle.getString("errorDataConn"));
  175. return null;
  176. }
  177. },
  178. kill : function(override) {
  179. this.progressEventSink.bytesTotal = 0; // stop uploads
  180. this.dataListener.bytesTotal = 0; // stop downloads
  181. try {
  182. if (this.dataInstream && this.dataInstream.close) {
  183. this.dataInstream.close();
  184. }
  185. } catch(ex) { }
  186. try {
  187. if ((!this.emptyFile || override) && this.dataOutstream && this.dataOutstream.flush) {
  188. this.dataOutstream.flush();
  189. }
  190. if ((!this.emptyFile || override) && this.dataOutstream && this.dataOutstream.close) {
  191. this.dataOutstream.close();
  192. }
  193. } catch(ex) { }
  194. try {
  195. if ((!this.emptyFile || override) && this.fileInstream && this.fileInstream.close) {
  196. this.fileInstream.close();
  197. }
  198. } catch(ex) { }
  199. try {
  200. if ((!this.emptyFile || override)) { // XXX empty files are (still) special cases
  201. if (this.dataTransport && this.dataTransport.close) {
  202. this.dataTransport.close("Finished");
  203. }
  204. }
  205. } catch(ex) { }
  206. try {
  207. if (this.dataListener.binaryOutstream && this.dataListener.binaryOutstream.close) {
  208. this.dataListener.binaryOutstream.close();
  209. }
  210. } catch(ex) { }
  211. try {
  212. if (this.dataListener.fileOutstream && this.dataListener.fileOutstream.close) {
  213. this.dataListener.fileOutstream.close();
  214. }
  215. } catch(ex) { }
  216. try {
  217. if (this.serverSocket && this.serverSocket.close) {
  218. this.serverSocket.close();
  219. }
  220. } catch(ex) { }
  221. this.progressEventSink.parent = null; // stop memory leakage!
  222. this.dataListener.parent = null; // stop memory leakage!
  223. this.finished = true;
  224. if (this.security) {
  225. try {
  226. this.certOverride.clearValidityOverride(this.host, this.port);
  227. } catch (ex) {
  228. this.observer.onDebug(ex);
  229. }
  230. }
  231. }
  232. };
  233. function dataListener() { }
  234. dataListener.prototype = {
  235. parent : null,
  236. localPath : "",
  237. dataInstream : "",
  238. data : "",
  239. file : "",
  240. fileOutstream : "",
  241. binaryOutstream : "",
  242. bytesTotal : 0,
  243. bytesDownloaded : 0,
  244. bytesPartial : 0,
  245. timeStart : new Date(),
  246. dataBuffer : "",
  247. isNotList : false,
  248. useCompression : false,
  249. asciiMode : false,
  250. asciiCarryover : false,
  251. onStartRequest : function(request, context) {
  252. if (this.isNotList) {
  253. this.timeStart = new Date();
  254. try {
  255. this.file = localFile.init(this.localPath);
  256. this.fileOutstream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
  257. if (this.bytesPartial) {
  258. this.fileOutstream.init(this.file, 0x04 | 0x10, 0644, 0);
  259. } else {
  260. this.fileOutstream.init(this.file, 0x04 | 0x08 | 0x20, 0644, 0);
  261. }
  262. this.binaryOutstream = Components.classes["@mozilla.org/binaryoutputstream;1"].createInstance(Components.interfaces.nsIBinaryOutputStream);
  263. this.binaryOutstream.setOutputStream(this.fileOutstream);
  264. } catch (ex) {
  265. this.failure(ex);
  266. }
  267. }
  268. },
  269. onStopRequest : function(request, context, status) {
  270. if (this.isNotList) {
  271. try {
  272. if (this.asciiMode && this.getPlatform() != "windows") {
  273. if (this.asciiCarryover) {
  274. var buffer = '\r';
  275. this.binaryOutstream.writeBytes(buffer, buffer.length);
  276. this.asciiCarryover = false;
  277. }
  278. }
  279. } catch (ex) {
  280. this.failure(ex);
  281. }
  282. }
  283. if (!this.isNotList && this.parent) {
  284. this.parent.listData = this.data;
  285. }
  286. if (this.parent) {
  287. this.parent.kill();
  288. }
  289. },
  290. onDataAvailable : function(request, context, inputStream, offset, count) {
  291. if (this.useCompression) {
  292. this.dataInstream.setInputStream(inputStream);
  293. }
  294. if (this.isNotList) {
  295. try {
  296. this.dataBuffer = this.dataInstream.readBytes(count);
  297. var length = this.dataBuffer.length;
  298. if (this.asciiMode && this.getPlatform() != "windows") {
  299. if (this.asciiCarryover) {
  300. this.dataBuffer = '\r' + this.dataBuffer;
  301. }
  302. this.asciiCarryover = false;
  303. this.dataBuffer = this.dataBuffer.replace(/\r\n/g, '\n');
  304. if (this.dataBuffer.charAt(this.dataBuffer.length - 1) == '\r') {
  305. this.asciiCarryover = true;
  306. this.dataBuffer = this.dataBuffer.substring(0, this.dataBuffer.length - 1);
  307. }
  308. }
  309. this.binaryOutstream.writeBytes(this.dataBuffer, this.dataBuffer.length);
  310. this.bytesDownloaded += length;
  311. // XXX laaaamesauce, if socket has security enabled, then we never get an onStopRequest telling us
  312. // that the connection is finished
  313. if (this.parent.security && this.bytesTotal == this.bytesDownloaded) {
  314. this.parent.kill();
  315. }
  316. } catch (ex) {
  317. this.failure(ex);
  318. }
  319. } else {
  320. this.data += this.dataInstream.readBytes(count);
  321. }
  322. },
  323. failure : function(ex) {
  324. this.parent.observer.onDebug(ex);
  325. this.parent.observer.onError(gStrbundle.getFormattedString("failedSave", [this.localPath]));
  326. this.parent.exception = true;
  327. this.parent.kill();
  328. },
  329. getPlatform : function() {
  330. var platform = navigator.platform.toLowerCase();
  331. if (platform.indexOf('linux') != -1) {
  332. return 'linux';
  333. }
  334. if (platform.indexOf('mac') != -1) {
  335. return 'mac';
  336. }
  337. return 'windows';
  338. }
  339. };
  340. function progressEventSink() { }
  341. progressEventSink.prototype = {
  342. parent : null,
  343. localPath : "",
  344. bytesTotal : 0,
  345. sendPrevSent : 0,
  346. bytesUploaded : 0,
  347. timeStart : new Date(),
  348. bytesPartial : 0,
  349. dataOutstream : null,
  350. fileInstream : null,
  351. compressFirst : true,
  352. compressStream : false,
  353. compressTotal : 0,
  354. compressDone : false,
  355. compressBuffer : "",
  356. asciiMode : false,
  357. asciiCarryover : false,
  358. onStartRequest : function(request, context) { },
  359. onStopRequest : function(request, context, status) {
  360. this.compressDone = true;
  361. },
  362. onDataAvailable : function(request, context, inputStream, offset, count) {
  363. try {
  364. var dataInstream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
  365. dataInstream.setInputStream(inputStream);
  366. this.compressTotal += count;
  367. this.compressBuffer += dataInstream.readBytes(count);
  368. if (this.compressFirst) {
  369. this.compressFirst = false;
  370. this.dataOutstream.writeBytes(this.compressBuffer, this.compressBuffer.length);
  371. this.compressBuffer = "";
  372. }
  373. } catch (ex) {
  374. this.failure(ex);
  375. }
  376. },
  377. onTransportStatus : function (transport, status, progress, progressMax) {
  378. this.bytesUploaded += progress - this.sendPrevSent;
  379. this.sendPrevSent = progress;
  380. if ((!this.compressStream && this.bytesUploaded == this.bytesTotal)
  381. || (this.compressStream && this.compressDone && this.bytesUploaded == this.compressTotal)) { // finished writing
  382. this.parent.kill(); // can't rely on this.fileInstream.available() - corrupts uploads
  383. return;
  384. }
  385. if (this.compressStream) {
  386. this.dataOutstream.writeBytes(this.compressBuffer, this.compressBuffer.length);
  387. this.compressBuffer = "";
  388. } else {
  389. var dataBuffer = this.dataInstream.readBytes(this.dataInstream.available() < 4096 ? this.dataInstream.available() : 4096);
  390. var diff = dataBuffer.length;
  391. if (this.asciiMode) {
  392. var didCarryover = false;
  393. if (this.asciiCarryover && dataBuffer.length && dataBuffer.charAt(0) == '\n') {
  394. didCarryover = true;
  395. dataBuffer = dataBuffer.substring(1);
  396. }
  397. dataBuffer = dataBuffer.replace(/(^|[^\r])\n/g, "$1\r\n");
  398. if (didCarryover) {
  399. dataBuffer = '\n' + dataBuffer;
  400. }
  401. this.asciiCarryover = false;
  402. if (dataBuffer.length && dataBuffer.charAt(dataBuffer.length - 1) == '\r') {
  403. this.asciiCarryover = true;
  404. }
  405. }
  406. this.bytesTotal += dataBuffer.length - diff;
  407. this.dataOutstream.writeBytes(dataBuffer, dataBuffer.length);
  408. }
  409. },
  410. failure : function(ex) {
  411. this.parent.observer.onDebug(ex);
  412. this.parent.observer.onError(gStrbundle.getFormattedString("failedUpload", [this.localPath]));
  413. this.parent.exception = true;
  414. this.parent.kill();
  415. }
  416. };