home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2005 March / PCpro_2005_03.ISO / files / firefox / fireftp-i18n.xpi / chrome / fireftp.jar / content / fireftp.js < prev    next >
Encoding:
JavaScript  |  2005-01-13  |  199.1 KB  |  5,935 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Mozilla Public License
  4.  * Version 1.1 (the "License"); you may not use this file except in
  5.  * compliance with the License. You may obtain a copy of the License at
  6.  * http://www.mozilla.org/MPL/
  7.  * 
  8.  * Software distributed under the License is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10.  * for the specific language governing rights and limitations under the
  11.  * License.
  12.  * 
  13.  * The Original Code is fireftp
  14.  *
  15.  * The Initial Developer of the Original Code is Mime Cuvalo
  16.  * Portions created by the Initial Developer are Copyright (C) 2004
  17.  * the Initial Developer. All Rights Reserved.
  18.  * 
  19.  * Contributor(s):
  20.  *    Mime Cuvalo, http://www.chasaidthekitty.com/email.php?name=mime, original author
  21.  *
  22.  * Alternatively, the contents of this file may be used under the
  23.  * terms of the GNU Public License (the "GPL"), in which case the
  24.  * provisions of the GPL are applicable instead of those above.
  25.  * If you wish to allow use of your version of this file only
  26.  * under the terms of the GPL and not to allow others to use your
  27.  * version of this file under the MPL, indicate your decision by
  28.  * deleting the provisions above and replace them with the notice
  29.  * and other provisions required by the GPL.  If you do not delete
  30.  * the provisions above, a recipient may use your version of this
  31.  * file under either the MPL or the GPL.
  32.  *
  33.  */
  34.  
  35. // yeah, all this code could be a lot more elegant, bite me
  36.  
  37. // NOTE: i've listed 'messy, messy, messy' in a couple places
  38. // this code is messy but necessary b/c when shortening arrays
  39. // if you are left with only one object, javascript will just
  40. // give you the object and not an object inside of an array.
  41. // thus the code 'stuff = new Array(stuff)' in case of this.
  42. // are there better solutions?
  43.  
  44. // *************************************************************************************************
  45. // *************************************** global variables ****************************************
  46. // *************************************************************************************************
  47.  
  48. var welcomeMessage;
  49. var isConnected = false;        // are we connected?
  50. var isReady = false;            // are we busy writing/reading the control socket
  51. var initialPath;                // path we go to first onload
  52. var connectedHost;                // name of the host we connect to plus username
  53. var control_instream;
  54. var control_outstream;
  55. var data_transport;
  56. var data_outstream;
  57. var datahost;
  58. var dataport;                    // calculated from PASV command
  59. var legitClose = true;            // are we the ones initiating the close or is it a network error
  60. var isReconnecting;
  61. var reconnectAttempts;
  62. var data;                        // holds list data
  63. var file_instream;
  64. var data_remember;                // function to evaluate after data socket closes
  65. var data_finished = true;        // is data socket finished?
  66. var moreToRemember = "";        // function to evaluate after changing directories
  67. var eventQueue = new Array;        // commands to be sent
  68. var trashQueue = new Array;        // once commands are read, throw them away here
  69.                                 // but we might have to recycle these if there is an error
  70.  
  71. var remotesize;                    // total size for current remote directory
  72. var remotecache = new Array;    // local cache
  73. var remotedircache = new Array( {path:"/", open:true, empty:false, children:-1} );
  74. var hiddenremotedircache = new Array;
  75. var refreshRemote = false;        // are we refreshing?
  76.  
  77. var localsize;                    // total size for current local directory
  78. var localAvailableDiskSpace;
  79. var localdircache = new Array;
  80. var hiddenlocaldircache = new Array;
  81. var refreshLocal = false;        // are we refreshing?
  82. var slash;                        // holds '/' or '\\' depending on local system
  83.  
  84. var localPasteFiles;            // local files to be pasted
  85. var remotePasteFiles;            // remote files to be pasted
  86. var localIsCut;                    // is the local paste a cut or copy (remote has only cut)
  87.  
  88. var strbundle;                    // i18n
  89.  
  90. var gLocalPath;
  91. var gLocalTree;
  92. var gLocalDirTree;
  93. var gRemotePath;                // document.getElementById() references
  94. var gRemoteTree;
  95. var gRemoteDirTree;
  96. var gConnectButton;
  97.  
  98. var networkTimeoutID = 0;        // a counter increasing with each write to the control socket
  99.  
  100. var prefAsciiFiles = new Array();        // needed to workaround bug #7700
  101.  
  102. var welcomemode;
  103. var errormode;
  104. var refreshmode;
  105. var passwordmode;
  106. var sessionsmode;
  107. var hiddenmode;
  108. var interfacemode;
  109. var sslmode;                    // preferences (UI overhaul for 0.85)
  110. var network;
  111. var timeoutmode;
  112. var retry;
  113. var attempts;
  114. var port;
  115. var destructmode;
  116. var filemode;
  117. var loadmode;
  118. var logmode;
  119. var pasvmode;
  120. var debugmode;
  121. var proxymode;
  122.  
  123. var activeCmd;                    // for active mode - way too messy for my tastes
  124. var activeRemember;                // find a better way by 1.0
  125. var activeRemember2;
  126. var activeRemember3;
  127.  
  128. var emptyFileSoCloseLater;        // uploading empty files was causing problems
  129.  
  130. // *************************************************************************************************
  131. // ****************************************** startup/close ****************************************
  132. // *************************************************************************************************
  133.  
  134. function startup()
  135. {
  136.     // set up references
  137.     strbundle = document.getElementById("strings");
  138.     gLocalPath = document.getElementById('localpath');
  139.     gLocalTree = document.getElementById('localtree');
  140.     gLocalDirTree = document.getElementById('localdirtree');
  141.     gRemotePath = document.getElementById('remotepath');
  142.     gRemoteTree = document.getElementById('remotetree');
  143.     gRemoteDirTree = document.getElementById('remotedirtree');
  144.     gConnectButton = document.getElementById('connectbutton');
  145.  
  146.     gConnectButton.label = strbundle.getString("connectButton");
  147.     gConnectButton.setAttribute('command','cmd_connect');
  148.     gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  149.  
  150.     // load preferences
  151.     readPreferences();
  152.  
  153.     // for bug #7731 we need to help those who have stuck folder views
  154.     if (gRemoteDirTree.collapsed)
  155.         document.getElementById('remotesplitter').state = "collapsed";
  156.     if (gLocalDirTree.collapsed)
  157.         document.getElementById('localsplitter').state = "collapsed";
  158.  
  159.     var el = document.getElementById('cmdlog').appendItem("       fireFTP 0.86.2", "       fireFTP 0.86.2");
  160.     el.setAttribute('style', 'color:blue;font-weight:bold');
  161.  
  162.     // rev the engine
  163.     if (gLocalPath.value != "")
  164.         onLocalPathChange();
  165.  
  166.     document.getElementById('host').focus();
  167. }
  168. function disconnect()
  169. {
  170.     // haven't connected yet, changed our minds and decided to invade canada instead (just playin' canada...naw, i'm serious)
  171.     if (!isConnected && eventQueue.length && eventQueue[0].cmd == "welcome")
  172.     {
  173.         document.getElementById('statusbytes').label = "";
  174.         document.getElementById('statusmeter').setAttribute("mode", "determined");
  175.         document.getElementById('statusmeter').setAttribute("value", "0%");
  176.         gConnectButton.label = strbundle.getString("connectButton");
  177.         gConnectButton.setAttribute('command','cmd_connect');
  178.         gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  179.         eventQueue = new Array;
  180.  
  181.         // you're young, try everything
  182.         try {
  183.             control_instream.close();
  184.         }catch(ex){ debug(ex,1); }
  185.         try {
  186.             control_outstream.close();
  187.         }catch(ex){ debug(ex,2); }
  188.     }
  189.     else if (isConnected)
  190.     {
  191.         if (eventQueue.length)
  192.             if (!confirm(strbundle.getString("reallyclose")))
  193.                 return;
  194.         legitClose = true;    // this close() is ok, don't try to reconnect
  195.         gRemotePath.value = '/';
  196.         remotePathFocus = '/';
  197.         close();
  198.     }
  199. }
  200. function close()
  201. {
  202.     if (isConnected)
  203.     {
  204.         eventQueue.unshift( { cmd: "goodbye", parameter: "", remember: "" } );
  205.         writeControl("QUIT");
  206.  
  207.         // you're young, try everything
  208.         try {
  209.             control_instream.close();
  210.         }catch(ex){ debug(ex,3); }
  211.         try {
  212.             control_outstream.close();
  213.         }catch(ex){ debug(ex,4); }
  214.         try {
  215.             data_transport.close("Finished");
  216.         }catch(ex){ debug(ex,5); }
  217.         try {
  218.             file_instream.close();
  219.         }catch(ex){ debug(ex,6); }
  220.  
  221.         data_finished = true;
  222.  
  223.         // clear out remote window
  224.         data = '';
  225.         remotedircache = new Array;
  226.         hiddenremotedircache = new Array;
  227.         changeRemoteView();
  228.     }
  229. }
  230.  
  231. // *************************************************************************************************
  232. // ******************************************** preferences ****************************************
  233. // *************************************************************************************************
  234.  
  235. function readPreferences()
  236. {
  237.     try {
  238.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  239.             getService(Components.interfaces.nsIPrefService);
  240.         var prefBranch = prefs.getBranch("fireftp.");
  241.         if (!prefBranch.prefHasUserValue("welcomemode"))
  242.             savePreferences(true);
  243.         if (!prefBranch.prefHasUserValue("filemode"))    // update from version 0.82
  244.         {
  245.             prefBranch.setIntPref("filemode", 0);
  246.             prefBranch.setCharPref("asciifiles", "ai,bas,bat,c,cc,cgi,conf,cpp,css,diz,eps,h,htm,html,hqx,java,map,nfo,pas,php,pl,ps,py,sh,shtml,txt,uue,xml");
  247.             prefBranch.setIntPref("loadmode", 1);
  248.         }
  249.         if (!prefBranch.prefHasUserValue("logmode"))    // update from previous version
  250.             prefBranch.setBoolPref("logmode", false);
  251.         if (!prefBranch.prefHasUserValue("pasvmode"))    // update from previous version
  252.             prefBranch.setBoolPref("pasvmode", true);
  253.         if (!prefBranch.prefHasUserValue("debugmode"))    // update from previous version
  254.             prefBranch.setBoolPref("debugmode", false);
  255.         if (!prefBranch.prefHasUserValue("proxymode"))    // update from previous version
  256.             prefBranch.setBoolPref("proxymode", false);
  257.  
  258.         welcomemode = prefBranch.getBoolPref("welcomemode");
  259.         errormode = prefBranch.getBoolPref("errormode");
  260.         refreshmode = prefBranch.getBoolPref("refreshmode");
  261.         passwordmode = prefBranch.getBoolPref("passwordmode");
  262.         sessionsmode = prefBranch.getBoolPref("sessionsmode");
  263.         hiddenmode = prefBranch.getBoolPref("hiddenmode");
  264.         interfacemode = prefBranch.getIntPref("interfacemode");
  265.         sslmode = prefBranch.getBoolPref("sslmode");
  266.         pasvmode = prefBranch.getBoolPref("pasvmode");
  267.         network = prefBranch.getIntPref("network");
  268.         timeoutmode = prefBranch.getBoolPref("timeoutmode");
  269.         retry = prefBranch.getIntPref("retry");
  270.         attempts = prefBranch.getIntPref("attempts");
  271.         port = prefBranch.getIntPref("port");
  272.         destructmode = prefBranch.getBoolPref("destructmode");
  273.         filemode = prefBranch.getIntPref("filemode");
  274.         loadmode = prefBranch.getIntPref("loadmode");
  275.         logmode = prefBranch.getBoolPref("logmode");
  276.         debugmode = prefBranch.getBoolPref("debugmode");
  277.         proxymode = prefBranch.getBoolPref("proxymode");
  278.         gLocalPath.value = prefBranch.getCharPref("folder");
  279.  
  280.         if (interfacemode == 0)
  281.             interfaceChange(false, false);
  282.         else if (interfacemode == 1)
  283.             interfaceChange(false, true);
  284.         else if (interfacemode == 2)
  285.             interfaceChange(true, false);
  286.  
  287.         if (logmode)
  288.         {
  289.             document.getElementById('cmdlog').collapsed = false;
  290.             document.getElementById('logsplitter').state = '';
  291.             document.getElementById('logbutton').checked = true;
  292.         }
  293.         else
  294.         {
  295.             document.getElementById('cmdlog').collapsed = true;
  296.             document.getElementById('logsplitter').state = 'collapsed';
  297.             document.getElementById('logbutton').checked = false;
  298.         }
  299.  
  300.         var transferTypes = new Array("Auto", "Binary", "ASCII");
  301.         document.getElementById('statustype').label = transferTypes[filemode];
  302.  
  303.         var asciiFiles = prefBranch.getCharPref("asciifiles");
  304.         asciiFiles = asciiFiles.split(",");
  305.         for (var x = 0; x < asciiFiles.length; ++x)
  306.             prefAsciiFiles.push(asciiFiles[x]);
  307.     } catch (ex) { debug(ex,7); }
  308. }
  309. function savePreferences(firstTime)
  310. {
  311.     try {
  312.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  313.             getService(Components.interfaces.nsIPrefService);
  314.         var prefBranch = prefs.getBranch("fireftp.");
  315.  
  316.         if (firstTime)
  317.         {
  318.             prefBranch.setBoolPref("welcomemode", true);
  319.             prefBranch.setBoolPref("errormode", true);
  320.             prefBranch.setBoolPref("refreshmode", true);
  321.             prefBranch.setBoolPref("passwordmode", true);
  322.             prefBranch.setBoolPref("sessionsmode", true);
  323.             prefBranch.setBoolPref("hiddenmode", false);
  324.             prefBranch.setIntPref("interfacemode", 0);
  325.             prefBranch.setBoolPref("sslmode", false);
  326.             prefBranch.setBoolPref("pasvmode", true);
  327.             prefBranch.setIntPref("network", 30);
  328.             prefBranch.setBoolPref("timeoutmode", true);
  329.             prefBranch.setIntPref("retry", 10);
  330.             prefBranch.setIntPref("attempts", 40);
  331.             prefBranch.setIntPref("port", 21);
  332.             prefBranch.setBoolPref("destructmode", false);
  333.             prefBranch.setIntPref("filemode", 0);
  334.             prefBranch.setCharPref("asciifiles", "ai,bas,bat,c,cc,cgi,conf,cpp,css,diz,eps,h,htm,html,hqx,java,map,nfo,pas,php,pl,ps,py,sh,shtml,txt,uue,xml");
  335.             prefBranch.setIntPref("loadmode", 1);
  336.             prefBranch.setBoolPref("logmode", false);
  337.             prefBranch.setBoolPref("debugmode", false);
  338.             prefBranch.setBoolPref("proxymode", false);
  339.  
  340.             var file = Components.classes["@mozilla.org/file/directory_service;1"].
  341.                     createInstance(Components.interfaces.nsIProperties).
  342.                     get("Home", Components.interfaces.nsIFile);
  343.             if (file.path.indexOf('/') != -1)
  344.                 prefBranch.setCharPref("folder", file.path.substring(0, file.path.indexOf('/') + 1));
  345.             else if (file.path.indexOf('\\') != -1)
  346.                 prefBranch.setCharPref("folder", file.path.substring(0, file.path.indexOf('\\') + 1));
  347.         }
  348.         else
  349.         {
  350.             prefBranch.setIntPref("filemode", filemode);
  351.             prefBranch.setCharPref("folder", gLocalPath.value);
  352.             prefBranch.setBoolPref("logmode", logmode);
  353.         }
  354.     } catch (ex) { debug(ex,8); }
  355. }
  356.  
  357. // *************************************************************************************************
  358. // *************************************** sessions/passwords **************************************
  359. // *************************************************************************************************
  360.  
  361. function onHostChange()
  362. {
  363.     var host = {value:""};
  364.     var login =  {value:""};
  365.     var password = {value:""}; 
  366.  
  367.     // try to auto-fill the username and password based on host
  368.     if (passwordmode)
  369.     {
  370.         try {
  371.             var passManager = Components.classes["@mozilla.org/passwordmanager;1"]
  372.                 .getService(Components.interfaces.nsIPasswordManagerInternal);
  373.  
  374.             if (document.getElementById('host').value.indexOf("ftp.") == 0)
  375.                 passManager.findPasswordEntry(document.getElementById('host').value, "", "", host, login, password);
  376.             else
  377.                 passManager.findPasswordEntry("ftp." + document.getElementById('host').value, "", "", host, login, password);
  378.  
  379.             document.getElementById('login').value = login.value;
  380.             document.getElementById('password').value = password.value;
  381.         } catch (ex) { }
  382.     }
  383. }
  384. function saveSession()
  385. {
  386.     connectedHost = document.getElementById('host').value + document.getElementById('login').value;
  387.     var host = {value:""};
  388.     var login =  {value:""};
  389.     var password = {value:""};
  390.  
  391.     try {
  392.         var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
  393.             .getService(Components.interfaces.nsIFormHistory);
  394.         formHistory.addEntry(document.getElementById('host').getAttribute("autocompletesearchparam"), document.getElementById('host').value);
  395.  
  396.         var passManager = Components.classes["@mozilla.org/passwordmanager;1"]
  397.             .getService(Components.interfaces.nsIPasswordManager);
  398.         var passManagerIn = Components.classes["@mozilla.org/passwordmanager;1"]
  399.             .getService(Components.interfaces.nsIPasswordManagerInternal);
  400.         
  401.         // remember last account only
  402.         while (1)
  403.         {
  404.             try {
  405.                 if (document.getElementById('host').value.indexOf("ftp.") == 0)
  406.                     passManagerIn.findPasswordEntry(document.getElementById('host').value, "", "", host, login, password);
  407.                 else
  408.                     passManagerIn.findPasswordEntry("ftp." + document.getElementById('host').value, "", "", host, login, password);
  409.             } catch (ex) { }
  410.             if (login.value)
  411.             {
  412.                 passManager.removeUser(host.value, login.value);
  413.                 login.value = "";
  414.             }
  415.             else
  416.                 break;
  417.         }
  418.         
  419.         // save username & password
  420.         if (passwordmode)
  421.         {
  422.             if (document.getElementById('host').value.indexOf("ftp.") == 0)
  423.                 passManager.addUser(document.getElementById('host').value,
  424.                     document.getElementById('login').value,
  425.                     document.getElementById('password').value);
  426.             else
  427.                 passManager.addUser("ftp." + document.getElementById('host').value,
  428.                     document.getElementById('login').value,
  429.                     document.getElementById('password').value);
  430.         }
  431.     } catch (ex) { debug(ex,11); }
  432. }
  433.  
  434. // *************************************************************************************************
  435. // *************************************** control socket ******************************************
  436. // *************************************************************************************************
  437.  
  438. function controlSocketConnect(manual)
  439. {
  440.     document.getElementById('host').value.replace(/^ftp:\/*/, '');    // error checking - get rid of 'ftp://'
  441.     var host = document.getElementById("host").value;
  442.  
  443.     if (host == "about:mozilla")
  444.     {
  445.         var windowHeight = document.getElementById('main-window').boxObject.height;
  446.         var windowWidth = document.getElementById('main-window').boxObject.width;
  447.  
  448.         window.openDialog("chrome://fireftp/content/welcomeDialog.xul","welcomeDialog",
  449.                 "screenY=0,screenX=0,width=" + windowWidth +",height=" + windowHeight +
  450.                 ",chrome,modal=no,dialog=yes,resizable=yes", welcomeMessage, true);
  451.         gConnectButton.label = "Flame On!";
  452.         gConnectButton.setAttribute('accesskey', "F");
  453.         return;
  454.     }
  455.  
  456.     if (!host)
  457.     {
  458.         var windowHeight = document.getElementById('main-window').boxObject.height;
  459.         var windowWidth = document.getElementById('main-window').boxObject.width;
  460.  
  461.         window.openDialog("chrome://fireftp/content/alertDialog.xul","alertDialog",
  462.                 "screenY="+(windowHeight/2 - 100)+",screenX="+(windowWidth/2 - 200)+
  463.                 ",width=400,height=200,chrome,modal=yes,dialog=yes,resizable=yes", strbundle.getString("alertFillHost"));
  464.         return;
  465.     }
  466.     if (!port || !parseInt(port))
  467.     {
  468.         var windowHeight = document.getElementById('main-window').boxObject.height;
  469.         var windowWidth = document.getElementById('main-window').boxObject.width;
  470.  
  471.         window.openDialog("chrome://fireftp/content/alertDialog.xul","alertDialog",
  472.                 "screenY="+(windowHeight/2 - 100)+",screenX="+(windowWidth/2 - 200)+
  473.                 ",width=400,height=200,chrome,modal=yes,dialog=yes,resizable=yes", strbundle.getString("alertFillPort"));
  474.         return;
  475.     }
  476.     
  477.     document.getElementById('statusbytes').label = strbundle.getString("connecting");
  478.     document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  479.     gConnectButton.label = strbundle.getString("disconnectButton");
  480.     gConnectButton.setAttribute('command','cmd_disconnect');
  481.     gConnectButton.setAttribute('accesskey', strbundle.getString("disconnectAccess"));
  482.  
  483.     if (!eventQueue.length || eventQueue[0].cmd != "welcome")
  484.         eventQueue.unshift( { cmd: "welcome", parameter: "", remember: "" } );
  485.     
  486.     if (manual)
  487.     {
  488.         isReconnecting = false;
  489.         reconnectAttempts = parseInt(attempts);
  490.         if (!reconnectAttempts || reconnectAttempts < 1)
  491.             reconnectAttempts = 1;
  492.     }
  493.  
  494.     // create a control socket
  495.     try
  496.     {
  497.         var transportService =
  498.           Components.classes["@mozilla.org/network/socket-transport-service;1"]
  499.             .getService(Components.interfaces.nsISocketTransportService);
  500.  
  501.         var proxyInfo = null;
  502.         
  503.         if (proxymode)
  504.         {
  505.             var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  506.                 getService(Components.interfaces.nsIPrefService);
  507.             var prefBranch = prefs.getBranch("network.proxy.");
  508.  
  509.             var proxyService = Components.classes["@mozilla.org/network/protocol-proxy-service;1"]
  510.                 .getService(Components.interfaces.nsIProtocolProxyService);
  511.             proxyInfo = proxyService.newProxyInfo("socks" + prefBranch.getIntPref("socks_version"),
  512.                     prefBranch.getCharPref("ftp"), prefBranch.getIntPref("ftp_port"));
  513.         }
  514.  
  515.         var control_transport;
  516.         if (sslmode)
  517.             control_transport = transportService.createTransport(["ssl"],1,host,parseInt(port),proxyInfo);
  518.         else
  519.             control_transport = transportService.createTransport(null,0,host,parseInt(port),proxyInfo);
  520.  
  521.         control_outstream = control_transport.openOutputStream(0,0,0);
  522.         var control_stream = control_transport.openInputStream(0,0,0);
  523.         control_instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
  524.             .createInstance(Components.interfaces.nsIScriptableInputStream);
  525.         control_instream.init(control_stream);
  526.  
  527.         // async data listener for the control socket
  528.         var dataListener = {
  529.             data : "",
  530.             onStartRequest: function(request, context){},
  531.             onStopRequest: function(request, context, status){
  532.                 if (!isConnected)    // no route to host
  533.                 {
  534.                     var el = document.getElementById('cmdlog').appendItem(strbundle.getString("errorConn"), strbundle.getString("errorConn"));
  535.                     el.setAttribute("style", 'color:red;font-weight:bold;');
  536.                     document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  537.                 }
  538.                 isConnected = false;
  539.  
  540.                 document.getElementById('statusbytes').label = "";
  541.                 document.getElementById('statusmeter').setAttribute("mode", "determined");
  542.                 document.getElementById('statusmeter').setAttribute("value", "0%");
  543.                 gConnectButton.label = strbundle.getString("connectButton");
  544.                 gConnectButton.setAttribute('command','cmd_connect');
  545.                 gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  546.  
  547.                 if (!legitClose && timeoutmode)
  548.                 {
  549.                     isReconnecting = true;
  550.                     var seconds = parseInt(retry);
  551.                     if (!seconds || seconds < 1)
  552.                         seconds = 1;
  553.                     document.getElementById('statusbytes').label =
  554.                         strbundle.getString("reconnect") + ' ' + seconds + ' ' + strbundle.getString("seconds") +
  555.                         ' ' + reconnectAttempts + ' ' + strbundle.getString("attempts");
  556.                     document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  557.                     setTimeout("tryReconnect()", seconds * 1000);
  558.                 }
  559.                 else
  560.                     eventQueue = new Array;
  561.             },
  562.             onDataAvailable: function(request, context, inputStream, offset, count){
  563.                 this.data = control_instream.read(count);
  564.                 readControl(this.data);
  565.             },
  566.         };
  567.  
  568.         var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
  569.             createInstance(Components.interfaces.nsIInputStreamPump);
  570.         pump.init(control_stream, -1, -1, 0, 0, false);
  571.         pump.asyncRead(dataListener,null);
  572.  
  573.     } catch(ex) {
  574.         error(strbundle.getString("errorConn"));
  575.         debug(ex,12);
  576.         return;
  577.     }
  578. }
  579. function tryReconnect()    // ahhhh! our precious connection has been lost, must...get it...back...our...precious
  580. {
  581.     if (!isReconnecting)
  582.     {
  583.         document.getElementById('statusbytes').label = "";
  584.         document.getElementById('statusmeter').setAttribute("mode", "determined");
  585.         document.getElementById('statusmeter').setAttribute("value", "0%");
  586.         return;
  587.     }
  588.  
  589.     --reconnectAttempts;
  590.     if (reconnectAttempts < 0)
  591.     {
  592.         document.getElementById('statusbytes').label = "";
  593.         document.getElementById('statusmeter').setAttribute("mode", "determined");
  594.         document.getElementById('statusmeter').setAttribute("value", "0%");
  595.         return;
  596.     }
  597.     
  598.     controlSocketConnect();
  599. }
  600. // helper function
  601. function addEventQueue(cmd, parameter, remember)
  602. {
  603.     parameter = parameter ? parameter : '';
  604.     remember = remember ? remember : '';
  605.     eventQueue.push( { cmd: cmd, parameter: parameter, remember: remember } );
  606. }
  607.  
  608. // *************************************************************************************************
  609. // ************************************* control socket write **************************************
  610. // *************************************************************************************************
  611.  
  612. function writeControl(cmd, parameter)
  613. {
  614.     try
  615.     {
  616.         if (cmd == "aborted" || cmd == "goodbye")
  617.         {
  618.             eventQueue.shift();
  619.             if (eventQueue.length)
  620.             {
  621.                 cmd = eventQueue[0].cmd;
  622.                 parameter = eventQueue[0].parameter;
  623.             }
  624.             else
  625.                 return;
  626.         }
  627.  
  628.         isReady = false;
  629.         if (cmd != "QUIT")
  630.         {
  631.             document.getElementById('statusbytes').label = strbundle.getString("working");
  632.             document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  633.         }
  634.         else
  635.         {
  636.             document.getElementById('statusbytes').label = "";
  637.             document.getElementById('statusmeter').setAttribute("mode", "determined");
  638.             document.getElementById('statusmeter').setAttribute("value", "0%");
  639.         }
  640.         var filesleft = 0;
  641.         for (var x = 0; x < eventQueue.length; ++x)
  642.             if (eventQueue[x].cmd == "RETR" || eventQueue[x].cmd == "APPE"
  643.                     || eventQueue[x].cmd == "STOR" || eventQueue[x].cmd == "DELE" || eventQueue[x].cmd == "RMD")
  644.             ++filesleft;
  645.         if (filesleft)
  646.             document.getElementById('statusbytes').label +=
  647.                 ' - ' + filesleft + ' ' + strbundle.getString("filesleft");
  648.         if (!pasvmode && cmd == "PASV")
  649.         {
  650.             cmd = "PORT";
  651.             activeConnected = false;
  652.             try {
  653.                 var DNSService = Components.classes["@mozilla.org/network/dns-service;1"]
  654.                         .getService(Components.interfaces.nsIDNSService);
  655.                 var ipAddress = DNSService.resolve(DNSService.myHostName, false).getNextAddrAsString();
  656.                 var re = /\x2e/g;
  657.                 var serverSocket = Components.classes["@mozilla.org/network/server-socket;1"]
  658.                         .createInstance(Components.interfaces.nsIServerSocket);
  659.                 var serverListener = {
  660.                     onSocketAccepted: function(serv, transport){
  661.                         data_transport = transport;
  662.                         if (activeCmd == "LIST")
  663.                             dataSocketConnect(false, '', 0, 0, transport);
  664.                         else if (activeCmd == "RETR")
  665.                             dataSocketConnect(false, activeRemember, activeRemember2, 0, transport);
  666.                         else if (activeCmd == "REST")
  667.                             dataSocketConnect(false, activeRemember, activeRemember2, activeRemember3, transport);
  668.                         else if (activeCmd == "STOR")
  669.                             dataSocketConnect(true, activeRemember, 0, 0, transport);
  670.                         else if (activeCmd == "APPE")
  671.                             dataSocketConnect(true, activeRemember, 0, activeRemember2, transport);
  672.                     },
  673.                     onStopListening: function(serv, status){ }
  674.                 };
  675.                 serverSocket.init(-1, false, -1);
  676.                 serverSocket.asyncListen(serverListener);
  677.                 parameter = ipAddress.replace(re, ",") + "," + parseInt(serverSocket.port / 256) + "," + serverSocket.port % 256;
  678.             } catch (ex) { 
  679.                 error(strbundle.getString("errorDataConn"));
  680.                 debug(ex,13);
  681.                 return;
  682.             }
  683.         }
  684.         var outputData = cmd + (parameter ? (' ' + parameter) : '') + "\r\n";    // thanks to devin
  685.         control_outstream.write(outputData,outputData.length);
  686.  
  687.         // alright, here's a little magic do-hickey
  688.         ++networkTimeoutID;
  689.         var seconds = parseInt(network);
  690.         if (!seconds || seconds < 1)
  691.             seconds = 1;
  692.         setTimeout("checkTimeout(" + networkTimeoutID + ",'" + cmd + "')", seconds * 1000);
  693.         
  694.         outputData = cmd + (parameter ? (' ' + parameter) : '');
  695.         if (cmd != "PASS")
  696.         {
  697.             var el = document.getElementById('cmdlog').appendItem("       " + outputData, "       " + outputData);
  698.             el.setAttribute('style', 'color:blue');
  699.             document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  700.         }
  701.         else
  702.         {
  703.             var el = document.getElementById('cmdlog').appendItem("       PASS " + strbundle.getString("passNotShown"), "       PASS " + strbundle.getString("passNotShown"));
  704.             el.setAttribute('style', 'color:blue');
  705.             document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  706.         }
  707.     } catch(ex) {
  708.         error(strbundle.getString("errorConn"));
  709.         debug(ex,14);
  710.     }
  711. }
  712.  
  713. // *************************************************************************************************
  714. // *************************************** control socket read *************************************
  715. // *************************************************************************************************
  716.  
  717. function readControl(buffer)
  718. {
  719.     var lastLineOfBuffer = buffer.split("\r\n");
  720.  
  721.     if (buffer != "2")    // these are self-generated fake messages
  722.     {
  723.         for (var x = 0; x < lastLineOfBuffer.length; ++x)
  724.         {
  725.             var message = lastLineOfBuffer[x].charAt(lastLineOfBuffer[x].length - 1) == '\r'
  726.                     ? lastLineOfBuffer[x].substring(0, lastLineOfBuffer[x].length - 1) : lastLineOfBuffer[x];
  727.             var el = document.getElementById('cmdlog').appendItem(message, message);
  728.             document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  729.             if (lastLineOfBuffer[x].charAt(0) == '4' || lastLineOfBuffer[x].charAt(0) == '5')
  730.                 el.setAttribute('style', 'color:red;font-weight:bold');
  731.             else
  732.                 el.setAttribute('style', 'color:green');
  733.         }
  734.     }
  735.     
  736.     lastLineOfBuffer = lastLineOfBuffer[lastLineOfBuffer.length - 1];
  737.     var returnCode;
  738.  
  739.     // see if they're still talking, blah blah blah
  740.     if ((lastLineOfBuffer.length > 3 && lastLineOfBuffer.charAt(3) == '-') || lastLineOfBuffer.charAt(0) == ' ')
  741.     {
  742.         if (eventQueue[0].cmd == "USER" || eventQueue[0].cmd == "PASS")
  743.             welcomeMessage += buffer;
  744.         return;
  745.     }
  746.     else
  747.         returnCode = parseInt(lastLineOfBuffer.charAt(0));    // no intelligence here, folks
  748.  
  749.     var cmd;
  750.     var parameter;
  751.     var remember;
  752.     
  753.     if (eventQueue.length)
  754.     {
  755.         cmd = eventQueue[0].cmd;
  756.         parameter = eventQueue[0].parameter;
  757.         remember = eventQueue[0].remember;
  758.         if (cmd != "LIST" && cmd != "RETR" && cmd != "STOR" && cmd != "APPE")
  759.         {
  760.             var throwAway = eventQueue.shift();
  761.             if (throwAway.cmd != "USER" && throwAway.cmd != "PASS" && throwAway.cmd != "PWD" &&
  762.                     throwAway.cmd != "welcome" && throwAway.cmd != "goodbye" && throwAway.cmd != "aborted")
  763.                 trashQueue.push(throwAway);
  764.             if (trashQueue.length > 4)    // don't remember too much junk
  765.                 trashQueue.shift();
  766.         }
  767.         if (eventQueue.cmd)    // messy, messy, messy
  768.             eventQueue = new Array(eventQueue);
  769.     }
  770.     else
  771.         cmd = "default";    // an unexpected reply - perhaps a 421 timeout message
  772.  
  773.     switch(cmd)
  774.     {
  775.         case "welcome":
  776.             welcomeMessage = buffer;
  777.             
  778.             if (returnCode != 2)
  779.             {
  780.                 displayWelcomeMessage();    // not so welcome
  781.                 eventQueue = new Array;
  782.                 addEventQueue("welcome");
  783.                 gConnectButton.label = strbundle.getString("connectButton");
  784.                 gConnectButton.setAttribute('command','cmd_connect');
  785.                 gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  786.                 document.getElementById('login').focus();
  787.                 break;
  788.             }
  789.             
  790.             isConnected = true;
  791.             reconnectAttempts = parseInt(attempts);
  792.             if (!reconnectAttempts || reconnectAttempts < 1)
  793.                 reconnectAttempts = 1;
  794.  
  795.             //favicon - just for kicks
  796.             var host = document.getElementById("host").value;
  797.             if (host.indexOf("ftp.") == 0)
  798.                 host = host.substring(4);
  799.             document.getElementById('favicon').src = "http://" + host + "/favicon.ico";
  800.  
  801.             initialPath = gRemotePath.value;     // save the path (and the seamonkeys)
  802.             // send username
  803.             if (!document.getElementById('login').value) // send anonymous username if field blank
  804.                 document.getElementById('login').value = "anonymous";
  805.             eventQueue.unshift( { cmd: "USER", parameter: document.getElementById('login').value, remember: "" } );
  806.             break;
  807.         case "goodbye":        // you say yes, i say no, you stay stop...
  808.             if (returnCode != 2)
  809.                 error(buffer, true);
  810.             return;
  811.         case "USER":
  812.             if (returnCode == 2)
  813.             {
  814.                 welcomeMessage += buffer;
  815.                 displayWelcomeMessage();
  816.                 saveSession();
  817.  
  818.                 // list the current directory
  819.                 if (!legitClose)
  820.                 {
  821.                     recoverFromDisaster();
  822.                     break;
  823.                 }
  824.                 legitClose = false;
  825.                 remotePathFocus = '/';
  826.                 gRemotePath.value = initialPath;
  827.  
  828.                 remotedircache = new Array( {path: "/", open: true, empty: false, children: -1} );
  829.                 hiddenremotedircache = new Array;
  830.                 
  831.                 eventQueue.unshift( { cmd: "PWD", parameter: "", remember: "" } );
  832.             }
  833.             else if (returnCode == 3)
  834.             {
  835.                 // send password
  836.                 if (!document.getElementById('password').value) // send anonymous password if field blank
  837.                     document.getElementById('password').value = "anon@mous.com";
  838.                 eventQueue.unshift( { cmd: "PASS", parameter: document.getElementById('password').value, remember: "" } );
  839.             }
  840.             else
  841.             {
  842.                 eventQueue = new Array;
  843.                 addEventQueue("welcome");
  844.                 error(buffer, true);
  845.                 gConnectButton.label = strbundle.getString("connectButton");
  846.                 gConnectButton.setAttribute('command','cmd_connect');
  847.                 gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  848.                 document.getElementById('login').focus();
  849.                 return;
  850.             }
  851.             break;
  852.         case "PASS":
  853.             if (returnCode == 2)
  854.             {
  855.                 welcomeMessage += buffer;
  856.                 displayWelcomeMessage();
  857.                 saveSession();
  858.  
  859.                 // list the current directory
  860.                 if (!legitClose)
  861.                 {
  862.                     recoverFromDisaster();
  863.                     break;
  864.                 }
  865.                 legitClose = false;
  866.                 remotePathFocus = '/';
  867.                 gRemotePath.value = initialPath;
  868.  
  869.                 remotedircache = new Array( {path: "/", open: true, empty: false, children: -1} );
  870.                 hiddenremotedircache = new Array;
  871.  
  872.                 eventQueue.unshift( { cmd: "PWD", parameter: "", remember: "" } );
  873.             }
  874.             else
  875.             {
  876.                 eventQueue = new Array;
  877.                 addEventQueue("welcome");
  878.                 error(buffer, true);
  879.                 gConnectButton.label = strbundle.getString("connectButton");
  880.                 gConnectButton.setAttribute('command','cmd_connect');
  881.                 gConnectButton.setAttribute('accesskey', strbundle.getString("connectAccess"));
  882.                 document.getElementById('password').focus();
  883.                 return;
  884.             }
  885.             break;
  886.         case "PASV":
  887.             if (returnCode != 2)
  888.             {
  889.                 error(buffer, true);
  890.                 break;
  891.             }
  892.  
  893.             emptyFileSoCloseLater = false;
  894.             if (pasvmode)
  895.             {
  896.                 // whoa, string manipulation ahoy
  897.                 buffer = buffer.substring(buffer.indexOf("(") + 1, buffer.indexOf(")"));
  898.  
  899.                 var re = /,/g;
  900.                 buffer = buffer.replace(re,".");
  901.                 dataport = parseInt(buffer.substring(buffer.lastIndexOf(".") + 1));
  902.                 dataport += 256 * parseInt(buffer.substring(buffer.lastIndexOf(".", buffer.lastIndexOf(".") - 1) + 1, buffer.lastIndexOf(".") + 1));
  903.                 datahost = buffer.substring(0, buffer.lastIndexOf(".", buffer.lastIndexOf(".") - 1));
  904.  
  905.                 if (eventQueue[0].cmd == "LIST")
  906.                 {
  907.                     remember = remember ? remember : false;
  908.                     data_remember = "afterList(\"" + eventQueue[0].remember + "\"," + remember + ")";
  909.                     dataSocketConnect();
  910.                 }
  911.                 else if (eventQueue[0].cmd == "RETR")
  912.                     dataSocketConnect(false, eventQueue[0].remember, remember);
  913.                 else if (eventQueue[0].cmd == "REST")
  914.                     dataSocketConnect(false, eventQueue[1].remember, remember, eventQueue[0].parameter);
  915.                 else if (eventQueue[0].cmd == "STOR")
  916.                     dataSocketConnect(true, eventQueue[0].remember, 0, 0);
  917.                 else if (eventQueue[0].cmd == "APPE")
  918.                     dataSocketConnect(true, eventQueue[0].remember[0], 0, eventQueue[0].remember[1]);
  919.             }
  920.             else
  921.             {
  922.                 activeCmd = eventQueue[0].cmd;
  923.                 if (eventQueue[0].cmd == "LIST")
  924.                 {
  925.                     remember = remember ? remember : false;
  926.                     data_remember = "afterList(\"" + eventQueue[0].remember + "\"," + remember + ")";
  927.                 }
  928.                 else if (eventQueue[0].cmd == "RETR")
  929.                 {
  930.                     activeRemember = eventQueue[0].remember;
  931.                     activeRemember2 = remember;
  932.                 }
  933.                 else if (eventQueue[0].cmd == "REST")
  934.                 {
  935.                     activeRemember = eventQueue[1].remember;
  936.                     activeRemember2 = remember;
  937.                     activeRemember3 = eventQueue[0].parameter;
  938.                 }
  939.                 else if (eventQueue[0].cmd == "STOR")
  940.                     activeRemember = eventQueue[0].remember;
  941.                 else if (eventQueue[0].cmd == "APPE")
  942.                 {
  943.                     activeRemember = eventQueue[0].remember[0];
  944.                     activeRemember2 = eventQueue[0].remember[1];
  945.                 }
  946.             }
  947.             break;
  948.         case "TYPE":
  949.             if (returnCode != 2)
  950.                 error(buffer, true);
  951.             break;
  952.         case "LIST":
  953.             eventQueue[0].cmd = "LIST2";
  954.             if (returnCode == 2)
  955.             {
  956.                 if (data_finished)
  957.                 {
  958.                     var throwAway = eventQueue.shift();
  959.                     trashQueue.push(throwAway);
  960.                     if (trashQueue.length > 4)    // don't remember too much junk
  961.                         trashQueue.shift();
  962.                     eval(data_remember);
  963.                     break;
  964.                 }
  965.                 else
  966.                 {
  967.                     setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  968.                     return;
  969.                 }
  970.             }
  971.             if (returnCode != 1)
  972.             {
  973.                 moreToRemember = "";
  974.                 error(buffer, true);
  975.                 eventQueue.shift();
  976.                 break;
  977.             }
  978.             return;
  979.         case "LIST2":
  980.             if (returnCode != 2)
  981.             {
  982.                 error(buffer, true);
  983.                 break;
  984.             }
  985.  
  986.             if (data_finished)
  987.                 eval(data_remember);
  988.             else
  989.             {
  990.                 eventQueue.unshift( { cmd: "LIST2", parameter: "", remember: "" } );
  991.                 setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  992.                 return;
  993.             }
  994.             break;
  995.         case "MKD":
  996.             if (returnCode != 2)
  997.             {
  998.                 moreToRemember = "";
  999.                 error(buffer, true);
  1000.                 break;
  1001.             }
  1002.             if (!storeRemoteParent && refreshmode)
  1003.                 refreshRemoteView();
  1004.             break;
  1005.         case "RNFR":
  1006.             if (returnCode != 3)
  1007.             {
  1008.                 moreToRemember = "";
  1009.                 eventQueue = new Array;
  1010.                 error(buffer, true);
  1011.             }
  1012.             break;
  1013.         case "RNTO":
  1014.             if (returnCode != 2)
  1015.             {
  1016.                 moreToRemember = "";
  1017.                 error(buffer, true);
  1018.                 break;
  1019.             }
  1020.             if (remember == "pasting")
  1021.                 break;
  1022.             if (remember)
  1023.                 onRemoteCDUP();
  1024.             moreToRemember = "renameRemoteFile2(" + remember + ")";
  1025.             if (refreshmode)
  1026.                 refreshRemoteView();
  1027.             break;
  1028.         case "DELE":
  1029.         case "RMD":
  1030.             if (returnCode != 2)
  1031.             {
  1032.                 error(buffer, true);
  1033.                 break;
  1034.             }
  1035.  
  1036.             // clear out of cache
  1037.             if (cmd == "RMD")
  1038.             {
  1039.                 for (var x = 0; x < remotecache.length; ++x)
  1040.                     if (remotecache[x].path == parameter && remotecache[x].host == connectedHost)
  1041.                     {
  1042.                         remotecache.splice(x, 1);
  1043.                         if (remotecache.data)
  1044.                             remotecache = new Array(remotecache);// messy, messy, messy
  1045.                         break;
  1046.                     }
  1047.                 // remove from nsICache
  1048.                 if (sessionsmode)
  1049.                 {
  1050.                     try {
  1051.                         var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
  1052.                             .getService(Components.interfaces.nsICacheService);
  1053.                         var cacheSession = cacheService.createSession("fireftp", 0, true);
  1054.                         var cacheDesc = cacheSession.openCacheEntry("ftp://" + connectedHost + parameter,
  1055.                             Components.interfaces.nsICache.ACCESS_WRITE, false);
  1056.                         cacheDesc.doom();
  1057.                         cacheDesc.close();
  1058.                     } catch (ex) { debug(ex,15); }
  1059.                 }
  1060.             }
  1061.  
  1062.             eval(remember);
  1063.             break;
  1064.         case "RETR":
  1065.             eventQueue[0].cmd = "RETR2";
  1066.             if (returnCode == 2)
  1067.             {
  1068.                 if (data_finished)
  1069.                 {
  1070.                     var throwAway = eventQueue.shift();
  1071.                     trashQueue.push(throwAway);
  1072.                     if (trashQueue.length > 4)    // don't remember too much junk
  1073.                         trashQueue.shift();
  1074.                     break;
  1075.                 }
  1076.                 else
  1077.                 {
  1078.                     setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1079.                     return;
  1080.                 }
  1081.             }
  1082.             if (returnCode != 1)
  1083.             {
  1084.                 error(buffer, true);
  1085.                 eventQueue.shift();
  1086.                 break;
  1087.             }
  1088.             return;
  1089.         case "RETR2":
  1090.             if (returnCode != 2)
  1091.             {
  1092.                 if (buffer.substring(0,3) != "450")    // ignore this error - from the pseudo-ABOR we generate
  1093.                     error(buffer, true);
  1094.                 break;
  1095.             }
  1096.  
  1097.             if (!data_finished)
  1098.             {
  1099.                 eventQueue.unshift( { cmd: "RETR2", parameter: "", remember: "" } );
  1100.                 setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1101.                 return;
  1102.             }
  1103.             break;
  1104.         case "REST":
  1105.             if (returnCode != 3)
  1106.             {
  1107.                 error(buffer, true);    // should still be able to go on without this, just not with resuming
  1108.                 break;
  1109.             }
  1110.             break;
  1111.         case "SITE CHMOD":
  1112.             if (returnCode != 2)
  1113.             {
  1114.                 moreToRemember = "";
  1115.                 error(buffer, true);
  1116.                 break;
  1117.             }
  1118.             moreToRemember = "getRemoteProperties2(\'" + remember + "\')";
  1119.             if (refreshmode)
  1120.                 refreshRemoteView();
  1121.             break;
  1122.         case "APPE":
  1123.             eventQueue[0].cmd = "APPE2";
  1124.             if (returnCode == 2)
  1125.             {
  1126.                 if (data_finished)
  1127.                 {
  1128.                     var throwAway = eventQueue.shift();
  1129.                     trashQueue.push(throwAway);
  1130.                     if (trashQueue.length > 4)    // don't remember too much junk
  1131.                         trashQueue.shift();
  1132.                     break;
  1133.                 }
  1134.                 else
  1135.                 {
  1136.                     setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1137.                     return;
  1138.                 }
  1139.             }
  1140.             if (returnCode != 1)
  1141.             {
  1142.                 error(buffer, true);
  1143.                 eventQueue.shift();
  1144.                 break;
  1145.             }
  1146.             return;
  1147.         case "APPE2":
  1148.             if (returnCode != 2)
  1149.             {
  1150.                 error(buffer, true);
  1151.                 break;
  1152.             }
  1153.  
  1154.             if (!data_finished)
  1155.             {
  1156.                 eventQueue.unshift( { cmd: "APPE2", parameter: "", remember: "" } );
  1157.                 setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1158.                 return;
  1159.             }
  1160.             break;
  1161.         case "STOR":
  1162.             if (emptyFileSoCloseLater)
  1163.             {
  1164.                 try {
  1165.                     data_outstream.close();
  1166.                 } catch (ex) { debug(ex,42); }
  1167.             }
  1168.             eventQueue[0].cmd = "STOR2";
  1169.             if (returnCode == 2)
  1170.             {
  1171.                 if (data_finished)
  1172.                 {
  1173.                     var throwAway = eventQueue.shift();
  1174.                     trashQueue.push(throwAway);
  1175.                     if (trashQueue.length > 4)    // don't remember too much junk
  1176.                         trashQueue.shift();
  1177.                     break;
  1178.                 }
  1179.                 else
  1180.                 {
  1181.                     setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1182.                     return;
  1183.                 }
  1184.             }
  1185.             if (returnCode != 1)
  1186.             {
  1187.                 error(buffer, true);
  1188.                 eventQueue.shift();
  1189.                 break;
  1190.             }
  1191.             return;
  1192.         case "STOR2":
  1193.             if (returnCode != 2)
  1194.             {
  1195.                 error(buffer, true);
  1196.                 break;
  1197.             }
  1198.  
  1199.             if (!data_finished)
  1200.             {
  1201.                 eventQueue.unshift( { cmd: "STOR2", parameter: "", remember: "" } );
  1202.                 setTimeout("readControl(\"2\")", 500);    // give data stream some time to finish up
  1203.                 return;
  1204.             }
  1205.             break;
  1206.         case "SIZE":
  1207.             if (returnCode == 2)
  1208.             {
  1209.                 var size = buffer.split(" ");
  1210.                 size = parseInt(size[1]);
  1211.                 for (var x = 0; x < eventQueue.length; ++x)
  1212.                     if (remember == eventQueue[x].cmd)
  1213.                     {
  1214.                         if (remember == "STOR")
  1215.                         {
  1216.                             eventQueue[x].cmd = "APPE";
  1217.                             eventQueue[x].remember = new Array(eventQueue[x].remember, size);
  1218.                         }
  1219.                         if (remember == "APPE")
  1220.                             eventQueue[x].remember = new Array(eventQueue[x].remember[0], size);
  1221.                         break;
  1222.                     }
  1223.             }
  1224.             break;
  1225.         case "aborted":
  1226.             break;
  1227.         case "CWD":
  1228.             if (returnCode != 2)    // it's not a directory
  1229.                 retr();
  1230.             else
  1231.             {
  1232.                 gRemotePath.value = parameter;
  1233.                 onRemotePathChange();
  1234.             }
  1235.             break;
  1236.         case "PWD":    // gotta check for chrooted directories
  1237.             buffer = buffer.substring(buffer.indexOf("\"") + 1, buffer.lastIndexOf("\""));
  1238.  
  1239.             if (buffer != '/')    // chrooted
  1240.             {
  1241.                 remotecache.push( { path: '/', host: connectedHost, data: '' });
  1242.                 gRemotePath.value = buffer;
  1243.             }
  1244.         
  1245.             onRemotePathChange();
  1246.             break;
  1247.         default:
  1248.             if (returnCode != 2)
  1249.                 error(buffer, true);
  1250.             break;
  1251.     }
  1252.  
  1253.     isReady = true;
  1254.     if (eventQueue.length && eventQueue[0].cmd != "welcome")    // start the next command
  1255.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  1256.     else    // or refresh in the appropriate manner
  1257.     {
  1258.         document.getElementById('statusbytes').label = "";
  1259.         document.getElementById('statusmeter').setAttribute("mode", "determined");
  1260.         document.getElementById('statusmeter').setAttribute("value", "0%");
  1261.         if (retrieveLocalParent == gLocalPath.value)
  1262.         {
  1263.             retrieveLocalParent = "";
  1264.             setTimeout("refreshLocalView()", 1000);    // give it a little time to freshen up
  1265.         }
  1266.         else if (storeRemoteParent && refreshmode)
  1267.         {
  1268.             if (storeRemoteParent == gRemotePath.value)
  1269.                 refreshRemoteView();
  1270.             else
  1271.             {
  1272.                 refreshRemote = true;
  1273.                 list(storeRemoteParent, "refreshRemote = false", true);
  1274.                 if (isReady)    // get the ball rollin' if it isn't already
  1275.                     writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  1276.             }
  1277.             storeRemoteParent = "";
  1278.         }
  1279.         else if (remoteCutParentDir && remotePasteReady && refreshmode)
  1280.         {
  1281.             var tempPath = gRemotePath.value;
  1282.             if (remoteCutParentDir2)
  1283.             {
  1284.                 gRemotePath.value =    remoteCutParentDir.substring(0,
  1285.                     remoteCutParentDir.lastIndexOf('/')    ? remoteCutParentDir.lastIndexOf('/') : 1);
  1286.                 onRemotePathChange();
  1287.                 moreToRemember = "refreshPasting(\'" + tempPath + "\')";
  1288.                 refreshRemoteView();
  1289.             }
  1290.             else
  1291.             {
  1292.                 gRemotePath.value = isRemoteDirCut
  1293.                     ? remoteCutParentDir.substring(0, remoteCutParentDir.lastIndexOf('/') ? remoteCutParentDir.lastIndexOf('/') : 1)
  1294.                     : remoteCutParentDir;
  1295.                 remoteCutParentDir = "";
  1296.                 onRemotePathChange();
  1297.                 moreToRemember = "moreToRemember='';gRemotePath.value=\'" + tempPath + "\';refreshRemoteView();";
  1298.                 refreshRemoteView();
  1299.             }
  1300.             remotePasteReady = false;
  1301.         }
  1302.     }
  1303. }
  1304.  
  1305. // *************************************************************************************************
  1306. // ******************************************** data socket ****************************************
  1307. // *************************************************************************************************
  1308.  
  1309. function dataSocketConnect(write, localpath, fileTotalBytes, filePartialBytes, activeTransport)
  1310. {
  1311.     // create a data socket
  1312.     try
  1313.     {
  1314.         if (pasvmode)
  1315.         {
  1316.             var transportService =
  1317.                     Components.classes["@mozilla.org/network/socket-transport-service;1"]
  1318.                     .getService(Components.interfaces.nsISocketTransportService);
  1319.  
  1320.             var proxyInfo = null;
  1321.         
  1322.             if (proxymode)
  1323.             {
  1324.                 var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  1325.                     getService(Components.interfaces.nsIPrefService);
  1326.                 var prefBranch = prefs.getBranch("network.proxy.");
  1327.  
  1328.                 var proxyService = Components.classes["@mozilla.org/network/protocol-proxy-service;1"]
  1329.                     .getService(Components.interfaces.nsIProtocolProxyService);
  1330.                 proxyInfo = proxyService.newProxyInfo("socks" + prefBranch.getIntPref("socks_version"),
  1331.                         prefBranch.getCharPref("ftp"), prefBranch.getIntPref("ftp_port"));
  1332.             }
  1333.         
  1334.             if (sslmode)
  1335.                 data_transport = transportService.createTransport(["ssl"],1,datahost,dataport,proxyInfo);
  1336.             else
  1337.                 data_transport = transportService.createTransport(null,0,datahost,dataport,proxyInfo);
  1338.         }
  1339.         else
  1340.             data_transport = activeTransport;
  1341.  
  1342.         if (write)
  1343.         {
  1344.             data_finished = false;
  1345.             data_outstream = data_transport.openOutputStream(0,0,-1);
  1346.  
  1347.             // open the file
  1348.             var file;
  1349.             try {
  1350.                 file = Components.classes['@mozilla.org/file/local;1']
  1351.                     .createInstance(Components.interfaces.nsILocalFile);
  1352.                 file.initWithPath(localpath);
  1353.                 file_instream = Components.classes["@mozilla.org/network/file-input-stream;1"]
  1354.                     .createInstance();
  1355.                 file_instream.QueryInterface(Components.interfaces.nsIFileInputStream);
  1356.                 file_instream.init(file, 0x01, 420, 0);
  1357.                 file_instream.QueryInterface(Components.interfaces.nsISeekableStream);
  1358.                 file_instream.seek(0, filePartialBytes);    // append or not to append
  1359.             } catch (ex) {
  1360.                 debug(ex,16);
  1361.                 error(strbundle.getString("failedUpload") + " '" + localpath + "'.");
  1362.                 data_outstream.close();
  1363.                 data_transport.close("Finished");
  1364.                 file_instream.close();
  1365.                 data_finished = true;
  1366.             }
  1367.             
  1368.             // async write, update status bar at the same time
  1369.             var outputStreamCallback = {
  1370.                 sendBytesTotal : file.fileSize,
  1371.                 sendBytesUploaded : parseInt(filePartialBytes),
  1372.                 sendTimeStart : new Date(),
  1373.                 sendOffset : parseInt(filePartialBytes),
  1374.                 onOutputStreamReady: function(stream){
  1375.                     try {
  1376.                         var numBytesToWrite = file_instream.available();
  1377.                         data_outstream.writeFrom(file_instream, file_instream.available());
  1378.  
  1379.                         // NOTE: for the time being, this doesn't work, we ignore it in checkTimeout
  1380.                         ++networkTimeoutID;
  1381.                         var seconds = parseInt(network);
  1382.                         if (!seconds || seconds < 1)
  1383.                             seconds = 1;
  1384.                         setTimeout("checkTimeout(" + networkTimeoutID + ", '', true)", seconds * 1000);
  1385.                         
  1386.                         this.sendBytesUploaded += numBytesToWrite - file_instream.available();
  1387.                         var timeElapsed = ((new Date()) - this.sendTimeStart) / 1000;
  1388.                         timeElapsed = timeElapsed ? timeElapsed : 1; // no dividing by 0
  1389.                         var averageRate = ((this.sendBytesUploaded - this.sendOffset) / 1024 / timeElapsed).toFixed(2);
  1390.                         averageRate = averageRate ? averageRate : 0.01; // no dividing by 0
  1391.                         var timeRemaining = (this.sendBytesTotal - this.sendBytesUploaded) / 1024 * (1 / averageRate);
  1392.                         averageRate += " KB/s";
  1393.  
  1394.                         var filesleft = 1;
  1395.                         for (var x = 0; x < eventQueue.length; ++x)
  1396.                             if (eventQueue[x].cmd == "RETR" || eventQueue[x].cmd == "APPE"
  1397.                                     || eventQueue[x].cmd == "STOR")
  1398.                                 ++filesleft;
  1399.                         document.getElementById('statusbytes').label =
  1400.                                 commas(this.sendBytesUploaded) + " / " + commas(this.sendBytesTotal) + ' - ' +
  1401.                                 filesleft + ' ' + strbundle.getString("filesleft");
  1402.                         var hours = parseInt(timeElapsed / 3600);
  1403.                         var min = parseInt((timeElapsed - hours * 3600) / 60);
  1404.                         var sec = parseInt(timeElapsed - hours * 3600 - min * 60);
  1405.                         document.getElementById('statuselapsed').label = zeros(hours)+":"+zeros(min)+":"+zeros(sec);
  1406.  
  1407.                         hours = parseInt(timeRemaining / 3600);
  1408.                         min = parseInt((timeRemaining - hours * 3600) / 60);
  1409.                         sec = parseInt(timeRemaining - hours * 3600 - min * 60);
  1410.                         document.getElementById('statusremaining').label = zeros(hours)+":"+zeros(min)+":"+zeros(sec);
  1411.  
  1412.                         document.getElementById('statusrate').label = averageRate;
  1413.                         var total = this.sendBytesTotal ? this.sendBytesTotal : 1;    // no dividing by 0
  1414.                         document.getElementById('statusmeter').setAttribute("mode", "determined");
  1415.                         document.getElementById('statusmeter').setAttribute("value", parseInt(this.sendBytesUploaded / total * 100) + "%");
  1416.                         document.title = document.getElementById('statusmeter').getAttribute("value") + " - " + averageRate;
  1417.  
  1418.                         if (!file_instream.available())    // finished writing
  1419.                         {
  1420.                             if (file.fileSize)    // empty files are bad
  1421.                                 data_outstream.close();
  1422.                             else
  1423.                                 emptyFileSoCloseLater = true;
  1424.                             file_instream.close();
  1425.                             data_finished = true;
  1426.                             document.getElementById('statusbytes').label = strbundle.getString("working");
  1427.                             var filesleft = 0;
  1428.                             for (var x = 0; x < eventQueue.length; ++x)
  1429.                                 if (eventQueue[x].cmd == "RETR" || eventQueue[x].cmd == "APPE"
  1430.                                         || eventQueue[x].cmd == "STOR")
  1431.                                     ++filesleft;
  1432.                             if (filesleft)
  1433.                             {
  1434.                                 document.getElementById('statusbytes').label +=
  1435.                                         ' - ' + filesleft + ' ' + strbundle.getString("filesleft");
  1436.                                 document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  1437.                             }
  1438.                             else
  1439.                                 document.getElementById('statusmeter').setAttribute("mode", "determined");
  1440.                             document.getElementById('statuselapsed').label = "";
  1441.                             document.getElementById('statusremaining').label = "";
  1442.                             document.getElementById('statusrate').label = "";
  1443.                             document.getElementById('statusmeter').setAttribute("value", "0%");
  1444.                             document.title = "fireFTP";
  1445.                         }
  1446.                         else
  1447.                             data_outstream.asyncWait(outputStreamCallback, 0, 0, null);
  1448.                     } catch (ex) { debug(ex,17); /*WOULD_BLOCK*/  } // shouldn't get here
  1449.                 }
  1450.             }
  1451.             data_outstream.QueryInterface(Components.interfaces.nsIAsyncOutputStream);
  1452.             data_outstream.asyncWait(outputStreamCallback, 0, 0, null);
  1453.         }
  1454.         else    // read data
  1455.         {
  1456.             data_finished = false;
  1457.             var data_stream = data_transport.openInputStream(0,0,0);
  1458.             var data_instream = Components.classes["@mozilla.org/binaryinputstream;1"]
  1459.                 .createInstance(Components.interfaces.nsIBinaryInputStream);
  1460.             data_instream.setInputStream(data_stream);
  1461.  
  1462.             fileTotalBytes = fileTotalBytes ? fileTotalBytes : 0;
  1463.             filePartialBytes = filePartialBytes ? filePartialBytes : 0;
  1464.  
  1465.             // async read, update status bar at the same time
  1466.             var dataListener = {
  1467.                 dummy : "",
  1468.                 data : "",
  1469.                 file : "",
  1470.                 file_outstream : "",
  1471.                 binary_outstream : "",
  1472.                 bytesTotal : fileTotalBytes,
  1473.                 bytesDownloaded : filePartialBytes,
  1474.                 bytesPartial : filePartialBytes,
  1475.                 timeStart : 0,
  1476.                 byteArray : new Array,
  1477.                 isNotList : eventQueue[0].cmd.indexOf("LIST") == -1,
  1478.                 onStartRequest: function(request, context){
  1479.                     data = "";
  1480.                     if (this.isNotList)
  1481.                     {
  1482.                         this.timeStart = new Date();
  1483.                         try {
  1484.                             this.file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  1485.                             this.file.initWithPath(localpath);
  1486.                             this.file_outstream = Components.classes["@mozilla.org/network/file-output-stream;1"]
  1487.                                 .createInstance(Components.interfaces.nsIFileOutputStream);
  1488.                             if (filePartialBytes)
  1489.                                 this.file_outstream.init(this.file, 0x04 | 0x10, 420, 0);
  1490.                             else
  1491.                                 this.file_outstream.init(this.file, 0x04 | 0x08 | 0x20, 420, 0);
  1492.                             this.binary_outstream = Components.classes["@mozilla.org/binaryoutputstream;1"]
  1493.                                 .createInstance(Components.interfaces.nsIBinaryOutputStream); 
  1494.                             this.binary_outstream.setOutputStream(this.file_outstream);
  1495.                         } catch (ex) {
  1496.                             debug(ex,18);
  1497.                             error(strbundle.getString("failedSave") + " '" + localpath + "' " + strbundle.getString("failedSave2"));
  1498.                             data_instream.close();
  1499.                             data_transport.close("Finished");
  1500.                             this.binary_outstream.close();
  1501.                             this.file_outstream.close();
  1502.                             data_finished = true;
  1503.                         }
  1504.                     }
  1505.                 },
  1506.                 onStopRequest: function(request, context, status){
  1507.                     data_instream.close();
  1508.                     data_transport.close("Finished");
  1509.                     if (this.isNotList)
  1510.                     {
  1511.                         this.binary_outstream.close();
  1512.                         this.file_outstream.close();
  1513.                     }
  1514.                     var filesleft = 0;
  1515.                     for (var x = 0; x < eventQueue.length; ++x)
  1516.                         if (eventQueue[x].cmd == "RETR" || eventQueue[x].cmd == "APPE"
  1517.                                 || eventQueue[x].cmd == "STOR")
  1518.                             ++filesleft;
  1519.  
  1520.                     if (filesleft)
  1521.                     {
  1522.                         document.getElementById('statusbytes').label = strbundle.getString("working") + ' - ' +
  1523.                             filesleft + ' ' + strbundle.getString("filesleft");
  1524.                         document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  1525.                     }
  1526.                     else
  1527.                     {
  1528.                         document.getElementById('statusbytes').label = "";
  1529.                         document.getElementById('statusmeter').setAttribute("mode", "determined");
  1530.                     }
  1531.                     document.getElementById('statuselapsed').label = "";
  1532.                     document.getElementById('statuselapsed').label = "";
  1533.                     document.getElementById('statusremaining').label = "";
  1534.                     document.getElementById('statusrate').label = "";
  1535.                     document.getElementById('statusmeter').setAttribute("value", "0%");
  1536.                     document.title = "fireFTP";
  1537.                     data = this.data;
  1538.                     data_finished = true;
  1539.                 },
  1540.                 onDataAvailable: function(request, context, inputStream, offset, count){
  1541.                     if (this.isNotList)
  1542.                     {
  1543.                         try {
  1544.                             this.byteArray = data_instream.readByteArray(count, this.byteArray);
  1545.                             this.binary_outstream.writeByteArray(this.byteArray, this.byteArray.length);
  1546.  
  1547.                             ++networkTimeoutID;
  1548.                             var seconds = parseInt(network);
  1549.                             if (!seconds || seconds < 1)
  1550.                                 seconds = 1;
  1551.                             setTimeout("checkTimeout(" + networkTimeoutID + ", '', true)", seconds * 1000);
  1552.                             
  1553.                             this.bytesDownloaded += this.byteArray.length;
  1554.                             var timeElapsed = ((new Date()) - this.timeStart) / 1000;
  1555.                             timeElapsed = timeElapsed ? timeElapsed : 1; // no dividing by 0
  1556.                             var averageRate = ((this.bytesDownloaded - this.bytesPartial) / 1024 / timeElapsed).toFixed(2);
  1557.                             averageRate = averageRate ? averageRate : 0.01; // no dividing by 0
  1558.                             var timeRemaining = (this.bytesTotal - this.bytesDownloaded) / 1024 * (1 / averageRate);
  1559.  
  1560.                             averageRate += " KB/s";
  1561.                             var filesleft = 1;
  1562.                             for (var x = 0; x < eventQueue.length; ++x)
  1563.                                 if (eventQueue[x].cmd == "RETR" || eventQueue[x].cmd == "APPE"
  1564.                                         || eventQueue[x].cmd == "STOR")
  1565.                                     ++filesleft;
  1566.                             document.getElementById('statusbytes').label =
  1567.                                     commas(this.bytesDownloaded) + " / " + commas(this.bytesTotal) + ' - ' +
  1568.                                     filesleft + ' ' + strbundle.getString("filesleft");
  1569.                             var hours = parseInt(timeElapsed / 3600);
  1570.                             var min = parseInt((timeElapsed - hours * 3600) / 60);
  1571.                             var sec = parseInt(timeElapsed - hours * 3600 - min * 60);
  1572.                             document.getElementById('statuselapsed').label = zeros(hours)+":"+zeros(min)+":"+zeros(sec);
  1573.  
  1574.                             hours = parseInt(timeRemaining / 3600);
  1575.                             min = parseInt((timeRemaining - hours * 3600) / 60);
  1576.                             sec = parseInt(timeRemaining - hours * 3600 - min * 60);
  1577.                             document.getElementById('statusremaining').label = zeros(hours)+":"+zeros(min)+":"+zeros(sec);
  1578.  
  1579.                             document.getElementById('statusrate').label = averageRate;
  1580.                             var total = this.bytesTotal ? this.bytesTotal : 1;    // no dividing by 0
  1581.                             document.getElementById('statusmeter').setAttribute("mode", "determined");
  1582.                             document.getElementById('statusmeter').setAttribute("value", parseInt(this.bytesDownloaded / total * 100) + "%");
  1583.                             document.title = document.getElementById('statusmeter').getAttribute("value") + " - " + averageRate;
  1584.                         } catch (ex) {
  1585.                             debug(ex,19);
  1586.                             error(strbundle.getString("failedSave") + " '" + localpath + "' " + strbundle.getString("failedSave2"));
  1587.                             data_instream.close();
  1588.                             data_transport.close("Finished");
  1589.                             this.binary_outstream.close();
  1590.                             this.file_outstream.close();
  1591.                             data_finished = true;
  1592.                         }
  1593.                     }
  1594.                     else
  1595.                         this.data += data_instream.readBytes(data_instream.available());
  1596.                 },
  1597.             };
  1598.  
  1599.             var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
  1600.                 createInstance(Components.interfaces.nsIInputStreamPump);
  1601.             pump.init(data_stream, -1, -1, 0, 0, false);
  1602.             pump.asyncRead(dataListener,null);
  1603.         }
  1604.         
  1605.     } catch(ex) {
  1606.         error(strbundle.getString("errorDataConn"));
  1607.         debug(ex,20);
  1608.         return;
  1609.     }
  1610. }
  1611.  
  1612. // *************************************************************************************************
  1613. // *********************************************** LIST ********************************************
  1614. // *************************************************************************************************
  1615.  
  1616. // execute a LIST command gracefully, if necessary
  1617. var listPath;
  1618. function list(path, remember, noRemoteViewUpdate, deletingRecursively)
  1619. {
  1620.     noRemoteViewUpdate = noRemoteViewUpdate ? noRemoteViewUpdate : false;
  1621.     listPath = path;
  1622.     
  1623.     if (!refreshRemote)
  1624.     {
  1625.         if (!noRemoteViewUpdate)
  1626.             gRemotePath.value = path;
  1627.  
  1628.         // first, see if directory is in local cache
  1629.         var index = checkRemoteCache(path);
  1630.         if (index != -1)
  1631.         {
  1632.             data = remotecache[index].data;
  1633.             if (!noRemoteViewUpdate)
  1634.                 changeRemoteView();
  1635.             if (remember)
  1636.                 eval(remember);
  1637.             return;
  1638.         }
  1639.  
  1640.         // next, try nsICache
  1641.         if (sessionsmode)
  1642.         {
  1643.             try {
  1644.                 var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
  1645.                     .getService(Components.interfaces.nsICacheService);
  1646.                 var cacheSession = cacheService.createSession("fireftp", 0, true);
  1647.                 var cacheDesc = cacheSession.openCacheEntry("ftp://" + connectedHost + path,
  1648.                     Components.interfaces.nsICache.ACCESS_READ, false);
  1649.                 if (cacheDesc.dataSize)
  1650.                 {
  1651.                     var cacheIn = cacheDesc.openInputStream(0);
  1652.                     var cacheInstream = Components.classes["@mozilla.org/binaryinputstream;1"]
  1653.                         .createInstance(Components.interfaces.nsIBinaryInputStream);
  1654.                     cacheInstream.setInputStream(cacheIn);
  1655.                     data = cacheInstream.readBytes(cacheInstream.available());
  1656.                     remotecache.push( { path: path, host: connectedHost, data: data }); // add to local cache
  1657.                     cacheInstream.close();
  1658.                     if (!noRemoteViewUpdate)
  1659.                         changeRemoteView();
  1660.                     if (remember)
  1661.                         eval(remember);
  1662.                     return;
  1663.                 }
  1664.                 cacheDesc.close();
  1665.             } catch (ex) { }
  1666.         }
  1667.     }
  1668.  
  1669.     // do things backwards if deleting
  1670.     if (deletingRecursively)
  1671.     {
  1672.         eventQueue.unshift( { cmd: "LIST", parameter: hiddenmode ? "-al" : '', remember: remember } );
  1673.         eventQueue.unshift( { cmd: "PASV", parameter: "", remember: noRemoteViewUpdate } );
  1674.         eventQueue.unshift( { cmd: "CWD", parameter: path, remember: "" } );
  1675.         eventQueue.unshift( { cmd: "TYPE", parameter: "A", remember: "" } );
  1676.     }
  1677.     else
  1678.     {
  1679.         addEventQueue("TYPE", "A");
  1680.         addEventQueue("CWD", path);    // i thought i could do without you, CWD, but bugs #7588 and #7496 say otherwise
  1681.         addEventQueue("PASV", "", noRemoteViewUpdate);
  1682.         addEventQueue("LIST", hiddenmode ? "-al" : '', remember);
  1683.     }
  1684.     if (isReady && !noRemoteViewUpdate)    // get the ball rollin' if it isn't already
  1685.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  1686. }
  1687. function afterList(remember, noRemoteViewUpdate)
  1688. {
  1689.     data_remember = "";
  1690.     if (refreshRemote)    // update cache
  1691.     {
  1692.         for (var x = 0; x < remotecache.length; ++x)
  1693.             if (remotecache[x].path == listPath && remotecache[x].host == connectedHost)
  1694.             {
  1695.                 remotecache[x].data = data;
  1696.                 break;
  1697.             }
  1698.     }
  1699.     else
  1700.         remotecache.push( { path: listPath, host: connectedHost, data: data }); // add to local cache
  1701.  
  1702.     // add to nsICache
  1703.     if (sessionsmode)
  1704.     {
  1705.         try {
  1706.             var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
  1707.                 .getService(Components.interfaces.nsICacheService);
  1708.             var cacheSession = cacheService.createSession("fireftp", 0, true);
  1709.             var cacheDesc = cacheSession.openCacheEntry("ftp://" + connectedHost + listPath,
  1710.                 Components.interfaces.nsICache.ACCESS_WRITE, false);
  1711.             var cacheOut = cacheDesc.openOutputStream(0);
  1712.             cacheOut.write(data, data.length);
  1713.             cacheOut.close();
  1714.             cacheDesc.close();
  1715.         } catch (ex) { debug(ex,22); }
  1716.     }
  1717.  
  1718.     if (!noRemoteViewUpdate)
  1719.         changeRemoteView();
  1720.     eval(remember);
  1721. }
  1722.  
  1723. // *************************************************************************************************
  1724. // ********************************************* RETR/REST *****************************************
  1725. // *************************************************************************************************
  1726.  
  1727. // execute RETR (and REST if necessary)
  1728. var retrieveLocalParent;
  1729. function retr()
  1730. {
  1731.     if (!isConnected)
  1732.         return;
  1733.     var fileList = gRemoteTree.view.selection;
  1734.     if (fileList.count == 0)
  1735.         return;
  1736.  
  1737.     var retrievePrompt = true;
  1738.     var retrieveSkipAll = false;
  1739.     var resume;
  1740.     var localParent = gLocalPath.value;
  1741.     retrieveLocalParent = localParent;
  1742.     var remoteParent = gRemotePath.value;
  1743.  
  1744.     for (var x = 0; x < gRemoteTree.view.rowCount; ++x)
  1745.         if (gRemoteTree.view.selection.isSelected(x))
  1746.         {
  1747.             var remotepath = gRemoteTree.view.getCellText(x, "remotename");
  1748.             var isDirectory = gRemoteTree.view.getCellText(x, "remoteattr").charAt(0) == 'd';
  1749.             
  1750.             if (remoteParent.charAt(remoteParent.length - 1) != '/')
  1751.                 remotepath = remoteParent + '/' + remotepath;
  1752.             else
  1753.                 remotepath = remoteParent + remotepath;
  1754.             
  1755.             var localpath = remotepath.substring(remoteParent.length + (remoteParent != "/" ? 1 : 0));
  1756.             if (slash == "\\")
  1757.                 localpath = localpath.replace(/\x2f/g, "\\");
  1758.             if (localParent.charAt(localParent.length - 1) != slash)
  1759.                 localpath = localParent + slash + localpath;
  1760.             else
  1761.                 localpath = localParent + localpath;
  1762.             
  1763.             var index = checkRemoteCache(remoteParent);
  1764.             var files = parseListData(remotecache[index].data);
  1765.             var file = getLocalFileSpec(localpath);
  1766.             var filesIndex = 0;
  1767.             for (var y = 0; y < files.length; ++y)
  1768.                 if (files[y].name == file.leafName)
  1769.                 {
  1770.                     filesIndex = y;
  1771.                     break;
  1772.                 }
  1773.             
  1774.             if (file.exists() && retrieveSkipAll)
  1775.                 continue;
  1776.  
  1777.             // overwrite prompt
  1778.             resume = false;
  1779.             if (file.exists() && retrievePrompt && !isDirectory)
  1780.             {
  1781.                 var response = new Object();
  1782.                 response.value = 0;
  1783.                 var windowHeight = document.getElementById('main-window').boxObject.height;
  1784.                 var windowWidth = document.getElementById('main-window').boxObject.width;
  1785.                 var timer = destructmode;
  1786.  
  1787.                 if (file.fileSize < files[filesIndex].size)
  1788.                     resume = true;
  1789.                 else
  1790.                     resume = false;
  1791.  
  1792.                 window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  1793.                         "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  1794.                         ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes", response, file.leafName, resume, false, timer);
  1795.                 if (response.value == 1)
  1796.                     resume = false;
  1797.                 else if (response.value == 2)
  1798.                 {
  1799.                     retrievePrompt = false;
  1800.                     resume = false;
  1801.                 }
  1802.                 else if ((response.value == 3) || (response.value == 0))
  1803.                     continue;
  1804.                 else if (response.value == 4)
  1805.                     resume = true;
  1806.                 else if (response.value == 5)
  1807.                 {
  1808.                     retrieveSkipAll = true;
  1809.                     continue;
  1810.                 }
  1811.             }
  1812.  
  1813.             if (isDirectory)
  1814.             {
  1815.                 try{
  1816.                     file.createDir();
  1817.                 } catch (ex) {
  1818.                     error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1819.                     debug(ex,23);
  1820.                     continue;
  1821.                 }
  1822.                 if (!file.exists())
  1823.                 {
  1824.                     error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1825.                     continue;
  1826.                 }
  1827.  
  1828.                 //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  1829.                 list(remotepath, "retrieveRecursive(\'" + remotepath + "\',\'" + localParent.replace(/\x5c/g, "/") + "\',\'"
  1830.                         + remoteParent + "\'," + retrievePrompt + "," + retrieveSkipAll + ")", true);
  1831.             }
  1832.             else
  1833.             {
  1834.                 addEventQueue("TYPE", detectAscii(remotepath));
  1835.                 addEventQueue("PASV", "", files[filesIndex].size);
  1836.                 if (resume)
  1837.                 {
  1838.                     addEventQueue("REST", file.fileSize);
  1839.                     addEventQueue("RETR", remotepath, localpath);
  1840.                 }
  1841.                 else
  1842.                     addEventQueue("RETR", remotepath, localpath);
  1843.             }
  1844.         }
  1845.     if (isReady && eventQueue.length)    // get the ball rollin' if it isn't already
  1846.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  1847. }
  1848. // a RETR command sent from the folder view
  1849. function retrDir()
  1850. {
  1851.     if (!isConnected)
  1852.         return;
  1853.     if (gRemoteDirTree.view.selection.count == 0)
  1854.         return;
  1855.     var dir = gRemoteDirTree.view.selection.currentIndex;
  1856.     var localParent = gLocalPath.value;
  1857.     retrieveLocalParent = localParent;
  1858.     var remotepath = remotedircache[dir].path;
  1859.     var localpath = remotepath.substring(remotepath.lastIndexOf('/') + 1);
  1860.     
  1861.     if (localParent.charAt(localParent.length - 1) != slash)
  1862.         localpath = localParent + slash + localpath;
  1863.     else
  1864.         localpath = localParent + localpath;
  1865.  
  1866.     var localdir = getLocalFileSpec(localpath);
  1867.  
  1868.     try{
  1869.         localdir.createDir();
  1870.     } catch (ex) {
  1871.         error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1872.         debug(ex,24);
  1873.         return;
  1874.     }
  1875.     if (!localdir.exists())
  1876.     {
  1877.         error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1878.         return;
  1879.     }
  1880.     //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  1881.     localpath = localpath.replace(/\x5c/g, "/");
  1882.     var retrievePrompt = true;
  1883.     var retrieveSkipAll = false;
  1884.  
  1885.     list(remotepath, "retrieveRecursive(\'" + remotepath + "\',\'" + localpath + "\',\'" + remotepath
  1886.             + "\'," + retrievePrompt + "," + retrieveSkipAll + ")", true);
  1887.     if (isReady && eventQueue.length)    // get the ball rollin' if it isn't already
  1888.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  1889. }
  1890.  
  1891. // *************************************************************************************************
  1892. // *************************************** RETR/REST recursive *************************************
  1893. // *************************************************************************************************
  1894.  
  1895. function retrieveRecursive(parent, localParent, remoteParent, retrievePrompt, retrieveSkipAll)
  1896. {
  1897.     if (slash == "\\")    //XXX continuation of aforementioned hack, working around eval()
  1898.         localParent = localParent.replace(/\x2f/g, "\\");
  1899.     var resume;
  1900.     var files = parseListData(data);
  1901.     
  1902.     for (var x = 0; x < files.length; ++x)
  1903.     {
  1904.         var remotepath = files[x].name;
  1905.         var isDirectory = files[x].isDirectory;
  1906.         
  1907.         if (parent.charAt(parent.length - 1) != '/')
  1908.             remotepath = parent + '/' + remotepath;
  1909.         else
  1910.             remotepath = parent + remotepath;
  1911.  
  1912.         var localpath = remotepath.substring(remoteParent.length + (remoteParent != "/" ? 1 : 0));
  1913.         if (slash == "\\")
  1914.             localpath = localpath.replace(/\x2f/g, "\\");
  1915.         if (localParent.charAt(localParent.length - 1) != slash)
  1916.             localpath = localParent + slash + localpath;
  1917.         else
  1918.             localpath = localParent + localpath;
  1919.  
  1920.         var file = getLocalFileSpec(localpath);
  1921.         if (file.exists() && retrieveSkipAll)
  1922.             continue;
  1923.         resume = false;
  1924.         if (file.exists() && retrievePrompt && !isDirectory)
  1925.         {
  1926.             var response = new Object();
  1927.             response.value = 0;
  1928.             var windowHeight = document.getElementById('main-window').boxObject.height;
  1929.             var windowWidth = document.getElementById('main-window').boxObject.width;
  1930.             var timer = destructmode;
  1931.  
  1932.             if (file.fileSize < files[x].size)
  1933.                 resume = true;
  1934.             else
  1935.                 resume = false;
  1936.  
  1937.             window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  1938.                     "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  1939.                     ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes", response, file.leafName, resume, false, timer);
  1940.             if (response.value == 1)
  1941.                 resume = false;
  1942.             else if (response.value == 2)
  1943.             {
  1944.                 retrievePrompt = false;
  1945.                 resume = false;
  1946.             }
  1947.             else if ((response.value == 3) || (response.value == 0))
  1948.                 continue;
  1949.             else if (response.value == 4)
  1950.                 resume = true;
  1951.             else if (response.value == 5)
  1952.             {
  1953.                 retrieveSkipAll = true;
  1954.                 continue;
  1955.             }
  1956.         }
  1957.             
  1958.         if (isDirectory)
  1959.         {
  1960.             try{
  1961.                 file.createDir();
  1962.             } catch (ex) {
  1963.                 error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1964.                 debug(ex,25);
  1965.                 continue;
  1966.             }
  1967.             if (!file.exists())
  1968.             {
  1969.                 error(strbundle.getString("failedDir") + " '" + remotepath + "' " + strbundle.getString("failedDir2"));
  1970.                 continue;
  1971.             }
  1972.  
  1973.             //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  1974.             list(remotepath, "retrieveRecursive(\'" + remotepath + "\',\'" + localParent.replace(/\x5c/g, "/") + "\',\'" + remoteParent
  1975.                     + "\'," + retrievePrompt + "," + retrieveSkipAll + ")", true);
  1976.         }
  1977.         else
  1978.         {
  1979.             addEventQueue("TYPE", detectAscii(remotepath));
  1980.             addEventQueue("PASV", "", files[x].size);
  1981.             if (resume)
  1982.             {
  1983.                 addEventQueue("REST", file.fileSize);
  1984.                 addEventQueue("RETR", remotepath, localpath);
  1985.             }
  1986.             else
  1987.                 addEventQueue("RETR", remotepath, localpath);
  1988.         }
  1989.     }
  1990. }
  1991.  
  1992. // *************************************************************************************************
  1993. // ******************************************* STOR/APPE *******************************************
  1994. // *************************************************************************************************
  1995.  
  1996. // execute a STOR command (or APPE if necessary)
  1997. var storeRemoteParent;
  1998. function stor()
  1999. {
  2000.     if (!isConnected)
  2001.         return;
  2002.     var fileList = gLocalTree.view.selection;
  2003.     if (fileList.count == 0)
  2004.         return;
  2005.     
  2006.     var storePrompt = true;
  2007.     var storeSkipAll = false;
  2008.     var resume;
  2009.     var localParent = gLocalPath.value;
  2010.     var remoteParent = gRemotePath.value;
  2011.     storeRemoteParent = remoteParent;
  2012.     var index = checkRemoteCache(remoteParent);
  2013.     var remotefiles = parseListData(remotecache[index].data);
  2014.  
  2015.     for (var x = 0; x < gLocalTree.view.rowCount; ++x)
  2016.         if (gLocalTree.view.selection.isSelected(x))
  2017.         {
  2018.             var localpath = gLocalTree.view.getCellText(x, "localname");
  2019.             var isDirectory = gLocalTree.view.getCellText(x, "localsize") == "";
  2020.             
  2021.             if (localParent.charAt(localParent.length - 1) != slash)
  2022.                 localpath = localParent + slash + localpath;
  2023.             else
  2024.                 localpath = localParent + localpath;
  2025.             
  2026.             var remotepath = localpath.substring(localParent.length + (localParent.charAt(localParent.length - 1) != slash ? 1 : 0));
  2027.             remotepath = remotepath.replace(/\x5c/g, "/");
  2028.             if (remoteParent.charAt(remoteParent.length - 1) != '/')
  2029.                 remotepath = remoteParent + '/' + remotepath;
  2030.             else
  2031.                 remotepath = remoteParent + remotepath;
  2032.  
  2033.             var localfile = getLocalFileSpec(localpath);
  2034.             var exists = false;
  2035.             var theremotesize = 0;
  2036.  
  2037.             resume = false;
  2038.             if (storePrompt)
  2039.                 for (var y = 0; y < remotefiles.length; ++y)
  2040.                     if (remotefiles[y].name == localfile.leafName)
  2041.                     {
  2042.                         exists = true;
  2043.                         if (localfile.fileSize > remotefiles[y].size)
  2044.                         {
  2045.                             resume = true;
  2046.                             theremotesize = remotefiles[y].size;
  2047.                         }
  2048.                         else
  2049.                             resume = false;
  2050.                         break;
  2051.                     }
  2052.             if (exists && storeSkipAll)
  2053.                 continue;
  2054.             if (exists && storePrompt && !isDirectory)
  2055.             {
  2056.                 var response = new Object();
  2057.                 response.value = 0;
  2058.                 var windowHeight = document.getElementById('main-window').boxObject.height;
  2059.                 var windowWidth = document.getElementById('main-window').boxObject.width;
  2060.                 var timer = destructmode;
  2061.  
  2062.                 window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  2063.                         "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  2064.                         ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes",
  2065.                         response, localfile.leafName, resume, false, timer, true);
  2066.                 if (response.value == 1)
  2067.                     resume = false;
  2068.                 else if (response.value == 2)
  2069.                 {
  2070.                     storePrompt = false;
  2071.                     resume = false;
  2072.                 }
  2073.                 else if ((response.value == 3) || (response.value == 0))
  2074.                     continue;
  2075.                 else if (response.value == 4)
  2076.                     resume = true;
  2077.                 else if (response.value == 5)
  2078.                 {
  2079.                     storeSkipAll = true;
  2080.                     continue;
  2081.                 }
  2082.             }
  2083.             
  2084.             if (isDirectory)
  2085.             {
  2086.                 if (!exists)
  2087.                 {
  2088.                     addEventQueue("MKD", remotepath);
  2089.                     storeRecursive(localpath, localParent, remoteParent, true);
  2090.                 }
  2091.                 else
  2092.                 {
  2093.  
  2094.                     //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  2095.                     list(remotepath, "storeRecursive(\'" + localpath.replace(/\x5c/g, "/")
  2096.                             + "\',\'" + localParent.replace(/\x5c/g, "/") + "\',\'" + remoteParent
  2097.                             + "\',false," + storePrompt + "," + storeSkipAll + ")", true);
  2098.                 }
  2099.             }
  2100.             else
  2101.             {
  2102.                 addEventQueue("TYPE", detectAscii(remotepath));
  2103.                 addEventQueue("PASV");
  2104.                 if (resume)
  2105.                     addEventQueue("APPE", remotepath, new Array(localpath, theremotesize));
  2106.                 else
  2107.                     addEventQueue("STOR", remotepath, localpath);
  2108.             }
  2109.         }
  2110.     if (isReady && eventQueue.length)    // get the ball rollin' if it isn't already
  2111.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  2112. }
  2113. // a STOR command sent from the folder view
  2114. function storDir()
  2115. {
  2116.     if (!isConnected)
  2117.         return;
  2118.     if (gLocalDirTree.view.selection.count == 0)
  2119.         return;
  2120.     var dir = gLocalDirTree.view.selection.currentIndex;
  2121.     var remoteParent = gRemotePath.value;
  2122.     storeRemoteParent = remoteParent;
  2123.     var localpath = localdircache[dir].path;
  2124.     var remotepath = localpath.substring(localpath.lastIndexOf(slash) + 1);
  2125.     
  2126.     if (remoteParent.charAt(remoteParent.length - 1) != '/')
  2127.         remotepath = remoteParent + '/' + remotepath;
  2128.     else
  2129.         remotepath = remoteParent + remotepath;
  2130.     var localdir = getLocalFileSpec(localpath);
  2131.     var index = checkRemoteCache(remoteParent);
  2132.     var remotefiles = parseListData(remotecache[index].data);
  2133.     var storePrompt = true;
  2134.     var storeSkipAll = false;
  2135.  
  2136.     var exists = false;
  2137.     for (var y = 0; y < remotefiles.length; ++y)
  2138.         if (remotefiles[y].name == localpath.substring(localpath.indexOf(slash) + 1))
  2139.         {
  2140.             exists = true;
  2141.             break;
  2142.         }
  2143.  
  2144.     if (!exists)
  2145.     {
  2146.         addEventQueue("MKD", remotepath);
  2147.         storeRecursive(localpath, localpath, remotepath, true);
  2148.     }
  2149.     else
  2150.     {
  2151.         //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  2152.         localpath = localpath.replace(/\x5c/g, "/");
  2153.         
  2154.         list(remotepath, "storeRecursive(\'" + localpath + "\',\'" + localpath + "\',\'" + remotepath +
  2155.                 "\',false," + storePrompt + "," + storeSkipAll + ")", true);
  2156.     }
  2157.  
  2158.     if (isReady && eventQueue.length)    // get the ball rollin' if it isn't already
  2159.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  2160. }
  2161.  
  2162. // *************************************************************************************************
  2163. // *************************************** STOR/APPE recursively ***********************************
  2164. // *************************************************************************************************
  2165.  
  2166. function storeRecursive(parent, localParent, remoteParent, newDir, storePrompt, storeSkipAll)
  2167. {
  2168.     if (slash == "\\")    //XXX continuation of aforementioned hack, working around eval()
  2169.     {
  2170.         localParent = localParent.replace(/\x2f/g, "\\");
  2171.         parent = parent.replace(/\x2f/g, "\\");
  2172.     }
  2173.     var resume;
  2174.     var remotefiles = newDir ? new Array : parseListData(data);
  2175.     var localfiles = new Array;
  2176.     
  2177.     try {
  2178.         var dir = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  2179.         dir.initWithPath(parent);
  2180.         var entries = dir.directoryEntries;
  2181.         while (entries.hasMoreElements())
  2182.         {
  2183.             var file = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
  2184.             if (file.exists() && (!file.isHidden() || hiddenmode))
  2185.                 localfiles.push(file);
  2186.         }
  2187.     } catch (ex) {
  2188.         debug(ex,26);
  2189.         return;    // skip this directory
  2190.     }
  2191.     
  2192.     for (var x = 0; x < localfiles.length; ++x)
  2193.     {
  2194.         var localpath = localfiles[x].path;
  2195.         var isDirectory = localfiles[x].isDirectory();
  2196.         
  2197.         var remotepath = localpath.substring(localParent.length + (localParent.charAt(localParent.length - 1) != slash ? 1 : 0));
  2198.         remotepath = remotepath.replace(/\x5c/g, "/");
  2199.         if (remoteParent.charAt(remoteParent.length - 1) != '/')
  2200.             remotepath = remoteParent + '/' + remotepath;
  2201.         else
  2202.             remotepath = remoteParent + remotepath;
  2203.  
  2204.         var exists = false;
  2205.         var theremotesize = 0;
  2206.         resume = false;
  2207.         if (storePrompt)
  2208.             for (var y = 0; y < remotefiles.length; ++y)
  2209.                 if (remotefiles[y].name == localfiles[x].leafName)
  2210.                 {
  2211.                     exists = true;
  2212.                     if (localfile.fileSize > remotefiles[y].size)
  2213.                     {
  2214.                         resume = true;
  2215.                         theremotesize = remotefiles[y].size;
  2216.                     }
  2217.                     else
  2218.                         resume = false;
  2219.                     break;
  2220.                 }
  2221.         
  2222.         if (exists && storeSkipAll)
  2223.             continue;
  2224.         if (exists && storePrompt && !isDirectory)
  2225.         {
  2226.             var response = new Object();
  2227.             response.value = 0;
  2228.             var windowHeight = document.getElementById('main-window').boxObject.height;
  2229.             var windowWidth = document.getElementById('main-window').boxObject.width;
  2230.             var timer = destructmode;
  2231.  
  2232.             window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  2233.                     "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  2234.                     ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes",
  2235.                     response, localfiles[x].leafName, resume, false, timer, true);
  2236.             if (response.value == 1)
  2237.                 resume = false;
  2238.             else if (response.value == 2)
  2239.             {
  2240.                 storePrompt = false;
  2241.                 resume = false;
  2242.             }
  2243.             else if ((response.value == 3) || (response.value == 0))
  2244.                 continue;
  2245.             else if (response.value == 4)
  2246.                 resume = true;
  2247.             else if (response.value == 5)
  2248.             {
  2249.                 storeSkipAll = true;
  2250.                 continue;
  2251.             }
  2252.         }
  2253.  
  2254.         if (isDirectory)
  2255.         {
  2256.             if (!exists)
  2257.             {
  2258.                 addEventQueue("MKD", remotepath);
  2259.                 storeRecursive(localpath, localParent, remoteParent, true);
  2260.             }
  2261.             else
  2262.             {
  2263.  
  2264.                 //XXX hack - \\ are erased during eval() so here we convert them to / and change them back later
  2265.                 list(remotepath, "storeRecursive(\'" + localpath.replace(/\x5c/g, "/")
  2266.                         + "\',\'" + localParent.replace(/\x5c/g, "/") + "\',\'" + remoteParent
  2267.                         + "\',false," + storePrompt + "," + storeSkipAll + ")", true);
  2268.             }
  2269.         }
  2270.         else
  2271.         {
  2272.             addEventQueue("TYPE", detectAscii(remotepath));
  2273.             addEventQueue("PASV");
  2274.             if (resume)
  2275.                 addEventQueue("APPE", remotepath, new Array(localpath, theremotesize));
  2276.             else
  2277.                 addEventQueue("STOR", remotepath, localpath);
  2278.         }
  2279.     }
  2280. }
  2281.  
  2282. // *************************************************************************************************
  2283. // ************************************* remote folder functions ***********************************
  2284. // *************************************************************************************************
  2285.  
  2286. // helper function
  2287. function checkRemoteCache(path)
  2288. {
  2289.     for (var x = 0; x < remotecache.length; ++x)
  2290.         if (remotecache[x].path == path && remotecache[x].host == connectedHost)
  2291.             return x;
  2292.     return -1;
  2293. }
  2294. // helper function
  2295. function checkIsListing()
  2296. {
  2297.     for (var x = 0; x < eventQueue.length; ++x)
  2298.         if (eventQueue[x].cmd.indexOf("LIST") != -1)
  2299.             return true;
  2300.     return false;
  2301. }
  2302.  
  2303. function remoteDirChangeHelper(index, toggle)
  2304. {
  2305.     if (checkIsListing() || (eventQueue.length && checkRemoteCache(remotedircache[index].path) == -1))
  2306.     {
  2307.         for (var x = 0; x < remotedircache.length; ++x)
  2308.             if (remotedircache[x].path == gRemotePath.value)
  2309.             {
  2310.                 gRemoteDirTree.view.selection.select(x);
  2311.                 gRemoteDirTree.treeBoxObject.ensureRowIsVisible(x);
  2312.                 break;
  2313.             }
  2314.         return;
  2315.     }
  2316.  
  2317.     var state = remotedircache[index].open;
  2318.  
  2319.     var gFormHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
  2320.         .getService(Components.interfaces.nsIFormHistory);
  2321.     gFormHistory.addEntry(gRemotePath.getAttribute("autocompletesearchparam"), remotedircache[index].path);
  2322.  
  2323.     list(remotedircache[index].path, "remoteDirChangeHelper2(" + index + ", " + state + ", " + toggle + ")");
  2324. }
  2325. function remoteDirChangeHelper2(index, state, toggle)
  2326. {
  2327.     if (!state && !remotedircache[index].empty)
  2328.     {
  2329.         remotedircache[index].open = false;
  2330.         gRemoteDirTree.view.toggleOpenState(index);
  2331.     }
  2332.     if (toggle)
  2333.         gRemoteDirTree.view.toggleOpenState(index);
  2334.     gRemoteDirTree.view.selection.select(index);
  2335.  
  2336.     if (remoteLastRowSeen)
  2337.     {
  2338.         gRemoteDirTree.treeBoxObject.ensureRowIsVisible(index + (remoteLastRowSeen - index - 1));
  2339.         remoteLastRowSeen = 0;
  2340.     }
  2341.     else
  2342.         gRemoteDirTree.treeBoxObject.ensureRowIsVisible(index);
  2343.  
  2344.     refreshRemote = false;
  2345.     if (moreToRemember)
  2346.         eval(moreToRemember);
  2347. }
  2348. // does not execute a CDUP command literally - only locally changes directory
  2349. function onRemoteCDUP()
  2350. {
  2351.     if (!gRemoteDirTree.view || !isConnected)
  2352.         return;
  2353.     var parentIndex = gRemoteDirTree.view.
  2354.         getParentIndex(gRemoteDirTree.view.selection.currentIndex);
  2355.     if (parentIndex != -1)
  2356.     {
  2357.         gRemoteDirTree.view.selection.select(parentIndex);
  2358.         remoteDirChangeHelper(parentIndex);
  2359.     }
  2360. }
  2361. var remoteLastRowSeen = 0;
  2362. function onRemoteDirClick(event)
  2363. {
  2364.     if (isConnected && event.button == 0)
  2365.     {
  2366.         var row = {};
  2367.         var col = {};
  2368.         var child = {};
  2369.         gRemoteDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  2370.         var index = gRemoteDirTree.view.selection.currentIndex;
  2371.         remoteLastRowSeen = gRemoteDirTree.treeBoxObject.getLastVisibleRow();    // have you seen this row?
  2372.  
  2373.         if (index == row.value && remotedircache[index].path != gRemotePath.value)
  2374.             remoteDirChangeHelper(index);
  2375.     } else if (event.button == 1 && !document.getElementById('remotePasteContext').disabled)
  2376.         remoteDirPaste();
  2377. }
  2378. var remoteDirKeyDownIndex;    // global variables - yummy, as they say (you know who you are)
  2379. var remoteDirKeyDownOpen;
  2380. function onRemoteDirKeyDown(event)
  2381. {
  2382.     if (!isConnected)
  2383.         return;
  2384.     remoteDirKeyDownIndex = gRemoteDirTree.view.selection.currentIndex;
  2385.     remoteDirKeyDownOpen = remotedircache[remoteDirKeyDownIndex].open;
  2386. }
  2387. function onRemoteDirKeyPress(event)
  2388. {
  2389.     if (!isConnected)
  2390.         return;
  2391.     var index = gRemoteDirTree.view.selection.currentIndex;
  2392.     switch(event.keyCode)
  2393.     {
  2394.         case 37:    // left
  2395.             if (remoteDirKeyDownIndex != index)
  2396.             {
  2397.                 gRemoteDirTree.view.selection.select(index);
  2398.                 remoteDirChangeHelper(index);
  2399.             }
  2400.             break;
  2401.         case 38:    // up
  2402.             if (!index)
  2403.                 return;
  2404.  
  2405.             if (remotedircache[index - 1].open)
  2406.                 remoteDirChangeHelper(index - 1);
  2407.             else
  2408.                 remoteDirChangeHelper(index - 1, true);
  2409.             gRemoteDirTree.view.selection.select(index);
  2410.             break;
  2411.         case 40:    // down
  2412.             if (index == remotedircache.length - 1)
  2413.             {
  2414.                 gRemoteDirTree.view.selection.select(index - 1);    // this shouldn't work but it does
  2415.                 return;
  2416.             }
  2417.  
  2418.             if (remotedircache[index + 1].open)
  2419.                 remoteDirChangeHelper(index + 1);
  2420.             else
  2421.                 remoteDirChangeHelper(index + 1, true);
  2422.             gRemoteDirTree.view.selection.select(index);
  2423.             break;
  2424.         case 39:    // right
  2425.             if (remoteDirKeyDownOpen && !remotedircache[index].empty)
  2426.                 remoteDirChangeHelper(index + 1);
  2427.             break;
  2428.         case 8: // backspace
  2429.             onRemoteCDUP();
  2430.             break;
  2431.         case 116: // F5
  2432.             event.preventDefault();
  2433.             refreshRemoteView();
  2434.             break;
  2435.         case 113: // F2
  2436.             renameRemoteFile(remotedircache[index].path, true);
  2437.             break;
  2438.         case 46: // delete
  2439.             onRemoteDirDeleteContext();
  2440.             break;
  2441.         case 93: // context menu
  2442.             var x = {};
  2443.             var y = {};
  2444.             var width = {};
  2445.             var height = {};
  2446.             var childrenBoxX = document.getElementById('remotedirtreechildren').boxObject.x;
  2447.             var childrenBoxY = document.getElementById('remotedirtreechildren').boxObject.y;
  2448.             
  2449.             gRemoteDirTree.treeBoxObject.getCoordsForCellItem(index, "remotedirname", "text", x, y, width, height);
  2450.             document.getElementById('remotedirmenu').showPopup(document.getElementById('remotedirtreechildren'), childrenBoxX + 75, childrenBoxY + y.value + 30, "context");
  2451.             break;
  2452.         default:
  2453.             break;
  2454.     }
  2455.     if (event.ctrlKey)
  2456.     {
  2457.         switch(event.charCode)
  2458.         {
  2459.             case 120:    // ctrl-x
  2460.                 event.preventDefault();
  2461.                 remoteDirCut();
  2462.                 break;
  2463.             case 118:    // ctrl-v
  2464.                 event.preventDefault();
  2465.                 remoteDirPaste();
  2466.                 break;
  2467.             default:
  2468.                 break;
  2469.         }
  2470.     }
  2471. }
  2472.  
  2473. // *************************************************************************************************
  2474. // *************************************** remote context menus ************************************
  2475. // *************************************************************************************************
  2476.  
  2477. function onRemoteDirRenameContext()
  2478. {
  2479.     if (!isConnected)
  2480.         return;
  2481.     if (eventQueue.length)
  2482.         return;
  2483.     var index = gRemoteDirTree.view.selection.currentIndex;
  2484.     renameRemoteFile(remotedircache[index].path, true);
  2485. }
  2486. function onRemoteRenameContext()
  2487. {
  2488.     if (!isConnected)
  2489.         return;
  2490.     if (gRemoteDirTree.view.selection.count == 0)
  2491.         return;
  2492.     if (eventQueue.length)
  2493.         return;
  2494.     var fileList = gRemoteTree.view.selection;
  2495.     if (fileList.count == 0)
  2496.         return;
  2497.     var thepath = gRemoteTree.view.getCellText(fileList.currentIndex, "remotename");
  2498.     if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  2499.         thepath = gRemotePath.value + '/' + thepath;
  2500.     else
  2501.         thepath = gRemotePath.value + thepath;
  2502.     renameRemoteFile(thepath, false);
  2503. }
  2504. function onRemoteDirDeleteContext()
  2505. {
  2506.     if (!isConnected)
  2507.         return;
  2508.     if (gRemoteDirTree.view.selection.count == 0)
  2509.         return;
  2510.     if (eventQueue.length)
  2511.         return;
  2512.     if (!window.confirm(strbundle.getString("confirmDeleteDir")))
  2513.         return;
  2514.  
  2515.     var index = gRemoteDirTree.view.selection.currentIndex;
  2516.     var whatToDo = refreshmode ? "onRemoteCDUP();refreshRemoteView()" : "";
  2517.     eventQueue.unshift( { cmd: "RMD", parameter: remotedircache[index].path, remember: whatToDo } );
  2518.     list(remotedircache[index].path, "deleteRecursive(\'" + remotedircache[index].path + "\')", true, true);
  2519.     
  2520.     if (isReady)    // get the ball rollin' if it isn't already
  2521.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  2522. }
  2523. function onRemoteDeleteContext()
  2524. {
  2525.     if (!isConnected)
  2526.         return;
  2527.     if (eventQueue.length)
  2528.         return;
  2529.     var fileList = gRemoteTree.view.selection;
  2530.     if (fileList.count == 0)
  2531.         return;
  2532.     
  2533.     if (!window.confirm(strbundle.getString("confirmDelete") + " " + fileList.count + " " + strbundle.getString("confirmDelete2")))
  2534.         return;
  2535.  
  2536.     var thecount = 0;
  2537.     for (var x = 0; x < gRemoteTree.view.rowCount; ++x)
  2538.         if (gRemoteTree.view.selection.isSelected(x))
  2539.         {
  2540.             var afterLastDelete = (thecount == 0 && refreshmode)
  2541.                 ? "refreshRemoteView()" : "";
  2542.             ++thecount;
  2543.             var remotepath = gRemoteTree.view.getCellText(x, "remotename");
  2544.             var isDirectory = gRemoteTree.view.getCellText(x, "remoteattr").charAt(0) == 'd';
  2545.             if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  2546.                 remotepath = gRemotePath.value + '/' + remotepath;
  2547.             else
  2548.                 remotepath = gRemotePath.value + remotepath;
  2549.             if (isDirectory)
  2550.             {
  2551.                 eventQueue.unshift( { cmd: "RMD", parameter: remotepath, remember: afterLastDelete } );
  2552.                 list(remotepath, "deleteRecursive(\'" + remotepath + "\')", true, true);
  2553.             }
  2554.             else
  2555.                 eventQueue.unshift( { cmd: "DELE", parameter: remotepath, remember: afterLastDelete } );
  2556.         }
  2557.     if (isReady)    // get the ball rollin' if it isn't already
  2558.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  2559. }
  2560. function deleteRecursive(parent)
  2561. {
  2562.     var files = parseListData(data);
  2563.     
  2564.     for (var x = 0; x < files.length; ++x)
  2565.     {
  2566.         var remotepath = files[x].name;
  2567.         var isDirectory = files[x].isDirectory;
  2568.         
  2569.         if (parent.charAt(parent.length - 1) != '/')
  2570.             remotepath = parent + '/' + remotepath;
  2571.         else
  2572.             remotepath = parent + remotepath;
  2573.         if (isDirectory)
  2574.         {
  2575.             eventQueue.unshift( { cmd: "RMD", parameter: remotepath, remember: "" } );
  2576.             list(remotepath, "deleteRecursive(\'" + remotepath + "\')", true, true);
  2577.         }
  2578.         else
  2579.             eventQueue.unshift( { cmd: "DELE", parameter: remotepath, remember: "" } );
  2580.     }
  2581. }
  2582.  
  2583. // *************************************************************************************************
  2584. // *************************************** remote path changes *************************************
  2585. // *************************************************************************************************
  2586.  
  2587. var remotePathFocus;
  2588. function onRemotePathFocus(event)
  2589. {
  2590.     remotePathFocus = gRemotePath.value;
  2591. }
  2592. function onRemotePathBlur(event)
  2593. {
  2594.     gRemotePath.value = remotePathFocus;
  2595. }
  2596. function onRemotePathChange()
  2597. {
  2598.     gRemotePath.value = gRemotePath.value.replace(/\x5c/g, "/");
  2599.     if (gRemotePath.value != '/' && gRemotePath.value.charAt(gRemotePath.value.length - 1) == '/')
  2600.         gRemotePath.value =    gRemotePath.value.substring(0,gRemotePath.value.length - 1);
  2601.     if (gRemotePath.value.charAt(0) != '/')
  2602.         gRemotePath.value = '/' + gRemotePath.value;
  2603.     var newDir = gRemotePath.value;
  2604.     gRemoteDirTree.focus();
  2605.     var found = false;
  2606.     for (var x = 0; x < remotedircache.length; ++x)
  2607.         if (remotedircache[x].path == newDir)
  2608.         {
  2609.             found = true;
  2610.             remotePathFocus = newDir;
  2611.             remoteDirChangeHelper(x);
  2612.             return;
  2613.         }
  2614.     if (checkIsListing() || eventQueue.length)
  2615.     {
  2616.         gRemotePath.value = remotePathFocus;
  2617.         return;
  2618.     }
  2619.     findSubdirectory(newDir, -1);
  2620. }
  2621. function findSubdirectory(newDir, previousParent)
  2622. {
  2623.     for (var x = remotedircache.length - 1; x > -1; --x)
  2624.         if (newDir.indexOf(remotedircache[x].path) == 0)
  2625.         {
  2626.             moreToRemember = "findSubdirectory2(\"" + newDir + "\", " + x + ", " + previousParent + ")";
  2627.             remoteDirChangeHelper(x);
  2628.             return;
  2629.         }
  2630. }
  2631. function findSubdirectory2(newDir, findParent, previousParent)
  2632. {
  2633.     moreToRemember = "";
  2634.     for (var x = 0; x < remotedircache.length; ++x)
  2635.         if (remotedircache[x].path == newDir)
  2636.         {
  2637.             remotePathFocus = newDir;
  2638.             remoteDirChangeHelper(x);
  2639.             return;
  2640.         }
  2641.     if (previousParent == findParent)
  2642.     {
  2643.         // directory not found in conventional sense - don't freak out
  2644.         // just create directories above as if it were there and list
  2645.         //remotedircache = new Array( {path:newDir, open:true, empty:false, children:-1} );
  2646.         remotedircache = new Array( {path:'/', open:true, empty:false, children:-1} );
  2647.         hiddenremotedircache = new Array;
  2648.         
  2649.         var tempParent = "";
  2650.         newDir = newDir.substring(1);
  2651.         while (true)
  2652.         {
  2653.             if (newDir.indexOf('/') != -1)
  2654.             {
  2655.                 tempParent += '/' + newDir.substring(0, newDir.indexOf('/'));
  2656.                 remotedircache.push( {path:tempParent, open:true, empty:false, children:-1} );
  2657.                 newDir = newDir.substring(newDir.indexOf('/') + 1);
  2658.             }
  2659.             else
  2660.             {
  2661.                 tempParent += '/' + newDir;
  2662.                 remotedircache.push( {path:tempParent, open:true, empty:false, children:-1} );
  2663.                 break;
  2664.             }
  2665.         }
  2666.         gRemotePath.value = tempParent;
  2667.         onRemotePathChange();
  2668.         return;
  2669.  
  2670.         /*    old way of doing things - freak out - change to previous good directory
  2671.         gRemotePath.value = remotePathFocus;
  2672.         for (var x = 0; x < remotedircache.length; ++x)
  2673.             if (remotedircache[x].path == remotePathFocus)
  2674.             {
  2675.                 remoteDirChangeHelper(x);
  2676.                 break;
  2677.             }
  2678.         error(strbundle.getString("dirNotFound"));
  2679.         return;*/
  2680.     }
  2681.     findSubdirectory(newDir, findParent);
  2682. }
  2683.  
  2684. // *************************************************************************************************
  2685. // *************************************** remote mouse events *************************************
  2686. // *************************************************************************************************
  2687.  
  2688. function onRemoteDblClick(event)
  2689. {
  2690.     if (!isConnected)
  2691.         return;
  2692.     if (event.button != 0)
  2693.         return;
  2694.     var target = event.originalTarget;
  2695.     if (target.localName != "treechildren")
  2696.         return;
  2697.     
  2698.     var fileList = gRemoteTree.view.selection;
  2699.     if (fileList.count == 0)
  2700.         return;
  2701.  
  2702.     if (gRemoteTree.view.getCellText(fileList.currentIndex, "remoteattr").charAt(0) == 'd')
  2703.     {
  2704.         var thepath = gRemoteTree.view.getCellText(fileList.currentIndex, "remotename");
  2705.         if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  2706.             thepath = gRemotePath.value + '/' + thepath;
  2707.         else
  2708.             thepath = gRemotePath.value + thepath;
  2709.         if (checkIsListing() || (eventQueue.length && checkRemoteCache(thepath) == -1))
  2710.             return;
  2711.         gRemotePath.value = thepath;
  2712.         onRemotePathChange();
  2713.     }
  2714.     else if (gRemoteTree.view.getCellText(fileList.currentIndex, "remoteattr").charAt(0) == 'l')
  2715.     {
  2716.         var thepath = gRemoteTree.view.getCellText(fileList.currentIndex, "remotename");
  2717.         var index = checkRemoteCache(gRemotePath.value);
  2718.         var files = parseListData(remotecache[index].data);
  2719.  
  2720.         for (var x = 0; x < files.length; ++x)
  2721.             if (files[x].name == thepath)
  2722.             {
  2723.                 var linkedFile = files[x].symlink;
  2724.                 var parentPath = gRemotePath.value;
  2725.                 while (linkedFile.indexOf("./") == 0 || linkedFile.indexOf("../") == 0)
  2726.                 {
  2727.                     if (linkedFile.indexOf("./") == 0)
  2728.                         linkedFile = linkedFile.substring(2);
  2729.                     else
  2730.                     {
  2731.                         linkedFile = linkedFile.substring(3);
  2732.                         parentPath = parentPath.substring(0, parentPath.lastIndexOf('/') ? parentPath.lastIndexOf('/') : 1);
  2733.                     }
  2734.                 }
  2735.                 if (parentPath.charAt(parentPath.length - 1) != '/')
  2736.                     thepath = parentPath + '/' + linkedFile;
  2737.                 else
  2738.                     thepath = parentPath + linkedFile;
  2739.  
  2740.                 if (checkIsListing() || (eventQueue.length && checkRemoteCache(thepath) == -1))
  2741.                     return;
  2742.                 addEventQueue("CWD", thepath);
  2743.                 if (isReady)    // get the ball rollin' if it isn't already
  2744.                     writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  2745.             }
  2746.     }
  2747.     else
  2748.         retr();
  2749. }
  2750. function onRemoteClick(event)
  2751. {
  2752.     if (!isConnected)
  2753.         return;
  2754.     if (event.button == 1 && !document.getElementById('remotePasteContext').disabled)
  2755.         remotePaste();
  2756. }
  2757. function onRemoteMouseOver(event)
  2758. {
  2759.     if (gRemoteTree.view.rowCount)
  2760.         document.getElementById('statustxt').label =
  2761.         strbundle.getString("remoteListing") + " " + gRemoteTree.view.rowCount + " " + strbundle.getString("objects") + " " + commas(remotesize) + " KB";
  2762.     else
  2763.         document.getElementById('statustxt').label = strbundle.getString("remoteListingNoObjects");
  2764. }
  2765.  
  2766. // *************************************************************************************************
  2767. // *************************************** remote key commands *************************************
  2768. // *************************************************************************************************
  2769.  
  2770. function onRemoteKeyPress(event)
  2771. {
  2772.     if (!isConnected)
  2773.         return;
  2774.     if (event.keyCode == 13)
  2775.     {
  2776.         var fileList = gRemoteTree.view.selection;
  2777.         if (fileList.count == 0)
  2778.             return;
  2779.         if (fileList.count == 1 && gRemoteTree.view.getCellText(fileList.currentIndex, "remoteattr").charAt(0) == 'd')
  2780.         {
  2781.             var thepath = gRemoteTree.view.getCellText(fileList.currentIndex, "remotename");
  2782.             if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  2783.                 thepath = gRemotePath.value + '/' + thepath;
  2784.             else
  2785.                 thepath = gRemotePath.value + thepath;
  2786.             if (checkIsListing() || (eventQueue.length && checkRemoteCache(thepath) == -1))
  2787.                 return;
  2788.             gRemotePath.value = thepath;
  2789.             onRemotePathChange();
  2790.             gRemoteTree.focus();
  2791.         }
  2792.         else
  2793.             retr();
  2794.     }
  2795.     else if (event.ctrlKey && (event.which == 65 || event.which == 97)) // ctrl-a: select all
  2796.     {
  2797.         event.preventDefault();
  2798.         gRemoteTree.view.selection.selectAll();        // previous hack had window.blur() after this, now it's ok
  2799.     }
  2800.     else if (event.ctrlKey && event.which == 32)    // ctrl-space, select or deselect
  2801.         gRemoteTree.view.selection.toggleSelect(gRemoteTree.view.selection.currentIndex);
  2802.     else if (event.keyCode == 8)    // backspace
  2803.         onRemoteCDUP();
  2804.     else if (event.keyCode == 116) // F5
  2805.     {
  2806.         event.preventDefault();
  2807.         refreshRemoteView();
  2808.     }
  2809.     else if (event.keyCode == 113) // F2
  2810.         onRemoteRenameContext();
  2811.     else if (event.charCode == 109 && event.ctrlKey)    // ctrl-m
  2812.     {
  2813.         event.preventDefault();
  2814.         makeRemoteDirectory();
  2815.     }
  2816.     else if (event.keyCode == 46)    // del
  2817.         onRemoteDeleteContext();
  2818.     else if (event.keyCode == 93) // context menu
  2819.     {
  2820.         var fileList = gLocalTree.view.selection;
  2821.         if (fileList.count == 0)
  2822.             return;
  2823.         var index = fileList.currentIndex;
  2824.         var x = {};
  2825.         var y = {};
  2826.         var width = {};
  2827.         var height = {};
  2828.         var childrenBoxX = document.getElementById('remotetreechildren').boxObject.x;
  2829.         var childrenBoxY = document.getElementById('remotetreechildren').boxObject.y;
  2830.     
  2831.         gRemoteTree.treeBoxObject.getCoordsForCellItem(index, "remotename", "text", x, y, width, height);
  2832.         document.getElementById('remotemenu').showPopup(document.getElementById('remotetreechildren'), childrenBoxX + 75, childrenBoxY + y.value + 30, "context");
  2833.     }
  2834.     else if (event.charCode == 112 && event.ctrlKey)    // ctrl-p
  2835.     {
  2836.         event.preventDefault();
  2837.         getRemoteProperties();
  2838.     }
  2839.     else if (event.charCode == 120 && event.ctrlKey)    // ctrl-x
  2840.     {
  2841.         event.preventDefault();
  2842.         remoteCut();
  2843.     }
  2844.     else if (event.charCode == 118 && event.ctrlKey)    // ctrl-v
  2845.     {
  2846.         event.preventDefault();
  2847.         remotePaste();
  2848.     }
  2849. }
  2850.  
  2851. // *************************************************************************************************
  2852. // *************************************** highlighting multiple rows ******************************
  2853. // *************************************************************************************************
  2854.  
  2855. var remoteMouseDownRow;
  2856. var remoteMouseDownOnName;
  2857. var remoteMouseDownPressed;
  2858. var remoteMouseDirection;
  2859. var remoteMousePreviousY;
  2860.  
  2861. function onRemoteMouseDown(event)
  2862. {
  2863.     if (event.target.getAttribute('id') != 'remotetreechildren')
  2864.         return;
  2865.     remoteMouseDownPressed = true;
  2866.     var row = {};
  2867.     var col = {};
  2868.     var child = {};
  2869.     var x = {};
  2870.     var y = {};
  2871.     var width = {};
  2872.     var height = {};
  2873.     var childrenBoxX = document.getElementById('remotetreechildren').boxObject.x;
  2874.     var childrenBoxY = document.getElementById('remotetreechildren').boxObject.y;
  2875.     gRemoteTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  2876.     gRemoteTree.treeBoxObject.getCoordsForCellItem (row.value, "remotename", "text", x, y, width, height);
  2877.     if (row.value == -1)
  2878.     {
  2879.         gRemoteTree.view.selection.clearSelection();
  2880.         row.value = gRemoteTree.view.rowCount - 1;
  2881.     }
  2882.     remoteMouseDownRow = row.value;
  2883.     remoteMouseDownOnName = event.pageX - childrenBoxX < x.value + width.value ? true : false;
  2884.     if (remoteMouseDownOnName)
  2885.         dragSessionEnabled = true;
  2886.     else
  2887.         dragSessionEnabled = false;
  2888. }
  2889. function extendRemoteSelectionUpwards()
  2890. {
  2891.     if (remoteMouseDirection || !remoteMouseDownPressed)
  2892.         return;
  2893.     if (gRemoteTree.treeBoxObject.getFirstVisibleRow() == 0)
  2894.     {
  2895.         gRemoteTree.view.selection.rangedSelect(remoteMouseDownRow, gRemoteTree.treeBoxObject.getFirstVisibleRow(), false);
  2896.         return;
  2897.     }
  2898.     gRemoteTree.treeBoxObject.ensureRowIsVisible(gRemoteTree.treeBoxObject.getFirstVisibleRow() - 1);
  2899.     gRemoteTree.view.selection.rangedSelect(remoteMouseDownRow, gRemoteTree.treeBoxObject.getFirstVisibleRow(), false);
  2900.     if (remoteMouseDownPressed)
  2901.         setTimeout("extendRemoteSelectionUpwards()", 100);
  2902. }
  2903. function extendRemoteSelectionDownwards()
  2904. {
  2905.     if (!remoteMouseDirection || !remoteMouseDownPressed)
  2906.         return;
  2907.     if (gRemoteTree.view.rowCount - 1 < gRemoteTree.treeBoxObject.getLastVisibleRow())
  2908.     {
  2909.         gRemoteTree.view.selection.rangedSelect(remoteMouseDownRow, gRemoteTree.treeBoxObject.getLastVisibleRow(), false);
  2910.         return;
  2911.     }
  2912.     gRemoteTree.treeBoxObject.ensureRowIsVisible(gRemoteTree.treeBoxObject.getLastVisibleRow() + 1);
  2913.     gRemoteTree.view.selection.rangedSelect(remoteMouseDownRow, gRemoteTree.treeBoxObject.getLastVisibleRow(), false);
  2914.     if (remoteMouseDownPressed)
  2915.         setTimeout("extendRemoteSelectionDownwards()", 100);
  2916. }
  2917. function onRemoteMouseMove(event)    // does pageX or clientX make a difference?
  2918. {
  2919.     if (remoteMouseDownPressed && !event.ctrlKey && !dragSessionEnabled)
  2920.     {
  2921.         var row = {};
  2922.         var col = {};
  2923.         var child = {};
  2924.         if (remoteMousePreviousY)
  2925.             remoteMouseDirection = event.pageY - remoteMousePreviousY > 0 ? true : false;
  2926.         remoteMousePreviousY = event.pageY;
  2927.         if (event.pageY < document.getElementById('remotetreechildren').boxObject.y)
  2928.         {
  2929.             extendRemoteSelectionUpwards();
  2930.             return;
  2931.         }
  2932.         else if (event.pageY > document.getElementById('remotetreechildren').boxObject.y + document.getElementById('remotetreechildren').boxObject.height)
  2933.         {
  2934.             extendRemoteSelectionDownwards();
  2935.             return;
  2936.         }
  2937.         gRemoteTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  2938.         if (row.value == -1)
  2939.             return;
  2940.         gRemoteTree.view.selection.rangedSelect(remoteMouseDownRow, row.value, false);
  2941.     }
  2942. }
  2943. function onRemoteMouseUp(event)
  2944. {
  2945.     remoteMouseDownPressed = false;
  2946. }
  2947.  
  2948. // *************************************************************************************************
  2949. // *************************************** list data parser ****************************************
  2950. // *************************************************************************************************
  2951.  
  2952. function parseListData(thedata)
  2953. {
  2954.     /* Unix style:                     drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
  2955.      * Alternate Unix style:           drwxr-xr-x 1 user01 ftp 512 Jan 29 1997  prog
  2956.      * Alternate Unix style:           drwxr-xr-x 1 1      1   512 Jan 29 23:32 prog
  2957.      * A symbolic link in Unix style:  lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000
  2958.      * MS-DOS style:                   01-29-97 11:32PM <DIR> prog
  2959.      * Novell Style:                   drwxr-xr-x 1 user01     512 Jan 29 23:32 prog
  2960.      * OS/2 style:                     0           DIR 01-29-97  23:32  PROG
  2961.      * OS/2 style:                     2243        RA  04-05-103 00:22  PJL
  2962.      * OS/2 style:                     60              11-18-104 06:54  chkdsk.log
  2963.      * AIX style:                      -rw-r--r-- 1 sys   sys   30 05 Nov 2003  foo.txt
  2964.      *
  2965.      * CURRENTLY NOT SUPPORTED: (are these even used anymore?  i can't find any ftp servers using these)
  2966.      *  Macintosh style:       drwxr-xr-x folder 0 Jan 29 23:32 prog
  2967.      */
  2968.  
  2969.     debug(thedata,0);
  2970.  
  2971.     var treeitem = thedata.split("\r\n");
  2972.  
  2973.     // some ftp servers send 'count <number>' or 'total <number>' first
  2974.     if (treeitem.length && (treeitem[0].indexOf("count") == 0 || treeitem[0].indexOf("total") == 0))
  2975.         treeitem.shift();
  2976.  
  2977.     for (var x = 0; x < treeitem.length; ++x)
  2978.     {
  2979.         if (!treeitem[x]) // some servers put in blank lines b/w entries, aw, for cryin' out loud
  2980.         {
  2981.             treeitem.splice(x, 1);
  2982.             --x;
  2983.             continue;
  2984.         }
  2985.         var temp = treeitem[x];
  2986.         treeitem[x] = treeitem[x].split(" ");
  2987.         if (!parseInt(treeitem[x][0].charAt(0)) && treeitem[x][0].charAt(0) != '0') // unix style - so much simpler with you guys
  2988.         {
  2989.             var months = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
  2990.             var offset = 0;
  2991.             if (treeitem[x][4].search(months) != -1)    // added to support novell servers
  2992.                 offset = 1;
  2993.             var name = temp.substring(temp.indexOf(treeitem[x][7 - offset]) + treeitem[x][7 - offset].length + 1, temp.length);
  2994.             var symlink = "";
  2995.             name = name.substring(name.search(/[^\s]/));
  2996.             if (treeitem[x][0].charAt(0) == 'l')
  2997.             {
  2998.                 symlink = name;
  2999.                 name = name.substring(0, name.indexOf("->") - 1);
  3000.                 symlink = symlink.substring(symlink.indexOf("->") + 3);
  3001.             }
  3002.             treeitem[x] = {
  3003.                 attr: treeitem[x][0],
  3004.                 hardLink: treeitem[x][1],
  3005.                 user: treeitem[x][2],
  3006.                 group: (offset ? "" : treeitem[x][3]),
  3007.                 size: treeitem[x][4 - offset],
  3008.                 date: treeitem[x][5 - offset] + ' ' + treeitem[x][6 - offset] + ' ' + treeitem[x][7 - offset],
  3009.                 name: (name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1)),
  3010.                 isDirectory: treeitem[x][0].charAt(0) == 'd',
  3011.                 isSymlink: treeitem[x][0].charAt(0) == 'l',
  3012.                 symlink: symlink
  3013.             };
  3014.         }
  3015.         else if (treeitem[x][0].indexOf('-') == -1) // os/2 style
  3016.         {
  3017.             var offset = 0;
  3018.             if (treeitem[x][2].indexOf(':') != -1)    // if "DIR" and "A" are missing
  3019.                 offset = 1;
  3020.             var rawDate = treeitem[x][2 - offset].split("-");
  3021.             var rawTime = treeitem[x][3 - offset];
  3022.             var timeOrYear = rawTime;
  3023.             rawTime = rawTime.split(":");
  3024.             for (var y = 0; y < rawDate.length; ++y)
  3025.             {
  3026.                 if (rawDate[y].charAt(0) == '0') // javascript, seriously - can't you parse 09 to 9 and not 0?
  3027.                     rawDate[y] = rawDate[y][1];
  3028.                 rawDate[y] = parseInt(rawDate[y]);
  3029.             }
  3030.             for (var y = 0; y < rawTime.length; ++y)
  3031.             {
  3032.                 if (rawTime[y].charAt(0) == '0')
  3033.                     rawTime[y] = rawTime[y][1];
  3034.                 rawTime[y] = parseInt(rawTime[y]);
  3035.             }
  3036.             rawDate[2] = rawDate[2] + 1900;                // ah, that's better
  3037.             parsedDate = new Date(rawDate[2], rawDate[0] - 1, rawDate[1], rawTime[0], rawTime[1]);    // month-day-year format
  3038.             if (new Date() - parsedDate > 15600000000)    // roughly 6 months
  3039.                 timeOrYear = rawDate[2];
  3040.             var month = parsedDate.toLocaleDateString().split(" ");
  3041.             var name = temp.substring(temp.indexOf(treeitem[x][3 - offset]) + treeitem[x][3 - offset].length + 1, temp.length);
  3042.             name = name.substring(name.search(/[^\s]/));
  3043.             month = month[1].substring(0,3);    // abbr.
  3044.  
  3045.             treeitem[x] = {
  3046.                 attr: treeitem[x][1] == "DIR" ? "d---------" : "----------",
  3047.                 hardLink: "",
  3048.                 user: "",
  3049.                 group: "",
  3050.                 size: treeitem[x][0],
  3051.                 date: month + ' ' + rawDate[1] + ' ' + timeOrYear,
  3052.                 name: (name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1)),
  3053.                 isDirectory: treeitem[x][1] == "DIR",
  3054.                 isSymlink: false,
  3055.                 symlink: ""
  3056.             };
  3057.         }
  3058.         else                                    // ms-dos style
  3059.         {
  3060.             var rawDate = treeitem[x][0].split("-");
  3061.             var rawTime = treeitem[x][1].substring(0, 5);    // get rid of PM, AM
  3062.             var timeOrYear = rawTime;
  3063.             rawTime = rawTime.split(":");
  3064.             for (var y = 0; y < rawDate.length; ++y)
  3065.             {
  3066.                 if (rawDate[y].charAt(0) == '0') // javascript, seriously - can't you parse 09 to 9 and not 0?
  3067.                     rawDate[y] = rawDate[y][1];
  3068.                 rawDate[y] = parseInt(rawDate[y]);
  3069.             }
  3070.             for (var y = 0; y < rawTime.length; ++y)
  3071.             {
  3072.                 if (rawTime[y].charAt(0) == '0')
  3073.                     rawTime[y] = rawTime[y][1];
  3074.                 rawTime[y] = parseInt(rawTime[y]);
  3075.             }
  3076.             if (rawDate[2] < 70)                // assuming you didn't have some files left over from 1904
  3077.                 rawDate[2] = rawDate[2] + 2000;                // ah, that's better
  3078.             parsedDate = new Date(rawDate[2], rawDate[0] - 1, rawDate[1], rawTime[0], rawTime[1]);    // month-day-year format
  3079.             if (new Date() - parsedDate > 15600000000)    // roughly 6 months
  3080.                 timeOrYear = rawDate[2];
  3081.             var month = parsedDate.toLocaleDateString().split(" ");
  3082.             var name = temp.substring(temp.indexOf(treeitem[x][2]) + treeitem[x][2].length + 1, temp.length);
  3083.             name = name.substring(name.search(/[^\s]/));
  3084.             month = month[1].substring(0,3);    // abbr.
  3085.             treeitem[x] = { 
  3086.                 attr: treeitem[x][2] == "<DIR>" ? "d---------" : "----------",
  3087.                 hardLink: "",
  3088.                 user: "",
  3089.                 group: "",
  3090.                 size: treeitem[x][2] == "<DIR>" ? 0 : treeitem[x][2],
  3091.                 date: month + ' ' + rawDate[1] + ' ' + timeOrYear,
  3092.                 name: (name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1)),
  3093.                 isDirectory: treeitem[x][2] == "<DIR>",
  3094.                 isSymlink: false,
  3095.                 symlink: ""
  3096.             };
  3097.         }
  3098.     }
  3099.  
  3100.     // sort directories to the top
  3101.     var directories = new Array;
  3102.     var files = new Array;
  3103.     for (var x = 0; x < treeitem.length; ++x)
  3104.     {
  3105.         if (treeitem[x].isDirectory)
  3106.             directories.push(treeitem[x]);
  3107.         else
  3108.             files.push(treeitem[x]);
  3109.     }
  3110.  
  3111.     // get rid of "." or ".." - this can screw up things (i already got one angry letter) if you recursively delete
  3112.     if (directories.length && directories[0].name == ".")
  3113.         directories.shift();
  3114.     if (directories.length && directories[0].name == "..")
  3115.         directories.shift();
  3116.     
  3117.     treeitem = directories.concat(files);
  3118.  
  3119.     return treeitem;
  3120. }
  3121.  
  3122. // *************************************************************************************************
  3123. // *************************************** remote tree views ***************************************
  3124. // *************************************************************************************************
  3125.  
  3126. function changeRemoteView(column, direction)
  3127. {
  3128.     var treeitem = parseListData(data);
  3129.     var directories = new Array;
  3130.  
  3131.     // get directory size
  3132.     remotesize = 0;
  3133.     for (var x = 0; x < treeitem.length; ++x)
  3134.     {
  3135.         var size = parseInt(treeitem[x].size); 
  3136.         if (size != 0)
  3137.             size = parseInt(size / 1024) + 1;
  3138.         remotesize += size;
  3139.     }
  3140.     
  3141.     for (var x = 0; x < treeitem.length; ++x)
  3142.         if (treeitem[x].isDirectory)
  3143.             directories.push(treeitem[x]);
  3144.  
  3145.     // begin column sorting
  3146.     if (!column)
  3147.     {
  3148.         if (document.getElementById('remotename').getAttribute("sortDirection") &&
  3149.                 document.getElementById('remotename').getAttribute("sortDirection") != "natural")
  3150.         {
  3151.             column = "remotename";
  3152.             direction = document.getElementById('remotename').getAttribute("sortDirection");
  3153.         }
  3154.         if (document.getElementById('remotesize').getAttribute("sortDirection") &&
  3155.                 document.getElementById('remotesize').getAttribute("sortDirection") != "natural")
  3156.         {
  3157.             column = "remotesize";
  3158.             direction = document.getElementById('remotesize').getAttribute("sortDirection");
  3159.         }
  3160.         if (document.getElementById('remotedate').getAttribute("sortDirection") &&
  3161.                 document.getElementById('remotedate').getAttribute("sortDirection") != "natural")
  3162.         {
  3163.             column = "remotedate";
  3164.             direction = document.getElementById('remotedate').getAttribute("sortDirection");
  3165.         }
  3166.         if (document.getElementById('remoteattr').getAttribute("sortDirection") &&
  3167.                 document.getElementById('remoteattr').getAttribute("sortDirection") != "natural")
  3168.         {
  3169.             column = "remoteattr";
  3170.             direction = document.getElementById('remoteattr').getAttribute("sortDirection");
  3171.         }
  3172.     }
  3173.  
  3174.     if (column == "remotename")
  3175.         treeitem.sort(compareRemoteName);
  3176.     if (column == "remotesize")
  3177.         treeitem.sort(compareRemoteSize);
  3178.     if (column == "remotedate")
  3179.         treeitem.sort(compareRemoteDate);
  3180.     if (column == "remoteattr")
  3181.         treeitem.sort(compareRemoteAttr);
  3182.     if (direction == "ascending")
  3183.         treeitem.reverse();
  3184.     // end column sorting
  3185.  
  3186.     var treeView = {
  3187.     rowCount : treeitem.length,
  3188.     getCellText : function(row,column){
  3189.         if (row == -1)
  3190.             return "";
  3191.         switch(column)
  3192.         {
  3193.             case "remotename":
  3194.                 return treeitem[row].name;
  3195.             case "remotesize":
  3196.                 if (treeitem[row].size == 0)
  3197.                     return "0 KB  ";
  3198.                 return commas(parseInt(treeitem[row].size / 1024) + 1) + " KB  ";
  3199.             case "remotedate":
  3200.                 return treeitem[row].date;
  3201.             case "remoteattr":
  3202.                 return treeitem[row].attr;
  3203.             default:
  3204.                 return " "; // shouldn't get here
  3205.         }
  3206.     },
  3207.     setTree: function(treebox){ this.treebox = treebox; },
  3208.     isContainer: function(row){ return false; },
  3209.     isSeparator: function(row){ return false; },
  3210.     isSorted: function(row){ return false; },
  3211.     getLevel: function(row){ return 0; },
  3212.     getImageSrc: function(row,col)
  3213.     {
  3214.         if (row == -1)
  3215.             return "";
  3216.         if (col == "remotename")
  3217.         {
  3218.             if (treeitem[row].isDirectory)
  3219.                 return "res/directory.ico";      // kind of a hack - is there a way to get the folder icon from "moz-icon://"?
  3220.             if (treeitem[row].isSymlink)
  3221.                 return "res/link.ico";
  3222.             return "moz-icon://" + treeitem[row].name + "?size=16";
  3223.         }
  3224.     },
  3225.     getRowProperties: function(row,props){},
  3226.     getCellProperties: function(row,col,props){},
  3227.     getColumnProperties: function(colid,col,props){},
  3228.     cycleHeader: function(col, elem){ }
  3229.     };
  3230.  
  3231.     remoteDirManager(gRemotePath.value, directories);
  3232.  
  3233.     var dirTreeView = {
  3234.     rowCount : remotedircache.length,
  3235.     getCellText : function(row,column){
  3236.         if (row < 0 || row >= remotedircache.length)
  3237.             return "";
  3238.         if (row == 0)
  3239.             return "/";
  3240.         return remotedircache[row].path.substring(remotedircache[row].path.lastIndexOf('/') + 1);
  3241.     },
  3242.     getParentIndex: function(row){
  3243.         if (row < 0 || row >= remotedircache.length)
  3244.             return -1;
  3245.         if (remotedircache[row].path == "/")
  3246.             return -1;
  3247.         var parent = remotedircache[row].path.substring(0, remotedircache[row].path.lastIndexOf('/'));
  3248.         if (parent.length == 0)
  3249.             return 0;
  3250.         for (var x = 0; x < remotedircache.length; ++x)
  3251.             if (remotedircache[x].path == parent)
  3252.                 return x;
  3253.     },
  3254.     isContainerOpen: function(row){ if (row < 0 || row >= remotedircache.length) return false; return remotedircache[row].open; },
  3255.      isContainerEmpty:function(row){ if (row < 0 || row >= remotedircache.length) return true; return remotedircache[row].empty; },
  3256.      hasNextSibling: function(row,nextrow) { return remotedircache[row].hasnext; },
  3257.     getLevel:function(row) {
  3258.          if (row < 0 || row >= remotedircache.length)
  3259.              return 0;
  3260.          return row ? remotedircache[row].path.match(/\x2f/g).length : 0;
  3261.     },
  3262.     toggleOpenState:function(row){
  3263.         if (this.isContainerOpen(row))
  3264.         {
  3265.             if (checkIsListing())
  3266.                 return;
  3267.             var count = 1;
  3268.             while ((count + row) < remotedircache.length && this.getLevel(count + row) > this.getLevel(row))
  3269.                 ++count;
  3270.             --count;
  3271.             var temp = remotedircache.splice(row + 1, count);
  3272.             if (temp.path)
  3273.                 temp = new Array(temp); // messy, messy, messy
  3274.             if (remotedircache.path)
  3275.                 remotedircache = new Array(remotedircache); // messy, messy, messy
  3276.             if (hiddenremotedircache.path)
  3277.                 hiddenremotedircache = new Array(hiddenremotedircache); // messy, messy, messy
  3278.             hiddenremotedircache = temp.concat(hiddenremotedircache);
  3279.             if (hiddenremotedircache.path)
  3280.                 hiddenremotedircache = new Array(hiddenremotedircache); // messy, messy, messy
  3281.             gRemoteDirTree.treeBoxObject.rowCountChanged(row, -count);
  3282.             remotedircache[row].open = false;
  3283.             remotedircache[row].children = temp.length;
  3284.             if (gRemotePath.value.indexOf(remotedircache[row].path) != -1
  3285.                     && gRemotePath.value != remotedircache[row].path)
  3286.             {
  3287.                 list(remotedircache[row].path);
  3288.                 gRemoteDirTree.view.selection.select(row);
  3289.                 gRemoteDirTree.treeBoxObject.ensureRowIsVisible(row);
  3290.             }
  3291.             if (gRemotePath.value == remotedircache[row].path)
  3292.             {
  3293.                 gRemoteDirTree.view.selection.select(row);
  3294.                 gRemoteDirTree.treeBoxObject.ensureRowIsVisible(row);
  3295.             }
  3296.         }
  3297.         else
  3298.         {
  3299.             if (checkIsListing() || (eventQueue.length && checkRemoteCache(remotedircache[row].path) == -1))
  3300.                 return;
  3301.             var found = -1;
  3302.             var number = remotedircache[row].children;
  3303.             for (var x = 0; x < hiddenremotedircache.length; ++x)
  3304.                 if (hiddenremotedircache[x].path.indexOf(remotedircache[row].path) != -1)
  3305.                 {
  3306.                     found = x;
  3307.                     break;
  3308.                 }
  3309.             if (found == -1)
  3310.             {
  3311.                 findIt(row);
  3312.                 return;
  3313.             }
  3314.             var toBeAdded = hiddenremotedircache.splice(found, number);
  3315.             var piece = remotedircache.splice(0, row + 1);
  3316.             if (toBeAdded.path)
  3317.                 toBeAdded = new Array(toBeAdded);    // messy, messy, messy
  3318.             if (piece.path)
  3319.                 piece = new Array(piece); // messy, messy, messy
  3320.             if (remotedircache.path)
  3321.                 remotedircache = new Array(remotedircache); // messy, messy, messy
  3322.             if (hiddenremotedircache.path)
  3323.                 hiddenremotedircache = new Array(hiddenremotedircache); // messy, messy, messy
  3324.             remotedircache = piece.concat(toBeAdded, remotedircache);
  3325.             if (remotedircache.path)
  3326.                 remotedircache = new Array(remotedircache); // ditto, ditto, ditto
  3327.             gRemoteDirTree.treeBoxObject.rowCountChanged(row, toBeAdded.length);
  3328.             if (!remotedircache[row].empty)
  3329.                 remotedircache[row].open = true;
  3330.             remotedircache[row].children = -1;
  3331.             if (gRemotePath.value == remotedircache[row].path)
  3332.             {
  3333.                 gRemoteDirTree.view.selection.select(row);
  3334.                 gRemoteDirTree.treeBoxObject.ensureRowIsVisible(row + (remoteLastRowSeen - row - 1));
  3335.                 remoteLastRowSeen = 0;
  3336.             }
  3337.         }
  3338.     },
  3339.     isSeparator: function(row){ return false; },
  3340.     isSorted: function(row){ return false; },
  3341.       isContainer: function(row){ return true; },
  3342.     getImageSrc: function(row,col){
  3343.         if (this.isContainerOpen(row))
  3344.             return "res/directoryopen.ico";    // hack - folder icon from "moz-icon://"?
  3345.         else
  3346.             return "res/directory.ico";
  3347.     },
  3348.     setTree: function(treebox){ this.treebox = treebox; },
  3349.     getRowProperties: function(row,props){},
  3350.     getCellProperties: function(row,col,props){},
  3351.     getColumnProperties: function(colid,col,props){},
  3352.     };
  3353.  
  3354.     gRemoteTree.view = treeView;
  3355.     gRemoteDirTree.view = dirTreeView;
  3356.  
  3357.     // on startup, select '/' on remotedirtree
  3358.     if (gRemoteDirTree.view.selection.count == 0)
  3359.         gRemoteDirTree.view.selection.select(0);
  3360. }
  3361. function remoteDirManager(path, data)
  3362. {
  3363.     for (var x = 0; x < remotedircache.length; ++x)
  3364.         if (remotedircache[x].path == path)
  3365.         {
  3366.             if (data.length == 0)
  3367.             {
  3368.                 remotedircache[x].empty = true;
  3369.                 remotedircache[x].open = false;
  3370.             }
  3371.             else
  3372.                 remotedircache[x].empty = false;
  3373.             break;
  3374.         }
  3375.     if (path.charAt(path.length - 1) != '/')
  3376.         path += '/';
  3377.     for (var x = 0; x < data.length; ++x)
  3378.         data[x] = {path: path + data[x].name, open: false, empty: false, children: -1, hasnext: true };
  3379.     if (data.path)
  3380.         data = new Array(data); // messy, messy, messy
  3381.     data.sort(directorySort);
  3382.     if (data.length)
  3383.         data[data.length - 1].hasnext = false;
  3384.     remotedircache = remotedircache.concat(data);
  3385.     if (remotedircache.path)
  3386.         remotedircache = new Array(remotedircache); // messy, messy, messy
  3387.     remotedircache.sort(directorySort);
  3388.     // check for duplicates
  3389.     for (var x = 0; x < remotedircache.length - 1; ++x)
  3390.         if (remotedircache[x].path == remotedircache[x + 1].path)
  3391.         {
  3392.             if (remotedircache[x].open)
  3393.                 remotedircache[x + 1].open = true;
  3394.             if (remotedircache[x].empty)
  3395.                 remotedircache[x + 1].empty = true;
  3396.             if (remotedircache[x].children != -1)
  3397.                 remotedircache[x + 1].children = remotedircache[x].children;
  3398.             if (remotedircache[x].hasnext)
  3399.                 remotedircache[x + 1].hasnext = true;
  3400.             remotedircache.splice(x, 1);
  3401.             if (remotedircache.path)
  3402.                 remotedircache = new Array(remotedircache); // messy, messy, messy
  3403.         }
  3404.     var didSplice = false;
  3405.     for (var x = 0; x < remotedircache.length; ++x)
  3406.     {
  3407.         for (var y = 0; y < hiddenremotedircache.length; ++y)
  3408.         {
  3409.             if (x >= remotedircache.length) // splicing might cause invalid indices
  3410.                 break;
  3411.             if (remotedircache[x].path == hiddenremotedircache[y].path)
  3412.             {
  3413.                 remotedircache.splice(x, 1);
  3414.                 didSplice = true;
  3415.                 if (remotedircache.path)
  3416.                     remotedircache = new Array(remotedircache); // messy, messy, messy
  3417.             }
  3418.         }
  3419.         if (didSplice)    // gotta start from 0 again b/c array will have shifted around, better safe...
  3420.         {
  3421.             x = -1;    // will get incremented up to 0
  3422.             didSplice = false;
  3423.         }
  3424.     }
  3425. }
  3426. function findIt(row)    // again, elegance you will find in a ballroom, not here
  3427. {
  3428.     var temp = gRemotePath.value;
  3429.     if (!refreshRemote)
  3430.     {
  3431.         list(remotedircache[row].path, "findIt2(" + row + ", \'" + temp + "\')");
  3432.         return;
  3433.     }
  3434.     findIt3(row, temp);
  3435. }
  3436. function findIt2(row, temp)
  3437. {
  3438.     list(temp, "findIt3(" + row + ", \'" + temp + "\')");
  3439. }
  3440. function findIt3(row, temp)
  3441. {
  3442.     if (!remotedircache[row].empty)
  3443.         remotedircache[row].open = true;
  3444.     remotedircache[row].children = -1;
  3445.     for (var x = 0; x < remotedircache.length; ++x)
  3446.         if (remotedircache[x].path == temp)
  3447.         {
  3448.             gRemoteDirTree.view.selection.select(x); // when moving with keys
  3449.             gRemoteDirTree.treeBoxObject.ensureRowIsVisible(x + (remoteLastRowSeen - x - 1));
  3450.             remoteLastRowSeen = 0;
  3451.             break;
  3452.         }
  3453. }
  3454.  
  3455. // *************************************************************************************************
  3456. // *************************************** refresh remote view *************************************
  3457. // *************************************************************************************************
  3458.  
  3459. function refreshRemoteView()
  3460. {
  3461.     if (!isConnected)
  3462.         return;
  3463.     if (eventQueue.length && eventQueue[0].cmd != "aborted")
  3464.         return;
  3465.     refreshRemote = true;
  3466.     // get rid of old directory entries
  3467.     for (var x = 0; x < remotedircache.length; ++x)
  3468.         if (remotedircache[x].path.indexOf(gRemotePath.value) != -1
  3469.             && gRemotePath.value != remotedircache[x].path)
  3470.         {
  3471.             remotedircache.splice(x, 1);
  3472.             if (remotedircache.path)
  3473.                     remotedircache = new Array(remotedircache); // messy, messy, messy
  3474.             x = -1; // start from the top, to be safe
  3475.         }
  3476.     for (var x = 0; x < hiddenremotedircache.length; ++x)
  3477.         if (hiddenremotedircache[x].path.indexOf(gRemotePath.value) != -1
  3478.             && gRemotePath.value != hiddenremotedircache[x].path)
  3479.         {
  3480.             hiddenremotedircache.splice(x, 1);
  3481.             if (hiddenremotedircache.path)
  3482.                     hiddenremotedircache = new Array(hiddenremotedircache); // messy, messy, messy
  3483.             x = -1; // start from the top, to be safe
  3484.         }
  3485.     for (var x = 0; x < remotedircache.length; ++x)
  3486.         if (remotedircache[x].path == gRemotePath.value)
  3487.         {
  3488.             remoteDirChangeHelper(x);
  3489.             break;
  3490.         }
  3491. }
  3492.  
  3493. // *************************************************************************************************
  3494. // *************************************** rename remote file **************************************
  3495. // *************************************************************************************************
  3496.  
  3497. var newRemoteName;
  3498. function renameRemoteFile(path, openRenamedDirectory)
  3499. {
  3500.     if (!isConnected)
  3501.         return;
  3502.     var newName = window.prompt(strbundle.getString("renameTo"), path.substring(path.lastIndexOf('/') + 1), strbundle.getString("rename"));
  3503.     if (!newName)
  3504.         return;
  3505.     
  3506.     if (path.charAt(path.length - 1) == '/')
  3507.         path = path.substring(path.length - 1);
  3508.     newName = path.substring(0, path.lastIndexOf('/')) + '/' + newName;
  3509.     newRemoteName = newName;    // damn globals
  3510.     
  3511.     addEventQueue("RNFR", path);
  3512.     addEventQueue("RNTO", newName, openRenamedDirectory);
  3513.     if (isReady)    // get the ball rollin' if it isn't already
  3514.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  3515. }
  3516. function renameRemoteFile2(openRenamedDirectory)
  3517. {
  3518.     moreToRemember = "";
  3519.     if (openRenamedDirectory)
  3520.     {
  3521.         gRemotePath.value = newRemoteName;
  3522.         onRemotePathChange();
  3523.     }
  3524.     else
  3525.     {
  3526.         gRemoteTree.focus();
  3527.         for (var x = 0; x < gRemoteTree.view.rowCount; ++x)
  3528.             if (newRemoteName.substring(newRemoteName.lastIndexOf('/') + 1) == gRemoteTree.view.getCellText(x, "remotename"))
  3529.             {
  3530.                 gRemoteTree.view.selection.select(x);
  3531.                 break;
  3532.             }
  3533.     }
  3534. }
  3535.  
  3536. // *************************************************************************************************
  3537. // *************************************** make remote directory ***********************************
  3538. // *************************************************************************************************
  3539.  
  3540. function makeRemoteDirectory()
  3541. {
  3542.     if (!isConnected)
  3543.         return;
  3544.     if (eventQueue.length)
  3545.         return;
  3546.     var newName = window.prompt(strbundle.getString("directoryName"), "", strbundle.getString("newDirectory"));
  3547.     if (!newName)
  3548.         return;
  3549.     if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  3550.         newName = gRemotePath.value + '/' + newName;
  3551.     else
  3552.         newName = gRemotePath.value + newName;
  3553.     moreToRemember = "makeRemoteDirectory2(\"" + newName + "\")";
  3554.     addEventQueue("MKD", newName);
  3555.     if (isReady)    // get the ball rollin' if it isn't already
  3556.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  3557. }
  3558. function makeRemoteDirectory2(newName)
  3559. {
  3560.     moreToRemember = "";
  3561.     gRemotePath.value = newName;
  3562.     onRemotePathChange();
  3563.     gRemoteTree.focus();
  3564. }
  3565.  
  3566. // *************************************************************************************************
  3567. // *************************************** remote properties ***************************************
  3568. // *************************************************************************************************
  3569.  
  3570. function getRemoteProperties()
  3571. {
  3572.     var fileList = gRemoteTree.view.selection;
  3573.     if (fileList.count == 0)
  3574.         return;
  3575.  
  3576.     var thepath = gRemoteTree.view.getCellText(fileList.currentIndex, "remotename");
  3577.     var index = checkRemoteCache(gRemotePath.value);
  3578.     var files = parseListData(remotecache[index].data);
  3579.  
  3580.     for (var x = 0; x < files.length; ++x)
  3581.         if (files[x].name == thepath)
  3582.         {
  3583.             if (gRemotePath.value.charAt(gRemotePath.value.length - 1) != '/')
  3584.                 thepath = gRemotePath.value + '/' + thepath;
  3585.             else
  3586.                 thepath = gRemotePath.value + thepath;
  3587.  
  3588.             var windowHeight = document.getElementById('main-window').boxObject.height;
  3589.             var windowWidth = document.getElementById('main-window').boxObject.width;
  3590.  
  3591.             var permissions = new Object();
  3592.             permissions.value = "";
  3593.  
  3594.             window.openDialog("chrome://fireftp/content/propertiesDialog.xul","propertiesDialog",
  3595.                 "screenY="+(windowHeight/2 - 150)+",screenX="+(windowWidth/2 - 300)+
  3596.                 ",width=600,height=300,chrome,modal=yes,dialog=yes,resizable=yes",
  3597.                 thepath, files[x].name, files[x].size, files[x].date, files[x].attr, 'disabled', 'disabled',
  3598.                 files[x].isDirectory, files[x].user, files[x].group, permissions, files[x].isSymlink, files[x].symlink);
  3599.             if (permissions.value)
  3600.             {
  3601.                 addEventQueue("SITE CHMOD", permissions.value + ' ' + thepath, files[x].name);
  3602.                 if (isReady)    // get the ball rollin' if it isn't already
  3603.                     writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  3604.             }
  3605.             break;
  3606.         }
  3607. }
  3608. function getRemoteProperties2(remotename)
  3609. {
  3610.     moreToRemember = "";
  3611.     gRemoteTree.focus();
  3612.     for (var x = 0; x < gRemoteTree.view.rowCount; ++x)
  3613.         if (remotename == gRemoteTree.view.getCellText(x, "remotename"))
  3614.         {
  3615.             gRemoteTree.view.selection.select(x);
  3616.             break;
  3617.         }
  3618. }
  3619.  
  3620. // *************************************************************************************************
  3621. // *************************************** remote cut/paste ****************************************
  3622. // *************************************************************************************************
  3623.  
  3624. var remoteCutParentDir;
  3625. var remoteCutParentDir2;
  3626. var isRemoteDirCut;
  3627. var remotePasteReady = false;
  3628. function remoteCut()
  3629. {
  3630.     if (!isConnected)
  3631.         return;
  3632.     var fileList = gRemoteTree.view.selection;
  3633.     if (fileList.count == 0)
  3634.         return;
  3635.  
  3636.     remotePasteFiles = new Array;
  3637.     var remoteParent = gRemotePath.value;
  3638.  
  3639.     for (var x = 0; x < gRemoteTree.view.rowCount; ++x)
  3640.         if (gRemoteTree.view.selection.isSelected(x))
  3641.         {
  3642.             var remotepath = gRemoteTree.view.getCellText(x, "remotename");
  3643.             if (remoteParent.charAt(remoteParent.length - 1) != '/')
  3644.                 remotepath = remoteParent + '/' + remotepath;
  3645.             else
  3646.                 remotepath = remoteParent + remotepath;
  3647.  
  3648.             remotePasteFiles.push(remotepath);
  3649.         }
  3650.  
  3651.     remoteCutParentDir = remoteParent;
  3652.     remotePasteReady = false;
  3653.     isRemoteDirCut = false;
  3654.     document.getElementById('remotePasteContext').setAttribute("disabled", false);
  3655.     document.getElementById('remoteDirPasteContext').setAttribute("disabled", false);
  3656. }
  3657. function remotePaste(remotedir)
  3658. {
  3659.     if (!isConnected)
  3660.         return;
  3661.     var    parentDir;
  3662.     var prompt = true;
  3663.     var skipall = false;
  3664.     var remotefiles = parseListData(data);
  3665.  
  3666.     for (var x = 0; x < remotePasteFiles.length; ++x)
  3667.     {
  3668.         var parentDir = remotedir ? remotedir : gRemotePath.value;
  3669.  
  3670.         var newPath = parentDir.charAt(parentDir.length - 1) == '/'
  3671.                 ? parentDir + remotePasteFiles[x].substring(remotePasteFiles[x].lastIndexOf('/') + 1)
  3672.                 : parentDir + '/' + remotePasteFiles[x].substring(remotePasteFiles[x].lastIndexOf('/') + 1);
  3673.  
  3674.         var exists = false;
  3675.         var index;
  3676.         for (var y = 0; y < remotefiles.length; ++y)
  3677.             if (remotefiles[y].name == remotePasteFiles[x].substring(remotePasteFiles[x].lastIndexOf('/') + 1))
  3678.             {
  3679.                 exists = true;
  3680.                 index = y;
  3681.                 break;
  3682.             }
  3683.         if (exists && skipall)
  3684.             continue;
  3685.         if (exists && prompt && !remotefiles[index].isDirectory)
  3686.         {
  3687.             var response = new Object();
  3688.             response.value = 0;
  3689.             var windowHeight = document.getElementById('main-window').boxObject.height;
  3690.             var windowWidth = document.getElementById('main-window').boxObject.width;
  3691.             var timer = destructmode;
  3692.  
  3693.             window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  3694.                 "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  3695.                 ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes",
  3696.                 response, remotePasteFiles[x].substring(remotePasteFiles[x].lastIndexOf('/') + 1), true, true, timer);
  3697.             if (response.value == 2)
  3698.                 prompt = false;
  3699.             else if (response.value == 3)
  3700.                 continue;
  3701.             else if (response.value == 4 || response.value == 0)
  3702.                 return;
  3703.             else if (response.value == 5)
  3704.             {
  3705.                 skipall = true;
  3706.                 continue;
  3707.             }
  3708.         }
  3709.         
  3710.         addEventQueue("RNFR", remotePasteFiles[x]);
  3711.         addEventQueue("RNTO", newPath, "pasting");
  3712.     }
  3713.  
  3714.     remotePasteFiles = new Array;
  3715.     document.getElementById('remotePasteContext').setAttribute("disabled", true);
  3716.     document.getElementById('remoteDirPasteContext').setAttribute("disabled", true);
  3717.  
  3718.     remotePasteReady = true;
  3719.  
  3720.     if (isReady)    // get the ball rollin' if it isn't already
  3721.         writeControl(eventQueue[0].cmd, eventQueue[0].parameter);
  3722. }
  3723.  
  3724. function remoteDirCut()
  3725. {
  3726.     if (!isConnected)
  3727.         return;
  3728.     if (gRemoteDirTree.view.selection.count == 0)
  3729.         return;
  3730.  
  3731.     if (gRemoteDirTree.view.selection.currentIndex == 0)    // you can't cut the root
  3732.         return;                                                                        // and you can't stop the rock
  3733.  
  3734.     remotePasteFiles = new Array;
  3735.     var dir = gRemoteDirTree.view.selection.currentIndex;
  3736.  
  3737.     remotePasteFiles.push(remotedircache[dir].path);
  3738.  
  3739.     remoteCutParentDir = remotedircache[dir].path;
  3740.     remotePasteReady = false;
  3741.     isRemoteDirCut = true;
  3742.     document.getElementById('remotePasteContext').setAttribute("disabled", false);
  3743.     document.getElementById('remoteDirPasteContext').setAttribute("disabled", false);
  3744. }
  3745. function remoteDirPaste()
  3746. {
  3747.     if (!isConnected)
  3748.         return;
  3749.     if (gRemoteDirTree.view.selection.count == 0)
  3750.         return;
  3751.  
  3752.     var dir = gRemoteDirTree.view.selection.currentIndex;
  3753.     list(remotedircache[dir].path, "remoteDirPaste2(\'" + remotedircache[dir].path + "\')", true)
  3754. }
  3755. function remoteDirPaste2(path)
  3756. {
  3757.     remoteCutParentDir2 = path;
  3758.     remotePaste(path);
  3759. }
  3760. function refreshPasting(tempPath)
  3761. {
  3762.     moreToRemember = "";
  3763.     gRemotePath.value = remoteCutParentDir2;
  3764.     onRemotePathChange();
  3765.     moreToRemember = "refreshPasting2(\'" + tempPath + "\')";
  3766.     refreshRemoteView();
  3767. }
  3768. function refreshPasting2(tempPath)
  3769. {
  3770.     moreToRemember = "";
  3771.     if (!isRemoteDirCut || tempPath != remoteCutParentDir)
  3772.         gRemotePath.value = tempPath;
  3773.     else
  3774.         gRemotePath.value =
  3775.             remoteCutParentDir.substring(0, remoteCutParentDir.lastIndexOf('/')
  3776.             ? remoteCutParentDir.lastIndexOf('/') : 1);
  3777.     onRemotePathChange();
  3778.     remoteCutParentDir = "";
  3779.     remoteCutParentDir2 = "";
  3780.     refreshRemoteView();
  3781. }
  3782.  
  3783. // *************************************************************************************************
  3784. // *************************************** remote drag sessions ************************************
  3785. // *************************************************************************************************
  3786.  
  3787. function onRemoteDragOver(event)
  3788. {
  3789.     if (!isConnected)
  3790.         return;
  3791.     var row = {};
  3792.     var col = {};
  3793.     var child = {};
  3794.     var dragSession = whataDrag.getCurrentSession();
  3795.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  3796.     
  3797.     trans.addDataFlavor("text/unicode");
  3798.     dragSession.getData(trans, 0);
  3799.     var dataObj = new Object();
  3800.     var bestFlavor = new Object();
  3801.     var len = new Object();
  3802.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  3803.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  3804.  
  3805.     gRemoteTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  3806.  
  3807.     if (dataObj.data == "remotetreechildren")
  3808.     {
  3809.         if (gRemoteTree.view.getCellText(row.value, "remoteattr").charAt(0) == "d")
  3810.             dragSession.canDrop = true;
  3811.         else
  3812.             dragSession.canDrop = false;
  3813.     }
  3814.     else
  3815.         dragSession.canDrop = true;
  3816. }
  3817. function onRemoteDragDrop(event)
  3818. {
  3819.     var dragSession = whataDrag.getCurrentSession();
  3820.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  3821.     
  3822.     trans.addDataFlavor("text/unicode");
  3823.     dragSession.getData(trans, 0);
  3824.     var dataObj = new Object();
  3825.     var bestFlavor = new Object();
  3826.     var len = new Object();
  3827.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  3828.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  3829.  
  3830.     if (dataObj.data == "remotetreechildren")
  3831.     {
  3832.         var row = {};
  3833.         var col = {};
  3834.         var child = {};
  3835.         gRemoteTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  3836.         var remoteParent = gRemotePath.value;
  3837.         remoteCut();
  3838.         var remotepath = gRemoteTree.view.getCellText(row.value, "remotename");
  3839.         if (remoteParent.charAt(remoteParent.length - 1) != '/')
  3840.             remotepath = remoteParent + '/' + remotepath;
  3841.         else
  3842.             remotepath = remoteParent + remotepath;
  3843.         gRemotePath.value = remotepath;
  3844.         moreToRemember = "moreToRemember='';remotePaste()";
  3845.         onRemotePathChange();
  3846.     }
  3847.     else if (dataObj.data == "remotedirtreechildren")
  3848.     {
  3849.         var remoteParent = gRemotePath.value;
  3850.         remoteDirCut();
  3851.         remotePaste();
  3852.     }
  3853.     else if (dataObj.data == "localtreechildren")
  3854.         stor();
  3855.     else if (dataObj.data == "localdirtreechildren")
  3856.         storDir();
  3857.     event.preventBubble();
  3858. }
  3859. function onRemoteDirDragOver(event)
  3860. {
  3861.     if (!isConnected)
  3862.         return;
  3863.     var row = {};
  3864.     var col = {};
  3865.     var child = {};
  3866.     var dragSession = whataDrag.getCurrentSession();
  3867.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  3868.     trans.addDataFlavor("text/unicode");
  3869.     dragSession.getData(trans, 0);
  3870.     var dataObj = new Object();
  3871.     var bestFlavor = new Object();
  3872.     var len = new Object();
  3873.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  3874.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  3875.  
  3876.     if (dataObj.data == "localdirtreechildren" || dataObj.data == "localtreechildren")
  3877.     {
  3878.         dragSession.canDrop = false;
  3879.         return;
  3880.     }
  3881.     
  3882.     gRemoteDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  3883.     
  3884.     if (gRemoteDirTree.view.getCellText(row.value, "remotename") != "")
  3885.         dragSession.canDrop = true;
  3886.     else
  3887.         dragSession.canDrop = false;
  3888. }
  3889. function onRemoteDirDragDrop(event)
  3890. {
  3891.     var row = {};
  3892.     var col = {};
  3893.     var child = {};
  3894.     var dragSession = whataDrag.getCurrentSession();
  3895.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  3896.     
  3897.     trans.addDataFlavor("text/unicode");
  3898.     dragSession.getData(trans, 0);
  3899.     var dataObj = new Object();
  3900.     var bestFlavor = new Object();
  3901.     var len = new Object();
  3902.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  3903.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  3904.  
  3905.     var row = {};
  3906.     var col = {};
  3907.     var child = {};
  3908.     gRemoteDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  3909.  
  3910.     if (dataObj.data == "remotetreechildren")
  3911.     {
  3912.         gRemoteDirTree.view.selection.currentIndex = row.value;
  3913.         remoteCut();
  3914.         remoteDirPaste();
  3915.     }
  3916.     else if (dataObj.data == "remotedirtreechildren")
  3917.     {
  3918.         remoteDirCut();
  3919.         gRemoteDirTree.view.selection.currentIndex = row.value;
  3920.         remoteDirPaste();
  3921.     }
  3922.     event.preventBubble();
  3923. }
  3924.  
  3925. // *************************************************************************************************
  3926. // **************************************** local folder functions *********************************
  3927. // *************************************************************************************************
  3928.  
  3929. function getLocalFileSpec(path)
  3930. {
  3931.     try {
  3932.         var file = Components.classes['@mozilla.org/filespec;1'].createInstance(Components.interfaces.nsIFileSpec);
  3933.         file.unicodePath = path;
  3934.         return file;
  3935.     } catch (ex) { debug(ex,27); }
  3936. }
  3937. function localDirChangeHelper(index, toggle)
  3938. {
  3939.     var state = localdircache[index].open;
  3940.     gLocalPath.value = localdircache[index].path;
  3941.  
  3942.     var gFormHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
  3943.         .getService(Components.interfaces.nsIFormHistory);
  3944.     gFormHistory.addEntry(gLocalPath.getAttribute("autocompletesearchparam"), gLocalPath.value);
  3945.  
  3946.     savePreferences();
  3947.  
  3948.     changeLocalView();
  3949.  
  3950.     if (!state && !localdircache[index].empty)
  3951.     {
  3952.         localdircache[index].open = false;
  3953.         gLocalDirTree.view.toggleOpenState(index);
  3954.     }
  3955.     if (toggle)
  3956.         gLocalDirTree.view.toggleOpenState(index);
  3957.  
  3958.     gLocalDirTree.view.selection.select(index);
  3959.     if (localLastRowSeen)
  3960.     {
  3961.         gLocalDirTree.treeBoxObject.ensureRowIsVisible(index + (localLastRowSeen - index - 1));
  3962.         localLastRowSeen = 0;
  3963.     }
  3964.     else
  3965.         gLocalDirTree.treeBoxObject.ensureRowIsVisible(index);
  3966. }
  3967. function onLocalCDUP()
  3968. {
  3969.     var parentIndex = gLocalDirTree.view.getParentIndex(gLocalDirTree.view.selection.currentIndex);
  3970.     if (parentIndex != -1)
  3971.     {
  3972.         gLocalDirTree.view.selection.select(parentIndex);
  3973.         localDirChangeHelper(parentIndex);
  3974.     }
  3975. }
  3976.  
  3977. var localLastRowSeen = 0;
  3978. function onLocalDirClick(event)
  3979. {
  3980.     if (event.button == 0)
  3981.     {
  3982.         var row = {};
  3983.         var col = {};
  3984.         var child = {};
  3985.         gLocalDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  3986.         var index = gLocalDirTree.view.selection.currentIndex;
  3987.         localLastRowSeen = gLocalDirTree.treeBoxObject.getLastVisibleRow();    // have you seen this row?
  3988.  
  3989.         if (index == row.value && localdircache[index].path != gLocalPath.value)
  3990.             localDirChangeHelper(index);
  3991.  
  3992.     } else if (event.button == 1 && !document.getElementById('localPasteContext').disabled)
  3993.         localDirPaste();
  3994. }
  3995. var localDirKeyDownIndex;    // global variables - yummy, as they say (you know who you are)
  3996. var localDirKeyDownOpen;
  3997. function onLocalDirKeyDown(event)
  3998. {
  3999.     localDirKeyDownIndex = gLocalDirTree.view.selection.currentIndex;
  4000.     localDirKeyDownOpen = localdircache[localDirKeyDownIndex].open;
  4001. }
  4002. function onLocalDirKeyPress(event)
  4003. {
  4004.     var index = gLocalDirTree.view.selection.currentIndex;
  4005.     switch(event.keyCode)
  4006.     {
  4007.         case 37:    // left
  4008.             if (localDirKeyDownIndex != index)
  4009.             {
  4010.                 gLocalDirTree.view.selection.select(index);
  4011.                 localDirChangeHelper(index);
  4012.             }
  4013.             break;
  4014.         case 38:    // up
  4015.             if (!index)
  4016.                 return;
  4017.  
  4018.             // see if file is good first
  4019.             try {
  4020.                 dir = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  4021.                 dir.initWithPath(localdircache[index - 1].path);
  4022.                 var entries = dir.directoryEntries;
  4023.             } catch (ex) {
  4024.                 debug(ex,28);
  4025.                 gLocalPath.value = localdircache[index - 1].path;
  4026.                 onLocalPathChange();
  4027.                 gLocalDirTree.view.selection.select(index);
  4028.                 break;
  4029.             }
  4030.             
  4031.             if (localdircache[index - 1].open)
  4032.                 localDirChangeHelper(index - 1);
  4033.             else
  4034.                 localDirChangeHelper(index - 1, true);
  4035.             gLocalDirTree.view.selection.select(index);
  4036.             break;
  4037.         case 40:    // down
  4038.             if (index == localdircache.length - 1)
  4039.             {
  4040.                 gLocalDirTree.view.selection.select(index - 1);    // this shouldn't work but it does
  4041.                 return;
  4042.             }
  4043.  
  4044.             // see if file is good first
  4045.             try {
  4046.                 dir = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  4047.                 dir.initWithPath(localdircache[index + 1].path);
  4048.                 var entries = dir.directoryEntries;
  4049.             } catch (ex) {
  4050.                 debug(ex,29);
  4051.                 gLocalPath.value = localdircache[index + 1].path;
  4052.                 onLocalPathChange();
  4053.                 gLocalDirTree.view.selection.select(index);
  4054.                 break;
  4055.             }
  4056.             if (localdircache[index + 1].open)
  4057.                 localDirChangeHelper(index + 1);
  4058.             else
  4059.                 localDirChangeHelper(index + 1, true);
  4060.             gLocalDirTree.view.selection.select(index);
  4061.             break;
  4062.         case 39:    // right
  4063.             if (localDirKeyDownOpen && !localdircache[index].empty)
  4064.             {
  4065.                 // see if file is good first
  4066.                 try {
  4067.                     dir = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  4068.                     dir.initWithPath(localdircache[index + 1].path);
  4069.                     var entries = dir.directoryEntries;
  4070.                 } catch (ex) {
  4071.                     error(strbundle.getString("noPermission"));
  4072.                     debug(ex,30);
  4073.                     break;
  4074.                 }
  4075.  
  4076.                 localDirChangeHelper(index + 1);
  4077.             }
  4078.             break;
  4079.         case 8: // backspace
  4080.             onLocalCDUP();
  4081.             break;
  4082.         case 116: // F5
  4083.             event.preventDefault();
  4084.             refreshLocalView();
  4085.             break;
  4086.         case 113: // F2
  4087.             renameLocalFile(localdircache[index].path, true);
  4088.             break;
  4089.         case 46: // delete
  4090.             deleteLocalFile(localdircache[index].path, true, true);
  4091.             break;
  4092.         case 93: // context menu
  4093.             var x = {};
  4094.             var y = {};
  4095.             var width = {};
  4096.             var height = {};
  4097.             var childrenBoxX = document.getElementById('localdirtreechildren').boxObject.x;
  4098.             var childrenBoxY = document.getElementById('localdirtreechildren').boxObject.y;
  4099.             
  4100.             gLocalDirTree.treeBoxObject.getCoordsForCellItem(index, "localdirname", "text", x, y, width, height);
  4101.             document.getElementById('localdirmenu').showPopup(document.getElementById('localdirtreechildren'), childrenBoxX + 75, childrenBoxY + y.value + 30, "context");
  4102.             break;
  4103.         default:
  4104.             break;
  4105.     }
  4106.     if (event.ctrlKey)
  4107.     {
  4108.         switch(event.charCode)
  4109.         {
  4110.             case 120:    // ctrl-x
  4111.                 event.preventDefault();
  4112.                 localDirCut();
  4113.                 break;
  4114.             case 99:    // ctrl-c
  4115.                 event.preventDefault();
  4116.                 localDirCopy();
  4117.                 break;
  4118.             case 118:    // ctrl-v
  4119.                 event.preventDefault();
  4120.                 localDirPaste();
  4121.                 break;
  4122.             default:
  4123.                 break;
  4124.         }
  4125.     }
  4126. }
  4127.  
  4128. // *************************************************************************************************
  4129. // *************************************** local context menus *************************************
  4130. // *************************************************************************************************
  4131.  
  4132. function onLocalDirRenameContext()
  4133. {
  4134.     var index = gLocalDirTree.view.selection.currentIndex;
  4135.     renameLocalFile(localdircache[index].path, true);
  4136. }
  4137. function onLocalRenameContext()
  4138. {
  4139.     var fileList = gLocalTree.view.selection;
  4140.     if (fileList.count == 0)
  4141.         return;
  4142.     var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  4143.     if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  4144.         thepath = gLocalPath.value + slash + thepath;
  4145.     else
  4146.         thepath = gLocalPath.value + thepath;
  4147.     renameLocalFile(thepath, false);
  4148. }
  4149. function onLocalDirDeleteContext()
  4150. {
  4151.     var index = gLocalDirTree.view.selection.currentIndex;
  4152.     deleteLocalFile(localdircache[index].path, true, true);
  4153. }
  4154. function onLocalDeleteContext()
  4155. {
  4156.     var fileList = gLocalTree.view.selection;
  4157.     if (fileList.count == 0)
  4158.         return;
  4159.     
  4160.     if (fileList.count > 1)
  4161.     {
  4162.         if (!window.confirm(strbundle.getString("confirmDelete") + " " + fileList.count + " " + strbundle.getString("confirmDelete2")))
  4163.             return;
  4164.         for (var x = 0; x < gLocalTree.view.rowCount; ++x)
  4165.             if (gLocalTree.view.selection.isSelected(x))
  4166.             {
  4167.                 var thepath = gLocalTree.view.getCellText(x, "localname");
  4168.                 if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  4169.                     thepath = gLocalPath.value + slash + thepath;
  4170.                 else
  4171.                     thepath = gLocalPath.value + thepath;
  4172.                 deleteLocalFile(thepath, false, false);
  4173.             }
  4174.         refreshLocalView();
  4175.     }
  4176.     else
  4177.     {
  4178.         var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  4179.         if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  4180.             thepath = gLocalPath.value + slash + thepath;
  4181.         else
  4182.             thepath = gLocalPath.value + thepath;
  4183.         deleteLocalFile(thepath, false, true);
  4184.     }
  4185. }
  4186.  
  4187. // *************************************************************************************************
  4188. // *************************************** local path change ***************************************
  4189. // *************************************************************************************************
  4190.  
  4191. var localPathFocus;
  4192. function onLocalPathFocus(event)
  4193. {
  4194.     localPathFocus = gLocalPath.value;
  4195. }
  4196. function onLocalPathBlur(event)
  4197. {
  4198.     gLocalPath.value = localPathFocus;
  4199. }
  4200. function onLocalPathChange()
  4201. {
  4202.     var path = gLocalPath.value;
  4203.     if (localdircache.length == 0 || localdircache[0].path.charAt(0) != path.charAt(0))
  4204.     {
  4205.         localdircache = new Array;
  4206.         hiddenlocaldircache = new Array;
  4207.         var thepath;
  4208.         if (path.indexOf('/') == 0) // linux
  4209.         {
  4210.             thepath = "/";
  4211.             slash = "/";
  4212.         }
  4213.         else    // windows
  4214.         {
  4215.             if (path.indexOf('\\') == -1)
  4216.             {
  4217.                 gLocalPath.value += "\\";
  4218.                 path += "\\";
  4219.             }
  4220.             thepath = path.substring(0, path.indexOf('\\') + 1);
  4221.             slash = "\\";
  4222.         }
  4223.         localdircache.push( {path: thepath, open: true, empty: false, children: -1} );
  4224.         changeLocalView();
  4225.     }
  4226.  
  4227.     if (slash == "/")
  4228.         gLocalPath.value = gLocalPath.value.replace(/\x5c/g, "/");
  4229.     else
  4230.         gLocalPath.value = gLocalPath.value.replace(/\x2f/g, "\\");
  4231.     if (gLocalPath.value != '/' && gLocalPath.value.charAt(gLocalPath.value.length - 1) == slash)
  4232.         gLocalPath.value = gLocalPath.value.substring(0,gLocalPath.value.length - 1);
  4233.     if (slash == "\\" && gLocalPath.value.indexOf('\\') == -1)
  4234.         gLocalPath.value += "\\";
  4235.     if (slash == "/" && gLocalPath.value.charAt(0) != '/')
  4236.         gLocalPath.value = '/' + gLocalPath.value;
  4237.     var newDir = gLocalPath.value;
  4238.     gLocalDirTree.focus();
  4239.     var found = false;
  4240.  
  4241.     for (var x = 0; x < localdircache.length; ++x)
  4242.         if (localdircache[x].path == newDir)
  4243.         {
  4244.             localDirChangeHelper(x);
  4245.             found = true;
  4246.             localPathFocus = newDir;
  4247.             return;
  4248.         }
  4249.     
  4250.     var previousParent = -1;
  4251.     var findParent = -1;
  4252.     while (true)
  4253.     {
  4254.         for (var x = localdircache.length - 1; x > -1; --x)
  4255.             if (newDir.indexOf(localdircache[x].path) == 0)
  4256.             {
  4257.                 findParent = x;
  4258.                 localDirChangeHelper(x);
  4259.                 break;
  4260.             }
  4261.         for (var x = 0; x < localdircache.length; ++x)
  4262.             if (localdircache[x].path == newDir)
  4263.             {
  4264.                 localPathFocus = newDir;
  4265.                 localDirChangeHelper(x);
  4266.                 return;
  4267.             }
  4268.         if (findParent == -1 || previousParent == findParent)
  4269.         {
  4270.             gLocalPath.value = localPathFocus;
  4271.             for (var x = 0; x < localdircache.length; ++x)
  4272.                 if (localdircache[x].path == localPathFocus)
  4273.                 {
  4274.                     localDirChangeHelper(x);
  4275.                     break;
  4276.                 }
  4277.             error(strbundle.getString("localDirNotFound"));
  4278.             return;
  4279.         }
  4280.         previousParent = findParent;
  4281.     }
  4282. }
  4283.  
  4284. // *************************************************************************************************
  4285. // *************************************** local mouse events **************************************
  4286. // *************************************************************************************************
  4287.  
  4288. function onLocalDblClick(event)
  4289. {
  4290.     if (event.button != 0)
  4291.         return;
  4292.     var target = event.originalTarget;
  4293.     if (target.localName != "treechildren")
  4294.         return;
  4295.     
  4296.     var fileList = gLocalTree.view.selection;
  4297.     if (fileList.count == 0)
  4298.         return;
  4299.  
  4300.     if (gLocalTree.view.getCellText(fileList.currentIndex, "localsize") == "")
  4301.     {
  4302.         var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  4303.         if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  4304.             thepath = gLocalPath.value + slash + thepath;
  4305.         else
  4306.             thepath = gLocalPath.value + thepath;
  4307.         
  4308.         gLocalPath.value = thepath;
  4309.         onLocalPathChange();
  4310.     }
  4311.     else
  4312.         stor();
  4313. }
  4314. function onLocalClick(event)
  4315. {
  4316.     if (event.button == 1 && !document.getElementById('localPasteContext').disabled)
  4317.         localPaste();
  4318. }
  4319. function onLocalMouseOver(event)
  4320. {
  4321.     if (gLocalTree.view.rowCount)
  4322.         document.getElementById('statustxt').label =
  4323.         strbundle.getString("localListing") + " " + gLocalTree.view.rowCount + " " + strbundle.getString("objects") + " "
  4324.             + commas(localsize) + ", " + strbundle.getString("diskSpace") + " " + localAvailableDiskSpace;
  4325.     else
  4326.         document.getElementById('statustxt').label = strbundle.getString("localListingNoObjects");
  4327. }
  4328.  
  4329. // *************************************************************************************************
  4330. // *************************************** local key commands **************************************
  4331. // *************************************************************************************************
  4332.  
  4333. function onLocalKeyPress(event)
  4334. {
  4335.     if (event.keyCode == 13)
  4336.     {
  4337.         var fileList = gLocalTree.view.selection;
  4338.         if (fileList.count == 0)
  4339.             return;
  4340.         if (fileList.count == 1 && gLocalTree.view.getCellText(fileList.currentIndex, "localsize") == "")
  4341.         {
  4342.             var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  4343.             if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  4344.                 thepath = gLocalPath.value + slash + thepath;
  4345.             else
  4346.                 thepath = gLocalPath.value + thepath;
  4347.             gLocalPath.value = thepath;
  4348.             onLocalPathChange();
  4349.             gLocalTree.focus();
  4350.         }
  4351.         else
  4352.             stor();
  4353.     }
  4354.     else if (event.ctrlKey && (event.which == 65 || event.which == 97)) // ctrl-a: select all
  4355.     {
  4356.         event.preventDefault();
  4357.         gLocalTree.view.selection.selectAll();
  4358.     }
  4359.     else if (event.ctrlKey && event.which == 32)    // ctrl-space, select or deselect
  4360.         gLocalTree.view.selection.toggleSelect(gLocalTree.view.selection.currentIndex);
  4361.     else if (event.keyCode == 8)    // backspace
  4362.         onLocalCDUP();
  4363.     else if (event.keyCode == 116) // F5
  4364.     {
  4365.         event.preventDefault();
  4366.         refreshLocalView();
  4367.     }
  4368.     else if (event.keyCode == 113) // F2
  4369.         onLocalRenameContext();
  4370.     else if (event.charCode == 109 && event.ctrlKey)    // ctrl-m
  4371.     {
  4372.         event.preventDefault();
  4373.         makeLocalDirectory();
  4374.     }
  4375.     else if (event.keyCode == 46)    // del
  4376.         onLocalDeleteContext();
  4377.     else if (event.keyCode == 93) // context menu
  4378.     {
  4379.         var fileList = gLocalTree.view.selection;
  4380.         if (fileList.count == 0)
  4381.             return;
  4382.         var index = fileList.currentIndex;
  4383.         var x = {};
  4384.         var y = {};
  4385.         var width = {};
  4386.         var height = {};
  4387.         var childrenBoxX = document.getElementById('localtreechildren').boxObject.x;
  4388.         var childrenBoxY = document.getElementById('localtreechildren').boxObject.y;
  4389.         
  4390.         gLocalTree.treeBoxObject.getCoordsForCellItem(index, "localname", "text", x, y, width, height);
  4391.         document.getElementById('localmenu').showPopup(document.getElementById('localtreechildren'), childrenBoxX + 75, childrenBoxY + y.value + 30, "context");
  4392.     }
  4393.     else if (event.charCode == 112 && event.ctrlKey)    // ctrl-p
  4394.     {
  4395.         event.preventDefault();
  4396.         getLocalProperties();
  4397.     }
  4398.     else if (event.charCode == 120 && event.ctrlKey)    // ctrl-x
  4399.     {
  4400.         event.preventDefault();
  4401.         localCut();
  4402.     }
  4403.     else if (event.charCode == 99 && event.ctrlKey)    // ctrl-c
  4404.     {
  4405.         event.preventDefault();
  4406.         localCopy();
  4407.     }
  4408.     else if (event.charCode == 118 && event.ctrlKey)    // ctrl-v
  4409.     {
  4410.         event.preventDefault();
  4411.         localPaste();
  4412.     }
  4413. }
  4414.  
  4415. // *************************************************************************************************
  4416. // ******************************** highlighting multiple rows *************************************
  4417. // *************************************************************************************************
  4418.  
  4419. var localMouseDownRow;
  4420. var localMouseDownOnName;
  4421. var localMouseDownPressed;
  4422. var localMouseDirection;
  4423. var localMousePreviousY;
  4424.  
  4425. function onLocalMouseDown(event)
  4426. {
  4427.     if (event.target.getAttribute('id') != 'localtreechildren')
  4428.         return;
  4429.     localMouseDownPressed = true;
  4430.     var row = {};
  4431.     var col = {};
  4432.     var child = {};
  4433.     var x = {};
  4434.     var y = {};
  4435.     var width = {};
  4436.     var height = {};
  4437.     var childrenBoxX = document.getElementById('localtreechildren').boxObject.x;
  4438.     var childrenBoxY = document.getElementById('localtreechildren').boxObject.y;
  4439.     var thecell = gLocalTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  4440.     gLocalTree.treeBoxObject.getCoordsForCellItem (row.value, "localname", "text", x, y, width, height);
  4441.     if (row.value == -1)
  4442.     {
  4443.         gLocalTree.view.selection.clearSelection();
  4444.         row.value = gLocalTree.view.rowCount - 1;
  4445.     }
  4446.     localMouseDownRow = row.value;
  4447.     localMouseDownOnName = event.pageX - childrenBoxX < x.value + width.value ? true : false;
  4448.     if (localMouseDownOnName)
  4449.         dragSessionEnabled = true;
  4450.     else
  4451.         dragSessionEnabled = false;
  4452. }
  4453. function extendLocalSelectionUpwards()
  4454. {
  4455.     if (localMouseDirection || !localMouseDownPressed)
  4456.         return;
  4457.     if (gLocalTree.treeBoxObject.getFirstVisibleRow() == 0)
  4458.     {
  4459.         gLocalTree.view.selection.rangedSelect(localMouseDownRow, gLocalTree.treeBoxObject.getFirstVisibleRow(), false);
  4460.         return;
  4461.     }
  4462.     gLocalTree.treeBoxObject.ensureRowIsVisible(gLocalTree.treeBoxObject.getFirstVisibleRow() - 1);
  4463.     gLocalTree.view.selection.rangedSelect(localMouseDownRow, gLocalTree.treeBoxObject.getFirstVisibleRow(), false);
  4464.     if (localMouseDownPressed)
  4465.         setTimeout("extendLocalSelectionUpwards()", 100);
  4466. }
  4467. function extendLocalSelectionDownwards()
  4468. {
  4469.     if (!localMouseDirection || !localMouseDownPressed)
  4470.         return;
  4471.     if (gLocalTree.view.rowCount - 1 < gLocalTree.treeBoxObject.getLastVisibleRow())
  4472.     {
  4473.         gLocalTree.view.selection.rangedSelect(localMouseDownRow, gLocalTree.treeBoxObject.getLastVisibleRow(), false);
  4474.         return;
  4475.     }
  4476.     gLocalTree.treeBoxObject.ensureRowIsVisible(gLocalTree.treeBoxObject.getLastVisibleRow() + 1);
  4477.     gLocalTree.view.selection.rangedSelect(localMouseDownRow, gLocalTree.treeBoxObject.getLastVisibleRow(), false);
  4478.     if (localMouseDownPressed)
  4479.         setTimeout("extendLocalSelectionDownwards()", 100);
  4480. }
  4481. function onLocalMouseMove(event)    // does pageX or clientX make a difference?
  4482. {
  4483.     if (localMouseDownPressed && !event.ctrlKey && !dragSessionEnabled)
  4484.     {
  4485.         var row = {};
  4486.         var col = {};
  4487.         var child = {};
  4488.         if (localMousePreviousY)
  4489.             localMouseDirection = event.pageY - localMousePreviousY > 0 ? true : false;
  4490.         localMousePreviousY = event.pageY;
  4491.         if (event.pageY < document.getElementById('localtreechildren').boxObject.y)
  4492.         {
  4493.             extendLocalSelectionUpwards();
  4494.             return;
  4495.         }
  4496.         else if (event.pageY > document.getElementById('localtreechildren').boxObject.y + document.getElementById('localtreechildren').boxObject.height)
  4497.         {
  4498.             extendLocalSelectionDownwards();
  4499.             return;
  4500.         }
  4501.         gLocalTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  4502.         if (row.value == -1)
  4503.             return;
  4504.         gLocalTree.view.selection.rangedSelect(localMouseDownRow, row.value, false);
  4505.     }
  4506. }
  4507. function onLocalMouseUp(event)
  4508. {
  4509.     localMouseDownPressed = false;
  4510. }
  4511.  
  4512. // *************************************************************************************************
  4513. // ***************************************** local tree views **************************************
  4514. // *************************************************************************************************
  4515.  
  4516. function changeLocalView(column, direction)
  4517. {
  4518.     var dir;
  4519.     var treeitem = new Array;
  4520.     
  4521.     try {
  4522.         dir = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  4523.         dir.initWithPath(gLocalPath.value);
  4524.         var entries = dir.directoryEntries;
  4525.         while (entries.hasMoreElements())
  4526.         {
  4527.             var file = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
  4528.             if (file.exists() && (!file.isHidden() || hiddenmode))
  4529.                 treeitem.push(file);
  4530.         }
  4531.     } catch (ex) {
  4532.         error(strbundle.getString("noPermission"));
  4533.         debug(ex,31);
  4534.     }
  4535.  
  4536.     
  4537.     // sort directories to the top
  4538.     var directories = new Array;
  4539.     var files = new Array;
  4540.     for (var x = 0; x < treeitem.length; ++x)
  4541.     {
  4542.         if (treeitem[x].isDirectory())
  4543.             directories.push(treeitem[x]);
  4544.         else
  4545.             files.push(treeitem[x]);
  4546.     }
  4547.     treeitem = directories.concat(files);
  4548.  
  4549.     // begin column sorting
  4550.     if (!column)
  4551.     {
  4552.         if (document.getElementById('localname').getAttribute("sortDirection") &&
  4553.                 document.getElementById('localname').getAttribute("sortDirection") != "natural")
  4554.         {
  4555.             column = "localname";
  4556.             direction = document.getElementById('localname').getAttribute("sortDirection");
  4557.         }
  4558.         if (document.getElementById('localsize').getAttribute("sortDirection") && 
  4559.                 document.getElementById('localsize').getAttribute("sortDirection") != "natural")
  4560.         {
  4561.             column = "localsize";
  4562.             direction = document.getElementById('localsize').getAttribute("sortDirection");
  4563.         }
  4564.         if (document.getElementById('localdate').getAttribute("sortDirection") &&
  4565.                 document.getElementById('localdate').getAttribute("sortDirection") != "natural")
  4566.         {
  4567.             column = "localdate";
  4568.             direction = document.getElementById('localdate').getAttribute("sortDirection");
  4569.         }
  4570.         if (document.getElementById('localattr').getAttribute("sortDirection") &&
  4571.                 document.getElementById('localattr').getAttribute("sortDirection") != "natural")
  4572.         {
  4573.             column = "localattr";
  4574.             direction = document.getElementById('localattr').getAttribute("sortDirection");
  4575.         }
  4576.     }
  4577.  
  4578.     if (column == "localname")
  4579.         treeitem.sort(compareLocalName);
  4580.     if (column == "localsize")
  4581.         treeitem.sort(compareLocalSize);
  4582.     if (column == "localdate")
  4583.         treeitem.sort(compareLocalDate);
  4584.     if (column == "localattr")
  4585.         treeitem.sort(compareLocalAttr);
  4586.     if (direction == "ascending")
  4587.         treeitem.reverse();
  4588.     // end column sorting
  4589.  
  4590.     // get directory size
  4591.     localsize = 0;
  4592.     localAvailableDiskSpace = dir.diskSpaceAvailable;
  4593.     for (var x = 0; x < treeitem.length; ++x)
  4594.         localsize += treeitem[x].fileSize;
  4595.  
  4596.     if (localsize > 1024 * 1024 * 1024)
  4597.         localsize = parseInt(localsize / 1024 / 1024 / 1024).toFixed(1) + " GB";
  4598.     else if (localsize > 1024 * 1024)
  4599.         localsize = parseFloat(localsize / 1024 / 1024).toFixed(1) + " MB";
  4600.     else if (localsize > 1024)
  4601.         localsize = parseFloat(localsize / 1024).toFixed(1) + " KB";
  4602.     else
  4603.         localsize = localsize + " Bytes";
  4604.     
  4605.     if (localAvailableDiskSpace > 1024 * 1024 * 1024)
  4606.         localAvailableDiskSpace = parseFloat(localAvailableDiskSpace / 1024 / 1024 / 1024).toFixed(1) + " GB";
  4607.     else if (localAvailableDiskSpace > 1024 * 1024)
  4608.         localAvailableDiskSpace = parseFloat(localAvailableDiskSpace / 1024 / 1024).toFixed(1) + " MB";
  4609.     else if (localAvailableDiskSpace > 1024)
  4610.         localAvailableDiskSpace = parseFloat(localAvailableDiskSpace / 1024).toFixed(1) + " KB";
  4611.     else
  4612.         localAvailableDiskSpace = localAvailableDiskSpace + " Bytes";
  4613.  
  4614.     var treeView = {
  4615.     rowCount : treeitem.length,
  4616.     getCellText : function(row,column){
  4617.         if (row == -1)
  4618.             return " ";
  4619.         switch(column)
  4620.         {
  4621.             case "localname":
  4622.                 return treeitem[row].leafName;
  4623.             case "localsize":
  4624.                 if (treeitem[row].isDirectory())
  4625.                     return "";
  4626.                 if (treeitem[row].fileSize == 0)
  4627.                     return "0 KB  ";
  4628.                 return commas(parseInt(treeitem[row].fileSize / 1024) + 1) + " KB  ";
  4629.             case "localdate":
  4630.                 var date = new Date(treeitem[row].lastModifiedTime);
  4631.                 var monthsString = strbundle.getString("months");
  4632.                 var months = monthsString.split("|");
  4633.                 var currentDate = new Date();
  4634.                 if (currentDate.getYear() < date.getYear())
  4635.                     return months[date.getMonth()] + ' ' + date.getDate() + ' ' + date.getYear();
  4636.                 else
  4637.                 {
  4638.                     var time =  date.toLocaleTimeString();
  4639.                     return months[date.getMonth()] + ' ' + date.getDate() + ' ' + time.substring(0, time.lastIndexOf(':'));
  4640.                 }
  4641.             case "localattr":
  4642.                 if (treeitem[row].permissions)
  4643.                     return treeitem[row].permissions;
  4644.                 return "";
  4645.             default:
  4646.                 return " "; // shouldn't get here
  4647.         }
  4648.     },
  4649.     setTree: function(treebox){ this.treebox = treebox; },
  4650.     isContainer: function(row){ return false; },
  4651.     isSeparator: function(row){ return false; },
  4652.     isSorted: function(row){ return false; },
  4653.     getLevel: function(row){ return 0; },
  4654.     getImageSrc: function(row,col)
  4655.     {
  4656.         if (row == -1)
  4657.             return "";
  4658.         if (col == "localname")
  4659.         {
  4660.             if (treeitem[row].isDirectory())
  4661.                 return "res/directory.ico";      // kind of a hack - is there a way to get the folder icon from "moz-icon://"?
  4662.             return "moz-icon://" + this.getCellText(row, col) + "?size=16";
  4663.         }
  4664.     },
  4665.     getRowProperties: function(row,props){},
  4666.     getCellProperties: function(row,col,props){},
  4667.     getColumnProperties: function(colid,col,props){},
  4668.     cycleHeader: function(col, elem){ }
  4669.     };
  4670.  
  4671.     localDirManager(gLocalPath.value, directories);
  4672.  
  4673.     var dirTreeView = {
  4674.     rowCount : localdircache.length,
  4675.     getCellText : function(row,column){
  4676.         if (row == -1)
  4677.             return "";
  4678.         if (row == 0)
  4679.             return localdircache[row].path;
  4680.         return localdircache[row].path.substring(localdircache[row].path.lastIndexOf(slash) + 1);
  4681.     },
  4682.     getParentIndex: function(row){
  4683.         if (row == -1)
  4684.             return -1;
  4685.         if (row == 0)
  4686.             return -1;
  4687.         var parent = localdircache[row].path.substring(0, localdircache[row].path.lastIndexOf(slash));
  4688.         if (parent.length == 0)
  4689.             return 0;
  4690.         for (var x = 0; x < localdircache.length; ++x)
  4691.             if (localdircache[x].path == parent)
  4692.                 return x;
  4693.     },
  4694.     isContainerOpen: function(row){ return localdircache[row].open; },
  4695.      isContainerEmpty:function(row){ return localdircache[row].empty; },
  4696.      hasNextSibling: function(row,nextrow) { return localdircache[row].hasnext; },
  4697.     getLevel:function(row) {
  4698.         if (row == -1)
  4699.             return 0;
  4700.         if (row < 0 || row >= localdircache.length)
  4701.              return 0;
  4702.         if (slash == "/")
  4703.             return row ? localdircache[row].path.match(/\x2f/g).length : 0;
  4704.         else
  4705.             return row ? localdircache[row].path.match(/\x5c/g).length : 0;
  4706.     },
  4707.     toggleOpenState:function(row){
  4708.         if (this.isContainerOpen(row))
  4709.         {
  4710.             var count = 1;
  4711.             while ((count + row) < localdircache.length && this.getLevel(count + row) > this.getLevel(row))
  4712.                 ++count;
  4713.             --count;
  4714.             var temp = localdircache.splice(row + 1, count);
  4715.             if (temp.path)
  4716.                 temp = new Array(temp); // messy, messy, messy
  4717.             if (localdircache.path)
  4718.                 localdircache = new Array(localdircache); // messy, messy, messy
  4719.             if (hiddenlocaldircache.path)
  4720.                 hiddenlocaldircache = new Array(hiddenlocaldircache); // messy, messy, messy
  4721.             hiddenlocaldircache = temp.concat(hiddenlocaldircache);
  4722.             if (hiddenlocaldircache.path)
  4723.                 hiddenlocaldircache = new Array(hiddenlocaldircache); // messy, messy, messy
  4724.             gLocalDirTree.treeBoxObject.rowCountChanged(row, -count);
  4725.             localdircache[row].open = false;
  4726.             localdircache[row].children = temp.length;
  4727.             if (gLocalPath.value.indexOf(localdircache[row].path) != -1
  4728.                     && gLocalPath.value != localdircache[row].path)
  4729.             {
  4730.                 gLocalPath.value = localdircache[row].path;
  4731.                 changeLocalView();
  4732.                 gLocalDirTree.view.selection.select(row);
  4733.                 gLocalDirTree.treeBoxObject.ensureRowIsVisible(row);
  4734.             }
  4735.             if (gLocalPath.value == localdircache[row].path)
  4736.             {
  4737.                 gLocalDirTree.view.selection.select(row);
  4738.                 gLocalDirTree.treeBoxObject.ensureRowIsVisible(row);
  4739.             }
  4740.         }
  4741.         else
  4742.         {
  4743.             var found = -1;
  4744.             var number = localdircache[row].children;
  4745.             for (var x = 0; x < hiddenlocaldircache.length; ++x)
  4746.             {
  4747.                 if (hiddenlocaldircache[x].path.indexOf(localdircache[row].path) != -1)
  4748.                 {
  4749.                     found = x;
  4750.                     break;
  4751.                 }
  4752.             }
  4753.             if (found == -1)
  4754.             {
  4755.                 var temp = gLocalPath.value;
  4756.                 if (!refreshLocal)
  4757.                 {
  4758.                     gLocalPath.value = localdircache[row].path;
  4759.                     changeLocalView();
  4760.                     gLocalPath.value = temp;
  4761.                     changeLocalView();
  4762.                 }
  4763.                 if (!localdircache[row].empty)
  4764.                     localdircache[row].open = true;
  4765.                 localdircache[row].children = -1;
  4766.                 for (var x = 0; x < localdircache.length; ++x)
  4767.                     if (localdircache[x].path == temp)
  4768.                     {
  4769.                         gLocalDirTree.view.selection.select(x); // when moving with keys
  4770.                         gLocalDirTree.treeBoxObject.ensureRowIsVisible(x + (localLastRowSeen - x - 1));
  4771.                         localLastRowSeen = 0;
  4772.                         break;
  4773.                     }
  4774.                 return;
  4775.             }
  4776.             var toBeAdded = hiddenlocaldircache.splice(found, number);
  4777.             var piece = localdircache.splice(0, row + 1);
  4778.             if (toBeAdded.path)
  4779.                 toBeAdded = new Array(toBeAdded);    // messy, messy, messy
  4780.             if (piece.path)
  4781.                 piece = new Array(piece); // messy, messy, messy
  4782.             if (localdircache.path)
  4783.                 localdircache = new Array(localdircache); // messy, messy, messy
  4784.             if (hiddenlocaldircache.path)
  4785.                 hiddenlocaldircache = new Array(hiddenlocaldircache); // messy, messy, messy
  4786.             localdircache = piece.concat(toBeAdded, localdircache);
  4787.             if (localdircache.path)
  4788.                 localdircache = new Array(localdircache); // ditto, ditto, ditto
  4789.             gLocalDirTree.treeBoxObject.rowCountChanged(row, toBeAdded.length);
  4790.             if (!localdircache[row].empty)
  4791.                 localdircache[row].open = true;
  4792.             localdircache[row].children = -1;
  4793.             if (gLocalPath.value == localdircache[row].path)
  4794.             {
  4795.                 gLocalDirTree.view.selection.select(row);
  4796.                 gLocalDirTree.treeBoxObject.ensureRowIsVisible(row + (localLastRowSeen - row - 1));
  4797.                 localLastRowSeen = 0;
  4798.             }
  4799.         }
  4800.     },
  4801.     isSeparator: function(row){ return false; },
  4802.     isSorted: function(row){ return false; },
  4803.       isContainer: function(row){ return true; },
  4804.     getImageSrc: function(row,col){
  4805.         if (this.isContainerOpen(row))
  4806.             return "res/directoryopen.ico";    // hack - folder icon from "moz-icon://"?
  4807.         else
  4808.             return "res/directory.ico";
  4809.     },
  4810.     setTree: function(treebox){ this.treebox = treebox; },
  4811.     getRowProperties: function(row,props){},
  4812.     getCellProperties: function(row,col,props){},
  4813.     getColumnProperties: function(colid,col,props){},
  4814.     };
  4815.  
  4816.     gLocalTree.view = treeView;
  4817.     gLocalDirTree.view = dirTreeView;
  4818.  
  4819.     // on startup, select root on localdirtree
  4820.     if (gLocalDirTree.view.selection.count == 0)
  4821.         gLocalDirTree.view.selection.select(0);
  4822. }
  4823. function localDirManager(path, data)
  4824. {
  4825.     for (var x = 0; x < localdircache.length; ++x)
  4826.     {
  4827.         if (localdircache[x].path == path)
  4828.         {
  4829.             if (data.length == 0)
  4830.             {
  4831.                 localdircache[x].empty = true;
  4832.                 localdircache[x].open = false;
  4833.             }
  4834.             else
  4835.                 localdircache[x].empty = false;
  4836.             break;
  4837.         }
  4838.     }
  4839.     for (var x = 0; x < data.length; ++x)
  4840.         data[x] = {path: data[x].path, open: false, empty: false, children: -1, hasnext: true };
  4841.     if (data.path)
  4842.         data = new Array(data); // messy, messy, messy
  4843.     data.sort(directorySort);
  4844.     if (data.length)
  4845.         data[data.length - 1].hasnext = false;
  4846.     localdircache = localdircache.concat(data);    
  4847.     if (localdircache.path)
  4848.         localdircache = new Array(localdircache); // messy, messy, messy
  4849.     
  4850.     if (slash == "\\")    //XXX hack - damn you ms-dos: '\\' is greater than capital letters, so convert to '/'
  4851.     {
  4852.         for (var x = 0; x < localdircache.length; ++x)
  4853.             localdircache[x].path = localdircache[x].path.replace(/\x5c/g, "/");
  4854.     }
  4855.     localdircache.sort(directorySort);
  4856.     if (slash == "\\")    //XXX hack part deux- revert back to '\\'
  4857.     {
  4858.         for (var x = 0; x < localdircache.length; ++x)
  4859.             localdircache[x].path = localdircache[x].path.replace(/\x2f/g, "\\");
  4860.     }
  4861.     
  4862.     // check for duplicates
  4863.     for (var x = 0; x < localdircache.length - 1; ++x)
  4864.     {
  4865.         if (localdircache[x].path == localdircache[x + 1].path)
  4866.         {
  4867.             if (localdircache[x].open)
  4868.                 localdircache[x + 1].open = true;
  4869.             if (localdircache[x].empty)
  4870.                 localdircache[x + 1].empty = true;
  4871.             if (localdircache[x].children != -1)
  4872.                 localdircache[x + 1].children = localdircache[x].children;
  4873.             if (localdircache[x].hasnext)
  4874.                 localdircache[x + 1].hasnext = true;
  4875.             localdircache.splice(x, 1);
  4876.             if (localdircache.path)
  4877.                 localdircache = new Array(localdircache); // messy, messy, messy
  4878.         }
  4879.     }
  4880.     var didSplice = false;
  4881.     for (var x = 0; x < localdircache.length; ++x)
  4882.     {
  4883.         for (var y = 0; y < hiddenlocaldircache.length; ++y)
  4884.         {
  4885.             if (x >= localdircache.length) // splicing might cause invalid indices
  4886.                 break;
  4887.             if (localdircache[x].path == hiddenlocaldircache[y].path)
  4888.             {
  4889.                 localdircache.splice(x, 1);
  4890.                 didSplice = true;
  4891.                 if (localdircache.path)
  4892.                     localdircache = new Array(localdircache); // messy, messy, messy
  4893.             }
  4894.         }
  4895.         if (didSplice)    // gotta start from 0 again b/c array will have shifted around, better safe...
  4896.         {
  4897.             x = -1;    // will get incremented up to 0
  4898.             didSplice = false;
  4899.         }
  4900.     }
  4901. }
  4902. // *************************************************************************************************
  4903. // *************************************** refresh local view **************************************
  4904. // *************************************************************************************************
  4905.  
  4906. function refreshLocalView()
  4907. {
  4908.     refreshLocal = true;
  4909.     // get rid of old directory entries
  4910.     for (var x = 0; x < localdircache.length; ++x)
  4911.         if (localdircache[x].path.indexOf(gLocalPath.value) != -1
  4912.             && gLocalPath.value != localdircache[x].path)
  4913.         {
  4914.             localdircache.splice(x, 1);
  4915.             if (localdircache.path)
  4916.                     localdircache = new Array(localdircache); // messy, messy, messy
  4917.             x = -1; // start from the top, to be safe
  4918.         }
  4919.     for (var x = 0; x < hiddenlocaldircache.length; ++x)
  4920.         if (hiddenlocaldircache[x].path.indexOf(gLocalPath.value) != -1
  4921.             && gLocalPath.value != hiddenlocaldircache[x].path)
  4922.         {
  4923.             hiddenlocaldircache.splice(x, 1);
  4924.             if (hiddenlocaldircache.path)
  4925.                     hiddenlocaldircache = new Array(hiddenlocaldircache); // messy, messy, messy
  4926.             x = -1; // start from the top, to be safe
  4927.         }
  4928.     for (var x = 0; x < localdircache.length; ++x)
  4929.         if (localdircache[x].path == gLocalPath.value)
  4930.         {
  4931.             localDirChangeHelper(x);
  4932.             break;
  4933.         }
  4934.     refreshLocal = false;
  4935. }
  4936.  
  4937. // *************************************************************************************************
  4938. // *************************************** browse local folders ************************************
  4939. // *************************************************************************************************
  4940.  
  4941. function browseLocal()
  4942. {
  4943.     var nsIFilePicker = Components.interfaces.nsIFilePicker;
  4944.     var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  4945.     fp.init(window, strbundle.getString("selectFiles"), nsIFilePicker.modeGetFolder);
  4946.     var res = fp.show();
  4947.     if (res == nsIFilePicker.returnOK)
  4948.     {
  4949.         var thedir = fp.file;
  4950.         gLocalPath.value = thedir.path;
  4951.         onLocalPathChange();
  4952.     }
  4953. }
  4954.  
  4955. // *************************************************************************************************
  4956. // *************************************** rename local file ***************************************
  4957. // *************************************************************************************************
  4958.  
  4959. function renameLocalFile(path, openRenamedDirectory)
  4960. {
  4961.     var file = getLocalFileSpec(path);
  4962.     var newName = window.prompt(strbundle.getString("renameTo"), file.leafName, strbundle.getString("rename"));
  4963.     if (!newName)
  4964.         return;
  4965.  
  4966.     if (file.exists())
  4967.     {
  4968.         try {
  4969.             file.rename(newName);
  4970.         } catch (ex) {
  4971.             error(strbundle.getString("renameFail"));
  4972.             debug(ex,32);
  4973.             return;
  4974.         }
  4975.     }
  4976.     if (openRenamedDirectory)
  4977.         onLocalCDUP();
  4978.     refreshLocalView();
  4979.     if (openRenamedDirectory)
  4980.     {
  4981.         gLocalPath.value = file.unicodePath;
  4982.         onLocalPathChange();
  4983.     }
  4984.     else
  4985.     {
  4986.         gLocalTree.focus();
  4987.         for (var x = 0; x < gLocalTree.view.rowCount; ++x)
  4988.             if (newName == gLocalTree.view.getCellText(x, "localname"))
  4989.             {
  4990.                 gLocalTree.view.selection.select(x);
  4991.                 break;
  4992.             }
  4993.     }
  4994. }
  4995.  
  4996. // *************************************************************************************************
  4997. // *************************************** make local directory ************************************
  4998. // *************************************************************************************************
  4999.  
  5000. function makeLocalDirectory()
  5001. {
  5002.     var newName = window.prompt(strbundle.getString("directoryName"), "", strbundle.getString("newDirectory"));
  5003.     if (!newName)
  5004.         return;
  5005.     if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  5006.         newName = gLocalPath.value + slash + newName;
  5007.     else
  5008.         newName = gLocalPath.value + newName;
  5009.     var file = getLocalFileSpec(newName);
  5010.     try{
  5011.         file.createDir();
  5012.     } catch (ex) {
  5013.         error(strbundle.getString("dirFail"));
  5014.         debug(ex,33);
  5015.         return;
  5016.     }
  5017.     refreshLocalView();
  5018.     gLocalPath.value = file.unicodePath;
  5019.     onLocalPathChange();
  5020.     gLocalTree.focus();
  5021. }
  5022.  
  5023. // *************************************************************************************************
  5024. // *************************************** delete local file ***************************************
  5025. // *************************************************************************************************
  5026.  
  5027. function deleteLocalFile(path, cdUP, prompt)
  5028. {
  5029.     var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  5030.     file.initWithPath(path);
  5031.     var isDirectory = file.isDirectory();
  5032.  
  5033.     if (prompt && isDirectory)
  5034.     {
  5035.         if (!window.confirm(strbundle.getString("confirmDelete") + " '" + file.leafName + "' " + strbundle.getString("confirmDelete3")))
  5036.             return;
  5037.     }
  5038.     else if (prompt)
  5039.     {
  5040.         if (!window.confirm(strbundle.getString("confirmDelete") + " '" + file.leafName + "'?"))
  5041.             return;
  5042.     }
  5043.  
  5044.     try {
  5045.         file.remove(true);
  5046.     } catch (ex) {
  5047.         error(strbundle.getString("delFail"));
  5048.         debug(ex,34);
  5049.         return;
  5050.     }
  5051.     
  5052.     if (prompt)
  5053.     {
  5054.         if (cdUP)
  5055.             onLocalCDUP();
  5056.         refreshLocalView();
  5057.     }
  5058. }
  5059.  
  5060. // *************************************************************************************************
  5061. // *************************************** local properties ****************************************
  5062. // *************************************************************************************************
  5063.  
  5064. function getLocalProperties()
  5065. {
  5066.     var fileList = gLocalTree.view.selection;
  5067.     if (fileList.count == 0)
  5068.         return;
  5069.  
  5070.     var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  5071.     if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  5072.         thepath = gLocalPath.value + slash + thepath;
  5073.     else
  5074.         thepath = gLocalPath.value + thepath;
  5075.  
  5076.     try {
  5077.         var file = getLocalFileSpec(thepath);
  5078.         var file2 = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  5079.         file2.initWithPath(thepath);
  5080.     
  5081.         var windowHeight = document.getElementById('main-window').boxObject.height;
  5082.         var windowWidth = document.getElementById('main-window').boxObject.width;
  5083.  
  5084.         var date = new Date(file2.lastModifiedTime);
  5085.         var monthsString = strbundle.getString("months");
  5086.         var months = monthsString.split("|");
  5087.         date = months[date.getMonth()] + ' ' + date.getDate() + ' ' + date.getYear() + ' ' + date.toLocaleTimeString();
  5088.  
  5089.         // linux permissions (not supported right now): file2.permissions
  5090.         window.openDialog("chrome://fireftp/content/propertiesDialog.xul","propertiesDialog",
  5091.                 "screenY="+(windowHeight/2 - 100)+",screenX="+(windowWidth/2 - 300)+
  5092.                 ",width=600,height=200,chrome,modal=yes,dialog=yes,resizable=yes",
  5093.                 file.unicodePath, file.leafName, file.fileSize, date, 0,
  5094.                 file2.isWritable(), file.isHidden(), file.isDirectory());
  5095.     } catch (ex) { debug(ex,41); }
  5096. }
  5097.  
  5098. // *************************************************************************************************
  5099. // *************************************** local cut/copy/paste ************************************
  5100. // *************************************************************************************************
  5101.  
  5102. var localCutParentDir;
  5103. function localCut()
  5104. {
  5105.     localCopy(true);
  5106. }
  5107. function localCopy(cut)
  5108. {
  5109.     var fileList = gLocalTree.view.selection;
  5110.     if (fileList.count == 0)
  5111.         return;
  5112.  
  5113.     localPasteFiles = new Array;
  5114.     var localParent = gLocalPath.value;
  5115.  
  5116.     for (var x = 0; x < gLocalTree.view.rowCount; ++x)
  5117.         if (gLocalTree.view.selection.isSelected(x))
  5118.         {
  5119.             var localpath = gLocalTree.view.getCellText(x, "localname");
  5120.             if (localParent.charAt(localParent.length - 1) != slash)
  5121.                 localpath = localParent + slash + localpath;
  5122.             else
  5123.                 localpath = localParent + localpath;
  5124.  
  5125.             localPasteFiles.push(getLocalFileSpec(localpath));
  5126.         }
  5127.  
  5128.     if (cut)
  5129.         localIsCut = true;
  5130.     else
  5131.         localIsCut = false;
  5132.     localCutParentDir = localParent;
  5133.     document.getElementById('localPasteContext').setAttribute("disabled", false);
  5134.     document.getElementById('localDirPasteContext').setAttribute("disabled", false);
  5135. }
  5136. function localPaste(localdir)
  5137. {
  5138.     var    parentDir;
  5139.     var prompt = true;
  5140.     var skipall = false;
  5141.     
  5142.     try {
  5143.         for (var x = 0; x < localPasteFiles.length; ++x)
  5144.         {
  5145.             if (!localPasteFiles[x].theparent)
  5146.                 parentDir = localdir ? localdir : getLocalFileSpec(gLocalPath.value);
  5147.             else
  5148.             {
  5149.                 parentDir = getLocalFileSpec(localPasteFiles[x].theparent);
  5150.                 localPasteFiles[x] = localPasteFiles[x].thefile;
  5151.             }
  5152.  
  5153.             if (parentDir.unicodePath.indexOf(localPasteFiles[x].unicodePath) != -1)    // no, bad directory, bad!
  5154.                 return;
  5155.             if (parentDir.unicodePath ==                                                // aw, not on the carpet!
  5156.                     (localCutParentDir.unicodePath ? localCutParentDir.unicodePath : localCutParentDir))
  5157.                 return;
  5158.             
  5159.             var newPath = parentDir.unicodePath.charAt(parentDir.unicodePath.length - 1) == slash
  5160.                 ? parentDir.unicodePath + localPasteFiles[x].leafName
  5161.                 : parentDir.unicodePath + slash + localPasteFiles[x].leafName;
  5162.  
  5163.             var newFile = getLocalFileSpec(newPath);
  5164.             
  5165.             if (newFile.exists() && skipall)
  5166.                 continue;
  5167.             if (newFile.exists() && prompt && !newFile.isDirectory())
  5168.             {
  5169.                 var response = new Object();
  5170.                 response.value = 0;
  5171.                 var windowHeight = document.getElementById('main-window').boxObject.height;
  5172.                 var windowWidth = document.getElementById('main-window').boxObject.width;
  5173.                 var timer = destructmode;
  5174.  
  5175.                 window.openDialog("chrome://fireftp/content/confirmFileDialog.xul","confirmFileDialog",
  5176.                     "screenY="+(windowHeight/2 - 45)+",screenX="+(windowWidth/2 - 200)+
  5177.                     ",width=400,height=90,chrome,modal=yes,dialog=yes,resizable=yes", response, localPasteFiles[x].leafName, true, true, timer);
  5178.                 if (response.value == 2)
  5179.                     prompt = false;
  5180.                 else if (response.value == 3)
  5181.                     continue;
  5182.                 else if (response.value == 4 || response.value == 0)
  5183.                     return;
  5184.                 else if (response.value == 5)
  5185.                 {
  5186.                     skipall = true;
  5187.                     continue;
  5188.                 }
  5189.  
  5190.                 if ((response.value == 1 || response.value == 2) && !newFile.isDirectory())
  5191.                 {
  5192.                     // due to a lovely quirk in mozilla's api you can't use nsIFileSpec's delete() function
  5193.                     // b/c javascript thinks it's a dirty word, so hack hack hack, workaround
  5194.                     var file2 = Components.classes['@mozilla.org/file/local;1']
  5195.                         .createInstance(Components.interfaces.nsILocalFile);
  5196.                     file2.initWithPath(newFile.unicodePath);
  5197.                     file2.remove(false);
  5198.                 }
  5199.             }
  5200.             if (newFile.exists() && !prompt && !newFile.isDirectory())
  5201.             {
  5202.                 var file2 = Components.classes['@mozilla.org/file/local;1']
  5203.                         .createInstance(Components.interfaces.nsILocalFile);
  5204.                 file2.initWithPath(newFile.unicodePath);
  5205.                 file2.remove(false);
  5206.             }
  5207.  
  5208.             if (localPasteFiles[x].isDirectory())
  5209.             {
  5210.                 if (!newFile.exists())
  5211.                     newFile.createDir();
  5212.  
  5213.                 var moreFiles = new Array;
  5214.                 try {
  5215.                     dir = Components.classes['@mozilla.org/file/local;1']
  5216.                         .createInstance(Components.interfaces.nsILocalFile);
  5217.                     dir.initWithPath(localPasteFiles[x].unicodePath);
  5218.                     var entries = dir.directoryEntries;
  5219.                     while (entries.hasMoreElements())
  5220.                     {
  5221.                         var file = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
  5222.                         moreFiles.push(file);
  5223.                     }
  5224.                 } catch (ex) { debug(ex,35); }
  5225.                 if (moreFiles.length)
  5226.                 {
  5227.                     if (localIsCut)
  5228.                         localPasteFiles.splice(x + 1, 0, localPasteFiles[x]);
  5229.                     for (var y = 0; y < moreFiles.length; ++y)
  5230.                         localPasteFiles.splice(x + 1, 0, { thefile : getLocalFileSpec(moreFiles[y].path), theparent: newFile.unicodePath });
  5231.                 }
  5232.                 else if (localIsCut)
  5233.                 {
  5234.                     var file2 = Components.classes['@mozilla.org/file/local;1']
  5235.                         .createInstance(Components.interfaces.nsILocalFile);
  5236.                     file2.initWithPath(localPasteFiles[x].unicodePath);
  5237.                     file2.remove(true);
  5238.                 }
  5239.                 continue;
  5240.             }
  5241.             if (localIsCut)
  5242.                 localPasteFiles[x].moveToDir(parentDir);
  5243.             else
  5244.                 localPasteFiles[x].copyToDir(parentDir);
  5245.         }
  5246.     } catch (ex) {
  5247.         debug(ex,36);
  5248.         error(strbundle.getString("pasteError"));
  5249.         localPasteFiles = new Array;
  5250.         document.getElementById('localPasteContext').setAttribute("disabled", true);
  5251.         document.getElementById('localDirPasteContext').setAttribute("disabled", true);
  5252.     }
  5253.     
  5254.     if (localIsCut)
  5255.     {
  5256.         localPasteFiles = new Array;        
  5257.         document.getElementById('localPasteContext').setAttribute("disabled", true);
  5258.         document.getElementById('localDirPasteContext').setAttribute("disabled", true);
  5259.     }
  5260.  
  5261.     var tempPath = gLocalPath.value;
  5262.     if (localdir)
  5263.     {
  5264.         var pathvalue = localCutParentDir.unicodePath
  5265.                 ? localCutParentDir.parent.unicodePath
  5266.                 : localCutParentDir.substring(0,
  5267.                     (localCutParentDir.lastIndexOf(slash) ? localCutParentDir.lastIndexOf(slash) : 1));
  5268.         gLocalPath.value = pathvalue;
  5269.         onLocalPathChange();
  5270.         refreshLocalView();
  5271.         gLocalPath.value = localdir.unicodePath;
  5272.         onLocalPathChange();
  5273.         refreshLocalView();
  5274.         if (!localCutParentDir.unicodePath || tempPath != localCutParentDir.unicodePath)
  5275.             gLocalPath.value = tempPath;
  5276.         else
  5277.             gLocalPath.value = pathvalue;
  5278.         onLocalPathChange();
  5279.     }
  5280.     else
  5281.     {
  5282.         gLocalPath.value = localCutParentDir.unicodePath ? localCutParentDir.parent.unicodePath : localCutParentDir;
  5283.         onLocalPathChange();
  5284.         refreshLocalView();
  5285.         gLocalPath.value = tempPath;
  5286.     }
  5287.  
  5288.     refreshLocalView();
  5289. }
  5290. function localDirCut()
  5291. {
  5292.     if (gLocalDirTree.view.selection.currentIndex == 0)    // you can't cut the root
  5293.         return;
  5294.  
  5295.     localDirCopy(true);
  5296. }
  5297. function localDirCopy(cut)
  5298. {
  5299.     if (gLocalDirTree.view.selection.count == 0)
  5300.         return;
  5301.     
  5302.     localPasteFiles = new Array;
  5303.     var dir = gLocalDirTree.view.selection.currentIndex;
  5304.     var localdir = getLocalFileSpec(localdircache[dir].path);
  5305.  
  5306.     localPasteFiles.push(localdir);
  5307.  
  5308.     if (cut)
  5309.         localIsCut = true;
  5310.     else
  5311.         localIsCut = false;
  5312.     localCutParentDir = localdir;
  5313.     document.getElementById('localPasteContext').setAttribute("disabled", false);
  5314.     document.getElementById('localDirPasteContext').setAttribute("disabled", false);
  5315. }
  5316. function localDirPaste()
  5317. {
  5318.     if (gLocalDirTree.view.selection.count == 0)
  5319.         return;
  5320.  
  5321.     var dir = gLocalDirTree.view.selection.currentIndex;
  5322.     var localdir = getLocalFileSpec(localdircache[dir].path);
  5323.  
  5324.     localPaste(localdir);
  5325. }
  5326.  
  5327. // *************************************************************************************************
  5328. // *************************************** local drag functions ************************************
  5329. // *************************************************************************************************
  5330.  
  5331. function onLocalDragOver(event)
  5332. {
  5333.     var row = {};
  5334.     var col = {};
  5335.     var child = {};
  5336.     var dragSession = whataDrag.getCurrentSession();
  5337.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  5338.     
  5339.     trans.addDataFlavor("text/unicode");
  5340.     dragSession.getData(trans, 0);
  5341.     var dataObj = new Object();
  5342.     var bestFlavor = new Object();
  5343.     var len = new Object();
  5344.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  5345.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  5346.  
  5347.     gLocalTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5348.  
  5349.     if (dataObj.data == "localtreechildren")
  5350.     {
  5351.         if (gLocalTree.view.getCellText(row.value, "localsize") == "")
  5352.             dragSession.canDrop = true;
  5353.         else
  5354.             dragSession.canDrop = false;
  5355.     }
  5356.     else
  5357.         dragSession.canDrop = true;
  5358. }
  5359. function onLocalDragDrop(event)
  5360. {
  5361.     var dragSession = whataDrag.getCurrentSession();
  5362.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  5363.     
  5364.     trans.addDataFlavor("text/unicode");
  5365.     dragSession.getData(trans, 0);
  5366.     var dataObj = new Object();
  5367.     var bestFlavor = new Object();
  5368.     var len = new Object();
  5369.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  5370.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  5371.  
  5372.     if (dataObj.data == "localtreechildren")
  5373.     {
  5374.         var row = {};
  5375.         var col = {};
  5376.         var child = {};
  5377.         gLocalTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5378.         var localParent = gLocalPath.value;
  5379.         localCut();
  5380.         var localpath = gLocalTree.view.getCellText(row.value, "localname");
  5381.         if (localParent.charAt(localParent.length - 1) != slash)
  5382.             localpath = localParent + slash + localpath;
  5383.         else
  5384.             localpath = localParent + localpath;
  5385.         gLocalPath.value = localpath;
  5386.         onLocalPathChange();
  5387.         localPaste();
  5388.     }
  5389.     else if (dataObj.data == "localdirtreechildren")
  5390.     {
  5391.         var localParent = gLocalPath.value;
  5392.         localDirCut();
  5393.         localPaste();
  5394.     }
  5395.     else if (dataObj.data == "remotetreechildren")
  5396.         retr();
  5397.     else if (dataObj.data == "remotedirtreechildren")
  5398.         retrDir();
  5399.     event.preventBubble();
  5400. }
  5401. function onLocalDirDragOver(event)
  5402. {
  5403.     var row = {};
  5404.     var col = {};
  5405.     var child = {};
  5406.     var dragSession = whataDrag.getCurrentSession();
  5407.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  5408.     trans.addDataFlavor("text/unicode");
  5409.     dragSession.getData(trans, 0);
  5410.     var dataObj = new Object();
  5411.     var bestFlavor = new Object();
  5412.     var len = new Object();
  5413.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  5414.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  5415.  
  5416.     if (dataObj.data == "remotedirtreechildren" || dataObj.data == "remotetreechildren")
  5417.     {
  5418.         dragSession.canDrop = false;
  5419.         return;
  5420.     }
  5421.  
  5422.     gLocalDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5423.     
  5424.     if (gLocalDirTree.view.getCellText(row.value, "localname") != "")
  5425.         dragSession.canDrop = true;
  5426.     else
  5427.         dragSession.canDrop = false;
  5428. }
  5429. function onLocalDirDragDrop(event)
  5430. {    
  5431.     var dragSession = whataDrag.getCurrentSession();
  5432.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  5433.     
  5434.     trans.addDataFlavor("text/unicode");
  5435.     dragSession.getData(trans, 0);
  5436.     var dataObj = new Object();
  5437.     var bestFlavor = new Object();
  5438.     var len = new Object();
  5439.     trans.getAnyTransferData(bestFlavor, dataObj, len);
  5440.     dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  5441.  
  5442.     var row = {};
  5443.     var col = {};
  5444.     var child = {};
  5445.     gLocalDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5446.  
  5447.     if (dataObj.data == "localtreechildren")
  5448.     {
  5449.         gLocalDirTree.view.selection.currentIndex = row.value;
  5450.         localCut();
  5451.         localDirPaste();
  5452.     }
  5453.     else if (dataObj.data == "localdirtreechildren")
  5454.     {
  5455.         localDirCut();
  5456.         gLocalDirTree.view.selection.currentIndex = row.value;
  5457.         localDirPaste();
  5458.     }
  5459.     event.preventBubble();
  5460. }
  5461.  
  5462. // *************************************************************************************************
  5463. // *********************************************** misc ********************************************
  5464. // *************************************************************************************************
  5465.  
  5466. function copyLog()
  5467. {
  5468.     var text = '';
  5469.     for (var x = 0; x < document.getElementById('cmdlog').getRowCount(); ++x)
  5470.         text += document.getElementById('cmdlog').getItemAtIndex(x).value + '\n';
  5471.     var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
  5472.             getService(Components.interfaces.nsIClipboardHelper );
  5473.     clipboard.copyString(text);
  5474. }
  5475.  
  5476. // log button
  5477. function showLog()
  5478. {
  5479.     logmode = !logmode;
  5480.     savePreferences();
  5481.     if (logmode)
  5482.     {
  5483.         document.getElementById('cmdlog').collapsed = false;
  5484.         document.getElementById('logsplitter').state = '';
  5485.         document.getElementById('logbutton').checked = false;
  5486.     }
  5487.     else
  5488.     {
  5489.         document.getElementById('cmdlog').collapsed = true;
  5490.         document.getElementById('logsplitter').state = 'collapsed';
  5491.         document.getElementById('logbutton').checked = true;
  5492.     }
  5493.     if (logmode)
  5494.         document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  5495. }
  5496.  
  5497. // preferences button
  5498. function showPreferences()
  5499. {
  5500.     var windowHeight = document.getElementById('main-window').boxObject.height;
  5501.     var windowWidth = document.getElementById('main-window').boxObject.width;
  5502.  
  5503.     var hiddenCheckboxChange = new Object();
  5504.     hiddenCheckboxChange.value = 0;
  5505.  
  5506.     window.openDialog("chrome://fireftp/content/preferencesDialog.xul","preferencesDialog",
  5507.             "screenY="+(windowHeight/2 - 175)+",screenX="+(windowWidth/2 - 200)+
  5508.             ",width=400,height=350,chrome,modal=yes,dialog=yes,resizable=yes", hiddenCheckboxChange);
  5509.     readPreferences();
  5510.     if (!hiddenCheckboxChange.value)
  5511.         return;
  5512.     if (!hiddenmode)
  5513.     {
  5514.         var fileSpec = getLocalFileSpec(gLocalPath.value);
  5515.         var hiddenFound = false;
  5516.         while (true)
  5517.         {
  5518.             if (fileSpec.isHidden() && fileSpec.unicodePath != localdircache[0].path)
  5519.             {
  5520.                 hiddenFound = true;
  5521.                 break;
  5522.             }
  5523.             if (fileSpec.unicodePath == fileSpec.parent.unicodePath)
  5524.                 break;
  5525.             fileSpec = fileSpec.parent;
  5526.         }
  5527.         if (hiddenFound)
  5528.             gLocalPath.value = localdircache[0].path;
  5529.     }
  5530.     localdircache = new Array;
  5531.     onLocalPathChange();
  5532. }
  5533.  
  5534. // change TYPE A/TYPE I/auto from statusbar
  5535. function onChangeType()
  5536. {
  5537.     var transferTypes = new Array("Auto", "Binary", "ASCII");
  5538.     filemode = (filemode + 1) % 3;
  5539.     document.getElementById('statustype').label = transferTypes[filemode];
  5540.     savePreferences();
  5541. }
  5542.  
  5543. // open a local file
  5544. function openFile()
  5545. {
  5546.     var fileList = gLocalTree.view.selection;
  5547.     if (fileList.count == 0)
  5548.         return;
  5549.     var thepath = gLocalTree.view.getCellText(fileList.currentIndex, "localname");
  5550.     if (gLocalPath.value.charAt(gLocalPath.value.length - 1) != slash)
  5551.         thepath = gLocalPath.value + slash + thepath;
  5552.     else
  5553.         thepath = gLocalPath.value + thepath;
  5554.  
  5555.     try {
  5556.         var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  5557.         file.initWithPath(thepath);
  5558.         if (file.exists())
  5559.             file.launch();
  5560.     } catch (ex) { debug(ex,37); }
  5561. }
  5562.  
  5563. // detect an ascii file - returns "A" or "I"
  5564. function detectAscii(path)
  5565. {
  5566.     if (filemode == 1)    // binary
  5567.         return "I";
  5568.     if (filemode == 2)    // ASCII
  5569.         return "A";
  5570.  
  5571.     path = path.substring(path.lastIndexOf('/') + 1);
  5572.     path = path.substring(path.lastIndexOf('.') + 1);
  5573.  
  5574.     var found = false;
  5575.     for (var x = 0; x < prefAsciiFiles.length; ++x)
  5576.         if (prefAsciiFiles[x] == path)
  5577.             found = true;
  5578.  
  5579.     if (found)
  5580.         return "A";
  5581.     return "I";
  5582. }
  5583.  
  5584. // add commas to numbers
  5585. function commas(num)
  5586. {
  5587.     num = num.toString();
  5588.     if (num.search(/\d{4}$/) == -1)
  5589.         return num;
  5590.     num = num.replace(/\d{3}$/, ",$&");
  5591.     while (num.search(/\d{4}\x2C/) != -1)
  5592.         num = num.replace(/\d{3}\x2C/, ",$&");
  5593.     return num;
  5594. }
  5595. // pad with zeros
  5596. function zeros(num)
  5597. {
  5598.     num = num.toString();
  5599.     if (num.length == 1)
  5600.         num = "0" + num;
  5601.     return num;
  5602. }
  5603. // send error message
  5604. function error(message, skipLog)
  5605. {
  5606.     if (!skipLog)
  5607.     {
  5608.         var el = document.getElementById('cmdlog').appendItem(message, message);
  5609.         el.setAttribute('style', 'color:red;font-weight:bold;');
  5610.         document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  5611.     }
  5612.     if (errormode)
  5613.     {
  5614.         var windowHeight = document.getElementById('main-window').boxObject.height;
  5615.         var windowWidth = document.getElementById('main-window').boxObject.width;
  5616.  
  5617.         window.openDialog("chrome://fireftp/content/alertDialog.xul","alertDialog",
  5618.             "screenY="+(windowHeight/2 - 100)+",screenX="+(windowWidth/2 - 200)+
  5619.             ",width=400,height=200,chrome,modal=yes,dialog=yes,resizable=yes", message);
  5620.     }
  5621.     document.getElementById('statusbytes').label = "";
  5622.     document.getElementById('statusmeter').setAttribute("mode", "determined");
  5623.     document.getElementById('statusmeter').setAttribute("value", "0%");
  5624. }
  5625. // add debug message to log; last error number is currently 42
  5626. function debug(message, errornum)
  5627. {
  5628.     if (debugmode)
  5629.     {
  5630.         message = "Error " + errornum + ": " + message;
  5631.         var el = document.getElementById('cmdlog').appendItem(message, message);
  5632.         el.setAttribute('style', 'color:red;font-weight:bold;');
  5633.         document.getElementById('cmdlog').ensureIndexIsVisible(document.getElementById('cmdlog').getRowCount() - 1);
  5634.     }
  5635. }
  5636. // change way main tab looks like
  5637. function interfaceChange(local, remote)
  5638. {
  5639.     document.getElementById('localview').collapsed = local;
  5640.     document.getElementById('storbutton').collapsed = local;
  5641.     document.getElementById('remoteview').collapsed = remote;
  5642.     document.getElementById('retrbutton').collapsed = remote;
  5643. }
  5644. function sendAbort()
  5645. {
  5646.     isReconnecting = false;
  5647.     document.getElementById('statusbytes').label = "";
  5648.     document.getElementById('statusmeter').setAttribute("mode", "determined");
  5649.     document.getElementById('statusmeter').setAttribute("value", "0%");
  5650.     if (!isConnected)
  5651.         return;
  5652.     //XXX writeControl("ABOR");
  5653.  
  5654.     document.getElementById('statusbytes').label = strbundle.getString("working");
  5655.     document.getElementById('statuselapsed').label = "";
  5656.     document.getElementById('statusremaining').label = "";
  5657.     document.getElementById('statusrate').label = "";
  5658.     document.getElementById('statusmeter').setAttribute("mode", "undetermined");
  5659.     document.title = "fireFTP";
  5660.  
  5661.     try {
  5662.         data_transport.close("Finished");     // ABOR does not seem to stop the connection in most cases
  5663.                                             // so this is a more direct approach
  5664.         data_finished = true;
  5665.         if (file_instream)
  5666.             file_instream.close();
  5667.     } catch (ex) { debug(ex,38); }
  5668.  
  5669.     moreToRemember = "";
  5670.     data_remember = "";
  5671.     eventQueue = new Array;
  5672.     addEventQueue("aborted");
  5673.     refreshLocalView();
  5674.     refreshRemoteView();
  5675. }
  5676. function recoverFromDisaster()
  5677. {
  5678.     if (eventQueue.length && eventQueue[0].cmd == "goodbye")
  5679.         eventQueue.shift();
  5680.     if (eventQueue.cmd)
  5681.         eventQueue = new Array(eventQueue);
  5682.     if (eventQueue.length && (eventQueue[0].cmd == "LIST" || eventQueue[0].cmd == "LIST2"
  5683.             || eventQueue[0].cmd == "RETR" || eventQueue[0].cmd == "RETR2"
  5684.             || eventQueue[0].cmd == "REST" || eventQueue[0].cmd == "APPE"
  5685.             || eventQueue[0].cmd == "STOR" || eventQueue[0].cmd == "STOR2"
  5686.             || eventQueue[0].cmd == "PASV" || eventQueue[0].cmd == "APPE2"))
  5687.     {
  5688.         var cmd = eventQueue[0].cmd;
  5689.         var parameter = eventQueue[0].parameter;
  5690.         if (cmd == "LIST2" || cmd == "RETR2" || cmd == "STOR2" || cmd == "APPE2")
  5691.             eventQueue[0].cmd = eventQueue[0].cmd.substring(0,4);
  5692.         cmd = eventQueue[0].cmd;
  5693.  
  5694.         // set up resuming for these poor interrupted transfers
  5695.         if (cmd == "REST")
  5696.         {
  5697.             try {
  5698.                 var file = getLocalFileSpec(eventQueue[1].remember);
  5699.                 if (file.fileSize)
  5700.                     eventQueue[0].parameter = file.fileSize;
  5701.             }catch(ex) { debug(ex,39); }
  5702.         }
  5703.         else if (cmd == "RETR")
  5704.         {
  5705.             try {
  5706.                 var file = getLocalFileSpec(eventQueue[0].remember);
  5707.                 if (file.fileSize)
  5708.                     eventQueue.unshift({ cmd: "REST", parameter: file.fileSize, remember: "" });
  5709.             }catch(ex) { debug(ex,40); }
  5710.         }
  5711.         
  5712.         // find the missing commands that complete the sets - this is where TYPE is handy
  5713.         var found = -1;
  5714.         var tempQueue = new Array;
  5715.         for (var x = 0; x < trashQueue.length; ++x)
  5716.             if (trashQueue[x].cmd == "TYPE" || found != -1)
  5717.             {
  5718.                 tempQueue.push(trashQueue[x]);
  5719.                 found = x;
  5720.             }
  5721.         eventQueue = tempQueue.concat(eventQueue);// ah, that's better
  5722.  
  5723.         // more resuming fun - this time for the stor/appe commandds
  5724.         if (cmd == "STOR" || cmd == "APPE")
  5725.             eventQueue.unshift({ cmd: "SIZE", parameter: parameter, remember: cmd });
  5726.     }
  5727.     else if (eventQueue.length && eventQueue[0].cmd == "RNTO") // don't worry RNTO, i didn't forget about you :)
  5728.     {
  5729.         if (trashQueue[trashQueue.length - 1].cmd == "RNFR")
  5730.             eventQueue.unshift(trashQueue[trashQueue.length - 1]); // ah, that's better
  5731.     }
  5732.     trashQueue = new Array;
  5733. }
  5734. function checkTimeout(id, cmd, dataSocket)
  5735. {
  5736.     // uploads currently can't be used with this function - we ignore them
  5737.     if (eventQueue.length && (eventQueue[0].cmd.indexOf("APPE") != -1 || eventQueue[0].cmd.indexOf("STOR") != -1))
  5738.         return;
  5739.     if (networkTimeoutID == id && eventQueue.length && (dataSocket || eventQueue[0].cmd.indexOf(cmd) != -1)) // still stuck here
  5740.     {
  5741.         legitClose = false;    // trying to restart the connection after a timeout
  5742.         close();
  5743.     }
  5744.         // try to restart the connection the hard way
  5745.         // are there better ways? if anybody is actually reading this, let me know
  5746. }
  5747. function displayWelcomeMessage()
  5748. {
  5749.     if (welcomemode)
  5750.     {
  5751.         var windowHeight = document.getElementById('main-window').boxObject.height;
  5752.         var windowWidth = document.getElementById('main-window').boxObject.width;
  5753.  
  5754.         window.openDialog("chrome://fireftp/content/welcomeDialog.xul","welcomeDialog",
  5755.                 "screenY="+(windowHeight/2 - 100)+",screenX="+(windowWidth/2 - 200)+
  5756.                 ",width=400,height=200,chrome,modal=no,dialog=yes,resizable=yes", welcomeMessage);
  5757.     }
  5758. }
  5759. // *************************************************************************************************
  5760. // *************************************** sort functions ******************************************
  5761. // *************************************************************************************************
  5762.  
  5763. function directorySort(a, b)
  5764. {
  5765.     var tempA = a.path.replace(/\x2f/g, "\x01").toLowerCase();    // make '/' less than everything (except null really)
  5766.     var tempB = b.path.replace(/\x2f/g, "\x01").toLowerCase();
  5767.  
  5768.     if (tempA < tempB)
  5769.         return -1;
  5770.     if (tempA > tempB)
  5771.         return 1;
  5772.     return 0;
  5773. }
  5774. function compareRemoteName(a, b)
  5775. {
  5776.     if (!a.isDirectory && b.isDirectory)
  5777.         return 1;
  5778.     if (a.isDirectory && !b.isDirectory)
  5779.         return -1;
  5780.     if (a.name.toLowerCase() < b.name.toLowerCase())
  5781.         return -1;
  5782.     if (a.name.toLowerCase() > b.name.toLowerCase())
  5783.         return 1;
  5784.     return 0;
  5785. }
  5786. function compareRemoteSize(a, b)
  5787. {
  5788.     if (!a.isDirectory && b.isDirectory)
  5789.         return 1;
  5790.     if (a.isDirectory && !b.isDirectory)
  5791.         return -1;
  5792.     return a.size - b.size;
  5793. }
  5794. function compareRemoteDate(a, b)
  5795. {
  5796.     if (!a.isDirectory && b.isDirectory)
  5797.         return 1;
  5798.     if (a.isDirectory && !b.isDirectory)
  5799.         return -1;
  5800.     var aTemp = a.date;
  5801.     var bTemp = b.date;
  5802.     if (a.date.indexOf(':') != -1)
  5803.         aTemp = aTemp + ' ' + ((new Date()).getYear() + 0);    // quirky
  5804.     if (b.date.indexOf(':') != -1)
  5805.         bTemp = bTemp + ' ' + ((new Date()).getYear() + 0);    // quirky
  5806.     return Date.parse(aTemp) - Date.parse(bTemp);
  5807. }
  5808. function compareRemoteAttr(a, b)
  5809. {
  5810.     if (!a.isDirectory && b.isDirectory)
  5811.         return 1;
  5812.     if (a.isDirectory && !b.isDirectory)
  5813.         return -1;
  5814.     if (a.attr < b.attr)
  5815.         return -1;
  5816.     if (a.attr > b.attr)
  5817.         return 1;
  5818.     return 0;
  5819. }
  5820. function compareLocalName(a, b)
  5821. {
  5822.     if (!a.isDirectory() && b.isDirectory())
  5823.         return 1;
  5824.     if (a.isDirectory() && !b.isDirectory())
  5825.         return -1;
  5826.     if (a.leafName.toLowerCase() < b.leafName.toLowerCase())
  5827.         return -1;
  5828.     if (a.leafName.toLowerCase() > b.leafName.toLowerCase())
  5829.         return 1;
  5830.     return 0;
  5831. }
  5832. function compareLocalSize(a, b)
  5833. {
  5834.     if (!a.isDirectory() && b.isDirectory())
  5835.         return 1;
  5836.     if (a.isDirectory() && !b.isDirectory())
  5837.         return -1;
  5838.     return a.fileSize - b.fileSize;
  5839. }
  5840. function compareLocalDate(a, b)
  5841. {
  5842.     if (!a.isDirectory() && b.isDirectory())
  5843.         return 1;
  5844.     if (a.isDirectory() && !b.isDirectory())
  5845.         return -1;
  5846.     return a.lastModifiedTime - b.lastModifiedTime;
  5847. }
  5848. function compareLocalAttr(a, b)
  5849. {
  5850.     if (!a.isDirectory() && b.isDirectory())
  5851.         return 1;
  5852.     if (a.isDirectory() && !b.isDirectory())
  5853.         return -1;
  5854.     if (a.permissions < b.permissions)
  5855.         return -1;
  5856.     if (a.permissions > b.permissions)
  5857.         return 1;
  5858.     return 0;
  5859. }
  5860. function customSort(target)
  5861. {
  5862.     if (target.localName != "treecol")
  5863.         return;
  5864.  
  5865.     var sortDirection =
  5866.         target.getAttribute("sortDirection") == "ascending" || target.getAttribute("sortDirection") == "natural"
  5867.         ? "descending" : "ascending";
  5868.     
  5869.     if (target.parentNode.parentNode.getAttribute("id") == "localtree")
  5870.     {
  5871.         document.getElementById('localname').setAttribute("sortDirection", "natural");
  5872.         document.getElementById('localsize').setAttribute("sortDirection", "natural");
  5873.         document.getElementById('localdate').setAttribute("sortDirection", "natural");
  5874.         document.getElementById('localattr').setAttribute("sortDirection", "natural");
  5875.         target.setAttribute("sortDirection", sortDirection);
  5876.         changeLocalView(target.getAttribute("id"), sortDirection);
  5877.         onLocalPathChange();
  5878.     }
  5879.     else
  5880.     {
  5881.         document.getElementById('remotename').setAttribute("sortDirection", "natural");
  5882.         document.getElementById('remotesize').setAttribute("sortDirection", "natural");
  5883.         document.getElementById('remotedate').setAttribute("sortDirection", "natural");
  5884.         document.getElementById('remoteattr').setAttribute("sortDirection", "natural");
  5885.         target.setAttribute("sortDirection", sortDirection);
  5886.         changeRemoteView(target.getAttribute("id"), sortDirection);
  5887.         onRemotePathChange();
  5888.     }
  5889. }
  5890.  
  5891. // *************************************************************************************************
  5892. // *************************************** create drag session *************************************
  5893. // *************************************************************************************************
  5894.  
  5895. var whataDrag = Components.classes["@mozilla.org/widget/dragservice;1"].getService(Components.interfaces.nsIDragService);
  5896. var dragSessionEnabled = false;
  5897. function onDragGesture(event, ignore)
  5898. {
  5899.     if (!isConnected && event.target.getAttribute("id").indexOf("remote") != -1)
  5900.         return;
  5901.     
  5902.     if (!dragSessionEnabled && !ignore)
  5903.         return;
  5904.  
  5905.     if (ignore)
  5906.     {
  5907.         var row = {};
  5908.         var col = {};
  5909.         var child = {};
  5910.         if (event.target.getAttribute("id") == "localdirtreechildren")
  5911.         {
  5912.             gLocalDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5913.             if (!gLocalDirTree.view.getCellText(row.value, "localdirname"))
  5914.                 return;
  5915.         }
  5916.         else
  5917.         {
  5918.             gRemoteDirTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
  5919.             if (!gRemoteDirTree.view.getCellText(row.value, "remotedirname"))
  5920.                 return;
  5921.         }
  5922.     }
  5923.  
  5924.     var transArray = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
  5925.     var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  5926.  
  5927.     trans.addDataFlavor("text/unicode");
  5928.     var origin = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
  5929.     origin.data = event.target.getAttribute("id");
  5930.     trans.setTransferData("text/unicode", origin, origin.data.length * 2);
  5931.     transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
  5932.     whataDrag.invokeDragSession(event.target, transArray, null, whataDrag.DRAGDROP_ACTION_MOVE);
  5933.     event.preventBubble();
  5934. }
  5935.