// distribution boxbackup-0.11.1 (svn version: 2821_2827)
// Box Backup, http://www.boxbackup.org/
// 
// Copyright (c) 2003-2010, Ben Summers and contributors.
// All rights reserved.
// 
// Note that this project uses mixed licensing. Any file with this license
// attached, or where the code LICENSE-DUAL appears on the first line, falls
// under this license. See the file COPYING.txt for more information.
// 
// This file is dual licensed. You may use and distribute it providing that you
// comply EITHER with the terms of the BSD license, OR the GPL license. It is
// not necessary to comply with both licenses, only one.
// 
// The BSD license option follows:
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  
// 3. Neither the name of the Box Backup nor the names of its contributors may
//    be used to endorse or promote products derived from this software without
//    specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// [http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29]
// 
// The GPL license option follows:
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
// --------------------------------------------------------------------------
//
// File
//		Name:    WinNamedPipeListener.h
//		Purpose: Windows named pipe socket connection listener
//			 for server
//		Created: 2008/09/30
//
// --------------------------------------------------------------------------

#ifndef WINNAMEDPIPELISTENER__H
#define WINNAMEDPIPELISTENER__H

#include <OverlappedIO.h>
#include <WinNamedPipeStream.h>

#include "ServerException.h"

#include "MemLeakFindOn.h"

// --------------------------------------------------------------------------
//
// Class
//		Name:    WinNamedPipeListener
//		Purpose: 
//		Created: 2008/09/30
//
// --------------------------------------------------------------------------
template<int ListenBacklog = 128>
class WinNamedPipeListener
{
private:
	std::auto_ptr<std::string> mapPipeName;
	std::auto_ptr<OverlappedIO> mapOverlapConnect;
	HANDLE mPipeHandle;

public:
	// Initialise
	WinNamedPipeListener()
	: mPipeHandle(INVALID_HANDLE_VALUE)
	{ }

private:
	WinNamedPipeListener(const WinNamedPipeListener &rToCopy)
	{ /* forbidden */ }

	HANDLE CreatePipeHandle(const std::string& rName)
	{
		std::string socket = WinNamedPipeStream::sPipeNamePrefix +
			rName;

		HANDLE handle = CreateNamedPipeA( 
			socket.c_str(),            // pipe name 
			PIPE_ACCESS_DUPLEX |       // read/write access 
			FILE_FLAG_OVERLAPPED,      // enabled overlapped I/O
			PIPE_TYPE_BYTE |           // message type pipe 
			PIPE_READMODE_BYTE |       // message-read mode 
			PIPE_WAIT,                 // blocking mode 
			ListenBacklog + 1,         // max. instances  
			4096,                      // output buffer size 
			4096,                      // input buffer size 
			NMPWAIT_USE_DEFAULT_WAIT,  // client time-out 
			NULL);                     // default security attribute 

		if (handle == INVALID_HANDLE_VALUE)
		{
			BOX_LOG_WIN_ERROR("Failed to create named pipe " <<
				socket);
			THROW_EXCEPTION(ServerException, SocketOpenError)
		}

		return handle;
	}

public:
	~WinNamedPipeListener()
	{
		Close();
	}

	void Close()
	{
		if (mPipeHandle != INVALID_HANDLE_VALUE)
		{
			if (mapOverlapConnect.get())
			{
				// outstanding connect in progress
				if (CancelIo(mPipeHandle) != TRUE)
				{
					BOX_LOG_WIN_ERROR("Failed to cancel "
						"outstanding connect request "
						"on named pipe");
				}

				mapOverlapConnect.reset();
			}

			if (CloseHandle(mPipeHandle) != TRUE)
			{
				BOX_LOG_WIN_ERROR("Failed to close named pipe "
					"handle");
			}

			mPipeHandle = INVALID_HANDLE_VALUE;
		}
	}

	// ------------------------------------------------------------------
	//
	// Function
	//		Name:    WinNamedPipeListener::Listen(std::string name)
	//		Purpose: Initialises socket name
	//		Created: 2003/07/31
	//
	// ------------------------------------------------------------------
	void Listen(const std::string& rName)
	{
		Close();
		mapPipeName.reset(new std::string(rName));
		mPipeHandle = CreatePipeHandle(rName);
	}

	// ------------------------------------------------------------------
	//
	// Function
	//		Name:    WinNamedPipeListener::Accept(int)
	//		Purpose: Accepts a connection, returning a pointer to
	//			 a class of the specified type. May return a
	//			 null pointer if a signal happens, or there's
	//			 a timeout. Timeout specified in
	//			 milliseconds, defaults to infinite time.
	//		Created: 2003/07/31
	//
	// ------------------------------------------------------------------
	std::auto_ptr<WinNamedPipeStream> Accept(int Timeout = INFTIM,
		const char* pLogMsgOut = NULL)
	{
		if(!mapPipeName.get())
		{
			THROW_EXCEPTION(ServerException, BadSocketHandle);
		}

		BOOL connected = FALSE;
		std::auto_ptr<WinNamedPipeStream> mapStream;

		if (!mapOverlapConnect.get())
		{
			// start a new connect operation
			mapOverlapConnect.reset(new OverlappedIO());
			connected = ConnectNamedPipe(mPipeHandle,
				&mapOverlapConnect->mOverlapped);

			if (connected == FALSE)
			{
				if (GetLastError() == ERROR_PIPE_CONNECTED)
				{
					connected = TRUE;
				}
				else if (GetLastError() != ERROR_IO_PENDING)
				{
					BOX_LOG_WIN_ERROR("Failed to connect "
						"named pipe");
					THROW_EXCEPTION(ServerException,
						SocketAcceptError);
				}
			}
		}

		if (connected == FALSE)
		{
			// wait for connection
			DWORD result = WaitForSingleObject(
				mapOverlapConnect->mOverlapped.hEvent,
				(Timeout == INFTIM) ? INFINITE : Timeout);

			if (result == WAIT_OBJECT_0)
			{
				DWORD dummy;

				if (!GetOverlappedResult(mPipeHandle,
					&mapOverlapConnect->mOverlapped,
					&dummy, TRUE))
				{
					BOX_LOG_WIN_ERROR("Failed to get "
						"overlapped connect result");
					THROW_EXCEPTION(ServerException,
						SocketAcceptError);
				}
				
				connected = TRUE;
			}
			else if (result == WAIT_TIMEOUT)
			{
				return mapStream; // contains NULL
			}
			else if (result == WAIT_ABANDONED)
			{
				BOX_ERROR("Wait for named pipe connection "
					"was abandoned by the system");
				THROW_EXCEPTION(ServerException,
					SocketAcceptError);
			}
			else if (result == WAIT_FAILED)
			{
				BOX_LOG_WIN_ERROR("Failed to wait for named "
					"pipe connection");
				THROW_EXCEPTION(ServerException,
					SocketAcceptError);
			}
			else
			{
				BOX_ERROR("Failed to wait for named pipe "
					"connection: unknown return code " <<
					result);
				THROW_EXCEPTION(ServerException,
					SocketAcceptError);
			}
		}

		ASSERT(connected == TRUE);

		mapStream.reset(new WinNamedPipeStream(mPipeHandle));
		mPipeHandle = CreatePipeHandle(*mapPipeName);
		mapOverlapConnect.reset();

		return mapStream;
	}
};

#include "MemLeakFindOff.h"

#endif // WINNAMEDPIPELISTENER__H
