Ejemplo n.º 1
0
int main (int argc, char **argv) 
{
	OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n"
				   "(transport build: OTMessage -> TCP -> SSL)\n"
				   "NOTE: IF YOU PREFER TO USE XmlRpc with Http transport, then rebuild using:\n"
				   "\"make -f Makefile.rpc\" (but make sure the client is built the same way.)\n\n", 
				   OTLog::Version());
	
	// -----------------------------------------------------------------------
	
	// This object handles all the actual transaction notarization details.
	// (This file you are reading is a wrapper for OTServer, which adds the transport layer.)
	OTServer theServer;

	// -----------------------------------------------------------------------
	
#ifdef _WIN32
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD( 2, 2 );
	int nWSA = WSAStartup( wVersionRequested, &wsaData );
	OT_ASSERT_MSG(0 != nWSA, "Error calling WSAStartup.\n");	
#endif

	OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version());
	
	// -----------------------------------------------------------------------

	OTString strCAFile, strDHFile, strKeyFile, strSSLPassword;

	if (argc < 2)
	{
		OTLog::vOutput(0, "Usage:  transaction <SSL-password> <data_folder path>\n\n"
				"(Password defaults to '%s' if left blank on the command line.)\n"
				"(Folder defaults to '%s' if left blank.)\n", KEY_PASSWORD, SERVER_PATH_DEFAULT);
		
		strSSLPassword.Set(KEY_PASSWORD);
		OTLog::SetMainPath(SERVER_PATH_DEFAULT);
	}
	else if (argc < 3)
	{
		OTLog::vOutput(0, "Usage:  transaction <SSL-password> <data_folder path>\n\n"
				"(Folder defaults to '%s' if left blank.)\n", SERVER_PATH_DEFAULT);
		
		strSSLPassword.Set(argv[1]);
		OTLog::SetMainPath(SERVER_PATH_DEFAULT);
	}
	else 
	{
		strSSLPassword.Set(argv[1]);
		OTLog::SetMainPath(argv[2]);
	}	

	strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE);
	strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE);
	strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE);

	
	// -----------------------------------------------------------------------

	// Init loads up server's nym so it can decrypt messages sent in envelopes.
	// It also does various other initialization work.
	//
	// (Envelopes prove that ONLY someone who actually had the server contract,
	// and had loaded it into his wallet, could ever connect to the server or 
	// communicate with it. And if that person is following the contract, there
	// is only one server he can connect to, and one key he can use to talk to it.)
	
	OTLog::Output(0, 
				  "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n"
				  "its private key. (Default password is \"test\".)\n");

	// Initialize SSL -- This MUST occur before any Private Keys are loaded!
    SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first.

	theServer.Init(); // Keys, etc are loaded here.

	// -----------------------------------------------------------------------

	// We're going to listen on the same port that is listed in our server contract.
	//
	//
	OTString	strHostname;	// The hostname of this server, according to its own contract.
	int			nPort=0;		// The port of this server, according to its own contract.

	OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort),
				  "Unable to find my own connect info (which is in my server contract BTW.)\n");
	
	const int	nServerPort = nPort;
	
	// -----------------------------------------------------------------------
	
	
	SFSocket *socket = NULL;
	listOfConnections theConnections;
	
    // Alloc Socket
	socket = SFSocketAlloc();
	OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n");
	
    // Initialize SSL Socket
	int nSFSocketInit = SFSocketInit(socket,
					 strCAFile.Get(), 
					 strDHFile.Get(), 
					 strKeyFile.Get(),
					 strSSLPassword.Get(), 
									 NULL);
	
	OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n");
	
    // Listen on Address/Port
	int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort);
	
	OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n");
	
	
	theServer.ActivateCron();
	
    do 
	{
        SFSocket * clientSocket = NULL;

        // Accept Client Connection
        if (NULL != (clientSocket = SFSocketAccept(socket)))
		{
			OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer);
			theConnections.push_back(pClient);
			OTLog::Output(0, "Accepting new connection.\n");
		}
		
		// READ THROUGH ALL CLIENTS HERE, LOOP A LIST
		// AND process in-buffer onto our list of messages.
		
		OTClientConnection * pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				// Here we read the bytes from the pipe into the client's buffer
				// As necessary this function also processes those bytes into OTMessages
				// and adds them to the Input List for that client.
				pConnection->ProcessBuffer();
			}
		}
		
		
		
		// Now loop through them all again, and process their messages onto the reply list.
		
		pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				OTMessage * pMsg = NULL;
				
				while (pMsg = pConnection->GetNextInputMessage())
				{
					OTMessage * pReply = new OTMessage;
					
					OT_ASSERT(NULL != pReply);
					
					if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection))
					{
						OTLog::vOutput(0, "Successfully processed user command: %s.\n", 
								pMsg->m_strCommand.Get());
						
						pConnection->AddToOutputList(*pReply);
					}
					else
					{
						OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n");
						delete pReply;
						pReply = NULL;
					}
					
					// I am responsible to delete this here.
					delete pMsg;
					pMsg = NULL;
				}
			}
		}
		
		
		// Now loop through them all again, and process their replies down the pipe
		
		pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				OTMessage * pMsg = NULL;
				
				while (pMsg = pConnection->GetNextOutputMessage())
				{
					pConnection->ProcessReply(*pMsg);
						
					// I am responsible to delete this here.
					delete pMsg;
					pMsg = NULL;
				}
			}
		}
		
		
		
		
		
		// The Server now processes certain things on a regular basis.
		// This method call is what gives it the opportunity to do that.
	
		theServer.ProcessCron();
		
		
		
		
		// Now go to sleep for a tenth of a second.
		// (Main loop processes ten times per second, currently.)
		
		OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10)
		
		
		
		
    } while (1);

    // Close and Release Socket Resources
    SFSocketRelease(socket);

#ifdef _WIN32
	WSACleanup();
#endif

    return(0);
}
Ejemplo n.º 2
0
int main (int argc, char **argv) 
{
	OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n"
				   "(transport build: OTMessage -> TCP -> SSL)\n"
				   "IF YOU PREFER TO USE XmlRpc with HTTP, then rebuild from main folder like this:\n\n"
				   "cd ..; make clean; make rpc\n\n", 
				   OTLog::Version());
	
	// -----------------------------------------------------------------------
	
	// This object handles all the actual transaction notarization details.
	// (This file you are reading is a wrapper for OTServer, which adds the transport layer.)
	OTServer theServer;

	// -----------------------------------------------------------------------
	
#ifdef _WIN32
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD( 2, 2 );
	int err = WSAStartup( wVersionRequested, &wsaData );

	/* Tell the user that we could not find a usable		*/
	/* Winsock DLL.											*/		

	OT_ASSERT_MSG((err == 0), "WSAStartup failed!\n");


	/*	Confirm that the WinSock DLL supports 2.2.			*/
	/*	Note that if the DLL supports versions greater		*/
	/*	than 2.2 in addition to 2.2, it will still return	*/
	/*	2.2 in wVersion since that is the version we		*/
	/*	requested.											*/

	bool bWinsock = (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2);

	/* Tell the user that we could not find a usable */
	/* WinSock DLL.                                  */

	if (!bWinsock) WSACleanup();  // do cleanup.
	OT_ASSERT_MSG((!bWinsock), "Could not find a usable version of Winsock.dll\n");

	/* The Winsock DLL is acceptable. Proceed to use it. */
	/* Add network programming using Winsock here */
	/* then call WSACleanup when done using the Winsock dll */
	OTLog::vOutput(0,"The Winsock 2.2 dll was found okay\n");
#endif

	OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version());
	
	// -----------------------------------------------------------------------
	// The beginnings of an INI file!!
	
#ifndef _WIN32 // if UNIX (NOT windows)
	wordexp_t exp_result;
	wordexp(OT_INI_FILE_DEFAULT, &exp_result, 0);
	
	const OTString strIniFileDefault(exp_result.we_wordv[0]);
	
	wordfree(&exp_result); 
#else
	const OTString strIniFileDefault(OT_INI_FILE_DEFAULT);
#endif
	
	OTString strPath(SERVER_PATH_DEFAULT);
	
	{
		CSimpleIniA ini;
		
		SI_Error rc = ini.LoadFile(strIniFileDefault.Get());
		
		if (rc >=0)
		{
			const char * pVal = ini.GetValue("paths", "server_path", SERVER_PATH_DEFAULT); // todo stop hardcoding.
			
			if (NULL != pVal)
			{
				strPath.Set(pVal);
				OTLog::vOutput(0, "Reading ini file (%s). \n Found Server data_folder path: %s \n", 
							   strIniFileDefault.Get(), strPath.Get());
			}
			else
			{
				strPath.Set(SERVER_PATH_DEFAULT);
				OTLog::vOutput(0, "Reading ini file (%s): \n Failed reading Server data_folder path. Using: %s \n", 
							   strIniFileDefault.Get(), strPath.Get());
			}
		}
		else 
		{
			strPath.Set(SERVER_PATH_DEFAULT);
			OTLog::vOutput(0, "Unable to load ini file (%s) to find data_folder path\n Will assume that server data_folder is at path: %s \n", 
						   strIniFileDefault.Get(), strPath.Get());
		}
	}
	// -----------------------------------------------------------------------
	
	OTString strCAFile, strDHFile, strKeyFile;
	//, strSSLPassword;
	
	if (argc < 1)
	{
		OTLog::vOutput(0, "\n==> USAGE:    %s  [absolute_path_to_data_folder]\n\n"
					   
//		OTLog::vOutput(0, "\n==> USAGE:    %s  <SSL-password>  [absolute_path_to_data_folder]\n\n"
#if defined (FELLOW_TRAVELER)					   
//					   "(Password defaults to '%s' if left blank.)\n"
					   "(Folder defaults to '%s' if left blank.)\n"
#else
					   "The test password is always 'test'.\n"
					   "OT will try to read the data_folder path from your ini file. If you prefer\n"
					   "to specify it at the command line, and you want to see the exact path, type:\n"
					   "     cd data_folder && pwd && cd ..\n"
#endif
					   "\n\n", argv[0]
#if defined (FELLOW_TRAVELER)					   
//					   , KEY_PASSWORD, 
					   , strPath.Get()
#endif					   
					   );
		
#if defined (FELLOW_TRAVELER)
//		strSSLPassword.Set(KEY_PASSWORD);
		OTLog::SetMainPath(strPath.Get());
#else
		exit(1);
#endif
	}
	else if (argc < 2)
	{		
//		strSSLPassword.Set(argv[1]);
		OTLog::SetMainPath(strPath.Get());
	}
	else 
	{
//		strSSLPassword.Set(argv[1]);
		OTLog::SetMainPath(argv[1]); // formerly [2]
	}
	
	OTLog::vOutput(0, "Using as path to data folder:  %s\n", OTLog::Path());

	strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE);
	strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE);
	strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE);
	
	
	// -----------------------------------------------------------------------

	// Init loads up server's nym so it can decrypt messages sent in envelopes.
	// It also does various other initialization work.
	//
	// (Envelopes prove that ONLY someone who actually had the server contract,
	// and had loaded it into his wallet, could ever connect to the server or 
	// communicate with it. And if that person is following the contract, there
	// is only one server he can connect to, and one key he can use to talk to it.)
	
	OTLog::Output(0, 
				  "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n"
				  "its private key. (Default password is \"test\".)\n");

	// Initialize SSL -- This MUST occur before any Private Keys are loaded!
    SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first.

	theServer.Init(); // Keys, etc are loaded here.

	// -----------------------------------------------------------------------

	// We're going to listen on the same port that is listed in our server contract.
	//
	//
	OTString	strHostname;	// The hostname of this server, according to its own contract.
	int			nPort=0;		// The port of this server, according to its own contract.

	OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort),
				  "Unable to find my own connect info (which is in my server contract BTW.)\n");
	
	const int	nServerPort = nPort;
	
	// -----------------------------------------------------------------------
	
	
	SFSocket *socket = NULL;
	listOfConnections theConnections;
	
    // Alloc Socket
	socket = SFSocketAlloc();
	OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n");
	
    // Initialize SSL Socket
	int nSFSocketInit = SFSocketInit(socket,
					 strCAFile.Get(), 
					 strDHFile.Get(), 
					 strKeyFile.Get(),
					 strSSLPassword.Get(), 
									 NULL);
	
	OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n");
	
    // Listen on Address/Port
	int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort);
	
	OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n");
	
	
	theServer.ActivateCron();
	
    do 
	{
        SFSocket * clientSocket = NULL;

        // Accept Client Connection
        if (NULL != (clientSocket = SFSocketAccept(socket)))
		{
			OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer);
			theConnections.push_back(pClient);
			OTLog::Output(0, "Accepting new connection.\n");
		}
		
		// READ THROUGH ALL CLIENTS HERE, LOOP A LIST
		// AND process in-buffer onto our list of messages.
		
		OTClientConnection * pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				// Here we read the bytes from the pipe into the client's buffer
				// As necessary this function also processes those bytes into OTMessages
				// and adds them to the Input List for that client.
				pConnection->ProcessBuffer();
			}
		}
		
		
		
		// Now loop through them all again, and process their messages onto the reply list.
		
		pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				OTMessage * pMsg = NULL;
				
				while (pMsg = pConnection->GetNextInputMessage())
				{
					OTMessage * pReply = new OTMessage;
					
					OT_ASSERT(NULL != pReply);
					
					if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection))
					{
						OTLog::vOutput(0, "Successfully processed user command: %s.\n", 
								pMsg->m_strCommand.Get());
						
						pConnection->AddToOutputList(*pReply);
					}
					else
					{
						OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n");
						delete pReply;
						pReply = NULL;
					}
					
					// I am responsible to delete this here.
					delete pMsg;
					pMsg = NULL;
				}
			}
		}
		
		
		// Now loop through them all again, and process their replies down the pipe
		
		pConnection = NULL;
		
		for (listOfConnections::iterator ii = theConnections.begin(); 
			 ii != theConnections.end(); ++ii)
		{
			if (pConnection = *ii)
			{
				OTMessage * pMsg = NULL;
				
				while (pMsg = pConnection->GetNextOutputMessage())
				{
					pConnection->ProcessReply(*pMsg);
						
					// I am responsible to delete this here.
					delete pMsg;
					pMsg = NULL;
				}
			}
		}
		
		
		
		
		
		// The Server now processes certain things on a regular basis.
		// This method call is what gives it the opportunity to do that.
	
		theServer.ProcessCron();
		
		
		
		
		// Now go to sleep for a tenth of a second.
		// (Main loop processes ten times per second, currently.)
		
		OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10)
		
		
		
		
    } while (1);

    // Close and Release Socket Resources
    SFSocketRelease(socket);

#ifdef _WIN32
	WSACleanup();
#endif

    return(0);
}
// *********************************************************************************************************
//
//
//             *** SERVER MAIN ***
//
//
// The MAIN function for the server software, which starts up the ZMQ listener, as well
// as well as the Open Transactions library and the OT Server object.
//
// After initialization, this function becomes the "main loop" of OT server.
//
int main(int argc, char* argv[])
{
	OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n"
				   "(transport build: OTMessage -> OTEnvelope -> ZMQ )\n\n", OTLog::Version());

	// WINSOCK WINDOWS
	// -----------------------------------------------------------------------
#ifdef _WIN32

	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
	/* Tell the user that we could not find a usable		*/
	/* Winsock DLL.											*/
		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}

	/*	Confirm that the WinSock DLL supports 2.2.			*/
	/*	Note that if the DLL supports versions greater		*/
	/*	than 2.2 in addition to 2.2, it will still return	*/
	/*	2.2 in wVersion since that is the version we		*/
	/*	requested.											*/

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
		/* Tell the user that we could not find a usable */
		/* WinSock DLL.                                  */
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return 1;
	}
	else
		printf("The Winsock 2.2 dll was found okay\n");

	/* The Winsock DLL is acceptable. Proceed to use it. */
	/* Add network programming using Winsock here */
	/* then call WSACleanup when done using the Winsock dll */
#endif





	// ***********************************************************************
    // INITIALIZATION and CLEANUP (for the OT library, and for this server application.)
    //
    class __ot_server_
    {
        OTServer * m_pServer;
    public:
        OTServer * GetServer() { return m_pServer; }
        // -----------------------------------
        __ot_server_() : m_pServer(NULL) // INIT 
        {
            // -----------------------------------------------------------------------   
	
            // -----------------------------------------------------------------------
            // OTLog class exists on both client and server sides.
            // #define OT_NO_SIGNAL_HANDLING if you want to turn off OT's signal handling.
            //
#if defined(OT_SIGNAL_HANDLING)
            OTLog::SetupSignalHandler(); // This is optional! (I, of course, am using it in this test app...)
#endif
            // -----------------------------------------------------------------------    
            // I instantiate this here (instead of globally) so that I am assured that any globals and other
            // setup is already done before we instantiate the server object itself.
            //
            OT_ASSERT_MSG(NULL == m_pServer, "server main(): ASSERT: NULL == m_pServer.");
            m_pServer = new OTServer;
            //
            // (This .cpp file you are currently reading is a wrapper for OTServer,
            // which adds the transport layer.)
            //
            OT_ASSERT_MSG(NULL != m_pServer, "server main(): ASSERT: Unable to instantiate OT server.\n");

			OTString pathUserAppDataPath, pathIniFileLocation;

			pathUserAppDataPath = GetRoamingAppDataLocation();
			pathIniFileLocation.Format("%s%s%s", pathUserAppDataPath.Get(), OTLog::PathSeparator(), SERVER_INI_FILE_DEFAULT);


			OTString pathOTServerDataLocation;

			OTLog::vOutput(0, "\nFound ot_init.cfg in: \n     %s \nNow checking to see if it contains the OT Server path...", pathIniFileLocation.Get());
			// Read the File, If successful use result

			if (false == GetOTAppDataFolderLocation(pathIniFileLocation, pathOTServerDataLocation))
			{
				OTLog::vOutput(0, "Path not found... Will attempt default!... \n");
				// Not successfull will will assume it is in default location:
				pathOTServerDataLocation.Format("%s%s%s", pathUserAppDataPath.Get(), OTLog::PathSeparator(), SERVER_PATH_DEFAULT);
			};

			OTLog::vOutput(0, "     %s \n", pathOTServerDataLocation.Get());


			OTLog::SetMainPath(pathOTServerDataLocation.Get());              // <============ SET MAIN PATH
            OTLog::vOutput(0, "Using server_data path:  %s\n", OTLog::Path());


            // -----------------------------------------------------------------------    
            
            OTCrypto::It()->Init();  // <========== (OpenSSL gets initialized here.)
            
        }
        // ****************************************
        //
        ~__ot_server_()  // CLEANUP
        {
            OTLog::vOutput(0, "\n\n OT version %s, shutting down and cleaning up.\n",
                           OTLog::Version());

            // ------------------------------
            if (NULL != m_pServer)
                delete m_pServer;
            m_pServer = NULL;
            // ------------------------------
            // We clean these up in reverse order from the Init function, which just seems
            // like the best default, in absence of any brighter ideas.
            //
            OTCrypto::It()->Cleanup();  // <======= (OpenSSL gets cleaned up here.)

            // -------------------------
            // (This is at the bottom, since we do the cleanup in the 
            // reverse order from initialization.)
#ifdef _WIN32
            WSACleanup();
#endif
        }
    };	
	// ***********************************************************************
    //
    // INSTANTIATE and INITIALIZE...
    //
    // (Cleanup happens automatically when this object goes out of scope.)
    //
    __ot_server_  the_server_obj;
    OTServer * pServer = the_server_obj.GetServer();
    OT_ASSERT(NULL != pServer);
    // -----------------------------------------------------------------------
//	OTString strCAFile, strDHFile, strKeyFile;  //, strSSLPassword;
//	strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE);
//	strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE);
//	strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE);
	// -----------------------------------------------------------------------
    //
    // UPDATE: This was moved to OTLog::OT_Init(), which is called above, by the
    // nested cleanup class.
    //
	// Initialize SSL -- This MUST occur before any Private Keys are loaded!
//	SSL_library_init();
//	SSL_load_error_strings();
	
	// -----------------------------------------------------------------------
	// OTServer::Init loads up server's nym so it can decrypt messages sent in
    // envelopes. It also does various other initialization work.
	//
	// (Envelopes prove that ONLY someone who actually had the server contract,
	//  and had loaded it into his wallet, could ever connect to the server or 
	//  communicate with it. And if that person is following the contract, there
	//  is only one server he can connect to, and one key he can use to talk to it.)
	//
	OTLog::vOutput(0, 
				   "\nNow loading the server nym, which will also ask you for a password, to unlock\n"
				   "its private key. (Default password is \"%s\".)\n", KEY_PASSWORD);
	
	pServer->Init(); // Keys, etc are loaded here. ===> Assumes main path is set! <===
	
	// -----------------------------------------------------------------------
	// We're going to listen on the same port that is listed in our server contract.
	//
	//
	OTString	strHostname;	// The hostname of this server, according to its own contract.
	int			nPort=0;		// The port of this server, according to its own contract.
	
    const bool bConnectInfo = pServer->GetConnectInfo(strHostname, nPort);
    
	OT_ASSERT_MSG(bConnectInfo, "server main: Unable to find my own connect info (which SHOULD be in my server contract, BTW.) Perhaps you failed trying to open that contract? Have you tried the test password? (\"test\")\n");
	
	const int   nServerPort = nPort;
	
	// -----------------------------------------------------------------------
    // OT CRON
    //
    // A heartbeat for recurring transactions, such as markets, payment plans,
    // and smart contracts.

	pServer->ActivateCron();

    // NOTE: Currently we trigger OT Cron's processing internally, but there's no reason why, in the
    // future, we can't make an actual cron job that triggers a script, that fires a message
    // to OT, causing OT to process its Cron (even if we were single-threaded we could do this...)
    //
    // Have to put some thought into it...
    //
    // Wouldn't require much security, since OT can still be smart enough not to process cron any
    // more often than X minutes, no matter HOW many times the ProcessCron script fires.
    // Thing is, though, that with this setup, we can't really guarantee that cron will EVER be
    // triggered -- whereas the way OT is now, at least we know it WILL fire every X seconds.
    //
    
	// --------------------------------------
    //
    // NETWORK
    //
	// Prepare our context and listening socket...

	OTSocket theSocket;
	OTString strBindPath; strBindPath.Format("%s%d", "tcp://*:", nServerPort);
	theSocket.Listen(strBindPath);
    
    // ******************************************************************************************
    //
    //      *** MAIN LOOP ***
    //
	do // ---------------------------
	{
        // =-=-=- HEARTBEAT -=-=-=
		//
		// The Server now processes certain things on a regular basis.
		// ProcessCron is what gives it the opportunity to do that.
		// All of the Cron Items (including market trades, payment plans, smart contracts...) 
		// they all have their hooks here...
		//
		pServer->ProcessCron();  // Internally this is smart enough to know how often to actually activate itself.
                                   // Most often it just returns doing nothing (waiting for its timer.)		
		// -----------------------------------------------------------------------
		// Wait for client http requests (and process replies out to them.)
		// ----------------------------------------------------------------------
		// Number of requests to process per heartbeat: OTServer::GetHeartbeatNoRequests()
		//
		// Loop: process up to 10 client requests, then sleep for 1/10th second.
		// That's a total of 100 requests per second. Can the computers handle it? 
		// Is it too much or too little? Todo: load testing.
		//
		// Then: check for shutdown flag.
		//
		// Then: go back to the top ("do") and repeat the loop.... process cron, 
		// process 10 client requests, sleep, check for shutdown, etc.
		//
		//
                
		Timer t;	// start timer
		t.start();
		const double tick1 = t.getElapsedTimeInMilliSec();
		// -----------------------------------------------------
		//
        // PROCESS X NUMBER OF REQUESTS (THIS PULSE.)
        //
        // Theoretically the "number of requests" that we process EACH PULSE.
        // (The timing code here is still pretty new, need to do some load testing.)
        //
		for (int i = 0; i < /*10*/OTServer::GetHeartbeatNoRequests(); i++) 
		{
			std::string	str_Message;
			
			// With 100ms heartbeat, receive will try 100 ms, then 200 ms, then 400 ms, total of 700.
			// That's about 15 Receive() calls every 10 seconds. Therefore if I want the ProcessCron()
			// to trigger every 10 seconds, I need to set the cron interval to roll over every 15 heartbeats.
			// Therefore I will be using a real Timer for Cron, instead of the damn intervals.
			//
			bool bReceived = theSocket.Receive(str_Message);  
			
			if  (bReceived)
			{
				std::string str_Reply; // Output.
				
				if (str_Message.length() <= 0)
				{
					OTLog::Error("server main: Received a message, but of 0 length or less. Weird. (Skipping it.)\n");
				}
				else // ------------------------------------
				{
                    // true  == YES, DISCONNECT m_pSocket, something must have gone wrong.
                    // false ==  NO, do NOT disconnect m_pSocket, everything went wonderfully!
                    //
					const bool bShouldDisconnect = ProcessMessage_ZMQ(*pServer, str_Message, str_Reply); // <================== PROCESS the message!
					// --------------------------------------------------

					if ((str_Reply.length() <= 0) || bShouldDisconnect)
					{
						OTLog::vOutput(0, "server main: ERROR: Unfortunately, not every client request is "
                                       "legible or worthy of a server response. :-)  "
									   "Msg:\n\n%s\n\n", str_Message.c_str());
                        
                        theSocket.Listen();
					}
					else
					{
						bool bSuccessSending = theSocket.Send(str_Reply); // <===== SEND THE REPLY
						
						if (false == bSuccessSending)
							OTLog::vError("server main: Socket ERROR: failed while trying to send reply "
                                          "back to client! \n\n MESSAGE:\n%s\n\nREPLY:\n%s\n\n", 
										  str_Message.c_str(), str_Reply.c_str());
						// --------------------------------------------------
					}
				}
			}
		} //  for
		
		// -----------------------------------------------------------------------
		//
        // IF the time we had available wasn't all used up -- if some of it is still
        // available, then SLEEP until we reach the NEXT PULSE. (In practice, we will 
        // probably use TOO MUCH time, not too little--but then again OT isn't ALWAYS
        // processing a message. There could be plenty of dead time in between...)
        //
		const	double tick2	= t.getElapsedTimeInMilliSec();
		const	long elapsed	= static_cast<long>(tick2 - tick1);
		long	lSleepMS		= 0;
			
		if (elapsed < /*100*/OTServer::GetHeartbeatMsBetweenBeats()) 
		{
			lSleepMS = OTServer::GetHeartbeatMsBetweenBeats() - elapsed;

			// Now go to sleep.
			// (The main loop processes ten times per second, currently.)		
			OTLog::SleepMilliseconds(lSleepMS); // 100 ms == (1 second / 10)
		}
		
		// -----------------------------------------------------------------------
		// ARTIFICIAL LIMIT:
		// 10 requests per heartbeat, 10 rounds per second == 100 requests per second.
		//
		// *** ONE HUNDRED CLIENT MESSAGES PER SECOND is the same as:
		// 
		//     6000 PER MINUTE == 360,000 PER HOUR == 8,640,000 PER DAY***
		//
		// Speeding it up is just a matter of adjusting the above numbers, and LOAD TESTING,
        // to see if OT can handle it. (Not counting optimization of course.)
		//
		// -----------------------------------------------------------------------

		if (pServer->IsFlaggedForShutdown())
		{
			OTLog::Output(0, "main: OT Server is shutting down gracefully....\n");
			break;
		}
        
    } while (1);  // (MAIN LOOP)
    
	// ------------------------------------
    
	return 0;
}