﻿//-----------------------------------------------------------------------------
// XTextDoc
//
// Copyright 2005-2010 - Xcential Group LLC.
//
//-----------------------------------------------------------------------------

XTextDoc.prototype = new XObject;
XTextDoc.prototype.constructor = XTextDoc;

//=============================================================================
// Constructor

function XTextDoc(
   pathRefOrText,
   charSet,
   create
)
{
   pathRefOrText = (pathRefOrText == null) ? null : pathRefOrText;
   charSet = (charSet == null) ? "UTF-8" : charSet;
   create = (create == null) ? true : false;

   if (create)
      return new XTextDoc(pathRefOrText, charSet, false);

   //--------------------------------------------------------------------------
   // Private Interface

   //--------------------------------------------------------------------------
   // Privileged Interface

   this.valueOf = function()
   {

      return oTextDoc;
   }

   //--------------------------------------------------------------------------

   this.setObjectValue = function(
      pathRefOrText,
      charSet
   )
   {
      pathRefOrText = (pathRefOrText == null) ? null : pathRefOrText.toString();
      charSet = (charSet == null) ? null : charSet;

      oTextDoc = new Array();
      oTextDoc.charSet = charSet;
      oTextDoc.ref = null;
      oTextDoc.path = null;
      oTextDoc.text = null;

      if (XRef.isRef(pathRefOrText))
         oTextDoc.ref = pathRefOrText;
      else if (XApp.getMode() == XApp.MODE_SERVER && XFilePath.isPath(pathRefOrText))
         oTextDoc.path = pathRefOrText;
      else if (XApp.getMode() == XApp.MODE_STANDALONE && XFilePath.isPath(pathRefOrText))
         oTextDoc.path = pathRefOrText;
      else
         oTextDoc.text = pathRefOrText;

      return oTextDoc;
   }

   //--------------------------------------------------------------------------
   // Initialization

   var oTextDoc = this.setObjectValue(pathRefOrText, charSet);

}

XTextDoc.prototype.objectClass = "XTextDoc";

//=============================================================================
// Static Interface

XTextDoc.ADO_OVERWRITE = 2;

XTextDoc.HEAD = "HEAD";
XTextDoc.GET = "GET";
XTextDoc.POST = "POST";

XTextDoc.NO_CACHE = true;
XTextDoc.CACHE    = false;

//-----------------------------------------------------------------------------

XTextDoc.readFtpFile = function(
   url,
   method
)
{
   method = (method == null) ? "GET" : method;

   var jobId = XUtils.generateId("Ftp.", XUtils.SCHEME_RANDOM) + ".";

   var domain = url.replace(/^ftp:\/\/([^\/]+).*/,"$1");
   var ftpFileName = url.replace(/.*\/([^\/]+)$/, "$1");
   var ftpCommandsName = "ftpCommands.bat";

   if (XString(domain).isNothing())
      return null;

   if (XApp.getMode() == XApp.MODE_CLIENT)
      throw XMsg("Can only retrieve an FTP file on the server. Client-side support is not available.");

   // *************************************************************************
   //
   // Note: The anonymous Internet account must be granted read permission to the ftp.exe
   // file for this to work.
   //
   // cacls "%systemRoot%\\System32\\ftp.exe /E /G %computerName%\IUSR_%computerName%:R
   //
   //**************************************************************************

   var tempDirPath = XRef.mapPath("/cache") + "\\temp";
   //var tempDirPath = XServer.system("TEMP");
   //var tempDirPath = XServer.process("TEMP");
   tempDirPath = XServer.shell.expandEnvironmentStrings(tempDirPath);

   try
   {
      var ftpCommandsFilePath = tempDirPath + "\\" + jobId + ftpCommandsName;

      try
      {
         ftpCommandsFile = XServer.fileSystem.createTextFile(ftpCommandsFilePath, XServer.OVERWRITE);
         ftpCommandsFile.writeLine("! echo \"--START--\"");
         ftpCommandsFile.writeLine("! echo %temp%");
         ftpCommandsFile.writeLine("! echo \"--END--\"");
         ftpCommandsFile.writeLine("quit");
         ftpCommandsFile.close();
      }
      catch (error)
      {
         throw XMsg("Unable to write FTP commands to " + ftpCommandsFilePath + ".", error);
      }

      XServer.shell.currentDirectory = tempDirPath;
      var command = "%systemRoot%\\System32\\ftp.exe -A -s:" + jobId + ftpCommandsName;
      command = XServer.shell.expandEnvironmentStrings(command);
      var cmdExec = XServer.shell.exec(command);
      var stdOutput = cmdExec.stdOut.readAll();

      var ftpTempDirPath = tempDirPath;
      if ((/--START--/).test(stdOutput))
      {
         ftpTempDirPath = stdOutput.replace(/\r?\n\r?/g,"");
         ftpTempDirPath = ftpTempDirPath.replace(/^.*?--START--/,"");
         ftpTempDirPath = ftpTempDirPath.replace(/--END--.*$/,"");
      }

      try
      {
         ftpCommandsFile = XServer.fileSystem.createTextFile(ftpCommandsFilePath, XServer.OVERWRITE);
         ftpCommandsFile.writeLine("lcd \"" + ftpTempDirPath + "\"");
         ftpCommandsFile.writeLine("cd " + url.replace(/^ftp:\/\/[^\/]*/,"").replace(/\/[^\/]*$/,""));
         ftpCommandsFile.writeLine(method.toLowerCase() + " " + ftpFileName + " " + jobId + ftpFileName);
         ftpCommandsFile.writeLine("! echo \"--START--\"");
         ftpCommandsFile.writeLine("! type \"" + jobId + ftpFileName + "\"");
         ftpCommandsFile.writeLine("! echo \"--END--\"");
         ftpCommandsFile.writeLine("! del \"" + jobId + ftpFileName + "\"");
         ftpCommandsFile.writeLine("quit");
         ftpCommandsFile.close();
      }
      catch (error)
      {
         throw XMsg("Unable to write FTP commands to " + ftpCommandsFilePath + ".", error);
      }

      XServer.shell.currentDirectory = tempDirPath;
      var command = "%systemRoot%\\System32\\ftp.exe -A -s:" + jobId + ftpCommandsName + " " + domain;
      command = XServer.shell.expandEnvironmentStrings(command);
      var cmdExec = XServer.shell.exec(command);
      var stdOutput = cmdExec.stdOut.readAll();

      var text = null;
      if ((/--START--/).test(stdOutput))
      {
         text = stdOutput;
         text = text.replace(/\r?\n\r?/g, "||");
         text = text.replace(/^.*?--START--/,"");
         text = text.replace(/--END--.*$/,"");
         text = text.replace(/\|\|/g,"\n");
      }

   }
   catch (error)
   {
      throw error;
   }
   finally
   {
      if (XServer.fileSystem.fileExists(ftpCommandsFilePath))
         XServer.fileSystem.deleteFile(ftpCommandsFilePath);
   }

   return text;
}

//-----------------------------------------------------------------------------

XTextDoc.readHttpFile = function(
   url,
   method,
   noCache,
   charSet
)
{
   method = (method == null) ? "GET" : method;
   noCache = (noCache == null) ? false : noCache;
   charSet = (charSet == null) ? "utf-8" : charSet;

   var httpRequest = XApp.getXmlHttpRequest();
   try
   {
      if (noCache)
         url += (((/\?/).test(url)) ? "&" : "?") + "random=" + XUtils.generateId(XUtils.SCHEME_RANDOM);
      httpRequest.open(method, url, false);
   }
   catch (error)
   {
      try
      {
         // References to the local domain may need to be removed - otherwise access denied issues could result.
         if (XRef.isRelative(url))
            url = XRef.getAbsolute(url);
         else
            url = XRef.getRelative(url);
         if (!url)
            throw error;
         httpRequest.open(method, url, false);
      }
      catch (error)
      {
         var errorMsg = "File at '" + url + "' cannot be retrieved.";
         var errorText = httpRequest.statusText;
         if (XString(errorText).isSomething)
            errorMsg += " (" + errorText + ")";
         throw XMsg(errorMsg);
      }
   }
   try
   {
      if (noCache)
      {
        httpRequest.setRequestHeader("Cache-Control", "no-cache");
        httpRequest.setRequestHeader("Pragma","no-cache");
      }
   }
   catch (error)
   { /* Chrome doesn't seem to like this */ }

   try
   {
      httpRequest.send(null);
      if (httpRequest.readyState != 4)
         throw XMsg("Text document at '" + url + "' cannot be retrieved.");
      if (httpRequest.status == 404)
         throw XMsg("Text document at '" + url + "' cannot be retrieved as it does not exist or is not available.");
      if (httpRequest.status != 200)
      {
         var errorMsg = "File at '" + url + "' cannot be retrieved.";
         var errorText = httpRequest.statusText;
         if (XString(errorText).isSomething)
            errorMsg += " (" + errorText + ")";
         throw XMsg(errorMsg);
      }
   }
   catch (error)
   {
      return "";
   }

   return XApp.getResponseText(httpRequest, charSet);
}

//-----------------------------------------------------------------------------

XTextDoc.doesFileExist = function(
   pathOrRef
)
{

   var text = null;

   var url = XRef(pathOrRef).getURL();
   try
   {
      if ((/^ftp\:/).test(url))
         text = XTextDoc.readFtpFile(url);
      else
         text = XTextDoc.readHttpFile(url, XTextDoc.HEAD, XTextDoc.NO_CACHE);
   }
   catch (error)
   {
      text = null;
   }

   return (text == null) ? false : true;
;
}

//------------------------------------------------------------------------------

XTextDoc.getCheckText = function(
   text
)
{
   text = (text == null) ? null : text;

   if (text == null || text.length == 0)
      return "";

   text = text.replace(/\r?\n\r?/g, " ");
   text = text.replace(/<.*?>/g, " ");
   text = text.replace(/^\s+/, "");
   text = text.replace(/\s+$/, "");
   text = text.replace(/\s+/g, " ");

   return text;
}


//=============================================================================
// Public Interface

XTextDoc.prototype.getCharSet = function()
{

   var textDoc = this.valueOf();

   return textDoc.charSet;
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.getPath = function()
{

   var textDoc = this.valueOf();

   return textDoc.path;
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.read = function(
   pathOrRef,
   charSet,
   noCache
)
{
   pathOrRef = (pathOrRef == null) ? null : pathOrRef.toString();
   charSet = (charSet == null) ? null : charSet;
   noCache = (noCache == null) ? false : noCache;

   var textDoc = this.valueOf();

   if (XTextDoc.docs == null)
      XTextDoc.docs = XCache();

   if(pathOrRef)
   {
      if (XRef.isRef(pathOrRef))
      {
         textDoc.ref = pathOrRef;
         if ((XApp.getMode() == XApp.MODE_SERVER || XApp.getMode() == XApp.MODE_STANDALONE) && XRef.isLocal(pathOrRef) && XRef(pathOrRef).getQuery() == null)
            textDoc.path = XRef.mapPath(pathOrRef);
      }
      else if (XApp.getMode() != XApp.MODE_CLIENT && XFilePath.isPath(pathOrRef))
         textDoc.path = pathOrRef;
      else
         throw XMsg("Cannot read '" + pathOrRef + "' as it cannot be identified.");
   }
   if (charSet)
      textDoc.charSet = charSet;

   if (!textDoc.text)
   {
      if (textDoc.path)
      {
         textDoc.checkSum = null;
         textDoc.text = XTextDoc.docs.getItem(textDoc.path);
         if (textDoc.text == null)
         {
            var stream = new ActiveXObject("ADODB.Stream"); // ADO is used as the FSO only works with UTF-16LE
            stream.open();
            try
            {
               stream.CharSet = (textDoc.charSet) ? textDoc.charSet : "UTF-8";
               stream.loadFromFile(textDoc.path);
               textDoc.text = stream.readText();
               if (!noCache)
                  XTextDoc.docs.addItem(textDoc.ref, textDoc.text);
            }
            catch (error)
            {
               throw error;
            }
            finally
            {
               stream.close();
            }
         }
      }
      else if (textDoc.ref)
      {
         textDoc.checkSum = null;
         textDoc.text = XTextDoc.docs.getItem(textDoc.ref);
         if (textDoc.text == null)
         {
            var url = XRef(textDoc.ref).getURL();
            if ((/^ftp\:/).test(url))
               var responseText = XTextDoc.readFtpFile(url);
            else
               var responseText = XTextDoc.readHttpFile(url, XTextDoc.GET, noCache, charSet);
            XApp.logEvent(XApp.EVENT_RETRIEVE, url);
            if (responseText == null || responseText.length == 0)
               throw XMsg("Cannot retrieve document text at '" + url + "'.");
            if ((/^<Error[\s>]/).test(responseText))
               throw XMsg(responseText);
            textDoc.text = responseText;
            if (!noCache)
               XTextDoc.docs.addItem(textDoc.ref, textDoc.text);
         }
      }
   }

   return textDoc.text;
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.saveAs = function(
   pathOrRef,
   charSet
)
{
   pathOrRef = (pathOrRef == null) ? null : pathOrRef.toString();
   charSet = (charSet == null) ? null : charSet;

   var textDoc = this.valueOf();

   if(pathOrRef)
   {
      if (XRef.isRef(pathOrRef))
         textDoc.ref = pathOrRef;
      else
         textDoc.path = pathOrRef;
   }
   if (charSet)
      textDoc.charSet = charSet;

   if (textDoc.text)
   {
      if (textDoc.path)
      {
         var stream = new ActiveXObject("ADODB.Stream"); // ADO is used as the FSO only works with UTF-16LE
         stream.open();
         try
         {
            stream.CharSet = (textDoc.charSet) ? textDoc.charSet : "UTF-8";
            stream.writeText(textDoc.text);
            stream.saveToFile(textDoc.path, XTextDoc.ADO_OVERWRITE);
         }
         catch (error)
         {
            throw error;
         }
         finally
         {
            stream.close();
         }
      }
      else if (textDoc.ref)
         throw XMsg("Cannot save to '" + ref + "'. Saving to a URI is not supported.");
      else
         throw XMsg("Cannot save as no location has been specified.");
   }

   return true;
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.toText = function()
{

   var textDoc = this.valueOf();

   return textDoc.text;
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.toString = function()
{

   return this.toText();
}

//-----------------------------------------------------------------------------

XTextDoc.prototype.getCheckSum = function()
{

   var textDoc = this.valueOf();

   if (!textDoc.text && (textDoc.ref || textDoc.path))
      this.read();

   if (textDoc.text && !textDoc.checkSum)
   {
      var tempDirPath = XRef.mapPath("/cache") + "\\temp";
      var tempFileName = "CheckSum.txt";
      var tempFilePath = tempDirPath + "\\" + tempFileName;
      XFilePath(tempDirPath + "\\").create();
      this.saveAs(tempFilePath);
      var command = "\"" + XRef.mapPath("/app/pkgs/xfw/fsum/fsum.exe") + "\" -crc32 -d\"" + tempDirPath + "\" " + tempFileName;
      var cmdExec = XServer.shell.exec(command);
      var result = "";
      do
      {
         result += cmdExec.stdOut.readAll();
      }
      while (cmdExec.status == 0)
      if (XMatch(result.replace(/\r?\n\r?/g,""), /;([^\s]+)\s*\?CRC32/i))
         textDoc.checkSum = XMatch.matches[1];
      XServer.fileSystem.deleteFile(tempFilePath);
   }

   return textDoc.checkSum;
}

//=============================================================================

