Changeset 1037
- Timestamp:
- 2008-08-08 04:58:20 (2 years ago)
- Files:
-
- trunk/chrome/content/dta/manager.js (modified) (33 diffs)
- trunk/chrome/locale/en-US/manager.properties (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/chrome/content/dta/manager.js
r1032 r1037 47 47 const NS_ERROR_NET_TIMEOUT = NS_ERROR_MODULE_NETWORK + 14; 48 48 const NS_ERROR_NET_RESET = NS_ERROR_MODULE_NETWORK + 20; 49 49 const NS_ERROR_FTP_CWD = NS_ERROR_MODULE_NETWORK + 22; 50 50 const Cc = Components.classes; 51 51 const Ci = Components.interfaces; … … 75 75 76 76 // in use by chunk.writer... 77 // in use by decompressor... beware, actual size might be more than twice as big! 77 // in use by decompressor... beware, actual size might be more than twice as 78 // big! 78 79 const MAX_BUFFER_SIZE = 5 * 1024 * 1024; 79 80 const MIN_BUFFER_SIZE = 1 * 1024 * 1024; … … 222 223 speed /= d.speeds.length; 223 224 224 // Calculate estimated time 225 // Calculate estimated time 225 226 if (advanced != 0 && d.totalSize > 0) { 226 227 let remaining = Math.ceil((d.totalSize - d.partialSize) / speed); … … 316 317 317 318 if (!this.offline) { 318 // XXX Improve 319 // XXX Improve 319 320 if (Prefs.autoRetryInterval) { 320 321 for (let d in Tree.all) { … … 514 515 if (!Prefs.tempLocation || Preferences.getExt("tempLocation", '') != '') { 515 516 // cannot perform this action if we don't use a temp file 516 // there might be far too many directories containing far too many tmpFiles. 517 // there might be far too many directories containing far too many 518 // tmpFiles. 517 519 // or part files from other users. 518 520 return; … … 645 647 } 646 648 }; 649 647 650 function Visitor() { 648 651 // sanity check … … 651 654 } 652 655 653 varnodes = arguments[0];656 let nodes = arguments[0]; 654 657 for (x in nodes) { 655 658 if (!name || !(name in this.cmpKeys)) { … … 661 664 662 665 Visitor.prototype = { 666 compare: function vi_compare(v) { 667 if (!(v instanceof Visitor)) { 668 return; 669 } 670 671 for (x in this.cmpKeys) { 672 // we don't have this header 673 if (!(x in this)) { 674 continue; 675 } 676 // v does not have this header 677 else if (!(x in v)) { 678 // allowed to be missing? 679 if (this.cmpKeys[x]) { 680 continue; 681 } 682 Debug.logString(x + " missing"); 683 throw new Exception(x + " is missing"); 684 } 685 // header is there, but differs 686 else if (this[x] != v[x]) { 687 Debug.logString(x + " nm: [" + this[x] + "] [" + v[x] + "]"); 688 throw new Exception("Header " + x + " doesn't match"); 689 } 690 } 691 }, 692 save: function vi_save(node) { 693 var rv = {}; 694 // salva su file le informazioni sugli headers 695 for (x in this.cmpKeys) { 696 if (!(x in this)) { 697 continue; 698 } 699 rv[x] = this[x]; 700 } 701 return rv; 702 } 703 }; 704 705 function HttpVisitor() { 706 Visitor.apply(this, arguments); 707 } 708 709 HttpVisitor.prototype = { 663 710 cmpKeys: { 664 'etag': true, // must not be modified from 200 to 206: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 665 //'content-length': false, 711 'etag': true, // must not be modified from 200 to 206: 712 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 713 // 'content-length': false, 666 714 'content-type': true, 667 715 'last-modified': true, // may get omitted later, but should not change 668 'content-encoding': true // must not change, or download will become corrupt. 669 }, 670 type: null, 671 overrideCharset: null, 672 encoding: null, 673 fileName: null, 674 acceptRanges: 'bytes', 675 contentLength: 0, 676 time: null, 677 716 'content-encoding': true // must not change, or download will become 717 // corrupt. 718 }, 678 719 QueryInterface: function(aIID) { 679 720 if ( … … 732 773 } 733 774 if (header == 'etag') { 734 // strip off the "inode"-part apache and others produce, as mirrors/caches usually provide different/wrong numbers here :p 775 // strip off the "inode"-part apache and others produce, as 776 // mirrors/caches usually provide different/wrong numbers here :p 735 777 this[header] = aValue 736 778 .replace(/^(?:[Ww]\/)?"(.+)"$/, '$1') … … 766 808 catch (ex) { 767 809 } 768 },769 compare: function vi_compare(v) {770 if (!(v instanceof Visitor)) {771 return;772 }773 774 for (x in this.cmpKeys) {775 // we don't have this header776 if (!(x in this)) {777 continue;778 }779 // v does not have this header780 else if (!(x in v)) {781 // allowed to be missing?782 if (this.cmpKeys[x]) {783 continue;784 }785 Debug.logString(x + " missing");786 throw new Exception(x + " is missing");787 }788 // header is there, but differs789 else if (this[x] != v[x]) {790 Debug.logString(x + " nm: [" + this[x] + "] [" + v[x] + "]");791 throw new Exception("Header " + x + " doesn't match");792 }793 }794 },795 save: function vi_save(node) {796 var rv = {};797 // salva su file le informazioni sugli headers798 for (x in this.cmpKeys) {799 if (!(x in this)) {800 continue;801 }802 rv[x] = this[x];803 }804 return rv;805 810 } 806 811 }; 807 812 813 Utils.merge(HttpVisitor.prototype, Visitor.prototype); 814 815 function FtpVisitor() { 816 Visitor.apply(this, arguments); 817 } 818 819 FtpVisitor.prototype = { 820 cmpKeys: { 821 'etag': true, 822 }, 823 time: null, 824 visitChan: function fv_visitChan(chan) { 825 try { 826 this.etag = chan.QueryInterface(Ci.nsIResumableChannel).entityID; 827 let m = this.etag.match(/\/(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(?:(\d{2})))?$/); 828 if (m) { 829 let time = m[1] + '/' + m[2] + '/' + m[3]; 830 if (m.length >= 4) { 831 time += ' ' + m[4] + ':' + m[5]; 832 if (m.length >= 7) { 833 time += ':' + m[6]; 834 } 835 this.time = Utils.getTimestamp(time); 836 Debug.logString(this.time); 837 } 838 } 839 } 840 catch (ex) { 841 Debug.log("visitChan:", ex); 842 } 843 } 844 }; 845 846 Utils.merge(FtpVisitor.prototype, Visitor.prototype); 847 808 848 /** 809 849 * Visitor Manager c'tor 850 * 810 851 * @author Nils 811 852 */ … … 818 859 VisitorManager.prototype = { 819 860 /** 820 * Loads a ::save'd JS Array 821 * Will silently bypass failed items!861 * Loads a ::save'd JS Array Will silently bypass failed items! 862 * 822 863 * @author Nils 823 864 */ … … 825 866 for each (let n in nodes) { 826 867 try { 827 this._visitors[n.url] = new Visitor(n.values); 868 let uri = IOService.newURI(n.url, null, null); 869 switch (uri.scheme) { 870 case 'http': 871 this._visitors[n.url] = new HttpVisitor(n.values); 872 break; 873 case 'ftp': 874 this._visitors[n.url] = new FtpVisitor(n.values); 875 break; 876 } 828 877 } 829 878 catch (ex) { … … 834 883 /** 835 884 * Saves/serializes the Manager and associated Visitors to an JS Array 885 * 836 886 * @return A ::load compatible Array 837 887 * @author Nils … … 854 904 /** 855 905 * Visit and compare a channel 906 * 856 907 * @returns visitor for channel 857 * @throws Exception if comparision yield a difference (i.e. channels are not "compatible") 908 * @throws Exception 909 * if comparision yield a difference (i.e. channels are not 910 * "compatible") 858 911 * @author Nils 859 912 */ 860 913 visit: function vm_visit(chan) { 861 var url = chan.URI.spec; 862 863 var visitor = new Visitor(); 864 chan.visitResponseHeaders(visitor); 865 if (url in this._visitors) 866 { 867 this._visitors[url].compare(visitor); 914 let url = chan.URI.spec; 915 916 let visitor; 917 switch(chan.URI.scheme) { 918 case 'http': 919 visitor = new HttpVisitor(); 920 chan.visitResponseHeaders(visitor); 921 break; 922 case 'ftp': 923 visitor = new FtpVisitor(chan); 924 visitor.visitChan(chan); 925 break; 926 } 927 928 if (url in this._visitors) { 929 this._visitors[url].compare(visitor); 868 930 } 869 931 return (this._visitors[url] = visitor); … … 871 933 /** 872 934 * return the first timestamp registered with a visitor 873 * @throws Exception if no timestamp found 935 * 936 * @throws Exception 937 * if no timestamp found 874 938 * @author Nils 875 939 */ … … 1039 1103 1040 1104 /** 1041 *Takes one or more state indicators and returns if this download is in state of any of them 1105 * Takes one or more state indicators and returns if this download is in state 1106 * of any of them 1042 1107 */ 1043 1108 is: function QI_is(state) { … … 1595 1660 Debug.logString("resumeDownload: " + this); 1596 1661 function cleanChunks(d) { 1597 // merge finished chunks together, so that the scoreboard does not bloat that much 1662 // merge finished chunks together, so that the scoreboard does not bloat 1663 // that much 1598 1664 for (let i = d.chunks.length - 2; i > -1; --i) { 1599 1665 let c1 = d.chunks[i], c2 = d.chunks[i + 1]; … … 1630 1696 var rv = false; 1631 1697 1632 // we didn't load up anything so let's start the main chunk (which will grab the info) 1698 // we didn't load up anything so let's start the main chunk (which will 1699 // grab the info) 1633 1700 if (this.chunks.length == 0) { 1634 1701 downloadNewChunk(this, 0, 0, true); … … 1952 2019 this.url = d.urlManager.getURL(); 1953 2020 var referrer = d.referrer; 1954 Debug.logString("starting: " + this.url.url );2021 Debug.logString("starting: " + this.url.url.spec); 1955 2022 1956 2023 this._chan = IOService.newChannelFromURI(this.url.url); … … 1965 2032 // no-op 1966 2033 } 1967 try { 1968 let http = this._chan.QueryInterface(Ci.nsIHttpChannel); 1969 if (c.start + c.written > 0) { 1970 http.setRequestHeader('Range', 'bytes=' + (c.start + c.written) + "-", false); 1971 } 1972 if (this.isInfoGetter && !d.fromMetalink) { 1973 http.setRequestHeader('Accept', 'application/metalink+xml;q=0.9', true); 1974 } 1975 if (referrer instanceof Ci.nsIURI) { 1976 http.referrer = referrer; 1977 } 1978 http.setRequestHeader('Keep-Alive', '', false); 1979 http.setRequestHeader('Connection', 'close', false); 1980 if (d.postData) { 1981 let uc = http.QueryInterface(Ci.nsIUploadChannel); 1982 uc.setUploadStream(new StringInputStream(d.postData, d.postData.length), null, -1); 1983 http.requestMethod = 'POST'; 1984 } 2034 if (this._chan instanceof Ci.nsIHttpChannel) { 2035 try { 2036 Debug.logString("http"); 2037 let http = this._chan.QueryInterface(Ci.nsIHttpChannel); 2038 if (c.start + c.written > 0) { 2039 http.setRequestHeader('Range', 'bytes=' + (c.start + c.written) + "-", false); 2040 } 2041 if (this.isInfoGetter && !d.fromMetalink) { 2042 http.setRequestHeader('Accept', 'application/metalink+xml;q=0.9', true); 2043 } 2044 if (referrer instanceof Ci.nsIURI) { 2045 http.referrer = referrer; 2046 } 2047 http.setRequestHeader('Keep-Alive', '', false); 2048 http.setRequestHeader('Connection', 'close', false); 2049 if (d.postData) { 2050 let uc = http.QueryInterface(Ci.nsIUploadChannel); 2051 uc.setUploadStream(new StringInputStream(d.postData, d.postData.length), null, -1); 2052 http.requestMethod = 'POST'; 2053 } 2054 } 2055 catch (ex) { 2056 Debug.log("error setting up http channel", ex); 2057 // no-op 2058 } 1985 2059 } 1986 catch (ex) { 1987 Debug.log("error setting up channel", ex); 1988 // no-op 2060 else if (this._chan instanceof Ci.nsIFTPChannel) { 2061 try { 2062 let ftp = this._chan.QueryInterface(Ci.nsIFTPChannel); 2063 if (c.start + c.written > 0) { 2064 let resumable = ftp.QueryInterface(Ci.nsIResumableChannel); 2065 resumable.resumeAt(c.start + c.written, ''); 2066 } 2067 } 2068 catch (ex) { 2069 Debug.log('error setting up ftp channel', ex); 2070 } 1989 2071 } 1990 2072 this.c.running = true; … … 2012 2094 return this; 2013 2095 } 2096 Debug.log("Interface not implemented " + iid, Components.results.NS_ERROR_NO_INTERFACE); 2014 2097 throw Components.results.NS_ERROR_NO_INTERFACE; 2015 2098 }, … … 2057 2140 return AuthPrompts.prompter; 2058 2141 } 2059 // for 1.92060 /* this one makes minefield ask for the password again and again :p2061 if ('nsIAuthPromptProvider' in Ci && iid.equals(Ci.nsIAuthPromptProvider)) {2062 return AuthPrompts.prompter.QueryInterface(Ci.nsIAuthPromptProvider);2063 }*/2064 // for 1.92065 2142 if ('nsIAuthPrompt2' in Ci && iid.equals(Ci.nsIAuthPrompt2)) { 2066 2143 return AuthPrompts.authPrompter.QueryInterface(Ci.nsIAuthPrompt2); … … 2096 2173 } 2097 2174 try { 2098 // we want to kill ftp chans as well which do not seem to respond to cancel correctly. 2175 // we want to kill ftp chans as well which do not seem to respond to 2176 // cancel correctly. 2099 2177 if (!this.c.write(aInputStream, aCount)) { 2100 2178 // we already got what we wanted … … 2109 2187 2110 2188 // nsIFTPEventSink 2111 OnFTPControlLog: function(server, msg) {}, 2189 OnFTPControlLog: function(server, msg) { 2190 /* 2191 * Very hacky :p If we don't handle it here, then nsIFTPChannel will + try 2192 * to CWD to the file (d'oh) + afterwards ALERT (modally) that the CWD 2193 * didn't succeed (double-d'oh) 2194 */ 2195 if (!server) { 2196 this._wasRetr = /^RETR/.test(msg) || /^REST/.test(msg); 2197 } 2198 if (server && this._wasRetr && /^[45]/.test(msg)) { 2199 Debug.logString("Server didn't allow retrieving stuff!"); 2200 if (!this.handleError()) { 2201 d.fail(_('servererror'), _('ftperrortext'), _('servererror')); 2202 } 2203 } 2204 // Debug.logString((server ? 's' : 'c') + ': ' + msg); 2205 }, 2112 2206 2113 2207 handleError: function DL_handleError() { … … 2131 2225 2132 2226 Debug.logString("affected: " + c); 2227 d.dumpScoreboard(); 2133 2228 2134 let max = -1, found = -1;2229 let max = -1, found = null; 2135 2230 for each (let cmp in d.chunks) { 2136 2231 if (cmp.start < c.start && cmp.start > max) { 2137 found = i;2232 found = cmp; 2138 2233 max = cmp.start; 2139 2234 } 2140 2235 } 2141 if (found > -1) {2236 if (found) { 2142 2237 Debug.logString("handleError: found joinable chunk; recovering suceeded, chunk: " + found); 2143 d.chunks[found].end = c.end;2238 found.end = c.end; 2144 2239 if (--d.maxChunks == 1) { 2145 // d.resumable = false;2146 } 2147 d.chunks = d.chunks.filter(function(ch) { return ch != c; });2148 d.chunks.sort(function(a, b) { return a.start - b.start; });2240 // d.resumable = false; 2241 } 2242 d.chunks = d.chunks.filter(function(ch) ch != c); 2243 d.chunks.sort(function(a, b) a.start - b.start); 2149 2244 2150 2245 // check for overlapping ranges we might have created … … 2173 2268 return true; 2174 2269 } 2270 Debug.logString("recovery failed"); 2175 2271 return false; 2176 2272 }, … … 2299 2395 // Generic handler for now :p 2300 2396 handleFtp: function DL_handleFtp(aChannel) { 2301 return this.handleGeneric(aChannel); 2397 let c = this.c; 2398 let d = this.d; 2399 try { 2400 let pb = aChannel.QueryInterface(Ci.nsIPropertyBag2); 2401 d.totalSize = Math.max(pb.getPropertyAsInt64('content-length'), 0); 2402 } 2403 catch (ex) { 2404 d.totalSize = 0; 2405 d.resumable = false; 2406 } 2407 2408 try { 2409 aChannel.QueryInterface(Ci.nsIResumableChannel).entityID; 2410 } 2411 catch (ex) { 2412 Debug.logString("likely not resumable or connection refused!"); 2413 if (!this.handleError()) { 2414 // restart download from the beginning 2415 d.fail(_('servererror'), _('ftperrortext'), _('servererror')); 2416 return false; 2417 } 2418 } 2419 2420 try { 2421 let visitor = d.visitors.visit(aChannel.QueryInterface(Ci.nsIChannel)); 2422 } 2423 catch (ex) { 2424 Debug.log("header failed! " + d, ex); 2425 // restart download from the beginning 2426 d.cancel(); 2427 d.resumable = false; 2428 d.safeRetry(); 2429 return false; 2430 } 2431 return false; 2302 2432 }, 2303 2433 … … 2335 2465 }, 2336 2466 2337 // nsIRequestObserver,2467 // nsIRequestObserver, 2338 2468 _supportedChannels: [ 2339 2469 {i:Ci.nsIHttpChannel, f:'handleHttp'}, … … 2352 2482 try { 2353 2483 chan = aRequest.QueryInterface(sc.i); 2354 }2355 catch (ex) {2356 continue;2357 }2358 if (chan) {2359 2484 if ((this.rexamine = this[sc.f](chan))) { 2360 2485 return; 2361 2486 } 2362 2487 break; 2363 } 2488 } 2489 catch (ex) { 2490 // continue 2491 } 2364 2492 } 2365 2493 … … 2369 2497 if (d.fileName.getExtension() == 'metalink') { 2370 2498 d.isMetalink = true; 2371 d.resumable = true;2499 d.resumable = false; 2372 2500 } 2373 2501 … … 2388 2516 } 2389 2517 var nsd = Utils.getFreeDisk(realDest); 2390 // Same save path or same disk (we assume that tmp.avail == dst.avail means same disk) 2518 // Same save path or same disk (we assume that tmp.avail == 2519 // dst.avail means same disk) 2391 2520 // simply moving should succeed 2392 2521 if (d.compression && (!tmp || Utils.getFreeDisk(vtmp) == nsd)) { 2393 // we cannot know how much space we will consume after decompressing. 2394 // so we assume factor 1.0 for the compressed and factor 1.5 for the decompressed file. 2522 // we cannot know how much space we will consume after 2523 // decompressing. 2524 // so we assume factor 1.0 for the compressed and factor 1.5 for 2525 // the decompressed file. 2395 2526 tsd *= 2.5; 2396 2527 } … … 2474 2605 d.markAutoRetry(); 2475 2606 return; 2476 } 2477 2607 } 2608 2609 // work-around for ftp crap 2610 // nsiftpchan for some reason assumes that if RETR fails it is a directory 2611 // and tries to advance into said directory 2612 if (aStatusCode == NS_ERROR_FTP_CWD) { 2613 Debug.logString("Cannot change to directory :p", aStatusCode); 2614 d.cancel(); 2615 return; 2616 } 2617 2478 2618 // routine for normal chunk 2479 2619 Debug.logString(d + ": Chunk " + c.start + "-" + c.end + " finished."); 2480 2620 2481 // rude way to determine disconnection: if connection is closed before download is started we assume a server error/disconnection 2621 // rude way to determine disconnection: if connection is closed before 2622 // download is started we assume a server error/disconnection 2482 2623 if (c.starter && d.is(RUNNING)) { 2483 2624 if (!d.urlManager.markBad(this.url)) { … … 2628 2769 } 2629 2770 else { 2630 qi.hash = null; // to initialize prettyHash 2771 qi.hash = null; // to initialize prettyHash 2631 2772 } 2632 2773 trunk/chrome/locale/en-US/manager.properties
r1031 r1037 62 62 loading=Loading downloads (%S of %S, %S%). Please wait... 63 63 adding=Adding downloads. Please wait... 64 ftperrortext=Wasn't able to communicate with the FTP Server or the Server responded incorrectly
