/// <summary>
        /// Add a crash passed in the payload as Xml to the database.
        /// </summary>
        /// <param name="id">Unused.</param>
        /// <returns>The row id of the newly added crash.</returns>
        public ActionResult AddCrash( int id )
        {
            using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(NewCrashId=" + id + ")" ) )
            {
                CrashRepository Crashes = new CrashRepository();

                CrashReporterResult NewCrashResult = new CrashReporterResult();
                NewCrashResult.ID = -1;

                try
                {
                    using( StreamReader Reader = new StreamReader( Request.InputStream, Request.ContentEncoding ) )
                    {
                        string Result = Reader.ReadToEnd();
                        CrashDescription NewCrash = XmlHandler.FromXmlString<CrashDescription>( Result );
                        NewCrashResult.ID = Crashes.AddNewCrash( NewCrash );
                        NewCrashResult.bSuccess = true;
                    }
                }
                catch( Exception Ex )
                {
                    NewCrashResult.Message = Ex.ToString();
                    NewCrashResult.bSuccess = false;
                }

                string ReturnResult = XmlHandler.ToXmlString<CrashReporterResult>( NewCrashResult );
                return Content( ReturnResult, "text/xml" );
            }
        }
        /// <summary>
        /// Add a crash passed in the payload as Xml to the database.
        /// </summary>
        /// <param name="id">Unused.</param>
        /// <returns>The row id of the newly added crash.</returns>
        public ActionResult AddCrash( int id )
        {
            CrashReporterResult NewCrashResult = new CrashReporterResult();
            NewCrashResult.ID = -1;

            try
            {
                using( StreamReader Reader = new StreamReader( Request.InputStream, Request.ContentEncoding ) )
                {
                    string Result = Reader.ReadToEnd();
                    CrashDescription NewCrash = XmlHandler.FromXmlString<CrashDescription>( Result );
                    NewCrashResult.ID = LocalCrashRepository.AddNewCrash( NewCrash );
                    NewCrashResult.bSuccess = true;
                }
            }
            catch( Exception Ex )
            {
                NewCrashResult.Message = Ex.ToString();
                NewCrashResult.bSuccess = false;
            }

            string ReturnResult = XmlHandler.ToXmlString<CrashReporterResult>( NewCrashResult );
            return Content( ReturnResult, "text/xml" );
        }
		/// <summary>
		/// The main listener callback to handle client requests.
		/// </summary>
		/// <param name="ClientRequest">The request from the client.</param>
		private void AsyncHandleHttpRequest( IAsyncResult ClientRequest )
		{
			try
			{
				HttpListenerContext Context = ServiceHttpListener.EndGetContext( ClientRequest );
				ServiceHttpListener.BeginGetContext( AsyncHandleHttpRequest, null );
				HttpListenerRequest Request = Context.Request;
				bool bIgnorePerfData = false;

				using( HttpListenerResponse Response = Context.Response )
				{
					// Extract the URL parameters
					string[] UrlElements = Request.RawUrl.Split( "/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );

					// http://*:57005/CrashReporter/CheckReport
					// http://*:57005/CrashReporter/CheckReportDetail
					CrashReporterResult ReportResult = new CrashReporterResult();
					if( UrlElements[0].ToLower() == "crashreporter" )
					{
						switch( UrlElements[1].ToLower() )
						{
						case "ping":
							ReportResult.bSuccess = true;
							break;
						case "checkreport":
							ReportResult = CheckReport( Context.Request );
							break;
						case "checkreportdetail":
							ReportResult = CheckReportDetail( Context.Request );
							break;
						case "uploadreportfile":
							ReportResult = ReceiveFile( Context.Request );
							bIgnorePerfData = true;
							break;
						case "uploadcomplete":
							ReportResult = UploadComplete( Context.Request );
							break;
						default:
							ReportResult.bSuccess = false;
							ReportResult.Message = "Invalid command: " + UrlElements[1];
							break;
						}
					}
					else
					{
						ReportResult.bSuccess = false;
						ReportResult.Message = "Invalid application: " + UrlElements[0] + " (expecting CrashReporter)";
					}

					string ResponseString = XmlHandler.ToXmlString<CrashReporterResult>( ReportResult );

					Response.SendChunked = true;
					Response.ContentType = "text/xml";

					byte[] Buffer = Encoding.UTF8.GetBytes( ResponseString );
					Response.ContentLength64 = Buffer.Length;
					Response.OutputStream.Write( Buffer, 0, Buffer.Length );

					Response.StatusCode = ( int )HttpStatusCode.OK;

					if( !bIgnorePerfData )
					{
						// Update the overhead data.
						Int64 ContentLenght = Response.ContentLength64 + Request.ContentLength64;
						Interlocked.Add( ref UploadsInProgress.CurrentReceivedData, ContentLenght );
					}
				}
			}
			catch( Exception Ex )
			{
				CrashReporterReceiverServicer.WriteEvent( "Error during async listen: " + Ex.Message );
			}
		}
		/// <summary>
		/// Rename to the temporary landing zone directory to the final location.
		/// </summary>
		/// <param name="Request">A request containing either the Report Id as a string or an XML representation of a CheckReportRequest class instance.</param>
		/// <returns>true if everything is renamed correctly.</returns>
		private CrashReporterResult UploadComplete(HttpListenerRequest Request)
		{
			var ReportResult = new CrashReporterResult();

			var RequestClass = new CheckReportRequest();
			RequestClass.ReportId = GetReportIdFromPostData(GetContentStreamString(Request));

			string IntermediatePathName = Path.Combine(FileReceiptPath, RequestClass.ReportId);

			if (!UploadsInProgress.TrySetReportComplete(RequestClass.ReportId))
			{
				ReportResult.Message = string.Format("Report \"{0}\" has already been completed", RequestClass.ReportId);
				ReportResult.bSuccess = false;
				return ReportResult;
			}

			DirectoryInfo DirInfo = new DirectoryInfo(IntermediatePathName);
			if (!DirInfo.Exists)
			{
				return ReportResult;
			}

			LandingZone.ReceiveReport(DirInfo, RequestClass.ReportId);
			ReportResult.bSuccess = true;

			int CurrentDay = DateTime.UtcNow.Day;
			if( CurrentDay > LastDay )
			{
				// Check the log and create a new one for a new day.
				CrashReporterReceiverServicer.Log.CreateNewLogFile();
				LastDay = CurrentDay;
			}

			return ReportResult;
		}
		/// <summary>
		/// Receive a file and write it to a temporary folder.
		/// </summary>
		/// <param name="Request">A request containing the file details in the headers (DirectoryName/FileName/FileLength).</param>
		/// <returns>true if the file is received successfully.</returns>
		/// <remarks>There is an arbitrary file size limit of CrashReporterConstants.MaxFileSizeToUpload as a simple exploit prevention method.</remarks>
		private CrashReporterResult ReceiveFile( HttpListenerRequest Request )
		{
			CrashReporterResult ReportResult = new CrashReporterResult();

			if( !Request.HasEntityBody )
			{
				return ReportResult;
			}

			// Take this opportunity to clean out folders for reports that were never completed
			CheckForAbandonedReports();

			// Make sure we have a sensible file size
			long BytesToReceive = 0;
			if (long.TryParse(Request.Headers["FileLength"], out BytesToReceive))
			{
				if (BytesToReceive >= CrashReporterConstants.MaxFileSizeToUpload)
				{
					return ReportResult;
				}
			}

			string DirectoryName = Request.Headers["DirectoryName"];
			string FileName = Request.Headers["FileName"];
			var T = Request.ContentLength64;
			bool bIsOverloaded = false;
			if( !UploadsInProgress.TryReceiveFile( DirectoryName, FileName, BytesToReceive, ref ReportResult.Message, ref bIsOverloaded ) )
			{
				CrashReporterReceiverServicer.WriteEvent(ReportResult.Message);
				ReportResult.bSuccess = false;
				return ReportResult;
			}

			string PathName = Path.Combine( FileReceiptPath, DirectoryName, FileName );

			// Recreate the file receipt directory, just in case.
			Directory.CreateDirectory( FileReceiptPath );

			// Create the folder to save files to
			DirectoryInfo DirInfo = new DirectoryInfo( Path.GetDirectoryName( PathName ) );
			DirInfo.Create();

			// Make sure the file doesn't already exist. If it does, delete it.
			if (File.Exists(PathName))
			{
				File.Delete(PathName);
			}

			FileInfo Info = new FileInfo( PathName );
			FileStream FileWriter = Info.OpenWrite();

			// Read in the input stream from the request, and write to a file
			long OriginalBytesToReceive = BytesToReceive;
			try
			{
				using (BinaryReader Reader = new BinaryReader(Request.InputStream))
				{
					byte[] Buffer = new byte[CrashReporterConstants.StreamChunkSize];

					while (BytesToReceive > 0)
					{
						int BytesToRead = Math.Min((int)BytesToReceive, CrashReporterConstants.StreamChunkSize);
						Interlocked.Add( ref UploadsInProgress.CurrentReceivedData, BytesToRead );

						int ReceivedChunkSize = Reader.Read(Buffer, 0, BytesToRead);

						if (ReceivedChunkSize == 0)
						{
							ReportResult.Message = string.Format("Partial file \"{0}\" received", FileName);
							ReportResult.bSuccess = false;
							CrashReporterReceiverServicer.WriteEvent(ReportResult.Message);

							return ReportResult;
						}

						BytesToReceive -= ReceivedChunkSize;
						FileWriter.Write(Buffer, 0, ReceivedChunkSize);
					}
				}
			}
			finally
			{
				FileWriter.Close();
				Request.InputStream.Close();

				UploadsInProgress.FileUploadAttemptDone( OriginalBytesToReceive, bIsOverloaded );

				bool bWriteMetadata = Path.GetExtension( FileName ) == ".ue4crash";
				if( bWriteMetadata )
				{
					string CompressedSize = Request.Headers["CompressedSize"];
					string UncompressedSize = Request.Headers["UncompressedSize"];
					string NumberOfFiles = Request.Headers["NumberOfFiles"];

					string MetadataPath = Path.Combine( FileReceiptPath, DirectoryName, Path.GetFileNameWithoutExtension( FileName ) + ".xml" );
					XmlHandler.WriteXml<FCompressedCrashInformation>( new FCompressedCrashInformation( CompressedSize, UncompressedSize, NumberOfFiles ), MetadataPath );
				}
			}

			ReportResult.bSuccess = true;
			return ReportResult;
		}
		/// <summary>
		/// Check to see if we wish to reject a report based on the WER meta data.
		/// </summary>
		/// <param name="Request">A request containing the XML representation of a WERReportMetadata class instance.</param>
		/// <returns>true if we do not reject.</returns>
		private CrashReporterResult CheckReportDetail( HttpListenerRequest Request )
		{
			CrashReporterResult ReportResult = new CrashReporterResult();

			string WERReportMetadataString = GetContentStreamString( Request );
			WERReportMetadata WERData = null;

			if( WERReportMetadataString.Length > 0 )
			{
				try
				{
					WERData = XmlHandler.FromXmlString<WERReportMetadata>( WERReportMetadataString );
				}
				catch( System.Exception Ex )
				{
					CrashReporterReceiverServicer.WriteEvent( "Error during XmlHandler.FromXmlString, probably incorrect encoding, trying to fix: " + Ex.Message );

					byte[] StringBytes = System.Text.Encoding.Unicode.GetBytes( WERReportMetadataString );
					string ConvertedXML = System.Text.Encoding.UTF8.GetString( StringBytes );
					WERData = XmlHandler.FromXmlString<WERReportMetadata>( ConvertedXML );
				}
			}

			if( WERData != null )
			{
				// Ignore crashes in the minidump parser itself
				ReportResult.bSuccess = true;
				if( WERData.ProblemSignatures.Parameter0.ToLower() == "MinidumpDiagnostics".ToLower() )
				{
					ReportResult.bSuccess = false;
					ReportResult.Message = "Rejecting MinidumpDiagnostics crash";
				}

				// Ignore Debug and DebugGame crashes
				string CrashingModule = WERData.ProblemSignatures.Parameter3.ToLower();
				if( CrashingModule.Contains( "-debug" ) )
				{
					ReportResult.bSuccess = false;
					ReportResult.Message = "Rejecting Debug or DebugGame crash";
				}
			}

			return ReportResult;
		}
		/// <summary>
		/// Check to see if a report has already been uploaded.
		/// </summary>
		/// <param name="Request">A request containing either the Report Id as a string or an XML representation of a CheckReportRequest class instance.</param>
		/// <returns>Result object, indicating whether the report has already been uploaded.</returns>
		private CrashReporterResult CheckReport(HttpListenerRequest Request)
		{
			var ReportResult = new CrashReporterResult();

			var RequestClass = new CheckReportRequest();
			RequestClass.ReportId = GetReportIdFromPostData(GetContentStreamString(Request));

			ReportResult.bSuccess = !LandingZone.HasReportAlreadyBeenReceived(RequestClass.ReportId);

			if( !ReportResult.bSuccess )
			{
				CrashReporterReceiverServicer.WriteEvent( string.Format( "Report \"{0}\" has already been received", RequestClass.ReportId ) );
			}

			return ReportResult;
		}
		/// <summary>
		/// Add a crash passed in the payload as Xml to the database.
		/// </summary>
		/// <param name="id">Unused.</param>
		/// <returns>The row id of the newly added crash.</returns>
		public ActionResult AddCrash( int id )
		{
            var newCrashResult = new CrashReporterResult();
            CrashDescription newCrash;
			newCrashResult.ID = -1;
			string payloadString;

            //Read the request payload
			try
			{
			    using (var reader = new StreamReader(Request.InputStream, Request.ContentEncoding))
			    {
			        payloadString = reader.ReadToEnd();
			        if (string.IsNullOrEmpty(payloadString))
			        {
			            FLogger.Global.WriteEvent(string.Format("Add Crash Failed : Payload string empty"));
			        }
			    }
			}
			catch (Exception ex)
			{
                var messageBuilder = new StringBuilder();
                messageBuilder.AppendLine("Error Reading Crash Payload");
                messageBuilder.AppendLine("Exception was:");
                messageBuilder.AppendLine(ex.ToString());

                FLogger.Global.WriteException(messageBuilder.ToString());

                newCrashResult.Message = messageBuilder.ToString();
                newCrashResult.bSuccess = false;
                    
                return Content(XmlHandler.ToXmlString<CrashReporterResult>(newCrashResult), "text/xml");
			}

            // De-serialise the payload string
			try
			{
			    newCrash = XmlHandler.FromXmlString<CrashDescription>(payloadString);
			}
			catch (Exception ex)
			{
                var messageBuilder = new StringBuilder();
                messageBuilder.AppendLine("Error Reading CrashDescription XML");
                messageBuilder.AppendLine("Exception was: ");
                messageBuilder.AppendLine(ex.ToString());

                FLogger.Global.WriteException(messageBuilder.ToString());

                newCrashResult.Message = messageBuilder.ToString();
                newCrashResult.bSuccess = false;

                return Content(XmlHandler.ToXmlString<CrashReporterResult>(newCrashResult), "text/xml");
			}
                
            //Add crash to database
			try
			{
			    var crash = CreateCrash(newCrash);
			    newCrashResult.ID = crash.Id;
			    newCrashResult.bSuccess = true;
			}
			catch (DbEntityValidationException dbentEx)
			{
                var messageBuilder = new StringBuilder();
                messageBuilder.AppendLine("Exception was:");
                messageBuilder.AppendLine(dbentEx.ToString());

			    var innerEx = dbentEx.InnerException;
			    while (innerEx != null)
			    {
			        messageBuilder.AppendLine("Inner Exception : " + innerEx.Message);
			        innerEx = innerEx.InnerException;
			    }

			    if (dbentEx.EntityValidationErrors != null)
			    {
			        messageBuilder.AppendLine("Validation Errors : ");
			        foreach (var valErr in dbentEx.EntityValidationErrors)
			        {
			            messageBuilder.AppendLine(valErr.ValidationErrors.Select(data => data.ErrorMessage).Aggregate((current, next) => current + "; /n" + next));
			        }
			    }

                messageBuilder.AppendLine("Received payload was:");
                messageBuilder.AppendLine(payloadString);

                FLogger.Global.WriteException(messageBuilder.ToString());

                newCrashResult.Message = messageBuilder.ToString();
                newCrashResult.bSuccess = false;
			}
			catch (SqlException sqlExc)
			{
				if (sqlExc.Number == -2)//If this is an sql timeout log the timeout and try again.
				{
					FLogger.Global.WriteEvent( string.Format( "AddCrash: Timeout" ) );
				}
				else
				{
                    var messageBuilder = new StringBuilder();
                    messageBuilder.AppendLine("Exception was:");
                    messageBuilder.AppendLine(sqlExc.ToString());
                    messageBuilder.AppendLine("Received payload was:");
                    messageBuilder.AppendLine(payloadString);

                    FLogger.Global.WriteException(messageBuilder.ToString());

					newCrashResult.Message = messageBuilder.ToString();
                    newCrashResult.bSuccess = false;
				}
			}
			catch (Exception ex)
			{
				var messageBuilder = new StringBuilder();
				messageBuilder.AppendLine("Exception was:");
				messageBuilder.AppendLine(ex.ToString());
				messageBuilder.AppendLine("Received payload was:");
				messageBuilder.AppendLine(payloadString);

                FLogger.Global.WriteException(messageBuilder.ToString());

				newCrashResult.Message = messageBuilder.ToString();
				newCrashResult.bSuccess = false;
			}

			string returnResult = XmlHandler.ToXmlString<CrashReporterResult>( newCrashResult );
                
			return Content( returnResult, "text/xml" );
		}