
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* ___________________
*
*  Copyright 2008 Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
* $DateTime: 2008/04/10 13:35:00 $
*
* Adobe Output Module 2.0
*
* MediaGallery Originally Written by Quality Process Incorporated, Enhanced by
* Adobe System China. Contact Sheet written by Adobe System China.
* 
*
**************************************************************************/

AOM.Ftp = function()
{
	this.ftpConnection = undefined;
	this.server = undefined;
	this.username = undefined;
	this.password = undefined;
	this.sourceFolder = undefined;
	this.destinationFolder = undefined;

	this.copyQueue = new Array();
	this.lastFtpFile = undefined;
	
	this.initLocalizedStrings();
}

AOM.Ftp.flagDirectory = 1;
AOM.Ftp.flagFile = 2;

AOM.Ftp.zstrings =
{
	dialogTitle		: { value : 0, description : "$$$/MediaGallery/ftp/dialogTitle=Create Gallery" },
	buttonOK		: { value : 1, description : "$$$/MediaGallery/ftp/buttonOK=OK" }
};

AOM.Ftp.statusCodes = 
{
	error 			: { value : -1, description : "$$$/MediaGallery/ftp/anErrorOccured=An error occured." },
	ftpComplete		: { value :  0, description : "$$$/MediaGallery/ftp/ftpComplete=FTP completed successfully." },
	ftpInProgress	: { value :  1, description : "$$$/MediaGallery/ftp/ftpInProgress=FTP in progress." }
};

AOM.Ftp.errorCodes =
{
	noError 						: { value :   0, description : "$$$/MediaGallery/ftp/noError=No error." },
	unknownError 					: { value :  -1, description : "$$$/MediaGallery/ftp/unknownError=Unknown error." },
	fileDoesNotExist 				: { value :  -2, description : "$$$/MediaGallery/ftp/fileDoesNotExist=File does not exist." },
	ftpMkdirFailed 					: { value :  -3, description : "$$$/MediaGallery/ftp/ftpMkdirFailed=Could not make folder." },
	destinationFolderDoesNotExist 	: { value :  -4, description : "$$$/MediaGallery/ftp/destinationFolderDoestNotExist=Folder does not exist." },
	folderFileConflict 				: { value :  -5, description : "$$$/MediaGallery/ftp/folderExistsWithSameNameAsFile=Folder exists with same name as file." },
	folderNotFound 					: { value :  -6, description : "$$$/MediaGallery/ftp/folderNotFound=Folder not found." },
	fileDeleteFailed 				: { value :  -7, description : "$$$/MediaGallery/ftp/fileDeleteFailed=Could not delete file." },
	notFileOrFolder					: { value :  -8, description : "$$$/MediaGallery/ftp/fileOrFolderRequired=File or folder required." },
	unableConnectServer				: { value :  -9, description : "$$$/MediaGallery/ftp/unableConnect=Unable to connect to FTP server."},
	sourceFolderDoesNotExist		: { value : -10, description : "$$$/MediaGallery/ftp/sourceFolderDoesntExist=Source folder does not exist." },
	tbcError						: { value : -11, description : "$$$/MediaGallery/ftp/errorToBeClassified=Unclassified error." },
	makeDestinationFailed			: { value : -12, description : "$$$/MediaGallery/ftp/makeDestinationFailed=Could not created remote folder." },
	cancel                  		: { value : -13, description : "" }, // no description so we don't nag the user...
	
	outOfMemory						: { value : -200, description: "$$$/MediaGallery/ftp/outOfMemory=Insufficient memory." },
	serverNotFound					: { value : -201, description: "$$$/MediaGallery/ftp/serverNotFound=The server could not be found." },
	couldntConnect					: { value : -202, description: "$$$/MediaGallery/ftp/couldntConnect=Connection to the server failed." },
	operationTimeout				: { value : -203, description: "$$$/MediaGallery/ftp/operationTimeout=Operation timed out." },
	loginFailed						: { value : -204, description: "$$$/MediaGallery/ftp/loginFailed=Invalid user name or password." },
	unknownProtocol					: { value : -205, description: "$$$/MediaGallery/ftp/unknownProtocol=The protocol was unknown." },
	fileExists						: { value : -206, description: "$$$/MediaGallery/ftp/fileExists=File already exists." },
	fileNotFound					: { value : -207, description: "$$$/MediaGallery/ftp/fileNotFound=File does not exist." },
	accessDenied					: { value : -208, description: "$$$/MediaGallery/ftp/accessDenied=Access was denied." },
	mkdirAccessDenied				: { value : -209, description: "$$$/MediaGallery/ftp/mkdirAccessDenied=Access was denied while creating directory." },
	rmdirAccessDenied				: { value : -210, description: "$$$/MediaGallery/ftp/rmdirAccessDenied=Access was denied while removing directory." },
	delAccessDenied					: { value : -211, description: "$$$/MediaGallery/ftp/delAccessDenied=Access was denied while deleting file." },
	diskFull						: { value : -212, description: "$$$/MediaGallery/ftp/diskFull=Insufficient disk space." },
	localIOError					: { value : -213, description: "$$$/MediaGallery/ftp/localIOError=Local IO error." },
	remoteIOError					: { value : -214, description: "$$$/MediaGallery/ftp/remoteIOError=Remote IO error." },
	uploadFailed					: { value : -215, description: "$$$/MediaGallery/ftp/uploadFailed=Upload was not successful." },
	ftpUnknownError					: { value : -216, description: "$$$/MediaGallery/ftp/unknownError=Unknown error." },
	serverHostKeyFailed				: { value : -217, description: "$$$/MediaGallery/ftp/serverHostKeyFailed=Server Host Key not authorized." }
};

AOM.Ftp.openStates =
{
	beforeFirstOpen		: 0,
	waitFirstOpen		: 1,
	beforeSecondOpen	: 2,
	waitSecondOpen		: 3,
	beforeThirdOpen		: 4,
	waitThirdOpen		: 5,
	finishTrying		: 6,
};

AOM.Ftp.prototype.getStatusCode = function(value)
{
	var i = undefined;
	for (i in AOM.Ftp.statusCodes)
	{
		if (AOM.Ftp.statusCodes[i].value == value)
			return AOM.Ftp.statusCodes[i];
	}
	return undefined;
}

AOM.Ftp.prototype.getErrorCode = function(value)
{
	var i = undefined;
	for (i in AOM.Ftp.errorCodes)
	{
		if (AOM.Ftp.errorCodes[i].value == value)
			return AOM.Ftp.errorCodes[i];
	}
	return AOM.Ftp.errorCodes.unknownError;
}

AOM.Ftp.prototype.initLocalizedStrings = function()
{
	var i = undefined;
	for (i in AOM.Ftp.zstrings)
	{
		AOM.Ftp.zstrings[i].description = AOM.AMG.localize(AOM.Ftp.zstrings[i].description);
	}
	for (i in AOM.Ftp.statusCodes)
	{
		AOM.Ftp.statusCodes[i].description = AOM.AMG.localize(AOM.Ftp.statusCodes[i].description);
	}
	for (i in AOM.Ftp.errorCodes)
	{
		AOM.Ftp.errorCodes[i].description = AOM.AMG.localize(AOM.Ftp.errorCodes[i].description);
	}
}
AOM.Ftp.prototype.copy = function(file)
{
	// if a file has been passed in, add it to the queue
	// return an item from AOM.Ftp.statusCodes
	if (typeof file != 'undefined')
	{
		if (file instanceof File == false && file instanceof Folder == false)
			return this.statusErrorDetail(AOM.Ftp.errorCodes.notFileOrFolder, folder);
		if (!file.exists)
			return this.statusErrorDetail(AOM.Ftp.errorCodes.fileDoesNotExist, file);
		this.copyQueue.push(file);
	}
	// return the ftp status if there are no more items in the queue
	if (this.copyQueue.length == 0)
		return this.status();
		
	// there are items in the queue but we have to wait for any prior ftp to complete
	if (this.status() == AOM.Ftp.statusCodes.ftpInProgress)
		return AOM.Ftp.statusCodes.ftpInProgress;
	
	// grab an item from the queue and ftp it
	var file = this.copyQueue.shift();
	var status = this.ftpFile(file);
	if (status == AOM.Ftp.statusCodes.error)
		return status;
	
	return AOM.Ftp.statusCodes.ftpInProgress;
}
AOM.Ftp.prototype.makeDisplayPath = function(fileOrFolder)
{
	var s = "..." + fileOrFolder.path.substring(fileOrFolder.path.lastIndexOf("/")) + "/";
	s += fileOrFolder.name + (fileOrFolder instanceof Folder ? "/" : "");
	return File.decode(s);
}
AOM.Ftp.prototype.ftpFile = function(fileOrFolder)
{
	// if a file ftp it, if a folder create it
	// return an item from AOM.Ftp.statusCodes
	var log = false;

	var displayPath = this.makeDisplayPath(fileOrFolder);
	if (this.ftpConnection.isComplete != true)
	{
		this.taskManager.retryLastTask(displayPath+" - Waiting to send...", 100);
	}

	this.lastFtpFile = displayPath;
	var destination = this.makeDestination(fileOrFolder);

	if (fileOrFolder instanceof Folder)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("AOM.Ftp.ftpFile - folder ("+destination+")");
		// create a new directory if none exists
		if (!this.ftpConnection.isDir(destination))
		{
			// it doesn't exit so try to create
			if (this.ftpConnection.mkdir(destination) != true)
			{
				var error = this.getErrorCode(this.ftpConnection.error);
				this.taskManager.stop({description:error.description, detail:destination, title:AOM.Ftp.zstrings.dialogTitle.description, button:AOM.Ftp.zstrings.buttonOK.description});
				return AOM.AMG.Log.writeError("ftpFile - mkdir failed ("+destination+")");
			}
		}
		else if (fileOrFolder.clearExistingFolder == true)
		{
			// it exists but we need to clear it first
			return this.removeFolder(destination);
		}
	}
	else
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("AOM.Ftp.ftpFile - file ("+destination+")");
		// copy the file. overwrite existing file with same name.
		var index = destination.lastIndexOf("/");
		var path = destination.substring(0, index);
		var file = destination.substring(index+1);
		if (this.ftpConnection.cd != path)
		{
			// need to cd to the destination.
			if (!this.ftpConnection.isDir(path))
			{
				var error = this.getErrorCode(this.ftpConnection.error);
				if (error == AOM.Ftp.errorCodes.noError ||
					error == AOM.Ftp.errorCodes.unknownError ||
					error == AOM.Ftp.errorCodes.ftpUnknownError)
						error = AOM.Ftp.errorCodes.destinationFolderDoesNotExist;
				this.taskManager.stop({description:error.description, detail:path, title:AOM.Ftp.zstrings.dialogTitle.description, button:AOM.Ftp.zstrings.buttonOK.description});
				return AOM.AMG.Log.writeError("ftpFile - destination folder does not exist ("+path+")\n		"+this.ftpConnection.errorString);
			}
			this.ftpConnection.cd = path;
		}
		if (this.ftpConnection.put(fileOrFolder, file) != true)
		{
			var error = this.getErrorCode(this.ftpConnection.error);
			this.taskManager.stop({description:error.description, detail:this.ftpConnection.cd + File.decode(file), title:AOM.Ftp.zstrings.dialogTitle.description, button:AOM.Ftp.zstrings.buttonOK.description});
			return AOM.AMG.Log.writeError("ftpFile - unable to upload file ("+path+file+")\n		"+this.ftpConnection.errorString);
		}
	}
	return this.status();
}
AOM.Ftp.prototype.upload = function ()
{
	var fileList = new Array();
	var folderList = new Array();
	folderList.push(new Folder(this.sourceFolder));
	
	// close the connection
	var that = this;
	this.taskManager.insertTask(function () {that.close();}, "...");
	// copy folders and files to the remote destination
	this.uploadFiles(this.destinationFolder, fileList, folderList)
	// add the files and folders to their respective lists...
	this.inventoryLocalFolder(this.sourceFolder, fileList, folderList, AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/ScanFoldersToUpload=Scan folders to upload"));
}
AOM.Ftp.prototype.inventoryLocalFolder = function (folder, fileList, folderList, groupDescription)
{
	var log = false;
	var that = this;

	var visitFolder = function (folder)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("inventoryLocalFolder visitFolder - " + folder);
		if (folder instanceof Folder == false)
			AOM.AMG.Log.writeError("inventoryLocalFolder - folder is not a Folder ("+typeof folder+")");
		var files = folder.getFiles()
		for (var i = 0; i < files.length; i++)
		{
			var file = files[i];
			if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("inventoryLocalFolder - " + file);
			if (file instanceof Folder)
			{
				visitFolder(file);
				folderList.push(file);	
			}
			else
				fileList.push(file);
		}
	}
	this.taskManager.startGroup(groupDescription, 10); // don't know the real number...
	this.taskManager.insertTask(function () {visitFolder(new Folder(folder));}, "...");
}
AOM.Ftp.prototype.uploadFiles = function (destination, fileList, folderList)
{
	var log = false;
	var that = this;
	
	var copyLocalFiles = function ()
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("copyLocalFiles - fileList.length is " + fileList.length);
		while (fileList.length > 0)
		{
			var file = fileList.shift();
			var f = (function (x){return function (){that.ftpFile(x);}})(file);
			that.taskManager.insertTask(f, that.makeDisplayPath(file));
			if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("copyLocalFiles - " + file);
		}
	}
	this.taskManager.insertTask(function () {copyLocalFiles();}, "...");
	this.taskManager.insertTask(function () {that.taskManager.startGroup(AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/CopyLocalFiles=Copy local files"), fileList.length);}, "...");
	
	var makeRemoteFolders = function ()
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("makeRemoteFolders - folderList.length is " + folderList.length);
		// between sort and pop and insert, the shallowest folder will be created first
		folderList.sort();
		while (folderList.length > 0)
		{
			var folder = folderList.pop();
			var f = (function (x){return function (){that.ftpFile(x);}})(folder);
			that.taskManager.insertTask(f, that.makeDisplayPath(folder));
			if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("makeRemoteFolders - " + folder);
		}
	}
	this.taskManager.insertTask(function () {makeRemoteFolders(0);}, "...");
	this.taskManager.insertTask(function () {that.taskManager.startGroup(AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/MakeRemoteFolders=Make remote folders"), folderList.length);}, "...");
}

AOM.Ftp.prototype.inventoryRemoteFolder = function (folder, fileList, folderList, groupDescription)
{
	// make a list of all the files and folders in a remote folder
	var log = false;
	var that = this;
	
	var visitFolder = function (folder)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("inventoryRemoteFolder visitFolder - " + folder);
		that.ftpConnection.cd = folder;
		that.ftpConnection.ls();
		while (that.ftpConnection.isComplete != true)
		{
			; // wait for asynchronous ls
		}
		for (var i = 0; i < that.ftpConnection.fileCount; i++)
		{
			var fullPath = folder + "/" + that.ftpConnection.files(i);
			if ((that.ftpConnection.flags(i) & AOM.Ftp.flagDirectory) == AOM.Ftp.flagDirectory)
			{
				var f = (function (x){return function (){visitFolder(x);}})(fullPath);
				var detail = that.makeDisplayPath(new Folder(fullPath));
				that.taskManager.insertTask(f, detail);
				folderList.push(fullPath);
				if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("inventoryRemoteFolder visitFolder - folder " + fullPath);
			}
			if ((that.ftpConnection.flags(i) & AOM.Ftp.flagFile) == AOM.Ftp.flagFile)
			{
				fileList.push(fullPath);
				if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("inventoryRemoteFolder visitFolder - file " + fullPath);
			}
		}
	}
	this.taskManager.startGroup(groupDescription, 10); // don't know the real number...
	this.taskManager.insertTask(function () {visitFolder(folder);}, "...");
}

AOM.Ftp.prototype.removeFolder = function(folder)
{
	// delete a folder and its contents
	var log = false;
	if (!this.ftpConnection.isDir(folder))
		return AOM.AMG.Log.writeError(AOM.AMG.localize("$$$/private/removeFolderIsDirFailed=AOM.Ftp.removeFolder - isDir failed."));
		
	var that = this;
	var foldersToDelete = new Array();
	foldersToDelete.push(folder);
	var filesToDelete = new Array();

	// we use insertTask to get this to happen before whatever else is queued up
	// but that means we need to insert the folder and file deletion tasks first...
	var deleteFolder = function (folder)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("deleteFolder - " + folder);
		that.ftpConnection.rmdir(folder);
	}
	var deleteFolders = function (folderList)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("deleteFolders - " + folderList.length + " folders");
		folderList.sort(); 
		// between sort and shift and insert, the deepest folder will be deleted first
		while (folderList.length > 0)
		{
			var folder = folderList.shift();
			var f = (function (x){return function (){deleteFolder(x);}})(folder);
			that.taskManager.insertTask(f, that.makeDisplayPath(new Folder(folder)));
		}		
	}
	this.taskManager.insertTask(function () {deleteFolders(foldersToDelete);}, "...");
	this.taskManager.insertTask(function () {that.taskManager.startGroup(AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/DeleteFolders=Delete folders"), foldersToDelete.length);}, "...");
	
	var deleteFile = function (file)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("deleteFile - " + file);
		that.ftpConnection.del(file);
	}
	var deleteFiles = function (fileList)
	{
		if (AOM.AMG.verbose || log) AOM.AMG.Log.writeLine("deleteFile - " + fileList.length + " files");
		for (var i = 0; i < fileList.length; i++)
		{
			var file = fileList[i];
			var f = (function (x){return function (){deleteFile(x);}})(file);
			that.taskManager.insertTask(f, that.makeDisplayPath(new File(file)));
		}		
	}
	this.taskManager.insertTask(function () {deleteFiles(filesToDelete);}, "...");
	this.taskManager.insertTask(function () {
        that.taskManager.startGroup(
            AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/DeleteFiles=Delete files"), filesToDelete.length);
        }, "...");
	//this.taskManager.startGroup("Delete files", filesToDelete.length);

	// add the files and folders to their respective lists...
	this.inventoryRemoteFolder(folder, filesToDelete, foldersToDelete,
                               AOM.AMG.localize("$$$/MediaGallery/JavaScript/FTP/ScanFoldersToDelete=Scan folders to delete"));
}

AOM.Ftp.prototype.open = function(server, username, password, sourceFolder, destinationFolder, taskManager)
{
	// returns an item from AOM.Ftp.errorCodes
	this.server = AOM.PDFGenerator.combineDiacriticMarks(server);
	this.username = AOM.PDFGenerator.combineDiacriticMarks(username);
	this.password = AOM.PDFGenerator.combineDiacriticMarks(password);
	this.sourceFolder = new Folder(sourceFolder);
	this.destinationFolder = AOM.PDFGenerator.combineDiacriticMarks(destinationFolder); // leave as a string so Folder doesn't try to prepend ~/ or /c/...
	this.taskManager = taskManager;

	var that = this;
	this.ftpConnection = new FtpConnection();
	var connection = this.server;
	var connectionMethod = "SFTP";
	if (connection.toLowerCase().indexOf("sftp://") == 0)
	{
		connection = connection.substring(7);
	}
	else if (connection.toLowerCase().indexOf("ftp://") == 0)
	{
		connection = connection.substring(6);
		connectionMethod = "FTP";
	}

	var ftpSftpCheck = function ()
	{
		try {
			that.connectionMethod = undefined;
			that.ftpConnection.encryptedUsername = username;
			that.ftpConnection.encryptedPassword = password;
			if (connectionMethod == "SFTP")
				that.state = AOM.Ftp.openStates.beforeFirstOpen;
			else
				that.state = AOM.Ftp.openStates.beforeThirdOpen;
			that.taskManager.insertTask(tryOpen, AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."));
		}catch (e) {
			AOM.AMG.Log.writeError("AOM.Ftp.open caught - " + e);
			that.taskManager.insertTask(afterTrying, AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."));
		}
	}
	
	var tryOpen = function()
	{
		try{
			switch (that.state)
			{
				case AOM.Ftp.openStates.beforeFirstOpen:
					that.ftpConnection.url = "sftp://" + connection;
					that.ftpConnection.start();
					that.state = AOM.Ftp.openStates.waitFirstOpen;
					break;
        	
				case AOM.Ftp.openStates.waitFirstOpen:case AOM.Ftp.openStates.waitSecondOpen:case AOM.Ftp.openStates.waitThirdOpen:
					if (that.ftpConnection.finished) {
						if (that.ftpConnection.isConnected) {
							if (that.state == AOM.Ftp.openStates.waitThirdOpen)
								that.connectionMethod = "FTP";
							else
								that.connectionMethod = "SFTP";
							that.state = AOM.Ftp.openStates.finishTrying;
						}
						else {
							if (that.state == AOM.Ftp.openStates.waitFirstOpen && that.ftpConnection.error == AOM.Ftp.errorCodes.serverHostKeyFailed.value)
								that.state = AOM.Ftp.openStates.beforeSecondOpen;
							else {
								if (that.state == AOM.Ftp.openStates.waitThirdOpen)
									that.state = AOM.Ftp.openStates.finishTrying;
								else
									that.state = AOM.Ftp.openStates.beforeThirdOpen;
							}
						}
					}
					break;
		  	  
				case AOM.Ftp.openStates.beforeSecondOpen:
					that.ftpConnection.start();
					that.state = AOM.Ftp.openStates.waitSecondOpen;
					break;
        	
				case AOM.Ftp.openStates.beforeThirdOpen:
					that.ftpConnection.url = "ftp://" + connection;
					that.ftpConnection.start();
					that.state = AOM.Ftp.openStates.waitThirdOpen;
					break;
        	
				default:break;
			}
			
			if (that.state != AOM.Ftp.openStates.finishTrying)
				that.taskManager.retryLastTask (AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."), 50);
			else
				that.taskManager.insertTask(afterTrying, AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."));
		}catch (e) {
			AOM.AMG.Log.writeError("AOM.Ftp.open caught - " + e);
			that.taskManager.insertTask(afterTrying, AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."));
		}
	}
  
	var afterTrying = function()
	{
		try {
			if (that.connectionMethod == undefined || that.connectionMethod == "" || that.ftpConnection.cd == undefined) {
				var error = that.getErrorCode(that.ftpConnection.error);
				if (error == AOM.Ftp.errorCodes.noError ||
				error == AOM.Ftp.errorCodes.unknownError ||
				error == AOM.Ftp.errorCodes.ftpUnknownError)
					error = AOM.Ftp.errorCodes.couldntConnect;
				that.taskManager.stop({description:error.description, title:AOM.Ftp.zstrings.dialogTitle.description, button:AOM.Ftp.zstrings.buttonOK.description});
			} else {
				that.ftpConnection.passive = true;
				that.ftpConnection.binary = true;
				that.ftpConnection.async = true;
			}
		}catch (e){
			AOM.AMG.Log.writeError("AOM.Ftp.open caught - " + e);
		}
	}

	var dirCheck = function ()
	{
		// add the path to user's login home to the destination folder.
		// for example, login home may be /ftp/pub/user which would be
		// added to a destination folder of amg/gallery for an absolute
		// destination of /ftp/pub/user/amg/gallery.
		// if the destination folder is rooted (e.g., /amg/gallery) then
		// assume this is an absolute path and use as is.
		if (that.destinationFolder.substring(0, 1) != "/")
			that.destinationFolder = that.ftpConnection.cd + that.destinationFolder;
		if (that.ftpConnection.isDir(that.destinationFolder))
		{
			// found - make sure the user doesn't mind if we overwrite
			overwriteText = AOM.AMG.localize("$$$/MediaGallery/ftp/galleryFoundUploadFolder=A gallery with the same name was found in the upload folder.");
			overwriteText += "\n\n" + AOM.AMG.localize("$$$/MediaGallery/ftp/overwriteExistingGallery=Overwrite existing gallery?");
			var overwrite = AOM.confirm(overwriteText);
			if (overwrite == true)
			{
				that.removeFolder(that.destinationFolder);
				//that.ftpConnection.rmdir(that.destinationFolder);
			}
			else
			{
				that.taskManager.abort();
				that.close();
			}
		}
	}
	
	// reversed here but the ftp/sftp check will happen before the directory check - which the way it should be...
	taskManager.insertTask(dirCheck, AOM.AMG.localize("$$$/MediaGallery/ftp/checkDestFolder=Checking for destination folder."));
	taskManager.insertTask(ftpSftpCheck, AOM.AMG.localize("$$$/MediaGallery/ftp/tryViaSFTP=Trying to connect to FTP/SFTP server."));
}
AOM.Ftp.prototype.close = function()
{
	if (this.ftpConnection != undefined)
	{
		this.ftpConnection.close();
		this.ftpConnection = undefined;
	}
	else
		AOM.alert(AOM.AMG.localize("$$$/private/FtpCloseError=AOM.Ftp.close - ftpConnection == undefined."));
}
AOM.Ftp.prototype.makeDestination = function(fileOrFolder)
{
	// to make the destination we subtract the source folder from fileOrFolder to get 
	// the relative part and then add it to the destination folder
	var relativeUri = fileOrFolder.absoluteURI.substr(this.sourceFolder.absoluteURI.length);
	var destination = this.destinationFolder + relativeUri;
	return destination;
}
AOM.Ftp.prototype.status = function()
{
	if (this.ftpConnection == undefined)
	{
		AOM.alert(AOM.AMG.localize("$$$/private/ftpConnectionUndefined=ftpConnection == undefined."));
		return AOM.Ftp.statusCodes.error;
	}
	return (this.ftpConnection.isComplete == true) ?  AOM.Ftp.statusCodes.ftpComplete : AOM.Ftp.statusCodes.ftpInProgress;
}
AOM.Ftp.prototype.statusErrorDetail = function(error, detail)
{
	// append the supplied ftp error to an ftp error status
	// if detail provided, include it in error description
	var status = AOM.Ftp.statusCodes.error;
	status.error = error;
	if (typeof detail != 'undefined')
		status.error.description += " (" + detail + ")";
	return status;
}
AOM.Ftp.prototype.abort = function()
{
	while (this.status() != AOM.Ftp.statusCodes.ftpComplete)
	{
		; // wait for any ftp in progress to end...
	}
	this.close();
}

// use a dialog's onIdle mechanism to drive the ftp process
AOM.FtpDialog = function(ftp, dialog)
{
	if (dialog != undefined)
	{
		this.dialog = dialog;
		if (this.dialog.action == undefined || this.dialog.detail == undefined || this.dialog.progress == undefined || this.dialog.cancel == undefined)
			AOM.alert(AOM.AMG.localize("$$$/private/dialogIncomplete=FtpDialog - dialog is incomplete."));
	}
	else
		AOM.alert(AOM.AMG.localize("$$$/private/dialogUndefined=FtpDialog - dialog is undefined."));

	this.dialog.cancelClicked = false;
	this.dialog.currentScanFolder = undefined;
	this.dialog.ftp = ftp;
	this.dialog.lastError = undefined; // set to an item from AOM.Ftp.errorCodes on error
	this.dialog.nextScanFileIndex = 0;
	this.dialog.scanCount = 0;
	this.dialog.scanFiles = new Array();
	this.dialog.scanFolderQueue = new Array();
	this.dialog.done = false;
	this.dialog.progress.value = 0;
	this.dialog.progress.maxvalue = 100;

	this.dialog.idleActions = {
		none:	{ value : 0, description : "" },
		init:	{ value : 1, description : "$$$/MediaGallery/ftp/initializing=Initializing..." },
		open:	{ value : 2, description : "$$$/MediaGallery/ftp/openingConnection=Opening connection to FTP server..." },
		scan:	{ value : 3, description : "$$$/MediaGallery/ftp/creatingListToCopy=Creating list of files to copy to FTP server..." },
		copy:	{ value : 4, description : "$$$/MediaGallery/ftp/copyingFileToServer=Copying file to FTP server..." },
		done:	{ value : 5, description : "$$$/MediaGallery/ftp/finished=Finished." },
		cancel:	{ value : 6, description : "$$$/MediaGallery/ftp/canceled=Canceled." },
		error:	{ value : 7, description : "$$$/MediaGallery/ftp/error=Error." }
	};
	for (i in this.dialog.idleActions)
	{
		this.dialog.idleActions[i].description = AOM.AMG.localize(this.dialog.idleActions[i].description);
	}
	
	if (this.dialog.ftp == undefined)
	{
		AOM.alert(AOM.AMG.localize("$$$/private/ftpUndefined=FtpDialog() - this.ftp is undefined."));
		this.lastError = AOM.Ftp.errorCodes.tbcError;
		this.dialog.nextIdleAction = this.idleActions.error
	}
	else
		this.dialog.nextIdleAction = this.dialog.idleActions.init;

	this.dialog.onIdle = function()
	{
		if (this.cancelClicked)
		{
			this.nextIdleAction = this.idleActions.cancel;
		}
		switch (this.nextIdleAction)
		{
		 case this.idleActions.init:
			this.nextIdleAction = this.init();
			break;
		 case this.idleActions.scan:
			this.nextIdleAction = this.scan();
			break;
		 case this.idleActions.copy:
			this.nextIdleAction = this.copy();
			break;
		 case this.idleActions.done:
			this.done = true;
			break;
		 case this.idleActions.cancel:
			this.ftp.abort();
			this.window.close();
			this.done = true;
			break;
		 case this.idleActions.error:
			this.displayStatus({action:undefined, detail:this.lastError.description});
			this.ftp.abort();
			this.nextIdleAction = this.idleActions.done;
			break;
		 case this.idleActions.none:
			this.done = true;
			break;
		 default:
			AOM.alert(AOM.AMG.localize("$$$/private/atDefaultNextIdleAction=dialog.onIdle() @ default for switch(")+this.nextIdleAction+AGM.localize("$$$/private/RP=)"));
			this.lastError = AOM.Ftp.errorCodes.tbcError;
			this.nextIdleAction = this.idleActions.error
			break;
		}
		this.displayStatus({action:this.nextIdleAction.description, detail:undefined}, false);

		if (!this.done)
			this.notify("onIdle");
		else
			this.close();
	}
	this.dialog.init = function()
	{
		// check that we have a source folder
		var source = new Folder(this.ftp.sourceFolder);
		if (source.exists)
			this.scanFolderQueue.push(source);
		else
		{
			this.lastError = AOM.Ftp.errorCodes.sourceFolderDoesNotExist;
			return this.idleActions.error;
		}
		// everything's ok so proceed to 'scan'.
		// but first reset the progress bar and set the progress maxvalue 
		// to 100 since we don't know how many items there until after the scan.
		this.progress.value = 0;
		this.progress.maxvalue = 25;
		return this.idleActions.scan;
	}
	this.dialog.scan = function()
	{
		// scan the source directory an item per call
		// returns an item from idleActions
		var d = this; // store the dialog locally for update progress;
		var updateProgress = function()
		{
			d.displayStatus({action:undefined, detail:++d.scanCount});
			if (d.progress.value++ > 24)
				d.progress.value = 0;
		}
		if (this.scanFiles.length > 0 && this.nextScanFileIndex < this.scanFiles.length )
		{
			// if there are files to process, do one per call
			var file = this.scanFiles[this.nextScanFileIndex];
			if (file instanceof File == true)
			{
				var status = this.ftp.copy(file);
				if (status == AOM.Ftp.statusCodes.error)
				{
					this.lastError = status.error;
					return this.idleActions.error;
				}
				else
					updateProgress();
			}
			else
				this.scanFolderQueue.push(file);
			this.nextScanFileIndex++;
		}
		else
		{
			// otherwise, if there are folders to process, do one per call
			if (this.scanFolderQueue.length > 0)
			{
				// get the next folder
				var folder = this.scanFolderQueue.shift(); 
				// create it on the ftp server
				var status = this.ftp.copy(folder); 
				if (status == AOM.Ftp.statusCodes.error)
				{
					this.lastError = status.error;
					return this.idleActions.error;
				}
				else
					updateProgress();
				// and then add its files to the list to process
				this.scanFiles = folder.getFiles();
				this.nextScanFileIndex = 0;
			}
			else // at this point the scan is complete so proceed to 'copy'
			{
				// but before we do reset the progress bar and set maxvalue to the total items that ftp has to copy
				this.progress.value = 0;
				this.progress.maxvalue = this.ftp.copyQueue.length;
				return this.idleActions.copy;
			}
		}
		// not done yet so continue with 'scan'
		return this.idleActions.scan;
	}
	this.dialog.copy = function()
	{
		// keep calling ftp.copy() until it says its done
		// returns an item from idleActions
		if (this.ftp.copy() == AOM.Ftp.statusCodes.ftpComplete)
		{
			return this.idleActions.done;
		}
		else
		{
			if (this.detail.text != this.ftp.lastFtpFile)
			{
				this.displayStatus({action:undefined, detail:this.ftp.lastFtpFile});
			}
		}
		return this.idleActions.copy;
	}
	this.dialog.cancel.onClick = function()
	{
		this.parent.cancelClicked = true;
		if (this.parent.done)
			this.parent.notify("onIdle");
	}
} // AOM.FtpDialog()

AOM.FtpDialog.prototype.transfer = function()
{
	this.dialog.notify("onIdle");
	this.dialog.show();
}

