home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2005 August / PCpro_2005_08.ISO / files / firefox / flashgot-0.5.9.6-fxmztb.xpi / components / flashgotService.js < prev   
Encoding:
JavaScript  |  2005-06-09  |  65.0 KB  |  2,157 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2005 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. function FlashGotDM(name) {
  23.   if(arguments.length>0) {
  24.     this._init(name);
  25.   }
  26. }
  27.  
  28. FlashGotDM.dms=[];
  29. FlashGotDM.dmtests={};
  30. FlashGotDM.executables={};
  31. FlashGotDM.cleanup=function() {
  32.   var name;
  33.   for(name in FlashGotDM.executables) {
  34.     var f=FlashGotDM.executables[name];
  35.     if(f instanceof Components.interfaces.nsIFile) {
  36.       try { f.remove(true); } catch(ex) {}
  37.     }
  38.   }
  39. }
  40.  
  41. FlashGotDM.prototype = {
  42.   _init: function(name) {
  43.     this.name=name;
  44.     const dms=FlashGotDM.dms;
  45.     dms[name]=dms[dms.length]=this;
  46.   }
  47. ,
  48.   _service: null,
  49.   _cookieManager: null,
  50.   _exeFile: false,
  51.   _supported: null
  52. ,
  53.   exeName: "FlashGot.exe",
  54.   askPath: [false,false,false],
  55.   cookieSupport: true
  56. ,  
  57.   get codeName() {
  58.     return this.name.replace(/\W/g,"_");
  59.   }
  60. ,
  61.   get service() {
  62.     return this._service?this._service:this._service=
  63.     Components.classes[SERVICE_CTRID].getService(Components.interfaces.nsISupports).wrappedJSObject;
  64.   }
  65. ,
  66.   get cookieManager() {
  67.     return this._cookieManager?this._cookieManager:this._cookieManager=
  68.       Components.classes["@mozilla.org/cookiemanager;1"
  69.         ].getService(Components.interfaces.nsICookieManager);
  70.   }
  71. ,
  72.   get exeFile() {
  73.     if(typeof(this._exeFile)=="object") return this._exeFile;
  74.     const exeName=this.exeName;
  75.     if(!exeName) return this._exeFile=null;
  76.     if(typeof(FlashGotDM.executables[exeName])=="object") {
  77.       return this._exeFile=FlashGotDM.executables[exeName];
  78.     }
  79.     try {
  80.       const exeFile=Components.classes["@mozilla.org/file/local;1"].createInstance(
  81.         Components.interfaces.nsILocalFile);
  82.       exeFile.initWithPath(this.service.globals.profDir.path);
  83.       exeFile.append(exeName);
  84.       if(exeFile.exists()) {
  85.         try { exeFile.remove(true); } catch(ex) { this.log(ex.message); }
  86.       }
  87.       this._exeFile=this.checkExePlatform(exeFile);
  88.       if(this._exeFile!=null && this.createExecutable()) {
  89.         this.log(this._exeFile.path+" created");
  90.       }
  91.     } catch(ex) {
  92.       this._exeFile=null;
  93.       this.log("Can't init "+exeName+":\n"+ex.message);
  94.     }
  95.     return FlashGotDM.executables[exeName]=this._exeFile;
  96.   }
  97. ,
  98.   checkExePlatform: function(exeFile) {
  99.     return /(\/.*\.exe)|(\\.*\.sh)$/i.test(exeFile.path)?null:exeFile;
  100.   }
  101. ,
  102.   get supported() {
  103.     if(typeof(this._supported)=="boolean") return this._supported;
  104.     if(!this.exeName) return true;
  105.     if(!this.exeFile) return false;
  106.     
  107.     var dmtest;
  108.     if(typeof(FlashGotDM.dmtests[this.exeName])!="string") {
  109.       const dmtestFile=this.service.tmpDir.clone();
  110.       dmtestFile.append(this.exeName+".test");
  111.       try {
  112.         this.launchSupportTest(dmtestFile);
  113.         this.log(dmtest=this.service.readFile(dmtestFile)); 
  114.       } catch(ex) {
  115.         this.log(ex.message);
  116.         dmtest="";
  117.       }
  118.       FlashGotDM.dmtests[this.exeName]=dmtest;
  119.     } else dmtest=FlashGotDM.dmtests[this.exeName];
  120.     return this._supported=dmtest.indexOf(this.name+"|OK")>-1;
  121.   }
  122. ,
  123.   launchSupportTest: function (testFile) {
  124.     this.runNative(["-o",testFile.path],true);
  125.   }
  126. ,
  127.   log: function(msg) {
  128.     this.service.log(msg);
  129.   }
  130. ,
  131.   updateProgress: function(links,idx,len) {
  132.     links.progress.value=50 + 49 * idx / len;
  133.   }
  134. ,
  135.   isValidLink: function(url) {
  136.     return true;
  137.   }
  138. ,
  139.   createJobHeader: function(links, opType) {
  140.     return links.length+";"+this.name+";"+
  141.       (this.service.getPref(this.codeName+".quiet."+opType,false)
  142.         ?this.service.OP_QET:opType)
  143.       +";"+links.folder+";\n"
  144.   }
  145. ,
  146.   createJobBody: function(links) {
  147.     var job="";
  148.     var l,url;
  149.     const len=links.length;
  150.     this.checkCookieSupport();
  151.     var postData=links.postData;
  152.     if(!postData) postData="";
  153.     for(var j=0; j<len; j++) {
  154.       job+="\n"+(url=(l=links[j]).href)+"\n"
  155.       +l._description+"\n"
  156.       +this.getCookie(l,links)+"\n"
  157.       +postData;
  158.       this.updateProgress(links,j,len);
  159.     }
  160.     return job;
  161.   }
  162. ,
  163.   createJob: function(links,opType) {
  164.     var job=this.createJobHeader(links,opType) 
  165.     + this.getReferrer(links)
  166.     + this.createJobBody(links)+"\n";
  167.      if(job.substring(job.length-1)!="\n") {
  168.       job+="\n";
  169.      }
  170.     if(typeof(links.document)=="object") {
  171.       job+= links.document.referrer+ "\n" +links.document.cookie;
  172.     } else {
  173.       job+="\n";
  174.     }
  175.     return job;
  176.   }
  177. ,
  178.   _bgJob: true,
  179.   get bgJob() {
  180.     return this._bgJob && this.service.bgProcessing; 
  181.   }
  182. ,
  183.   download: function(links, opType) {
  184.     try {
  185.       links.folder=(links.length>0)?this.selectFolder(links,opType):""; 
  186.       const dm=this;
  187.       var asyncError=null;
  188.       function performJob() {
  189.         try {
  190.           dm.performJob(dm.createJob(links,opType));
  191.         } catch(ex) {
  192.           asyncError=ex;
  193.         }
  194.         links.progress.value=100;
  195.       }
  196.       if(links.length>1 && this.bgJob) {
  197.         this.log("Async download job");
  198.         this.service.runAsync(
  199.           performJob,
  200.           function() { if(asyncError) dm.log(asyncError.message); }
  201.         );
  202.       } else {
  203.         this.log("Synchronous download job");
  204.         performJob();
  205.       }
  206.     } catch(ex) {
  207.       this.log(ex.message);
  208.       links.progress.value=100;
  209.     }
  210.   }
  211. ,
  212.   getReferrer: function(links) {
  213.     return this.service.getPref("autoReferrer",true)?
  214.       (links.referrer?links.referrer
  215.       :typeof(links.document)=="object"?links.document.URL
  216.       :links[0]?links[0].href:"about:blank"
  217.       ):this.service.getPref("fakeReferrer","");
  218.   }
  219. ,
  220.   checkCookieSupport: function() {
  221.     this.getCookie=this.cookieSupport && !this.service.getPref("omitCookies")
  222.     ?this._getCookie
  223.     :function() { return ""; }
  224.     ;
  225.   }
  226. ,
  227.   getCookie: function() { return ""; }
  228. ,
  229.   _getCookie: function(link,links) {
  230.     if(!this.cookieSupport) return (this.getCookie=function() { return ""; })();
  231.     
  232.     var host,cookies;
  233.     if(cookies=links.cookies) {
  234.       host=link.host;
  235.       if(host) {
  236.         var c=cookies[host];
  237.         return c?c:"";
  238.       }
  239.       return "";
  240.     }
  241.     
  242.     var j,objCookie;
  243.     const hostCookies={};
  244.     
  245.     
  246.     var rxHost=/http[s]{0,1}:\/\/([^\/]+\.[^\/]+)/i;
  247.     var l,parts;
  248.     for(j=links.length; j-->0;) {
  249.       l=links[j];
  250.       parts=l.href.match(rxHost);
  251.       if(parts) {
  252.         hostCookies[l.host=parts[1]]="";
  253.       } else {
  254.         l.host=null;
  255.       }
  256.     }
  257.     
  258.     var cookieHost,cookieTable,tmpCookie;
  259.     const domainCookies={};
  260.  
  261.     for(var iter = this.cookieManager.enumerator; iter.hasMoreElements();) {
  262.       if((objCookie=iter.getNext()) instanceof Components.interfaces.nsICookie) {
  263.         cookieHost=objCookie.host;
  264.         if(cookieHost.charAt(0)==".") {
  265.           cookieHost=cookieHost.substring(1);
  266.           cookieTable=domainCookies;
  267.           if(typeof(tmpCookie=domainCookies[cookieHost])!="string") {
  268.             tmpCookie="";
  269.           }
  270.         } else {
  271.           if(typeof(tmpCookie=hostCookies[cookieHost])!="string") continue;
  272.           cookieTable=hostCookies;
  273.         }
  274.         cookieTable[cookieHost]=tmpCookie.concat(objCookie.name+"="+objCookie.value+"; ");
  275.       }
  276.     }
  277.     
  278.    
  279.     for(cookieHost in hostCookies) {
  280.       var dotPos;
  281.       for(host=cookieHost; (dotPos=host.indexOf('.'))>=0; ) { 
  282.         if(tmpCookie=domainCookies[host]) {
  283.           hostCookies[cookieHost]+=tmpCookie;
  284.         }
  285.         host=host.substring(dotPos+1);
  286.       }
  287.     }
  288.     
  289.     links.cookies=hostCookies;
  290.     return this.getCookie(link,links);
  291.   }
  292. ,
  293.   createJobFile: function(job) {
  294.     const jobFile=this.service.tmpDir.clone();
  295.     jobFile.append("flashgot.fgt");
  296.     jobFile.createUnique(0,0700);
  297.     this.service.writeFile(jobFile,job);
  298.     return jobFile;
  299.   }
  300.   _waitForNative: true,
  301.   get waitForNative() {
  302.     return this._waitForNative && ! this.service.isUIThread;
  303.   }
  304. ,
  305.   performJob: function(job) {
  306.     const jobFile=this.createJobFile(job);
  307.     this.runNative([jobFile.path],this.waitForNative);
  308.   }
  309. ,
  310.   createExecutable: function() {
  311.     const exeFile=this.exeFile;
  312.     if(!exeFile) return false;
  313.     
  314.     const cc=Components.classes;
  315.     const ci=Components.interfaces;
  316.     const ios=cc['@mozilla.org/network/io-service;1'].getService(ci.nsIIOService);
  317.     const bis=cc['@mozilla.org/binaryinputstream;1'].createInstance(ci.nsIBinaryInputStream);
  318.     
  319.     var channel;
  320.     bis.setInputStream((
  321.       channel=
  322.         ios.newChannel("chrome://flashgot/content/"+this.exeName,null,null)
  323.     ).open())
  324.     ;
  325.     const bytesCount=channel.contentLength;
  326.     
  327.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  328.       ci.nsIFileOutputStream);
  329.     
  330.     try {
  331.       
  332.       os.init(exeFile,0x02 | 0x08, 0700, 0);
  333.       const bos=cc['@mozilla.org/binaryoutputstream;1'].createInstance(ci.nsIBinaryOutputStream);
  334.       bos.setOutputStream(os);
  335.       bos.writeByteArray(bis.readByteArray(bytesCount),bytesCount);
  336.       bos.close();
  337.  
  338.     } catch(ioErr) { // locked?
  339.       try {
  340.         if(exeFile.exists()) { // security check: it must be the right exe!
  341.           const testBis=cc['@mozilla.org/binaryinputstream;1'].createInstance(
  342.             ci.nsIBinaryInputStream);
  343.           testBis.setInputStream(
  344.             (channel=ios.newChannelFromURI(ios.newFileURI(exeFile))).open());
  345.           const error=new Error("Old, corrupt or unlegitemately modified "
  346.             +exeFile.path
  347.             +".\nThe file is locked: please delete it manually\n");
  348.             +ioErr.message;
  349.           if(channel.contentLength!=bytesCount) throw error;
  350.          
  351.           const legitimateData=bis.readByteArray(bytesCount);
  352.           const testData=testBis.readByteArray(bytesCount);
  353.           for(var j=bytesCount; j-->0;) {
  354.             if(legitimateData[j]!=testData[j]) throw new error;
  355.           }
  356.         } else throw ioErr;
  357.       } catch(unrecoverableErr) {
  358.          this.log("Error creating native executable\n"+exeFile.path+"\n"+unrecoverableErr.message);
  359.       }
  360.     } finally {
  361.       os.close();
  362.       bis.close();
  363.     }
  364.     
  365.     return true;
  366.   }
  367. ,
  368.   runNative: function(args,blocking,exeFile) {
  369.     try {
  370.       if(typeof(exeFile)=="object"
  371.         || (exeFile=this.exeFile).exists()
  372.         || this.createExecutable()) {
  373.         const proc=Components.classes['@mozilla.org/process/util;1'].createInstance(
  374.           Components.interfaces.nsIProcess);
  375.         proc.init(exeFile);
  376.         this.log("Running "+exeFile.path+" ("+(blocking?"blocking":"async")+")");
  377.         proc.run(blocking,args,args.length,{});
  378.         if(blocking && proc.exitValue!=0) {
  379.           this.log("Warning: native invocation of\n"
  380.             +exeFile.path
  381.             +"\nwith arguments <"
  382.             +args.join(" ")
  383.             +">\nreturned "+proc.exitValue);
  384.         }
  385.         return proc.exitValue;
  386.       } else {
  387.         this.log("Bad executable "+exeFile);
  388.       }
  389.     } catch(err) {
  390.       this.log("Error running native executable:\n"+exeFile.path+" "+args.join(" ")+"\n"+err.message);
  391.     }  
  392.     return 0xffffffff;
  393.   }
  394. ,
  395.   getWindow: function() {
  396.     return this.service.getWindow();
  397.   }
  398. ,
  399.   selectFolder: function(links,opType) { 
  400.     const cc=Components.classes;
  401.     const ci=Components.interfaces;
  402.     const downloadDirPref="browser.download.dir";
  403.     const autoPref_FF="browser.download.useDownloadDir";
  404.     const autoPref_Moz="browser.download.autoDownload";
  405.     
  406.     var initialDir=null;
  407.     var downloadDir=null;
  408.     links.quickDownload=false;
  409.     const pref = cc["@mozilla.org/preferences-service;1"].getService(ci.nsIPrefBranch);
  410.     
  411.     try {
  412.       initialDir = pref.getComplexValue("browser.download.dir", ci.nsILocalFile);
  413.     } catch(ex) {}
  414.    
  415.     try {
  416.       if(links.quickDownload=pref.getBoolPref(autoPref_FF)) {
  417.         downloadDir=pref.getComplexValue("browser.download.defaultFolder",ci.nsILocalFile);
  418.       }
  419.     } catch(noFFEx) {
  420.       try {
  421.         links.quickDownload=pref.getBoolPref(autoPref_Moz);
  422.         downloadDir=initialDir;
  423.       } catch(noMozEx) {
  424.         links.quickDownload=false;
  425.       }
  426.     } 
  427.    
  428.     if(!this.askPath[opType]) return "";
  429.     
  430.     if(downloadDir && downloadDir.exists() && downloadDir.isDirectory()  && 
  431.         links.quickDownload) {
  432.       return downloadDir.path;
  433.     }
  434.     
  435.     var title;
  436.     try {
  437.       var bundle = cc["@mozilla.org/intl/stringbundle;1"].getService(ci.nsIStringBundleService);
  438.       bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  439.       title = bundle.GetStringFromName("myDownloads");
  440.     } catch(ex) {
  441.       title="Download directory";
  442.     }
  443.     title='FlashGot ('+this.name+') - '+title;
  444.     
  445.    
  446.     const fp = cc["@mozilla.org/filepicker;1"].createInstance(ci.nsIFilePicker);
  447.     const win=this.getWindow();
  448.     fp.init(win, title, ci.nsIFilePicker.modeGetFolder);
  449.     try {
  450.       if (initialDir &&  initialDir.exists() && initialDir.isDirectory()) {
  451.         fp.displayDirectory = initialDir;
  452.       }
  453.     } catch (ex) { this.log(ex); }
  454.     
  455.     fp.appendFilters(ci.nsIFilePicker.filterAll);
  456.  
  457.     if (fp.show()==ci.nsIFilePicker.returnOK) {
  458.       var localFile = fp.file.QueryInterface(ci.nsILocalFile);
  459.       pref.setComplexValue(downloadDirPref, ci.nsILocalFile, localFile);
  460.       var path=new String(localFile.path);
  461.       path._fgSelected=true;
  462.       return path;
  463.     }
  464.     
  465.     throw new Error("Download cancelled by user");
  466.   }
  467.   
  468. }
  469.  
  470. function FlashGotDMX(name,cmd, argsTemplate) {
  471.   if(arguments.length!=0) {
  472.     this._init(name);
  473.     const cmds=FlashGotDMX.prototype.unixCmds;
  474.     cmds[cmds.length]={longName: name, shortName: cmd};
  475.     this.unixCmd=cmd;
  476.     if(argsTemplate) this.argsTemplate=argsTemplate;
  477.   }
  478. }
  479. FlashGotDMX.prototype=new FlashGotDM();
  480. FlashGotDMX.constructor=FlashGotDMX;
  481. FlashGotDMX.prototype.exeName="flashgot.sh";
  482. FlashGotDMX.prototype.cookieSupport=false;
  483. FlashGotDMX.prototype.askPath=[true,true,true];
  484. FlashGotDMX.prototype.unixCmds=[];
  485. FlashGotDMX.prototype.unixShell=null;
  486. FlashGotDMX.prototype.argsTemplate="[URL]";
  487. FlashGotDMX.prototype.launchSupportTest=function(testFile) {
  488.   const cmds=this.unixCmds;
  489.   var script="(\n";
  490.   var cmd;
  491.   for(var j=cmds.length; j-->0;) {
  492.     cmd=cmds[j];
  493.     script+=" [ -x \"`which '"+cmd.shortName+"'`\" ] && echo '"
  494.       +cmd.longName+"|OK' || echo '"+cmd.longName+"|KO'\n"; 
  495.   }
  496.   script+=") > '"+ testFile.path + "'\n"; 
  497.   this.performJob(script,true);
  498. };
  499. FlashGotDMX.prototype.createCmdLine=function(URL,REFERER,COOKIE,FOLDER,POST) {
  500.   return this.unixCmd+ " " +
  501.     this.argsTemplate.replace(/\[(.*?)\b(URL|REFERER|COOKIE|FOLDER|POST)\b(.*?)\]/g,
  502.       function(all,before,parm,after) { 
  503.           v=eval("typeof("+parm+")=='string'?"+parm+":null"); 
  504.           return v
  505.             ?before+v+after
  506.             :"";
  507.       }
  508.       ) +" &\n";
  509. };
  510. FlashGotDMX.prototype.createJob=function(links,opType) {
  511.   function shellEsc(s) {
  512.     return s?s.replace(/([\\\*\?\[\]\$&<>\|\(\)\{\};"'`])/g,"\\$1").replace(/\s/g,"\\ "):"";
  513.   }
  514.   // basic implementation
  515.   const len=links.length;
  516.   const folder=shellEsc(links.folder);
  517.   const referrer=shellEsc(this.getReferrer(links));
  518.   const postData=shellEsc(links.postData);
  519.   var job="";
  520.   var l,url;
  521.   this.checkCookieSupport();
  522.   for(var j=0; j<len; j++) {
  523.     l=links[j];
  524.     url=l.href;
  525.     job+=this.createCmdLine(
  526.       shellEsc(url), 
  527.       referrer, 
  528.       shellEsc(this.getCookie(l,links)), 
  529.       folder, 
  530.       postData);
  531.     this.updateProgress(links,j,len);
  532.   }
  533.   return job;
  534. };
  535. FlashGotDMX.prototype.performJob=function(job,blocking) {
  536.   const jobFile=this.createJobFile("#!"+this.unixShell.path+"\n"+job);
  537.   jobFile.permissions=0700;
  538.   this.runNative([],
  539.     this.waitForNative || (typeof(blocking)!="undefined" && blocking),
  540.     jobFile);
  541. };
  542. FlashGotDMX.prototype.checkExePlatform=function(exeFile) {
  543.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  544.     Components.interfaces.nsILocalFile);
  545.   try {
  546.     f.initWithPath("/bin/sh");
  547.     if(f.exists()) {
  548.       FlashGotDMX.prototype.unixShell=f;
  549.       return exeFile;
  550.     }
  551.     this.log(f.path+" not found");
  552.   } catch(ex) {
  553.     this.log(ex.message);
  554.   }
  555.   return null;
  556. };
  557. FlashGotDMX.prototype.createExecutable=function() {
  558.   return false;
  559. };
  560.  
  561. function FlashGotDMMac(name, creatorId, macAppName) {
  562.   if(arguments.length!=0) {
  563.     this._initMac(name,creatorId,macAppName);
  564.   }
  565. }
  566. FlashGotDMMac.prototype=new FlashGotDM();
  567. FlashGotDMMac.constructor=FlashGotDMMac;
  568. FlashGotDMMac.prototype.exeName="flashgot-mac.sh";
  569. FlashGotDMMac.prototype.cookieSupport=false;
  570. FlashGotDMMac.prototype.OSASCRIPT="/usr/bin/osascript";
  571. FlashGotDMMac.prototype.macCreators=[];
  572. FlashGotDMMac.prototype._initMac=function(name, creatorId, macAppName) {
  573.   this._init(name);
  574.   if(creatorId) {
  575.     const creators=FlashGotDMMac.prototype.macCreators;
  576.     creators[creators.length]={name: name, id: creatorId};
  577.   }
  578.   this.macAppName = macAppName?macAppName:name;
  579. };
  580. FlashGotDMMac.prototype.createScriptLauncher=function(scriptPath) {
  581.   return "#!/bin/sh\n"
  582.     +this.OSASCRIPT+" '"+scriptPath+"'";
  583. };
  584. FlashGotDMMac.prototype.checkExePlatform=function(exeFile) {
  585.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  586.     Components.interfaces.nsILocalFile);
  587.   try {
  588.     f.initWithPath(this.OSASCRIPT);
  589.     if(f.exists()) return exeFile;
  590.     this.log(f.path+" not found");
  591.   } catch(ex) {
  592.     this.log(ex.message);
  593.   }
  594.   return null;
  595. };
  596. FlashGotDMMac.prototype.createExecutable=function() {
  597.   const exeFile=this.exeFile;
  598.   if(exeFile) {
  599.     try {
  600.      const script=this.service.tmpDir.clone();
  601.      script.append("flashgot-test.scpt");
  602.      FlashGotDMMac.prototype.testAppleScript=script;
  603.      script.createUnique(0,0700);
  604.      if(exeFile.exists()) exeFile.remove(true);
  605.      exeFile.create(0,0700);
  606.      this.service.writeFile(exeFile,this.createScriptLauncher(script.path));
  607.      exeFile.permissions=0700;
  608.      return true;
  609.     } catch(ex) {
  610.       this.log(ex.message);
  611.     }
  612.   }
  613.   return false;
  614. };
  615. FlashGotDMMac.prototype.launchSupportTest=function(testFile) {
  616.   const creators=FlashGotDMMac.prototype.macCreators;
  617.   const RESP="    do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|";
  618.   function response(msg) {
  619.     return RESP+msg+"'\"\n";
  620.   }
  621.   var s="on test(theName, theCreator)\n"
  622.        +" tell app \"Finder\"\n"
  623.        +"  set theResponse to \"KO\"\n"
  624.        +"  try\n"
  625.        +"   get name of application file id theCreator\n"
  626.        +"   if result contains theName then\n"
  627.        +"     set theResponse to \"OK\"\n"
  628.        +"   end if\n"
  629.        +"  on error\n"
  630.        +"  end try\n"
  631.        +"  do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|\" & theResponse & \"'\"\n"
  632.        +" end tell\n"
  633.        +"end test\n"
  634.        +"\n";
  635.   for(var j=creators.length; j-->0;) {
  636.      s+='get test("'+creators[j].name+'","'+creators[j].id+'")\n'; 
  637.    }
  638.    this.service.writeFile(this.testAppleScript,s);
  639.    this.runNative([],true,this.exeFile);
  640. };
  641. FlashGotDMMac.prototype.performJob=function(job) {
  642.   const script=this.createJobFile(job);
  643.   const launcher=this.createJobFile(this.createScriptLauncher(script.path));
  644.   launcher.permissions=0700;
  645.   this.runNative([],this.waitForNative,launcher);
  646. };
  647. FlashGotDMMac.prototype.createJob=function(links,opType) {
  648.   const referrer=this.getReferrer(links);
  649.   var job = "tell application \""+ this.macAppName+ "\"\n";
  650.   for(var j=0,len=links.length; j<len; j++) {
  651.     job+="GetURL \""+links[j].href+"\" from \""+ referrer  +"\"\n";
  652.     this.updateProgress(links,j,len);
  653.   }
  654.   job+="end tell\n";
  655.   return job;
  656. };
  657.  
  658. // END DMS classes
  659.  
  660. // DMS initialization
  661.  
  662. FlashGotDM.initDMS=function() {
  663.   var dm;
  664.   
  665.   dm=new FlashGotDM("Download Accelerator Plus");
  666.   dm.exclusive=true;
  667.   
  668.   new FlashGotDM("Download Master");
  669.   
  670.   new FlashGotDM("FlashGet");
  671.   
  672.   dm=new FlashGotDM("Free Download Manager");
  673.   dm._waitForNative=false;
  674.   
  675.   new FlashGotDM("FreshDownload");
  676.   
  677.   dm=new FlashGotDM("GetRight");
  678.   dm.super_download=FlashGotDM.prototype.download;
  679.   dm.super_createJob=FlashGotDM.prototype.createJob;
  680.   dm.download=function(links, opType) {
  681.     const service=this.service;
  682.     if(opType==service.OP_ONE && !service.getPref("GetRight.quick")) {
  683.       opType=service.OP_SEL;
  684.     }
  685.     this.super_download(links,opType);
  686.   };
  687.   dm.createJob=function(links, opType) {
  688.     const service=this.service;
  689.     var folder=links.folder;
  690.     if(!(folder && folder._fgSelected)) folder=false;
  691.     
  692.     var referrer=this.getReferrer(links);
  693.     
  694.     switch(opType) {
  695.       case service.OP_ONE:
  696.         var job=this.super_createJob(links,opType);
  697.         if(this.service.getPref("GetRight.old")) job+="\nold";
  698.         return job;
  699.       case service.OP_SEL:
  700.       case service.OP_ALL:
  701.         var urlList="";
  702.         var referrerLine=(referrer && referrer.length>0)?"\r\nReferer: "+referrer+"\r\n":"\r\n";
  703.         var l,decodedURL,fileSpec,cookie;
  704.         for(var j=0, len=links.length; j<len; j++) {
  705.           l=links[j];
  706.           
  707.           if(folder) {
  708.             var decodedURL=l.href;
  709.             try { decodedURL=decodeURI(decodedURL) } catch(ex) {};
  710.             var urlParts=decodedURL.match(/\/\/.+[=\/]([^\/]+\.[\w\d]+)/);
  711.             if(!urlParts) urlParts=l.href.match(/.*\/(.+)/);
  712.             if(urlParts && (fileSpec=urlParts[1])
  713.               // && (links.length==1 ||  !/\.(php|[\w]?htm[l]?|asp|jsp|do|xml|rdf|\d+)$/i.test(fileSpec))
  714.              ) {
  715.               fileSpec="File: "+folder+"\\"+fileSpec+"\r\n";
  716.             } else continue;
  717.           } else fileSpec="";
  718.           urlList+="URL: "+l.href
  719.             +"\r\nDesc: "+l._description
  720.             +referrerLine+fileSpec;
  721.           if(cookie=this.getCookie(l,links)) {
  722.             urlList+="Cookie: " + cookie + "\r\n";
  723.           }
  724.           this.updateProgress(links,j,len);
  725.         }
  726.         var file=service.tmpDir.clone();
  727.         file.append("flashgot.grx");
  728.         file.createUnique(0,0600);
  729.         service.writeFile(file,urlList);
  730.         referrer=file.path;
  731.         break;
  732.     }
  733.     var cmdOpts="/Q";
  734.     if(service.getPref("GetRight.autostart",false)) { // CHECK ME!!!
  735.       cmdOpts+="\n /AUTO";
  736.     }
  737.     return this.createJobHeader({ length: 0, folder: "" },opType) +
  738.       referrer + "\n" + cmdOpts;
  739.   };
  740.   dm.askPath=[false,true,true];
  741.   
  742.   new FlashGotDM("HiDownload");
  743.   new FlashGotDM("InstantGet");
  744.   new FlashGotDM("Internet Download Accelerator");
  745.  
  746.   var lg2002=new FlashGotDM("LeechGet 2002");
  747.   var lg2004=new FlashGotDM("LeechGet");
  748.   lg2004._bgJob=lg2002._bgJob=false;
  749.   lg2004.super_createJob=lg2002.super_createJob=FlashGotDM.prototype.createJob;
  750.   lg2004.createJob=lg2002.createJob=function(links, opType) {
  751.     const service=this.service;
  752.     var referrer;
  753.     switch(opType) {
  754.       case service.OP_ONE:
  755.         return this.super_createJob(links, links.quickDownload?service.OP_ONE:service.OP_SEL);
  756.       case service.OP_SEL:
  757.         var htmlDoc="<html><head><title>FlashGot selection</title></head><body>";
  758.         var l;
  759.         for(var j=0, len=links.length; j<len; j++) {
  760.           l=links[j];
  761.           var des=l._description;
  762.           var tag=l.tagName?l.tagName.toLowerCase():"";
  763.           htmlDoc=htmlDoc.concat(tag=="img"
  764.             ?"<img src=\""+l.href+"\" alt=\""+des
  765.               +"\" width=\""+l.width+"\" height=\""+l.height+
  766.               "\" />\n"
  767.             :"<a href=\""+l.href+"\">"+des+"</a>\n");
  768.           this.updateProgress(links,j,len);
  769.         }
  770.         referrer=service.httpServer.addDoc(
  771.           htmlDoc.concat("</body></html>")
  772.         );
  773.         break;
  774.        default:
  775.         referrer=links.document.URL;
  776.         if(referrer.match(/^\s*file:/i)) { // fix for local URLs
  777.           // we serve local URLs through built-in HTTP server...
  778.           return this.createJob(links,service.OP_SEL);
  779.         }
  780.     }
  781.     return this.createJobHeader({ length: 0, folder: "" },opType)+referrer+"\n";
  782.   };
  783.  
  784.   new FlashGotDM("Net Transport");
  785.   new FlashGotDM("NetAnts");
  786.   new FlashGotDM("Mass Downloader");
  787.   
  788.   new FlashGotDM("ReGet"); 
  789.   
  790.   const httpFtpRx=/^(http:|ftp:)/;
  791.   const httpFtpValidator=function(url) {
  792.     return this.validLinkRx.test(url);
  793.   };
  794.   dm=new FlashGotDM("Star Downloader");
  795.   dm.cookieSupport=false;
  796.   dm.isValidLink=httpFtpValidator;
  797.   dm._waitForNative=false;
  798.   
  799.   dm=new FlashGotDM("TrueDownloader");
  800.   dm.isValidLink=httpFtpValidator;
  801.   dm._waitForNative=false;
  802.   
  803.   new FlashGotDM("Thunder");
  804.   new FlashGotDM("WellGet");
  805.   
  806.   dm=new FlashGotDMX("Aria","aria", "[-r REFERER] [-d FOLDER] -g [URL]");
  807.   dm.createJob=function(links,opType) {
  808.     return FlashGotDMX.prototype.createJob.call(this,links,opType) + "\nsleep 4\n" + this.unixCmd+" -s &\n";
  809.   };
  810.   dm._waitForNative=false;
  811.   
  812.   dm=new FlashGotDMX("Downloader 4 X (nt)","nt");
  813.   dm.createJob=function(links,opType) {
  814.     return this.unixCmd+"&\nsleep 1\n" +
  815.       (links.folder && links.folder._fgSelected
  816.       ? this.unixCmd+" -d '"+links.folder+"'\n"
  817.       :"") + 
  818.       FlashGotDMX.prototype.createJob.call(this,links,opType);
  819.   };
  820.   (new FlashGotDMX("Downloader 4 X","d4x")).createJob=dm.createJob;
  821.   
  822.   dm=new FlashGotDMX("KDE KGet","kget");
  823.   dm.askPath=[false,false,false];
  824.   dm.createJob=function(links,opType) {
  825.     return FlashGotDMX.prototype.createJob.call(this,links,opType);
  826.   };
  827.   
  828.   // Needs a lot of work, not ready for prime time
  829.  
  830.   dm=new FlashGotDMX("cURL","curl", '-L -O [--referer REFERER] [-b COOKIE] [-d POST] [URL]');
  831.   dm.createJob=function(links,opType) {
  832.     var job="[ -x \"`which 'xterm'`\" ] &&  CURL_CMD='xterm -e curl' || CURL_CMD='curl'\n";
  833.     if (links.folder) job+="cd '"+links.folder+"'\n";
  834.     this.unixCmd="$CURL_CMD";
  835.     return job + FlashGotDMX.prototype.createJob.call(this,links,opType);
  836.   };
  837.  
  838.   
  839.   function FlashGotDMSD(version) {
  840.     this._initMac("Speed Download "+version,"Spee");
  841.     this.version=version;
  842.   };
  843.   FlashGotDMSD.prototype=new FlashGotDMMac();
  844.   FlashGotDMSD.prototype.createJob=function(links,opType) {
  845.     const v=this.version;
  846.    
  847.     var job = "tell app \""+ this.macAppName+ "\" to AddURL {";
  848.     var urlList=[];
  849.     var cookieList=[];
  850.     var l;
  851.     for(var j=0,len=links.length; j<len; j++) {
  852.       l=links[j];
  853.       urlList[urlList.length]='"'+l.href+'"';
  854.       if(v>2) {
  855.         cookieList[cookieList.length]='"'+this.getCookie(l,links)+'"';
  856.       }
  857.       this.updateProgress(links,j,len);
  858.     }
  859.     job+=urlList.join(',')+"}";
  860.     if(v>2) {
  861.       if(links.postData && links.postData.length) { 
  862.         job+=' with form data "'+links.postData+'"';
  863.       }
  864.       const referer=this.getReferrer(links);
  865.       if(referer && referer.length) {
  866.         job+=' from "'+referer+'"';
  867.       }
  868.       if(cookieList.length) {
  869.         job+=' with cookies {' + cookieList.join(',') + '}';
  870.       }
  871.     }  
  872.     return job;
  873.   };
  874.   
  875.   new FlashGotDMSD(2);
  876.   new FlashGotDMSD(3).cookieSupport=true;
  877.  
  878.   new FlashGotDMMac("iGetter","iGET");
  879.   
  880.   FlashGotDM.dms.sort(function(a,b) { a=a.name.toLowerCase(); b=b.name.toLowerCase(); return a>b?1:a<b?-1:0; });
  881.   
  882.   
  883. };
  884. FlashGotDM.initDMS();
  885.  
  886. const SHUTDOWN="profile-before-change";
  887. const STARTUP="profile-after-change";
  888.  
  889. function FlashGotService() {
  890.   
  891.   const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  892.     Components.interfaces.nsIObserverService);
  893.   
  894.   osvr.addObserver(this,SHUTDOWN,false);
  895.   osvr.addObserver(this,"xpcom-shutdown",false);
  896.   osvr.addObserver(this,STARTUP,false);
  897.   
  898.   Components.classes["@mozilla.org/uriloader;1"].getService(
  899.     Components.interfaces.nsIURILoader).registerContentListener(this);
  900.   
  901. }
  902.  
  903. FlashGotService.prototype = {
  904.   OP_ONE: 0, 
  905.   OP_SEL: 1,
  906.   OP_ALL: 2,
  907.   OP_QET: 3
  908. ,
  909.   get wrappedJSObject() {
  910.     return this;
  911.   }
  912. ,
  913.   unregister: function() {
  914.     try {
  915.       const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  916.       Components.interfaces.nsIObserverService);
  917.       osvr.removeObserver(this,SHUTDOWN);
  918.       osvr.removeObserver(this,"xpcom-shutdown");
  919.       osvr.removeObserver(this,STARTUP);
  920.       Components.classes["@mozilla.org/uriloader;1"].getService(
  921.         Components.interfaces.nsIURILoader).unRegisterContentListener(this);
  922.     } catch(ex) {
  923.       this.log("Error unregistering service as observer: "+ex);
  924.     }
  925.   }
  926. ,
  927.   QueryInterface: function(iid) {
  928.      xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_NO_INTERFACE);
  929.      return this;
  930.   }
  931. ,
  932.   /* nsIObserver */  
  933.   observe: function(subject, topic, data) {
  934.     if(subject==this.prefs) {
  935.       this.syncPrefs(data);
  936.     } else {
  937.       switch(topic) {
  938.         case "xpcom-shutdown":
  939.           this.unregister();
  940.           break;
  941.         case SHUTDOWN: 
  942.           this.cleanup();
  943.           break;
  944.         case STARTUP:
  945.           // apparently the following caused problems reported in 0.5.9.5
  946.           // this.initGlobals(); 
  947.           break;
  948.       }
  949.     }
  950.   }
  951. ,
  952.   syncPrefs: function(name) {
  953.     this.logEnabled=this.getPref("logEnabled",true);
  954.     if(name) {
  955.       switch(name) {
  956.       case "hide-icons":
  957.         var w;
  958.         for(var wins=this.windowMediator.getEnumerator(null);   wins.hasMoreElements();) {
  959.            w=wins.getNext();
  960.            if(typeof(w.flashGot)=="object" && w.flashGot.toggleMainMenuIcon) {
  961.              w.flashGot.toggleMainMenuIcon();
  962.            }
  963.         }
  964.       }
  965.     }
  966.   }
  967. ,
  968.   _shouldIntercept: function(contentType) {
  969.     if(this.bypassAutostart || !this.DMS.found) { 
  970.       return false;
  971.     }
  972.     if(this.forceAutostart) return true;
  973.     if(!this.getPref("autoStart",true)) return false;
  974.     
  975.     if(this.getPref("interceptAll",false)) { 
  976.       if(!/\bxpinstall\b/.test(contentType)) {
  977.         return true;
  978.       }
  979.     }
  980.     
  981.     if(contentType=="application/x-unknown-content-type") return false;
  982.      var ms=Components.classes['@mozilla.org/uriloader/external-helper-app-service;1'
  983.       ].getService(Components.interfaces.nsIMIMEService);
  984.     const exts=this.extensions;
  985.     for(var j=exts.length; j-->0;) {
  986.       if(contentType==ms.getTypeFromExtension(exts[j])) return true;
  987.     }    
  988.     
  989.   }
  990.   _willHandle: function(url, contentType) {
  991.     if(!/^(http|https|ftp|sftp|rtsp|mms):/i.test(url) ) {
  992.       if((/^\s*javascript/i).test(url)) this.log("JavaScript url intercepted: "+url);
  993.       return false;
  994.     }
  995.     return true;
  996.   }
  997. ,
  998.   /* nsIURIContentListener */
  999.   canHandleContent: function(contentType, isContentPreferred, desiredContentType) {
  1000.     return this._shouldIntercept(contentType);
  1001.   }
  1002. ,
  1003.   doContent: function(contentType, isContentPreferred, channel, contentHandler) {
  1004.     const ci=Components.interfaces;
  1005.     channel.QueryInterface(ci.nsIChannel);
  1006.     const uri=channel.URI;
  1007.     
  1008.     if(!this._willHandle(uri.spec,contentType)) {
  1009.       throw new Error("FlashGot not interested in "+contentType+" from "+uri.spec);
  1010.     }
  1011.     
  1012.     this.log("Intercepting download...");
  1013.  
  1014.     const pathParts=uri.path.split(/\//);
  1015.     var links=[ {
  1016.      href: channel.URI.spec, 
  1017.      title: pathParts[pathParts.length-1]
  1018.     } ];
  1019.     
  1020.     if(channel instanceof ci.nsIHttpChannel) {
  1021.       links.referrer=channel.referrer.spec;
  1022.       if(channel instanceof ci.nsIUploadChannel 
  1023.           && channel.uploadStream instanceof Components.interfaces.nsISeekableStream) {
  1024.         this.log("Extracting post data...");
  1025.           try {
  1026.             channel.uploadStream.seek(0,0);
  1027.             const sis=Components.classes[
  1028.               '@mozilla.org/scriptableinputstream;1'].createInstance(
  1029.               ci.nsIScriptableInputStream);
  1030.             sis.init(channel.uploadStream);
  1031.             var postData=sis.read(sis.available()).replace(/\s$/,'').split(/[\r\n]/);
  1032.             links.postData=postData[postData.length-1];
  1033.             sis.close();
  1034.           } catch(ex) {
  1035.             this.log(ex.message);
  1036.           }
  1037.         }
  1038.       }
  1039.       
  1040.       if(this.download(links)) {
  1041.         this.log("...interception done!");
  1042.       } else {
  1043.          throw new Error("Can't download from this URL: "+uri.spec);
  1044.       }
  1045.       
  1046.       channel.cancel(Components.results.NS_BINDING_ABORTED); 
  1047.      
  1048.       this.log("Original request cancelled.");
  1049.       contentHandler.value=null;
  1050.       return true;
  1051.   }
  1052. ,
  1053.   isPreferred: function(contentType, desiredContentType) {
  1054.     return this._shouldIntercept(contentType);
  1055.   }
  1056. ,
  1057.   onStartURIOpen: function(uri) {
  1058.     return false;
  1059.   }
  1060. ,
  1061.   mailer: false // is Thunderbird?
  1062. ,
  1063.   get defaultDM() {
  1064.     return this.getPref("defaultDM",null);
  1065.   }
  1066. ,
  1067.   set defaultDM(name) {
  1068.     this.setPref("defaultDM", name);
  1069.     return name;
  1070.   }
  1071. ,
  1072.   get tmpDir() {
  1073.     return this.globals.tmpDir; 
  1074.   }
  1075. ,
  1076.   get profDir() {
  1077.     return this.globals.profDir; 
  1078.   }
  1079. ,
  1080.   get DMS() {
  1081.     return this.globals.DMS;
  1082.   }
  1083. ,
  1084.   get extensions() {
  1085.     var s=this.getPref("extensions","");
  1086.     return s?s.split(','):[];
  1087.   }
  1088. ,
  1089.   set extensions(v) {
  1090.     var arr=null;
  1091.     var s = typeof(v)=="string" 
  1092.       ? v : typeof(v)=="object" && typeof(v.join)=="function" 
  1093.         ? (arr=v).join(',') : "";
  1094.     this.setPref("extensions", s);
  1095.     return arr?arr:[];
  1096.   }
  1097. ,
  1098.   addExtension: function(ext) {
  1099.     if(ext) {
  1100.       var extensions=this.extensions;
  1101.       if(!this.extensionExists(ext,extensions)) {
  1102.         extensions[extensions.length]=ext;
  1103.         extensions.sort();
  1104.         this.extensions=extensions;
  1105.         return true;
  1106.       }
  1107.     }
  1108.     return false;
  1109.   }
  1110. ,
  1111.   removeExtension: function(ext) {
  1112.     var extensions=this.extensions;
  1113.     var j=this.indexOfExtension(ext,extensions);
  1114.     if(j>-1) {
  1115.       extensions.splice(j,1);
  1116.       this.extensions=extensions;
  1117.       return true;
  1118.     }
  1119.     return false;
  1120.   }
  1121. ,
  1122.   extensionExists: function(ext,extensions) {
  1123.     return this.indexOfExtension(ext,extensions)>-1;
  1124.   }
  1125. ,
  1126.   indexOfExtension: function(ext,extensions) {
  1127.     var ext=ext.toLowerCase();
  1128.     if(typeof(extensions)!="object") extensions=this.extensions;
  1129.     for(var j=extensions.length; j-->0;) {
  1130.       if(extensions[j].toLowerCase()==ext) return j;
  1131.     }
  1132.     return -1;
  1133.   }
  1134. ,
  1135.  
  1136.   get httpServer() {
  1137.     return (!this._httpServer) || this._httpServer.isDown
  1138.       ?this._httpServer=new FlashGotHttpServer(this)
  1139.       :this._httpServer;
  1140.   }
  1141.  
  1142. ,
  1143.   download: function(links, opType, dmName) {
  1144.     if(links.length==0) return false;
  1145.     if(!opType) opType=links.length>1?this.OP_SEL:this.OP_ONE;
  1146.     if(!dmName) dmName=this.defaultDM;
  1147.     const dm=this.globals.DMS[dmName];
  1148.     if(!dm) {
  1149.       this.log("FlashGot error: no download manager selected!");
  1150.       return false;
  1151.     }
  1152.     const encodedURLs=this.getPref(dm.codeName+".encode",this.getPref("encode",true));
  1153.     
  1154.     if(!links.postData) links.postData="";
  1155.     links.progress = { value: 0 };
  1156.     
  1157.     function parseDesc(link) {
  1158.         return link._description?link._description.replace(/\s+/g," ")
  1159.           :link._description = (typeof(link.title)=="string" && link.title.length>0
  1160.               ?link.title.replace(/\s+/g," "):link.innerHTML
  1161.                 ?link.innerHTML.replace(/\s+/g," ").replace(/<.*?>/g,"")
  1162.                 :"");
  1163.     }
  1164.  
  1165.     function prepareArray() {
  1166.       var l,j,len;
  1167.       for(j=0,len=links.length; j<len; j++) {
  1168.         l=links[j];
  1169.         links[j]={
  1170.            href: l.href,
  1171.           _description: parseDesc(l),
  1172.           _position: j
  1173.         };
  1174.       }
  1175.     }
  1176.     
  1177.     function processLinks() {
  1178.       const pg=links.progress;
  1179.       var len=links.length;
  1180.       if(len>1) {
  1181.         pg.value=12;
  1182.         // sort by href asc,_description.length desc
  1183.         dm.log("Sorting links");
  1184.         links.sort(function(a,b) {
  1185.           var aurl,burl,ades,bdes;
  1186.           return (aurl=a.href)>(burl=b.href)?1:aurl<burl?-1
  1187.               :(ades=a._description.length)>(bdes=b._description.length)?-1:ades<bdes?1
  1188.               :0;
  1189.         });
  1190.       }
  1191.       pg.value=24;
  1192.       //kill duplicates and sanitize
  1193.       dm.log("Killing duplicates and sanitizing");
  1194.       const escapeCheckNo=/(%[0-9a-f]{2,4})/i;
  1195.       const escapeCheckYes=/[\s]+/;
  1196.       var j,len,l,href,urlParts;
  1197.       var currentURL=null;
  1198.       var pos1,pos2;
  1199.       for(j=0,len=links.length; j<len; j++) {
  1200.         l=links[j];
  1201.         href=l.href;
  1202.         if( href==currentURL || (len>1 && !dm.isValidLink(href)) ) {
  1203.           links.splice(j--,1);
  1204.           --len;
  1205.         } else {
  1206.           currentURL=href;
  1207.           try {
  1208.             if(encodedURLs) { 
  1209.               if(escapeCheckYes.test(href) || !escapeCheckNo.test(href)) { 
  1210.                 href=encodeURI(href);
  1211.               }
  1212.               // workaround for malformed hash urls... I have still to think about it :-)
  1213.               while((pos1=href.indexOf('#'))>-1 
  1214.                 && ((pos2=href.indexOf('?'))<0 
  1215.                     || pos2>pos1 || pos1!=href.lastIndexOf('#'))) {
  1216.                 href=href.substring(0,pos1)+'%23'+href.substring(pos1+1);
  1217.               }
  1218.               l.href=href;
  1219.             } else {  
  1220.               l.href=decodeURI(href);
  1221.             }
  1222.           } catch(e) {
  1223.             dm.log("Problem "
  1224.               +encodedURLs?"escaping":"unescaping"
  1225.               +" URL "+href+": "+e.message);
  1226.           }
  1227.         }
  1228.         pg.value=25+25*j/len;
  1229.       }
  1230.       if(len>1) {
  1231.         dm.log("Sorting again "+links.length+" links");
  1232.         links.sort(function(a,b) {
  1233.           a=a._position; b=b._position;
  1234.           return a>b?1:a<b?-1:0;
  1235.         });
  1236.         dm.log("Preprocessing done");
  1237.       }
  1238.     }
  1239.     
  1240.  
  1241.  
  1242.     function trueDownload() {
  1243.       links.progress.value=50;
  1244.       dm.log("Starting download");
  1245.       dm.download(links,opType);
  1246.     }
  1247.     
  1248.     prepareArray();
  1249.     
  1250.     this.log("Processing "+links.length+" links");
  1251.     if(links.length>1 && this.bgProcessing) {
  1252.       this.log("(background processing)");
  1253.       this.runAsync(
  1254.         processLinks, 
  1255.         trueDownload);
  1256.     } else {
  1257.       this.log("(blocking processing)");
  1258.       processLinks();
  1259.       trueDownload();
  1260.     }
  1261.     return true;
  1262.   }
  1263. ,
  1264.   get bgProcessing() {
  1265.     return this.getPref("bgProcessing",true);
  1266.   }
  1267. ,
  1268.   runAsync: function(asyncJob,postJob) {
  1269.     const cc=Components.classes;
  1270.     const ci=Components.interfaces;
  1271.     var postRunnable=null;
  1272.     
  1273.     if(postJob) {
  1274.       postRunnable = cc["@mozilla.org/xpcomproxy;1"].getService(ci.nsIProxyObjectManager
  1275.         ).getProxyForObject(
  1276.           cc["@mozilla.org/event-queue-service;1"].getService(ci.nsIEventQueueService
  1277.               ).getSpecialEventQueue(ci.nsIEventQueueService.UI_THREAD_EVENT_QUEUE),
  1278.           ci.nsIRunnable, 
  1279.           {
  1280.             QueryInterface: function(iid) {
  1281.               if(iid.equals(ci.nsISupport) || iid.equals(ci.nsIRunnable)) {
  1282.                return this;
  1283.               }
  1284.               throw Components.results.NS_ERROR_NO_INTERFACE;
  1285.             },
  1286.             run: function() { postJob(); }
  1287.           }
  1288.         , 5); // 5 == PROXY_ALWAYS | PROXY_SYNC
  1289.     }
  1290.     
  1291.     const asyncRunnable={
  1292.       run: function() { 
  1293.         try {
  1294.           asyncJob();
  1295.         } catch(ex) {
  1296.         }
  1297.         if(postRunnable) postRunnable.run();
  1298.       }
  1299.     };
  1300.  
  1301.     const thread=cc['@mozilla.org/thread;1'].createInstance(ci.nsIThread);
  1302.     this.uiThread=thread.currentThread;
  1303.     thread.init(asyncRunnable, 0,  
  1304.       ci.nsIThread.PRIORITY_NORMAL, ci.nsIThread.SCOPE_GLOBAL, ci.nsIThread.STATE_JOINABLE);
  1305.   }
  1306. ,
  1307.   get prefService() {
  1308.     return Components.classes["@mozilla.org/preferences-service;1"].getService(
  1309.       Components.interfaces.nsIPrefService);
  1310.   }
  1311. ,
  1312.   getPref: function(name,def) {
  1313.     const IPC=Components.interfaces.nsIPrefBranch;
  1314.     const prefs=this.prefs;
  1315.     try {
  1316.       switch(prefs.getPrefType(name)) {
  1317.         case IPC.PREF_STRING:
  1318.           return prefs.getCharPref(name);
  1319.         case IPC.PREF_INT:
  1320.           return prefs.getIntPref(name);
  1321.         case IPC.PREF_BOOL:
  1322.           return prefs.getBoolPref(name);
  1323.       }
  1324.     } catch(e) {}
  1325.     return def;
  1326.   }
  1327. ,
  1328.   setPref: function(name,value) {
  1329.     const prefs=this.prefs;
  1330.     switch(typeof(value)) {
  1331.       case "string":
  1332.           prefs.setCharPref(name,value);
  1333.           break;
  1334.       case "boolean":
  1335.         prefs.setBoolPref(name,value);
  1336.         break;
  1337.       case "number":
  1338.         prefs.setIntPref(name,value);
  1339.         break;
  1340.       default:
  1341.         throw new Error("Unsupported type "+typeof(value)+" for preference "+name);
  1342.     }
  1343.   }
  1344. ,
  1345.   uiThread: null
  1346. ,
  1347.   get isUIThread() { 
  1348.     // returns true if current thread is equal to uiThread
  1349.     // if uiThread is not initialized, it gets initialized with current thread
  1350.     // and returned
  1351.     const uiThread=this.uiThread;
  1352.     const currentThread=this.currentThread;
  1353.     return uiThread?uiThread==currentThread:this.uiThread=currentThread;
  1354.   }
  1355. ,
  1356.   get currentThread() {
  1357.     return Components.classes['@mozilla.org/thread;1'
  1358.         ].createInstance(Components.interfaces.nsIThread).currentThread;
  1359.   }
  1360. ,
  1361.   _logFile: null,
  1362.   get logFile() {
  1363.     if(this._logFile==null) {
  1364.       this._logFile=this.profDir.clone();
  1365.       this._logFile.append("flashgot.log");
  1366.     }
  1367.     return this._logFile;
  1368.   }
  1369. ,
  1370.   logStream: null,
  1371.   logEnabled: false,
  1372.   log: function(msg) {
  1373.     if(this.logEnabled) {
  1374.       try {
  1375.         if(!this.logStream) {
  1376.           const logFile=this.logFile;
  1377.           const logStream=Components.classes["@mozilla.org/network/file-output-stream;1"
  1378.             ].createInstance(Components.interfaces.nsIFileOutputStream );
  1379.           logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
  1380.           this.logStream=logStream;
  1381.           const header="*** Log start at "+new Date().toGMTString()+"\n";
  1382.           this.logStream.write(header,header.length);
  1383.         }
  1384.         
  1385.         if(msg!=null) {
  1386.           msg+="\n";
  1387.           this.logStream.write(msg,msg.length);
  1388.         }
  1389.         this.logStream.flush();
  1390.       } catch(ex) {
  1391.         if(this.isUIThread) dump(ex.message+"\noccurred logging this message:\n"+msg);
  1392.       }
  1393.     }
  1394.   }
  1395. ,
  1396.   dumpStack: function(msg) {
  1397.     dump( (msg?msg:"")+"\n"+new Error().stack+"\n");
  1398.   }
  1399. ,
  1400.   clearLog: function() {
  1401.     try {
  1402.       if(this.logStream) {
  1403.         try {
  1404.           this.logStream.close();
  1405.         } catch(eexx) {
  1406.           dump(eexx.message);
  1407.         }
  1408.       }
  1409.       if(this.logFile) this.logFile.remove(true);
  1410.       this.logStream=null;
  1411.       this.log(null);
  1412.     } catch(ex) { dump(ex.message); }
  1413.   } 
  1414. ,
  1415.   get windowMediator() {
  1416.     return Components.classes["@mozilla.org/appshell/window-mediator;1"
  1417.       ].getService(Components.interfaces.nsIWindowMediator);
  1418.   }
  1419. ,
  1420.   getWindow: function() {
  1421.     return this.windowMediator.getMostRecentWindow(null);
  1422.   }
  1423. ,
  1424.   checkLocale: function() {
  1425.     if(typeof(this._checkedLocale)=="undefined") {
  1426.       try {
  1427.        this._checkedLocale=(new IA_LocaleChecker("flashgot")).check();
  1428.       } catch(ex) {
  1429.         this.log("Locale check failed: "+ex);
  1430.         this._checkedLocale=null;
  1431.       }
  1432.     }
  1433.     return this._checkedLocale;
  1434.   }
  1435. ,
  1436.   _globals: null,
  1437.   get globals() {
  1438.     if(!this._initialized) {
  1439.       const currentThread=this.currentThread;
  1440.       if(currentThread==this._initThread) return this._globals;
  1441.       this.initGlobals();
  1442.       for(var t=0; !this._initialized; t++) {
  1443.         currentThread.sleep(100);
  1444.         if(t>60) {
  1445.           this._initialized=true;
  1446.           throw new Error("FlashGot initialization timeout");
  1447.         }
  1448.       }
  1449.     }
  1450.     return this._globals;
  1451.   }
  1452. ,
  1453.   PREFS_BRANCH: "flashgot."
  1454. ,
  1455.   _prefs: null,
  1456.   get prefs() {
  1457.     var prefs=this._prefs;
  1458.     if(!prefs) {
  1459.       this._prefs=prefs=this.prefService.getBranch(this.PREFS_BRANCH
  1460.         ).QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  1461.     }
  1462.     return prefs;
  1463.   }
  1464. ,
  1465.   _initialized: false,
  1466.   _initializing: false,
  1467.   _initThread: null,
  1468.   _initException: null,
  1469.   initGlobals: function() {
  1470.     // this.dumpStack("FG.initGlobals()");
  1471.     if(this._globals || this._initializing || this._initialized) return;
  1472.     this._initializing=true;
  1473.     const service=this;
  1474.     this.runAsync(function() { service._asyncInit(); });
  1475.   }
  1476. ,
  1477.   _asyncInit: function() {
  1478.     function prepareTmp(t) {
  1479.       t.append("flashgot");
  1480.       t.createUnique(1,0700);
  1481.       return t;
  1482.     }
  1483.     try {
  1484.      
  1485.       this._initThread=this.currentThread;
  1486.       
  1487.       const startTime=new Date().getTime();
  1488.       const prefs=this.prefs;
  1489.       const cc=Components.classes;
  1490.       const ci=Components.interfaces; 
  1491.  
  1492.       const fileLocator=cc["@mozilla.org/file/directory_service;1"].getService(
  1493.         ci.nsIProperties);
  1494.       const profDir=fileLocator.get("ProfD",ci.nsIFile);
  1495.       var tmpDir;
  1496.       try {
  1497.         tmpDir=prepareTmp(prefs.getComplexValue("tmpDir", ci.nsILocalFile));
  1498.       } catch(ex) {
  1499.         tmpDir=prepareTmp(fileLocator.get("TmpD",ci.nsIFile));
  1500.       }
  1501.        
  1502.       this._globals={
  1503.         tmpDir: tmpDir,
  1504.         profDir: profDir,
  1505.         prefs: prefs
  1506.       };
  1507.       
  1508.       prefs.addObserver("", this, false);
  1509.       this.syncPrefs();
  1510.       
  1511.       this.log("Per-session init started");
  1512.         
  1513.       this._setupLegacyPrefs();
  1514.      
  1515.       this._globals.DMS=this.checkDownloadManagers();
  1516.       this.log("Per-session init done in "+(new Date().getTime()-startTime)+"ms");
  1517.     } catch(initEx) {
  1518.       this._initException=initEx;
  1519.     }
  1520.     this._initializing=false;
  1521.     this._initialized=true; 
  1522.   }
  1523. ,
  1524.   dispose: function() {
  1525.     this.prefs.removeObserver("",this);
  1526.     this._prefs=null;
  1527.     this._initializing=this._initialized=false;
  1528.     this._initThread=this._initException=null;
  1529.     this._globals=null;
  1530.   }
  1531. ,
  1532.   checkDownloadManagers: function() {
  1533.     
  1534.     const dms=FlashGotDM.dms;
  1535.     dms.found=false;
  1536.     
  1537.     
  1538.     var defaultDM=this.defaultDM;
  1539.     if(!dms[defaultDM]) defaultDM=null;
  1540.     var firstSupported=null;
  1541.     var dm;
  1542.     for(var j=dms.length; j-- >0;) {
  1543.       dm=dms[j];
  1544.       if(dm.supported ) {
  1545.         dms.found=true;
  1546.         firstSupported=dm.name;
  1547.       } else {
  1548.         this.log("Warning: download manager "+dm.name+" not found");
  1549.         if(defaultDM==dm.name) {
  1550.           defaultDM=null;
  1551.           this.log(dm.name+" was default download manager: resetting.");
  1552.         }
  1553.         if(dm.exclusive) {
  1554.           dms.splice(j,1);
  1555.         }
  1556.       }
  1557.     }
  1558.  
  1559.     if( (!defaultDM) && firstSupported!=null) {
  1560.       this.defaultDM=firstSupported;
  1561.       this.log("Default download manager set to "+this.defaultDM);
  1562.     } else if(!dms.found) {
  1563.       this.log("Serious warning! no supported download manager found...");
  1564.     } else {
  1565.       if(dms[defaultDM].exclusive) {
  1566.         for(j=dms.length; j-->0;) {
  1567.           if(dms[j].name!=defaultDM) {
  1568.             dms.splice(j,1);
  1569.           }
  1570.         }
  1571.       }
  1572.     }
  1573.     
  1574.     return dms;
  1575.   }
  1576. ,
  1577.   _cleaningup: false
  1578. ,
  1579.   cleanup: function() {
  1580.     if(this._cleaningup) return;
  1581.     try {
  1582.       this._cleaningup=true;
  1583.       this.log("Starting cleanup");
  1584.       if(this._httpServer) {
  1585.         this._httpServer.shutdown();
  1586.       }
  1587.       
  1588.       try {
  1589.         FlashGotDM.cleanup();
  1590.       } catch(eexx) {
  1591.         dump(eexx.message);
  1592.       }
  1593.       
  1594.       if(this._globals && this._globals.tmpDir.exists()) {
  1595.         this._globals.tmpDir.remove(true);
  1596.       }
  1597.       
  1598.       this.log("Cleanup done");
  1599.       if(this._logFile) try {
  1600.         if(this.logStream) this.logStream.close();
  1601.         var maxLogSize=Math.max(Math.min(this.getPref('maxLogSize',100000),1000000),50000);
  1602.         const logFile=this.logFile;
  1603.         const logSize=logFile.fileSize;
  1604.         if(logSize>maxLogSize) { // log rotation
  1605.           dump("Cutting log (size: "+logSize+", max: "+maxLogSize+")");
  1606.           const cc=Components.classes;
  1607.           const ci=Components.interfaces;
  1608.          
  1609.           const logBak=logFile.clone();
  1610.           logBak.leafName=logBak.leafName+".bak";
  1611.           if(logBak.exists()) logBak.remove(true);
  1612.           logFile.copyTo(logBak.parent,logBak.leafName);
  1613.           const is=cc['@mozilla.org/network/file-input-stream;1'].createInstance(
  1614.             ci.nsIFileInputStream);
  1615.           is.init(logBak,0x01, 0400, null);
  1616.           is.QueryInterface(ci.nsISeekableStream);
  1617.           is.seek(ci.nsISeekableStream.NS_SEEK_END,-maxLogSize);
  1618.           const sis=cc['@mozilla.org/scriptableinputstream;1'].createInstance(
  1619.           ci.nsIScriptableInputStream);
  1620.           sis.init(is);
  1621.           var buffer;
  1622.           var content="\n";
  1623.           var logStart=-1;
  1624.           while(buffer=sis.read(5000)) {
  1625.             content+=buffer;
  1626.             if((logStart=content.indexOf("\n*** Log start at "))>-1) { 
  1627.               content=content.substring(logStart);
  1628.               break;
  1629.             }
  1630.             content=buffer;
  1631.           }
  1632.           if(logStart>-1) {
  1633.              const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  1634.               ci.nsIFileOutputStream);
  1635.             os.init(logFile,0x02 | 0x08 | 0x20, 0700, 0);
  1636.             os.write(content,content.length);
  1637.             while(buffer=sis.read(20000)) {
  1638.               os.write(buffer,buffer.length);
  1639.             } 
  1640.             os.close();
  1641.           }
  1642.           sis.close();
  1643.         }
  1644.       } catch(eexx) {
  1645.         dump("Error cleaning up log: "+eexx.message);
  1646.       }
  1647.       this.logStream=null;
  1648.     } catch(ex) {
  1649.        this.log(ex.message);
  1650.     }
  1651.     this._cleaningup=false;
  1652.     this.dispose();
  1653.   }
  1654. ,
  1655.   readFile: function(file) {
  1656.     const cc=Components.classes;
  1657.     const ci=Components.interfaces; 
  1658.     
  1659.     const is = cc["@mozilla.org/network/file-input-stream;1"].createInstance(
  1660.           ci.nsIFileInputStream );
  1661.     is.init(file ,0x01, 0400, null);
  1662.     const sis = cc["@mozilla.org/scriptableinputstream;1"].createInstance(
  1663.       ci.nsIScriptableInputStream );
  1664.     sis.init(is);
  1665.     const res=sis.read(sis.available());
  1666.     is.close();
  1667.     return res;
  1668.   }
  1669. ,
  1670.   writeFile: function(file, content) {
  1671.     const cc=Components.classes;
  1672.     const ci=Components.interfaces;
  1673.     const unicodeConverter = cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(
  1674.     ci.nsIScriptableUnicodeConverter);
  1675.     unicodeConverter.charset = "UTF-8";
  1676.     content=unicodeConverter.ConvertFromUnicode(content);
  1677.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  1678.       ci.nsIFileOutputStream);
  1679.     os.init(file,0x02,0700,0);
  1680.     os.write(content,content.length);
  1681.     os.close();
  1682.   }
  1683. ,
  1684.   _setupLegacyPrefs: function() {
  1685.     try {
  1686.       const file=this._globals.profDir.clone();
  1687.       const defFile=file.clone();
  1688.       file.append("pref");
  1689.       file.append("flashgot.js");
  1690.       defFile.append("prefs.js");
  1691.       if(file.exists() && defFile.exists()) {
  1692.         this.prefService.readUserPrefs(file);
  1693.         this.prefService.readUserPrefs(defFile);
  1694.         this.prefService.savePrefFile(null);
  1695.         file.remove(true);
  1696.       }
  1697.     } catch(e) {
  1698.       this.log(e.message);
  1699.     }
  1700.   }
  1701. ,
  1702.   showDMSReference: function() {
  1703.     this.getWindow().open("http://www.flashgot.net/dms","_blank");
  1704.   }
  1705. ,
  1706. _dirtyJobsDone: false,
  1707. doDirtyJobs: function() {
  1708.     if(this._dirtyJobsDone) {
  1709.       return; 
  1710.     }
  1711.     const cc=Components.classes;
  1712.     const ci=Components.interfaces;
  1713.     const locator = cc["@mozilla.org/file/directory_service;1"].getService(ci.nsIProperties);
  1714.     const chromeDirs=[locator.get("AChrom", ci.nsIFile).path,
  1715.       locator.get("UChrm", ci.nsIFile).path];
  1716.     const rdf = cc["@mozilla.org/rdf/rdf-service;1"].getService(ci.nsIRDFService);
  1717.     const rdfUtil  = cc["@mozilla.org/rdf/container-utils;1"].getService(ci.nsIRDFContainerUtils);
  1718.     const overlays=new Object();
  1719.     overlays['browser']
  1720.     =overlays['navigator']  
  1721.       =["chrome://flashgot/localeCheckOverlay.xul","chrome://flashgot/content/localeCheckOverlay.xul"];
  1722.     
  1723.     var url, ds, overlayName, overlay, literal, res, src;
  1724.     for(var j = 0; j < chromeDirs.length; j++) { // Application & Profile Dirs
  1725.       // Loop files
  1726.       var overlayDir=chromeDirs[j].replace(/\\/g,'/')+'/overlayinfo/';
  1727.       for(overlayName in overlays){
  1728.         try{
  1729.           url="file:///"+ overlayDir +overlayName+ '/content/overlays.rdf';
  1730.           ds = rdf.GetDataSourceBlocking(url).QueryInterface(ci.nsIRDFRemoteDataSource);
  1731.           // Loop overlay elements for this file
  1732.           var overlay=overlays[overlayName];
  1733.           for(var i = 0; i < overlay.length; i++){
  1734.             var literal=rdf.GetLiteral(overlay[i]);
  1735.             // Find Element
  1736.             var archs = ds.ArcLabelsIn(literal);
  1737.             while(archs.hasMoreElements()){
  1738.               var res = archs.getNext().QueryInterface(ci.nsIRDFResource);
  1739.               var sources = ds.GetSources(res, literal, true);
  1740.               
  1741.               // Get parent element for element
  1742.               while(sources.hasMoreElements()){
  1743.                 src = sources.getNext().QueryInterface(ci.nsIRDFResource);
  1744.                 
  1745.                 try {
  1746.                     ds.Unassert(src, res, literal);
  1747.                     this.log("Removed "+ literal.Value +" from "+url);
  1748.                 } catch(ex) {
  1749.                     this.log("Failed to remove "+ listeral.Value +" from "+url);
  1750.                     this.log(ex.message);
  1751.                 }
  1752.               }
  1753.             }
  1754.           }
  1755.           
  1756.           ds.Flush();
  1757.           this.log("Cleaned "+url);
  1758.          } catch(ex) {
  1759.             this.log("Failed to clean "+url);
  1760.             this.log(ex.message);
  1761.          }
  1762.       }
  1763.     }
  1764.     
  1765.     try {
  1766.       const xulFile=locator.get("ProfD", ci.nsIFile).clone();
  1767.       xulFile.append("XUL.mfl");
  1768.       xulFile.remove(true);
  1769.       this.log(xulFile.path+" deleted");
  1770.     } catch(ex) { 
  1771.       this.log("Failed to delete XUL.mfl: "+ex.message);
  1772.     }
  1773.     this._dirtyJobsDone=true;
  1774.   }
  1775. }
  1776.  
  1777. /* Data Swap HTTP server */
  1778.  
  1779. FlashGotHttpServer=function(fgService) {
  1780.   this.fgService=fgService;
  1781.   this.isDown=true;
  1782.   this.serverSocket=Components.classes['@mozilla.org/network/server-socket;1'
  1783.     ].createInstance(Components.interfaces.nsIServerSocket);
  1784.   this.serverSocket.init(-1,true,-1);
  1785.   this.isDown=false;
  1786.   this.serverSocket.asyncListen(this);
  1787.   this.tmpDir=this.fgService.globals.tmpDir.clone();
  1788.   this.tmpDir.append("httpserv");
  1789.   this.logEnabled=fgService.getPref("LeechGet.httpLog",false);
  1790.   this.log("Listening");
  1791. }
  1792.  
  1793. FlashGotHttpServer.prototype={
  1794.   documents: []
  1795. ,
  1796.   log: function(msg){
  1797.     if(this.logEnabled && this.fgService.logEnabled) {
  1798.       try {
  1799.         if(!this.logStream) {
  1800.           const logFile=this.tmpDir.clone();
  1801.           logFile.append("server.log");
  1802.           logFile.createUnique(0,0600);
  1803.           const logStream=Components.classes["@mozilla.org/network/file-output-stream;1"
  1804.             ].createInstance(Components.interfaces.nsIFileOutputStream );
  1805.           logStream.init(logFile, 0x02 | 0x10, 0600, 0 );
  1806.           this.logStream=logStream;
  1807.         }
  1808.         msg="HttpServer:"+this.serverSocket.port+" - "+msg+"\n";
  1809.         this.logStream.write(msg,msg.length);
  1810.         this.logStream.flush();
  1811.       } catch(ex) {}
  1812.     }
  1813.   }
  1814. ,
  1815.   onSocketAccepted: function(ss,transport) {
  1816.     this.log("Accepted request from "
  1817.       +transport.host+":"+transport.port);
  1818.      try {
  1819.         new FlashGotHttpHandler(this,transport);
  1820.      } catch(ex) {
  1821.        this.log(ex.message);
  1822.      }
  1823.   }
  1824. ,
  1825.   onStopListening: function(ss,status) {
  1826.     this.isDown=true;
  1827.     if(this.logStream) {
  1828.       this.log("Stopped, status "+status);
  1829.     }
  1830.   }
  1831. ,
  1832.   randomName: function(len) {
  1833.     if(!len) len=8;
  1834.     var name="";
  1835.     for(var j=len; j-->0;) {
  1836.       name+=String.fromCharCode(65+(Math.round(Math.random()*25)));
  1837.     }
  1838.     return name;
  1839.   }
  1840. ,
  1841.   addDoc: function(docSource,docType) {
  1842.     if(typeof(docType)=="undefined") docType="html";
  1843.     var file=this.tmpDir.clone();
  1844.     file.append(this.randomName()+"."+docType);
  1845.     file.createUnique(0,0600);
  1846.     this.fgService.writeFile(file,docSource);
  1847.     const name=file.leafName;
  1848.     this.documents.push(name);
  1849.     return "http://localhost:"+this.serverSocket.port+"/"+name;
  1850.   }
  1851. ,
  1852.   getDoc: function(name) {
  1853.     const docs=this.documents;
  1854.     for(var j=docs.length; j-->0;) {
  1855.       if(docs[j]==name) break;
  1856.     }
  1857.     if(j<0) return null;
  1858.     var file=this.tmpDir.clone();
  1859.     file.append(name);
  1860.     return file.exists()?this.fgService.readFile(file):null;
  1861.   }
  1862. ,  
  1863.   shutdown: function() {
  1864.     try {
  1865.       this.log("Shutting down");
  1866.       if(this.logStream) {
  1867.         this.logStream.close();
  1868.         this.logStream=null;
  1869.       }
  1870.       this.serverSocket.close();
  1871.     } catch(ex) {}
  1872.   }
  1873. }
  1874.  
  1875. function FlashGotHttpHandler(server,transport) {
  1876.   this.server=server;
  1877.   this.inputBuffer="";
  1878.   this.transport=transport;
  1879.   this.asyncStream=transport.openInputStream(0,0,0).QueryInterface(
  1880.     Components.interfaces.nsIAsyncInputStream);
  1881.   this.log("Waiting for request data...");
  1882.   
  1883.   const nsIThread=Components.interfaces.nsIThread;
  1884.   var thread=Components.classes['@mozilla.org/thread;1'].createInstance(nsIThread);
  1885.   thread.init(this, 0,  nsIThread.PRIORITY_NORMAL, nsIThread.SCOPE_GLOBAL,nsIThread.STATE_JOINABLE);
  1886.   this.log("Thread started");
  1887. }
  1888.  
  1889. FlashGotHttpHandler.prototype = {
  1890.   log: function(msg) {
  1891.     this.server.log(this.transport.host+":"+this.transport.port+" - "+msg);
  1892.   }
  1893. ,
  1894.   run: function() {
  1895.      this.log("I'm in thread");
  1896.      this.asyncStream.asyncWait(this,0,0,null);
  1897.      this.log("Asyncwait issued");
  1898.   }
  1899. ,  
  1900.   onInputStreamReady: function(asyncStream) {
  1901.     const bytesCount=asyncStream.available();
  1902.     this.log("Input stream ready, available bytes: "+bytesCount);
  1903.     if(bytesCount) {
  1904.       const inStream=Components.classes['@mozilla.org/scriptableinputstream;1'].createInstance(
  1905.         Components.interfaces.nsIScriptableInputStream);
  1906.       inStream.init(asyncStream);
  1907.       var chunk=inStream.read(inStream.available());
  1908.       this.log("Received data chunk "+chunk);
  1909.       var buffer=this.inputBuffer.concat(chunk);
  1910.       var eor=chunk.length==0?buffer.length:buffer.search("\r?\n\r?\n");
  1911.       this.log("EOR: "+eor);
  1912.       if(eor>-1) {
  1913.         var request=buffer.substring(0,eor);
  1914.         this.inputBuffer="";
  1915.         this.handleRequest(request);
  1916.         this.close();
  1917.       } else {
  1918.         this.inputBuffer=buffer;
  1919.         this.run();
  1920.       }
  1921.     } else {
  1922.       this.close();
  1923.     }
  1924.   }
  1925. ,
  1926.   close: function() {
  1927.     this.asyncStream.close();
  1928.   }
  1929. ,
  1930.   buildResponse: function(body,status,contentType) {
  1931.     if(!contentType) contentType="text/html";
  1932.     if(!status) {
  1933.       status="200 OK";
  1934.     } else {
  1935.       body="<h1>"+status+"</h1><pre>"
  1936.         +body
  1937.         +"</pre><h5>FlashGot Http Server v. 0.1</h5>"
  1938.     }
  1939.     return "HTTP/1.1 "+status+"\r\nContent-type: "+contentType+"\r\n\r\n"+body;
  1940.   }
  1941. ,
  1942.   handleRequest: function(request) {
  1943.     var response;
  1944.     var match;
  1945.     this.log("Handling request\n"+request);
  1946.     try {
  1947.       if(!(match=request.match(/^GET \/([^\s]*)/))) {
  1948.         response=this.buildResponse(request,"400 Bad Request"); 
  1949.       } else {
  1950.         var doc=this.server.getDoc(match[1]);
  1951.         
  1952.         if(doc==null) {
  1953.           response=this.buildResponse(request,"404 Not Found");
  1954.         } else {
  1955.           response=this.buildResponse(doc);
  1956.         }
  1957.       }
  1958.     } catch(ex) {
  1959.       response=this.buildResponse(ex.message+"\n"+request,"500 Server error");
  1960.     }
  1961.     var out=this.transport.openOutputStream(1,0,0);
  1962.     out.write(response,response.length);
  1963.     out.close();
  1964.     this.log("Sent response\n"+response);
  1965.   } 
  1966. }
  1967.  
  1968.  
  1969. /* Locale check */
  1970. IA_LocaleChecker=function(module) {  
  1971.   this.module=module;
  1972. }
  1973.  
  1974. IA_LocaleChecker.prototype = {
  1975.  
  1976.   check: function() {
  1977.     const module=this.module;
  1978.     const cc=Components.classes;
  1979.     const ci=Components.interfaces;
  1980.  
  1981.     const localeURL="chrome://"+module+"/locale";
  1982.     
  1983.     try {
  1984.       const window=cc['@mozilla.org/appshell/appShellService;1'
  1985.             ].getService(ci.nsIAppShellService
  1986.         ).hiddenDOMWindow
  1987.     } catch(ex) {}
  1988.     
  1989.     function log(msg) {
  1990.       dump("LocaleChecker: "+msg+"\n");
  1991.     }
  1992.   
  1993.     function checkLocale(locale) {
  1994.       function isLocaleSelected() {
  1995.         try {
  1996.           return chreg.getSelectedLocale(module)==locale;
  1997.         } catch(ex) {
  1998.           log(ex.message);
  1999.           return false;
  2000.         }
  2001.       }
  2002.       
  2003.       try {
  2004.         if(!isLocaleSelected()) {
  2005.           log("trying to select locale "+locale);
  2006.           chreg.selectLocaleForPackage(locale,module,true);
  2007.           if(isLocaleSelected()) {
  2008.             
  2009.             for(var j=2; j-->0;) {
  2010.               chreg.checkForNewChrome();
  2011.               chreg.reloadChrome();
  2012.               log("chrome reloaded! "+j);
  2013.             }
  2014.             
  2015.           } else {
  2016.             log("Can't select locale "+locale+" for "+module); 
  2017.             locale=null;
  2018.           }
  2019.         }
  2020.       } catch(ex) {
  2021.         log("Error checking/setting locale "+locale+" for "+module+"\n"+ex.message);
  2022.         locale=null;
  2023.       }
  2024.       return locale;
  2025.     }
  2026.      
  2027.     var chreg=cc["@mozilla.org/chrome/chrome-registry;1"].getService(ci.nsIChromeRegistry);
  2028.     try {
  2029.       if(ci.nsIXULChromeRegistry) chreg.QueryInterface(ci.nsIXULChromeRegistry);
  2030.       return; // it is Firefox, we can leave safely now!
  2031.     } catch(ex) { log(ex.message); }
  2032.     try {
  2033.       if(ci.nsIChromeRegistrySea) chreg.QueryInterface(ci.nsIChromeRegistrySea);
  2034.     } catch(ex) { log(ex.message); }
  2035.     
  2036.     var browserLocale=null;
  2037.     try {
  2038.       browserLocale=chreg.getSelectedLocale("browser",true);
  2039.     } catch(ex1) {
  2040.       log("It seems no locale is selected for browser... trying with navigator");
  2041.       try {
  2042.         browserLocale=chreg.getSelectedLocale("navigator",true);
  2043.       } catch(ex2) {
  2044.         log("It seems no locale is selected for navigator...");
  2045.       }
  2046.     }
  2047.     var ret=null;
  2048.     if(browserLocale==null
  2049.       || !(ret=checkLocale(browserLocale))) {
  2050.         ret=checkLocale("en-US");
  2051.     }
  2052.     return ret;
  2053.   }
  2054. }
  2055.  
  2056.  
  2057. // XPCOM Scaffolding code
  2058.  
  2059. // component defined in this file
  2060.  
  2061. const SERVICE_NAME="FlashGot Service";
  2062. const SERVICE_CID =
  2063.     Components.ID("{2a55fc5c-7b31-4ee1-ab15-5ee2eb428cbe}");
  2064. const SERVICE_CTRID =
  2065.     "@maone.net/flashgot-service;1";
  2066.     
  2067. const SERVICE_CONSTRUCTOR=FlashGotService;
  2068.  
  2069.  
  2070. // interfaces implemented by this component
  2071. const SERVICE_IIDS = 
  2072. Components.interfaces.nsIObserver,
  2073. Components.interfaces.nsISupports,
  2074. Components.interfaces.nsISupportsWeakReference,
  2075. Components.interfaces.nsIURIContentListener
  2076. ];
  2077.  
  2078. // Factory object
  2079. const SERVICE_FACTORY = {
  2080.   _instance: null,
  2081.   createInstance: function (outer, iid) {
  2082.     if (outer != null)
  2083.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  2084.  
  2085.     xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_INVALID_ARG);
  2086.     // kept this for flexibility sake, but we're really adopting an
  2087.     // early instantiation and late init singleton pattern
  2088.     return this._instance==null?this._instance=new SERVICE_CONSTRUCTOR():this._instance;
  2089.   }
  2090. };
  2091.  
  2092. function xpcom_checkInterfaces(iid,iids,ex) {
  2093.   for(var j=iids.length; j-- >0;) {
  2094.     if(iid.equals(iids[j])) return true;
  2095.   }
  2096.   throw ex;
  2097. }
  2098.  
  2099. // Module
  2100.  
  2101. var Module = new Object();
  2102. Module.firstTime=true;
  2103. Module.registerSelf = function (compMgr, fileSpec, location, type) {
  2104.   if(this.firstTime) {
  2105.    
  2106.     debug("*** Registering "+SERVICE_CTRID+".\n");
  2107.     
  2108.     compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2109.       ).registerFactoryLocation(SERVICE_CID,
  2110.       SERVICE_NAME,
  2111.       SERVICE_CTRID, 
  2112.       fileSpec,
  2113.       location, 
  2114.       type);
  2115.       
  2116.     Components.classes['@mozilla.org/categorymanager;1'].getService(
  2117.       Components.interfaces.nsICategoryManager
  2118.      ).addCategoryEntry("app-startup",
  2119.         SERVICE_NAME, "service," + SERVICE_CTRID, true, true, null);
  2120.       
  2121.     this.firstTime=false;
  2122.   } 
  2123. }
  2124. Module.unregisterSelf = function(compMgr, fileSpec, location) {
  2125.   compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2126.     ).unregisterFactoryLocation(SERVICE_CID, fileSpec);
  2127.   Components.classes['@mozilla.org/categorymanager;1'].getService(
  2128.       Components.interfaces.nsICategoryManager
  2129.      ).deleteCategoryEntry("app-startup",SERVICE_NAME, true);
  2130. }
  2131.  
  2132. Module.getClassObject = function (compMgr, cid, iid) {
  2133.   if(cid.equals(SERVICE_CID))
  2134.     return SERVICE_FACTORY;
  2135.  
  2136.   if (!iid.equals(Components.interfaces.nsIFactory))
  2137.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  2138.   
  2139.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2140.     
  2141. }
  2142.  
  2143. Module.canUnload = function(compMgr) {
  2144.   return true;
  2145. }
  2146.  
  2147. // entrypoint
  2148. function NSGetModule(compMgr, fileSpec) {
  2149.   return Module;
  2150. }
  2151.  
  2152.  
  2153.  
  2154.