context: /*****************************************************************************
  *
  * Copyright (c) [2009] Novell, Inc.
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public License as
  * published by the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, contact Novell, Inc.
  *
  * To contact Novell about this file by physical or electronic mail,
  * you may find current contact information at www.novell.com
  *
  *-----------------------------------------------------------------------------
  *
  *                 $Author: Russ Young
  *                 $Modified by: <Modifier>
  *                 $Mod Date: <Date Modified>
  *                 $Revision: 0.1
  *-----------------------------------------------------------------------------
  * This module is used to:
  *        <Description of the functionality of the file >
  *
  *
  *******************************************************************************/
  
  
  using System;
  using System.IO;
  using System.Web;
  using System.Web.SessionState;
  using System.Net;
  using System.Collections;
  using System.Threading;
  using Simias;
  
  using Simias.Event;
  using Simias.Client;
  using Simias.Client.Event;
  
  using Simias.Storage;
  using Simias.Sync;
  using Simias.Sync.Delta;
  using Simias.Authentication;
  
  namespace Simias.Sync.Http
  {
  	/// <summary>
  	/// Definitions for the http handler.
  	/// </summary>
  	public class SyncHeaders
  	{
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	SyncVersion = "SSyncVersion";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	CopyOffset = "SSyncOffset";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	Range = "SSyncRange";
  		/// <summary>
  		/// The Method that is to be performed.
  		/// </summary>
  		public static string	Method = "SSyncMethod";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	Blocks = "SSyncBlocks";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	BlockSize = "SSyncBlockSize";
  		/// <summary>
  		/// Used to specify how many objects are sent with the request/response.
  		/// </summary>
  		public static string	ObjectCount = "SSyncObjects";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	UserName = "SSyncUser";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	UserID = "SSyncUserID";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	CollectionName = "SSyncCollectionName";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	CollectionID = "SSyncCollectionID";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	SyncContext = "SSyncContext";
  		/// <summary>
  		/// 
  		/// </summary>
  		public static string	SyncRequester = "SSyncRequester";
  	}
  
  	/// <summary>
  	/// 
  	/// </summary>
  	public enum SyncMethod
  	{
  		/// <summary>
  		/// 
  		/// </summary>
  		StartSync = 1,
  		/// <summary>
  		/// 
  		/// </summary>
  		GetNextInfoList,
  		/// <summary>
  		/// 
  		/// </summary>
  		PutNodes,
  		/// <summary>
  		/// 
  		/// </summary>
  		GetNodes,
  		/// <summary>
  		/// 
  		/// </summary>
  		PutDirs,
  		/// <summary>
  		/// 
  		/// </summary>
  		GetDirs,
  		/// <summary>
  		/// 
  		/// </summary>
  		DeleteNodes,
  		/// <summary>
  		/// 
  		/// </summary>
  		OpenFilePut,
  		/// <summary>
  		/// 
  		/// </summary>
  		OpenFileGet,
  		/// <summary>
  		/// 
  		/// </summary>
  		GetHashMap,
  		/// <summary>
  		/// 
  		/// </summary>
  		GetHashMap2,
  		/// <summary>
  		/// 
  		/// </summary>
  		ReadFile,
  		/// <summary>
  		/// 
  		/// </summary>
  		WriteFile,
  		/// <summary>
  		/// 
  		/// </summary>
  		CopyFile,
  		/// <summary>
  		/// 
  		/// </summary>
  		CloseFile,
  		/// <summary>
  		/// 
  		/// </summary>
  		EndSync,
  		/// <summary>
  		/// 
  		/// </summary>
  		PutHashMap,
  	}
  
  	/// <summary>
  	/// Class used to talk to the HTTP SyncService (SyncHandler.ashx.cs). 
  	/// </summary>
  	public class HttpSyncProxy
  	{
  		double						serverVersion;
  		//Collection                                      collection;
  		string						collectionID;
  		string						collectionName;
  		string						domainId;
  		string						url;
  		string						userName;
  		string						userID;
  		SimiasConnection			connection;
  		
  		/// <summary>
  		/// Sorry Russ Used to log messages.
  		/// </summary>
  		private static readonly ISimiasLog log = 
  			SimiasLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  
  
  
          /// <summary>
  		/// Constructor to create a Http sync proxy from collection for particular user
  		/// </summary>
  		/// <param name="collection">Collection for which proxy has to be created</param>
  		/// <param name="userName">Name of the user</param>
  		/// <param name="userID">ID of the user</param>
  		public HttpSyncProxy( Collection collection, string userName, string userID )
  		{
  			collectionID = collection.ID;
  			collectionName = collection.Name;
  			domainId = collection.Domain;
                          try
                          {
                                  log.Debug("In HttpSyncProxy: The host ID is: {0}", collection.HostID);
                          }
                          catch(Exception e)
                          {
                                   log.Debug("Exception while getting the host ID: {0}--{1}", e.Message, e.StackTrace);
                          }
                          url = null;
                          if( collection.DataMovement == true )
                          {
                                  HostNode hn = collection.Host;
                                  if( hn != null)
                                  {
                                          url = hn.PublicUrl.ToString();
                                          url = url.TrimEnd('/') + "/SyncHandler.ashx";
                                          log.Debug("The url is: {0}. no hardcoding now...", url);
                                  }
                          }
                          if( url == null)
                                  url = collection.MasterUrl.ToString().TrimEnd('/') + "/SyncHandler.ashx";
                          this.userName = userName;
                          this.userID = userID;
                          if(Store.IsEnterpriseServer == false)
                          {
                                  log.Debug("Not an enterprise server. authtype is basic");
                                  connection = new SimiasConnection( domainId, userID, SimiasConnection.AuthType.BASIC, collection );
                          }
                          else
                          {
                                  log.Debug("Enterprise server connection. auth type ppk.");
                                  if( collection.Host != null)
                                  {
                                          log.Debug("Calling the host node simias connection...");
                                          connection = new SimiasConnection( domainId, userID, SimiasConnection.AuthType.PPK, collection.Host );
                                  }
                                  else
                                  {
                                          log.Debug("Calling the collection simias connection...");
                                          connection = new SimiasConnection( domainId, userID, SimiasConnection.AuthType.PPK, collection );
                                  }
                          }
                          //Clearing cookie for client connections
  //                      if(Store.IsEnterpriseServer == false)
  //                              WebState.ResetWebState(domainId);
                          connection.Authenticate();
  		}
  
          /// <summary>
          /// Reset the established connection
          /// </summary>
  		public void ResetConnection()
  		{
  			log.Debug("In Clear connection...");
  			if( connection == null)
  				return;
  			log.Debug("Calling clear connection of simiasconnection");
  			connection.ClearConnection();
  			log.Debug("Out of clear connection");
  		}
  
  		/// <summary>
  		/// Gets an Http Request object with the specified Sync method set.
  		/// </summary>
  		/// <param name="method">Sync method ie GET or POST</param>
  		/// <returns>Get the Http Web request object</returns>
  		private HttpWebRequest GetRequest(SyncMethod method)
  		{
  			HttpWebRequest request = connection.GetRequest("SyncHandler.ashx");
  			request.ContentType = "application/octet-stream";
  			request.Method = "POST";
  			WebHeaderCollection headers = request.Headers;
  			headers.Add(SyncHeaders.SyncVersion, HttpService.version);
  			headers.Add(SyncHeaders.Method, method.ToString());
  			try {
  				headers.Add(SyncHeaders.UserName, userName);
  			}catch
  			{
  				// On Windows client, for some username with multi-byte chars, 
  				// Exception occurs. However, we can ignore this as we would already
  				// have the user ID with us
  				log.Debug("Syncing multi byte char user name");
  			}
  			
  			headers.Add(SyncHeaders.UserID, userID);
  			try {
  				//TODO: could be removed?
  				headers.Add(SyncHeaders.CollectionName, collectionName);
  			}catch
  			{
  				//there is some issue with headers.Add with chinese characters,
  				//as collectionName is not used on the other side,
  				//ignoring the exception. This is fine on Linux.
  			}
  			headers.Add(SyncHeaders.CollectionID, collectionID);
  			headers.Add(Simias.Security.Web.AuthenticationService.Login.DomainIDHeader, domainId);
  			if(! Store.IsEnterpriseServer)
  			{
  				// If sync request is done from client then add this info
  				headers.Add(SyncHeaders.SyncRequester, userID);
  			}
  			return request;
  		}
  
          /// <summary>
          /// Returns the string format of the SyncEngineVersion
          /// In future we may want to return Major and Minor seperately
          /// </summary>
          public string SyncEngineVersion()
          {
                  return serverVersion.ToString();
          }
  
  		/// <summary>
  		/// Return the exception and include the returned details.
  		/// </summary>
  		/// <param name="response">The response from the server.</param>
  		/// <returns>An Exception.</returns>
  		private SimiasException GetException(HttpWebResponse response)
  		{
  			string message;
  			if (response.ContentLength != 0 && response.ContentType == "text/html")
  			{
  				StreamReader reader = new StreamReader(response.GetResponseStream());
  				try
  				{
  					char[] info = new char[response.ContentLength];
  					reader.Read(info, 0, info.Length);
  					message = string.Format("{0} \n {1}", response.StatusDescription, info);
  					return new SimiasException(message);
  				}
  				catch {/* Fall through and return the default exception.*/ }
  				finally
  				{
  					reader.Close();
  				}
  			}
  			return new SimiasException(response.StatusDescription);
  		}
  		
  		/// <summary>
  		/// Start a sync pass.
  		/// </summary>
  		/// <param name="si">The StartSyncInfo to control the sync.</param>
  		public void StartSync(ref StartSyncInfo si)
  		{
  			serverVersion = 1.0;
  			HttpWebRequest request = GetRequest(SyncMethod.StartSync);
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			si.Serialize(writer);
  			writer.Close();
  
  			HttpWebResponse response = null;
  			response = connection.GetResponse(request);
  			
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					CookieCollection cc = request.CookieContainer.GetCookies(new Uri(url));
  					foreach (Cookie cookie in cc)
  					{
  						cookie.Expired = true;
  					}
  					throw GetException(response);
  				}
  				// Get the interface version.
  				string vString = response.Headers.Get(SyncHeaders.SyncVersion);
  				if (vString != null)
  					serverVersion = double.Parse(vString, System.Globalization.NumberFormatInfo.InvariantInfo);
  				// Now get the StartSyncInfo back;
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				si = new StartSyncInfo(reader);
  				request.CookieContainer.Add(response.Cookies);
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Returns the next set of NodeInfos.
  		/// </summary>
  		/// <returns></returns>
  		public SyncNodeInfo[] GetNextInfoList(out string context)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.GetNextInfoList);
  			request.ContentLength = 0;
  			request.GetRequestStream().Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Get the context;
  				context = response.Headers.Get(SyncHeaders.SyncContext);
  				// Get the size of the returned array.
  				int count = int.Parse(response.Headers.Get(SyncHeaders.ObjectCount));
                  if (count == 0)
  					return null;
  				SyncNodeInfo[] infoArray = new SyncNodeInfo[count];
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				for (int i = 0; i < count; i++)
  				{
  					infoArray[i] = new SyncNodeInfo(reader);
  				}
  
  				return infoArray;
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  		
  		/// <summary>
  		/// Sync the supplied nodes to the server.
  		/// </summary>
  		/// <param name="nodes">The nodes to sync.</param>
  		/// <returns>The status of the sync.</returns>
  		public SyncNodeStatus[] PutNodes(SyncNode[] nodes)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.PutNodes);
  			return PutNodes(request, nodes);
  		}
  		
  		/// <summary>
  		/// Download the nodes from the server.
  		/// </summary>
  		/// <param name="nids">The nodes to sync down.</param>
  		/// <returns>The nodes requested.</returns>
  		public SyncNode[] GetNodes(string[] nids)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.GetNodes);
  			return GetNodes(request, nids);
  		}
  
  		/// <summary>
  		/// Sync the Directory nodes to the server.
  		/// </summary>
  		/// <param name="nodes">The nodes to sync.</param>
  		/// <returns>The status of the sync.</returns>
  		public SyncNodeStatus[] PutDirs(SyncNode[] nodes)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.PutDirs);
  			return PutNodes(request, nodes);
  		}
  
  		/// <summary>
  		/// Sync the supplied nodes to the server. The method is already set.
  		/// This is used by Generic and Directory nodeTypes.
  		/// </summary>
  		/// <param name="request">The HttpWebRequest to use.</param>
  		/// <param name="nodes">The nodes to sync.</param>
  		/// <returns>The status of the sync.</returns>
  		private SyncNodeStatus[] PutNodes(HttpWebRequest request, SyncNode[] nodes)
  		{
  			WebHeaderCollection headers = request.Headers;
  			// Get the length to send.
  			headers.Add(SyncHeaders.ObjectCount, nodes.Length.ToString());
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			foreach (SyncNode sNode in nodes)
  			{
  				sNode.Serialize(writer);
  			}
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get the status.
  				int count = int.Parse(response.Headers.Get(SyncHeaders.ObjectCount));
  				SyncNodeStatus[] status = new SyncNodeStatus[count];
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				for (int i = 0; i < status.Length; ++i)
  				{
  					status[i] = new SyncNodeStatus(reader);
  				}
  				return status;
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Download the Directory nodes from the server.
  		/// </summary>
  		/// <param name="nids">The nodes to sync down.</param>
  		/// <returns>The nodes requested.</returns>
  		public SyncNode[] GetDirs(string[] nids)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.GetDirs);
  			return GetNodes(request, nids);
  		}
  
  		/// <summary>
  		/// Download the nodes from the server. The method is already set.
  		/// This is used by Generic and Directory nodeTypes.
  		/// </summary>
  		/// <param name="request">The request object.</param>
  		/// <param name="nids">The nodes to sync down.</param>
  		/// <returns>The nodes requested.</returns>
  		private SyncNode[] GetNodes(HttpWebRequest request, string[] nids)
  		{
  			WebHeaderCollection headers = request.Headers;
  			request.ContentLength = 16 * nids.Length;
  			headers.Add(SyncHeaders.ObjectCount, nids.Length.ToString());
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			foreach (string nid in nids)
  			{
  				writer.Write(new Guid(nid).ToByteArray());
  			}
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get Nodes.
  				SyncNode[] nodes = new SyncNode[nids.Length];
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				for (int i = 0; i < nodes.Length; ++i)
  				{
  					nodes[i] = new SyncNode(reader);
  				}
  				return nodes;
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Delete the nodes on the server.
  		/// </summary>
  		/// <param name="nodeIDs">The array of node IDs to delete.</param>
  		/// <returns>An array of status codes.</returns>
  		public SyncNodeStatus[] DeleteNodes(string[] nodeIDs)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.DeleteNodes);
  			WebHeaderCollection headers = request.Headers;
  			// Get the length to send.
  			request.ContentLength = nodeIDs.Length * 16;
  			headers.Add(SyncHeaders.ObjectCount, nodeIDs.Length.ToString());
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			foreach (string nodeID in nodeIDs)
  			{
  				writer.Write(new Guid(nodeID).ToByteArray());
  			}
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get the status.
  				int count = int.Parse(response.Headers.Get(SyncHeaders.ObjectCount));
  				SyncNodeStatus[] status = new SyncNodeStatus[count];
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				for (int i = 0; i < count; ++i)
  				{
  					status[i] = new SyncNodeStatus(reader);
  				}
  				return status;
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Opens the file to sync up to the server.
  		/// </summary>
  		/// <param name="node">The node to sync.</param>
  		/// <returns>The Status of the sync.</returns>
  		public SyncStatus OpenFilePut(SyncNode node)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.OpenFilePut);
  			//WebHeaderCollection headers = request.Headers;
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			node.Serialize(writer);
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get the Status.
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				return (SyncStatus)reader.ReadByte();
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Opens the file to sync down from the server.
  		/// </summary>
  		/// <param name="nodeID">The ID of the node.</param>
  		/// <returns>The node that represents the file.</returns>
  		public SyncNode OpenFileGet(string nodeID)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.OpenFileGet);
  			//WebHeaderCollection headers = request.Headers;
  			request.ContentLength = 16;
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			writer.Write(new Guid(nodeID).ToByteArray());
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get the SyncNode.
  				if (response.ContentLength != 0)
  				{
  					BinaryReader reader = new BinaryReader(response.GetResponseStream());
  					return new SyncNode(reader);
  				}
  				return null;
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Get the HashMap for the opened file.
  		/// </summary>
  		/// <returns>The HashData[]</returns>
  		public HashData[] GetHashMap(out int blockSize)
  		{
  			try
  			{
  				HttpWebRequest request = GetRequest(SyncMethod.GetHashMap2);
  				request.ContentLength = 0;
  				request.GetRequestStream().Close();
  				HttpWebResponse response = connection.GetResponse(request);
  				try
  				{
  					if (response.StatusCode != HttpStatusCode.OK)
  					{
  						throw GetException(response);
  					}
  					// Now get the HashData.
  					blockSize = 0;
  					int count = int.Parse(response.Headers.Get(SyncHeaders.ObjectCount));
  					string sBlockSize = response.Headers.Get(SyncHeaders.BlockSize);
  					if (sBlockSize != null)
  					{
  						blockSize = int.Parse(sBlockSize);
  					}
  					BinaryReader reader = new BinaryReader(response.GetResponseStream());
  					return HashMap.DeSerializeHashMap(reader, count);
  				}
  				finally
  				{
  					response.Close();
  				}
  			}
  			catch
  			{
  				blockSize = 0;
  				return new HashData[0];
  			}
  		}
  
  		/// <summary>
  		/// Reads the specified blocks from the server.
  		/// </summary>
  		/// <param name="seg">The range of blocks to read.</param>
  		/// <param name="blockSize">The block size.</param>
  		/// <returns>The response to the read. The data is in the responseStream.
  		/// This response must be closed.</returns>
  		public HttpWebResponse ReadFile(DownloadSegment seg, int blockSize)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.ReadFile);
  			WebHeaderCollection headers = request.Headers;
  			headers.Add(SyncHeaders.BlockSize, blockSize.ToString());
  			request.ContentLength = DownloadSegment.InstanceSize;
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			seg.Serialize(writer);
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			if (response.StatusCode == HttpStatusCode.OK)
  			{
  				return response;
  			}
  			// If we get here we had an error.
  			response.Close();
  			throw GetException(response);
  		}
  
  		/// <summary>
  		/// Write the file data to the server.
  		/// </summary>
  		/// <param name="stream">The stream containing the data.</param>
  		/// <param name="offset">The offset to write at.</param>
  		/// <param name="count">The number of bytes to write.</param>
  	        /// <param name="encryptionAlgorithm">Encryption Algorithm.</param>
          	/// <param name="EncryptionKey">Encryption key.</param>
  		public void WriteFile(StreamStream stream, long offset, int count, string encryptionAlgorithm, string EncryptionKey)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.WriteFile);
  			WebHeaderCollection headers = request.Headers;
  			int bytesRead;
  			
  			Stream rStream = request.GetRequestStream();
                       
  			if (encryptionAlgorithm !=null)
  			{
  				//bytesRead will be more if padding doen in encryption
  				bytesRead = stream.Read(rStream, count, encryptionAlgorithm, EncryptionKey);
  			}
  			else
  				bytesRead = stream.Read(rStream, count);
  
  			headers.Add(SyncHeaders.Range, offset.ToString() + "-" + ((long)(offset +bytesRead)).ToString());
  						
  			if (bytesRead < count)
  				throw new SimiasException("Could not write all data.");
  
  			rStream.Close();
  			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  		
  		/// <summary>
  		/// Put the HashMap for the syned file.
  		/// </summary>
  		public void PutHashMap(FileStream mapStream, int  length)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.PutHashMap);
  
  			// set the count
  			request.ContentLength = length; 
  
  			// Get the length to send.
  			WebHeaderCollection headers = request.Headers;
  			headers.Add(SyncHeaders.ObjectCount, length.ToString());
  
  			// point the request stream
  			StreamStream ss = new StreamStream(request.GetRequestStream());
  			//write into the request stream
  			ss.Write(mapStream, length);	
  			ss.Close();	
  			
  			// get the response
  			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  
  			try 
  			{
  				if(response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  			}
  			finally
  			{
  				response.Close();
  			}				
  		}
  
  		/// <summary>
  		/// Called to copy data from the original file on the server to the new file.
  		/// </summary>
  		/// <param name="copyArray">The array of blocks and offsets to copy from the original file.</param>
  		/// <param name="blockSize">blockSize.</param>
  		public void CopyFile(ArrayList copyArray, int blockSize)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.CopyFile);
  			WebHeaderCollection headers = request.Headers;
  			headers.Add(SyncHeaders.ObjectCount, copyArray.Count.ToString());
  			headers.Add(SyncHeaders.BlockSize, blockSize.ToString());
  			request.ContentLength = copyArray.Count * BlockSegment.InstanceSize;
  			// TODO: This is a kluge to work around a Apache/mod_ssl memory problem.
  			byte[] copyData = new byte[request.ContentLength];
  			BinaryWriter writer = new BinaryWriter(new MemoryStream(copyData));
  			//BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			foreach (BlockSegment seg in copyArray)
  			{
  				seg.Serialize(writer);
  			}
  			writer.Close();
  			Stream rStream = request.GetRequestStream();
  			rStream.Write(copyData, 0, copyData.Length);
  			rStream.Close();
  			// TODO: End.
  
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Called to close a downloaded file.
  		/// </summary>
  		public void CloseFile()
  		{
  			/*SyncNodeStatus stat = */CloseFile(false);
  		}
  
  		/// <summary>
  		/// Called to close the file being synced.
  		/// </summary>
  		/// <param name="commit">True if the file should be commited.</param>
  		/// <returns>The sync status.</returns>
  		public SyncNodeStatus CloseFile(bool commit)
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.CloseFile);
  			//WebHeaderCollection headers = request.Headers;
  			request.ContentLength = 1;
  			BinaryWriter writer = new BinaryWriter(request.GetRequestStream());
  			writer.Write(commit);
  			writer.Close();
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  				if (response.StatusCode != HttpStatusCode.OK)
  				{
  					throw GetException(response);
  				}
  				// Now get the status.
  				BinaryReader reader = new BinaryReader(response.GetResponseStream());
  				return new SyncNodeStatus(reader);
  			}
  			finally
  			{
  				response.Close();
  			}
  		}
  
  		/// <summary>
  		/// Called to end this sync cycle.
  		/// </summary>
  		public void EndSync()
  		{
  			HttpWebRequest request = GetRequest(SyncMethod.EndSync);
  			request.ContentLength = 0;
  			request.GetRequestStream().Close();
  			request.KeepAlive = false;
  			HttpWebResponse response = connection.GetResponse(request);
  			try
  			{
  
gt: 				if (response.StatusCode != HttpStatusCode.OK)