Esempio 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);
}
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);
}
// true  == YES, DISCONNECT m_pSocket, something must have gone wrong.
// false ==  NO, do NOT disconnect m_pSocket, everything went wonderfully!
//
bool ProcessMessage_ZMQ(OTServer & theServer, const std::string & str_Message, std::string & str_Reply)
{
	if (str_Message.size() < 1)
		return false;
	
    const char * szFunc = "ProcessMessage_ZMQ";
	// --------------------
	
	// return value.
	std::string resultString = ""; // Whatever we put in this string is what will get returned.
	
	// First we grab the client's message
	OTASCIIArmor ascMessage;
	ascMessage.MemSet(str_Message.data(), str_Message.size());
	
	// ------------------
//	
//	OTPayload thePayload;
//	thePayload.SetPayloadSize(str_Message.size());	
//	memcpy((void*)thePayload.GetPayloadPointer(), str_Message.data(), str_Message.size());
	
	// ----------------------------------------------------------------------
	
//	OTLog::vError("Envelope: \n%s\n Size: %ld\n", ascMessage.Get(), ascMessage.GetLength());
	
    
    bool bReturnVal = false; // "false" == no, do NOT disconnect. No errors. ("True" means YES, DISCONNECT!)
    
	OTMessage theMsg, theReply; // we'll need these in a sec...
	
//	OTEnvelope theEnvelope(ascMessage);
	OTEnvelope theEnvelope; 
	
	if (false == theEnvelope.SetAsciiArmoredData(ascMessage))
    {
		OTLog::vError("%s: Error retrieving envelope.\n", szFunc);
        bReturnVal = true; // disconnect the socket!
    }
	else
	{	// Now the base64 is decoded and the envelope is in binary form again.
		OTLog::vOutput(2, "%s: Successfully retrieved envelope from ZMQ message...\n", szFunc);
		
		OTString strEnvelopeContents;
		
//		OTString strPubkeyPath("TESTPUBKEY.txt");
//		theServer.GetServerNym().SavePublicKey(strPubkeyPath);
		
		// Decrypt the Envelope.    
		if (false == theEnvelope.Open(theServer.GetServerNym(), strEnvelopeContents)) // now strEnvelopeContents contains the decoded message.
        {
			OTLog::vError("%s: Unable to open envelope.\n", szFunc);
            bReturnVal = true; // disconnect the socket!
        }
		else
		{
			// All decrypted--now let's load the results into an OTMessage.
			// No need to call theMsg.ParseRawFile() after, since
			// LoadContractFromString handles it.
			//
			if (strEnvelopeContents.Exists() && theMsg.LoadContractFromString(strEnvelopeContents))
			{
				theReply.m_strCommand.Format("@%s", theMsg.m_strCommand.Get());
				theReply.m_strNymID		= theMsg.m_strNymID;	// UserID
				theReply.m_strServerID	= theMsg.m_strServerID;	// ServerID, a hash of the server contract.
				theReply.m_bSuccess		= false;				// The default reply. In fact this is probably superfluous.
				
				// In case you want to see all the incoming messages...
//				OTLog::vOutput(0, "%s\n\n", strEnvelopeContents.Get());
				
				// By constructing this without a socket, I put it in ZMQ mode, instead of tcp/ssl.
				OTClientConnection theClient(theServer); 
								
				// By optionally passing in &theClient, the client Nym's public key will be
				// set on it whenever verification is complete. (So for the reply, I'll 
				// have the key and thus I'll be able to encrypt reply to the recipient.)
				if (false == theServer.ProcessUserCommand(theMsg, theReply, &theClient))
				{
                    const OTString s1(theMsg), s2(theReply);
                    
					OTLog::vOutput(0, "%s: Unable to process user command.\n\n ********** "
                                   "REQUEST:\n\n%s\n\n ********** RESPONSE:\n\n%s\n\n", szFunc, s1.Get(), s2.Get());
					
					// NOTE: normally you would even HAVE a true or false if we're in this block. ProcessUserCommand()
					// is what tries to process a command and then sets false if/when it fails. Until that point, you
					// wouldn't get any server reply.  I'm now changing this slightly, so you still get a reply (defaulted
					// to success==false.) That way if a client needs to re-sync his request number, he will get the false
					// and therefore know to resync the # as his next move, vs being stuck with no server reply (and thus
					// stuck with a bad socket.)
					// We sign the reply here, but not in the else block, since it's already signed in cases where 
					// ProcessUserCommand() is a success, by the time that call returns.
					
					theReply.m_bSuccess = false; // Since the process call definitely failed, I'm making sure this here is definitely set to false (even though it probably was already.)
					theReply.SignContract(theServer.GetServerNym());
					theReply.SaveContract();
				}
				else	// At this point the reply is ready to go, and theClient has the public key of the recipient...
					OTLog::vOutput(1, "%s: Successfully processed user command: %s.\n", szFunc, theMsg.m_strCommand.Get());
				
				// -------------------------------------------------
				// The transaction is now processed (or not), and the server's reply message is in theReply.
				// Let's seal it up to the recipient's nym (in an envelope) and send back to the user...
                //
				OTEnvelope theRecipientEnvelope;
				
				bool bSealed = theClient.SealMessageForRecipient(theReply, theRecipientEnvelope);
				
				if (false == bSealed)
                {
					OTLog::vOutput(0, "%s: Unable to seal envelope. (No reply will be sent.)\n", szFunc);
                    bReturnVal = true; // disconnect the socket!
                }
				else
				{
//					OTPayload theReplyPayload;
//					theReplyPayload.SetEnvelope(theRecipientEnvelope);
//					resultString = ascReply.Get();
//					resultString.assign(theReplyPayload.GetPayloadPointer(), theReplyPayload.GetPayloadSize());
					
					OTASCIIArmor ascReply;
					if (theRecipientEnvelope.GetAsciiArmoredData(ascReply))
						resultString.assign(ascReply.Get(), ascReply.GetLength());
				}
			}
			else
            {
				OTLog::vError("%s: Error loading message from envelope contents:\n\n%s\n\n",
							  szFunc, strEnvelopeContents.Get());
                bReturnVal = true; // disconnect the socket!
            }
		}
	}
	// ----------------------------------------------------------------------
	
	str_Reply = resultString;
    
    return bReturnVal;
    
} // ProcessMessage_ZMQ