mirror of
				https://github.com/PurpleI2P/i2pd.git
				synced 2025-11-04 00:20:46 +00:00 
			
		
		
		
	Merge branch 'upstream-openssl' into restricted_routes
This commit is contained in:
		
						commit
						9a5984d750
					
				
					 23 changed files with 314 additions and 237 deletions
				
			
		| 
						 | 
				
			
			@ -30,7 +30,7 @@ before_install:
 | 
			
		|||
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink boost openssl && brew link boost openssl -f ; fi
 | 
			
		||||
env:
 | 
			
		||||
  matrix:
 | 
			
		||||
    - BUILD_TYPE=Release UPNP=ON
 | 
			
		||||
#    - BUILD_TYPE=Release UPNP=ON
 | 
			
		||||
    - BUILD_TYPE=Release UPNP=OFF
 | 
			
		||||
script:
 | 
			
		||||
  - cd build && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DWITH_UPNP=${UPNP} && make
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								Base.cpp
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								Base.cpp
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include "Log.h"
 | 
			
		||||
#include "Base.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
| 
						 | 
				
			
			@ -305,13 +304,10 @@ namespace data
 | 
			
		|||
		m_Inflator.next_out = out;
 | 
			
		||||
		m_Inflator.avail_out = outLen;
 | 
			
		||||
		int err;
 | 
			
		||||
		if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
 | 
			
		||||
		if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) {
 | 
			
		||||
			return outLen - m_Inflator.avail_out;	
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			LogPrint (eLogError, "Decompression error ", err);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}	
 | 
			
		||||
 | 
			
		||||
	bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s)
 | 
			
		||||
| 
						 | 
				
			
			@ -328,12 +324,10 @@ namespace data
 | 
			
		|||
			ret = inflate (&m_Inflator, Z_NO_FLUSH);
 | 
			
		||||
			if (ret < 0)
 | 
			
		||||
			{
 | 
			
		||||
				LogPrint (eLogError, "Decompression error ", ret);
 | 
			
		||||
				inflateEnd (&m_Inflator);
 | 
			
		||||
				s.setstate(std::ios_base::failbit);
 | 
			
		||||
				break;
 | 
			
		||||
			}	
 | 
			
		||||
			else
 | 
			
		||||
			s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
 | 
			
		||||
		}
 | 
			
		||||
		while (!m_Inflator.avail_out); // more data to read
 | 
			
		||||
| 
						 | 
				
			
			@ -377,14 +371,11 @@ namespace data
 | 
			
		|||
		m_Deflator.next_out = out;
 | 
			
		||||
		m_Deflator.avail_out = outLen;
 | 
			
		||||
		int err;
 | 
			
		||||
		if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) 
 | 
			
		||||
		if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
 | 
			
		||||
			return outLen - m_Deflator.avail_out;	
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			LogPrint (eLogError, "Compression error ", err);
 | 
			
		||||
		} /* else */
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								ChangeLog
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								ChangeLog
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,14 +1,23 @@
 | 
			
		|||
# for this file format description,
 | 
			
		||||
# see https://github.com/olivierlacan/keep-a-changelog
 | 
			
		||||
 | 
			
		||||
## [2.8.0] - UNRELEASED
 | 
			
		||||
## [2.9.0] - UNRELEASED
 | 
			
		||||
### Changed
 | 
			
		||||
- Proxy refactoring & speedup
 | 
			
		||||
 | 
			
		||||
## [2.8.0] - 2016-06-20
 | 
			
		||||
### Added
 | 
			
		||||
- Basic Android support
 | 
			
		||||
- I2CP implementation
 | 
			
		||||
- 'doxygen' target
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
 | 
			
		||||
- boost::regex no more needed
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
 | 
			
		||||
- properly close NTCP sessions (memleak)
 | 
			
		||||
 | 
			
		||||
## [2.7.0] - 2016-05-18
 | 
			
		||||
### Added
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,13 @@
 | 
			
		|||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include "I2PService.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "HTTPProxy.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -13,13 +20,12 @@
 | 
			
		|||
#include "I2PTunnel.h"
 | 
			
		||||
#include "Config.h"
 | 
			
		||||
#include "HTTP.h"
 | 
			
		||||
#include "HTTPServer.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace proxy
 | 
			
		||||
{
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace proxy {
 | 
			
		||||
	static const size_t http_buffer_size = 8192;
 | 
			
		||||
	class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler> 
 | 
			
		||||
	class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
 | 
			
		||||
	{
 | 
			
		||||
		private:
 | 
			
		||||
			enum state 
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +42,7 @@ namespace proxy
 | 
			
		|||
			void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
 | 
			
		||||
			void Terminate();
 | 
			
		||||
			void AsyncSockRead();
 | 
			
		||||
			void HTTPRequestFailed(/*std::string message*/);
 | 
			
		||||
			void HTTPRequestFailed(const char *message);
 | 
			
		||||
			void RedirectToJumpService();
 | 
			
		||||
			void ExtractRequest();
 | 
			
		||||
			bool IsI2PAddress();
 | 
			
		||||
| 
						 | 
				
			
			@ -59,26 +65,26 @@ namespace proxy
 | 
			
		|||
 | 
			
		||||
		public:
 | 
			
		||||
 | 
			
		||||
			HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) : 
 | 
			
		||||
			HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
 | 
			
		||||
				I2PServiceHandler(parent), m_sock(sock)
 | 
			
		||||
				{ EnterState(GET_METHOD); }
 | 
			
		||||
			~HTTPProxyHandler() { Terminate(); }
 | 
			
		||||
			~HTTPReqHandler() { Terminate(); }
 | 
			
		||||
			void Handle () { AsyncSockRead(); }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::AsyncSockRead()
 | 
			
		||||
	void HTTPReqHandler::AsyncSockRead()
 | 
			
		||||
	{
 | 
			
		||||
		LogPrint(eLogDebug, "HTTPProxy: async sock read");
 | 
			
		||||
		if(m_sock) {
 | 
			
		||||
			m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
 | 
			
		||||
						std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
 | 
			
		||||
						std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
 | 
			
		||||
								std::placeholders::_1, std::placeholders::_2));
 | 
			
		||||
		} else {
 | 
			
		||||
			LogPrint(eLogError, "HTTPProxy: no socket for read");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::Terminate() {
 | 
			
		||||
	void HTTPReqHandler::Terminate() {
 | 
			
		||||
		if (Kill()) return;
 | 
			
		||||
		if (m_sock) 
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -91,30 +97,34 @@ namespace proxy
 | 
			
		|||
 | 
			
		||||
	/* All hope is lost beyond this point */
 | 
			
		||||
	//TODO: handle this apropriately
 | 
			
		||||
	void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
 | 
			
		||||
	void HTTPReqHandler::HTTPRequestFailed(const char *message)
 | 
			
		||||
	{
 | 
			
		||||
		static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n\r\n";
 | 
			
		||||
		boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
 | 
			
		||||
					 std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
 | 
			
		||||
		std::size_t size = std::strlen(message);
 | 
			
		||||
		std::stringstream ss;
 | 
			
		||||
		ss << "HTTP/1.0 500 Internal Server Error\r\n"
 | 
			
		||||
		   << "Content-Type: text/plain\r\n";
 | 
			
		||||
		ss << "Content-Length: " << std::to_string(size + 2) << "\r\n"
 | 
			
		||||
		   << "\r\n"; /* end of headers */
 | 
			
		||||
		ss << message << "\r\n";
 | 
			
		||||
		std::string response = ss.str();
 | 
			
		||||
		boost::asio::async_write(*m_sock, boost::asio::buffer(response),
 | 
			
		||||
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::RedirectToJumpService(/*HTTPProxyHandler::errTypes error*/)
 | 
			
		||||
	void HTTPReqHandler::RedirectToJumpService(/*HTTPReqHandler::errTypes error*/)
 | 
			
		||||
	{
 | 
			
		||||
		std::stringstream response;
 | 
			
		||||
		std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
 | 
			
		||||
		uint16_t    httpPort; i2p::config::GetOption("http.port", httpPort);
 | 
			
		||||
 | 
			
		||||
		response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
 | 
			
		||||
		boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()),
 | 
			
		||||
					 std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
 | 
			
		||||
		std::stringstream ss;
 | 
			
		||||
		i2p::http::ShowJumpServices (ss, m_address);
 | 
			
		||||
		boost::asio::async_write(*m_sock, boost::asio::buffer(ss.str ()),
 | 
			
		||||
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) 
 | 
			
		||||
	void HTTPReqHandler::EnterState(HTTPReqHandler::state nstate)
 | 
			
		||||
	{
 | 
			
		||||
		m_state = nstate;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::ExtractRequest()
 | 
			
		||||
	void HTTPReqHandler::ExtractRequest()
 | 
			
		||||
	{
 | 
			
		||||
		LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
 | 
			
		||||
		i2p::http::URL url;
 | 
			
		||||
| 
						 | 
				
			
			@ -127,18 +137,18 @@ namespace proxy
 | 
			
		|||
		LogPrint(eLogDebug, "HTTPProxy: server: ", m_address, ", port: ", m_port, ", path: ", m_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HTTPProxyHandler::ValidateHTTPRequest() 
 | 
			
		||||
	bool HTTPReqHandler::ValidateHTTPRequest()
 | 
			
		||||
	{
 | 
			
		||||
		if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) 
 | 
			
		||||
		{
 | 
			
		||||
			LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
 | 
			
		||||
			HTTPRequestFailed(); //TODO: send right stuff
 | 
			
		||||
			HTTPRequestFailed("unsupported HTTP version");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::HandleJumpServices() 
 | 
			
		||||
	void HTTPReqHandler::HandleJumpServices()
 | 
			
		||||
	{
 | 
			
		||||
		static const char * helpermark1 = "?i2paddresshelper=";
 | 
			
		||||
		static const char * helpermark2 = "&i2paddresshelper=";
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +180,7 @@ namespace proxy
 | 
			
		|||
		m_path.erase(addressHelperPos);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HTTPProxyHandler::IsI2PAddress()
 | 
			
		||||
	bool HTTPReqHandler::IsI2PAddress()
 | 
			
		||||
	{
 | 
			
		||||
		auto pos = m_address.rfind (".i2p");
 | 
			
		||||
		if (pos != std::string::npos && (pos+4) == m_address.length ())
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +190,7 @@ namespace proxy
 | 
			
		|||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) 
 | 
			
		||||
	bool HTTPReqHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
 | 
			
		||||
	{
 | 
			
		||||
		ExtractRequest(); //TODO: parse earlier
 | 
			
		||||
		if (!ValidateHTTPRequest()) return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +245,7 @@ namespace proxy
 | 
			
		|||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
 | 
			
		||||
	bool HTTPReqHandler::HandleData(uint8_t *http_buff, std::size_t len)
 | 
			
		||||
	{
 | 
			
		||||
		while (len > 0) 
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -269,13 +279,13 @@ namespace proxy
 | 
			
		|||
						case '\n': EnterState(DONE); break;
 | 
			
		||||
						default:
 | 
			
		||||
							LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
 | 
			
		||||
							HTTPRequestFailed(); //TODO: add correct code
 | 
			
		||||
							HTTPRequestFailed("rejected invalid request");
 | 
			
		||||
							return false;
 | 
			
		||||
					}
 | 
			
		||||
				break;
 | 
			
		||||
				default:
 | 
			
		||||
					LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
 | 
			
		||||
					HTTPRequestFailed(); //TODO: add correct code 500
 | 
			
		||||
					HTTPRequestFailed("invalid parser state");
 | 
			
		||||
					return false;
 | 
			
		||||
			}
 | 
			
		||||
			http_buff++;
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +296,7 @@ namespace proxy
 | 
			
		|||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
 | 
			
		||||
	void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
 | 
			
		||||
	{
 | 
			
		||||
		LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes");
 | 
			
		||||
		if(ecode) 
 | 
			
		||||
| 
						 | 
				
			
			@ -301,7 +311,7 @@ namespace proxy
 | 
			
		|||
			if (m_state == DONE) 
 | 
			
		||||
			{
 | 
			
		||||
				LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
 | 
			
		||||
				GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
 | 
			
		||||
				GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
 | 
			
		||||
						shared_from_this(), std::placeholders::_1), m_address, m_port);
 | 
			
		||||
			} 
 | 
			
		||||
			else 
 | 
			
		||||
| 
						 | 
				
			
			@ -310,14 +320,14 @@ namespace proxy
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
 | 
			
		||||
	void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
 | 
			
		||||
	{
 | 
			
		||||
		if (ecode)
 | 
			
		||||
			LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
 | 
			
		||||
		Terminate();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
 | 
			
		||||
	void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
 | 
			
		||||
	{
 | 
			
		||||
		if (stream) 
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -331,19 +341,18 @@ namespace proxy
 | 
			
		|||
		else 
 | 
			
		||||
		{
 | 
			
		||||
			LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
 | 
			
		||||
			HTTPRequestFailed(); // TODO: Send correct error message host unreachable
 | 
			
		||||
			HTTPRequestFailed("error when creating the stream, check logs");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination): 
 | 
			
		||||
	HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
 | 
			
		||||
		TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) 
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
 | 
			
		||||
	std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
 | 
			
		||||
	{
 | 
			
		||||
		return std::make_shared<HTTPProxyHandler> (this, socket);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		return std::make_shared<HTTPReqHandler> (this, socket);
 | 
			
		||||
	}
 | 
			
		||||
} // http
 | 
			
		||||
} // i2p
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										25
									
								
								HTTPProxy.h
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								HTTPProxy.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,32 +1,21 @@
 | 
			
		|||
#ifndef HTTP_PROXY_H__
 | 
			
		||||
#define HTTP_PROXY_H__
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include "I2PService.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace proxy
 | 
			
		||||
{
 | 
			
		||||
	class HTTPProxyServer: public i2p::client::TCPIPAcceptor
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace proxy {
 | 
			
		||||
	class HTTPProxy: public i2p::client::TCPIPAcceptor
 | 
			
		||||
	{
 | 
			
		||||
		public:
 | 
			
		||||
 | 
			
		||||
			HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
 | 
			
		||||
			~HTTPProxyServer() {};
 | 
			
		||||
			HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
 | 
			
		||||
			~HTTPProxy() {};
 | 
			
		||||
 | 
			
		||||
		protected:
 | 
			
		||||
			// Implements TCPIPAcceptor
 | 
			
		||||
			std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
 | 
			
		||||
			const char* GetName() { return "HTTP Proxy"; }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	typedef HTTPProxyServer HTTPProxy;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // http
 | 
			
		||||
} // i2p
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -612,7 +612,7 @@ namespace http {
 | 
			
		|||
			HandleCommand (req, res, s);
 | 
			
		||||
		} else {
 | 
			
		||||
			ShowStatus (s);
 | 
			
		||||
			//res.add_header("Refresh", "5");
 | 
			
		||||
			res.add_header("Refresh", "10");
 | 
			
		||||
		}
 | 
			
		||||
		ShowPageTail (s);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,8 @@ namespace http {
 | 
			
		|||
			boost::asio::io_service::work m_Work;
 | 
			
		||||
			boost::asio::ip::tcp::acceptor m_Acceptor;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void ShowJumpServices (std::stringstream& s, const std::string& address);
 | 
			
		||||
} // http
 | 
			
		||||
} // i2p
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								I2CP.cpp
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								I2CP.cpp
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -114,7 +114,7 @@ namespace client
 | 
			
		|||
		}	
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
 | 
			
		||||
	I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
 | 
			
		||||
		m_Owner (owner), m_Socket (socket), m_Payload (nullptr),
 | 
			
		||||
		m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -594,7 +594,12 @@ namespace client
 | 
			
		|||
 | 
			
		||||
	I2CPServer::I2CPServer (const std::string& interface, int port):
 | 
			
		||||
		m_IsRunning (false), m_Thread (nullptr),
 | 
			
		||||
		m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port))
 | 
			
		||||
		m_Acceptor (m_Service, 
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
            I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address
 | 
			
		||||
#else
 | 
			
		||||
			I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port))
 | 
			
		||||
#endif
 | 
			
		||||
	{
 | 
			
		||||
		memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
 | 
			
		||||
		m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
 | 
			
		||||
| 
						 | 
				
			
			@ -655,12 +660,13 @@ namespace client
 | 
			
		|||
 | 
			
		||||
	void I2CPServer::Accept ()
 | 
			
		||||
	{
 | 
			
		||||
		auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
 | 
			
		||||
		auto newSocket = std::make_shared<I2CPSession::proto::socket> (m_Service);
 | 
			
		||||
		m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
 | 
			
		||||
			std::placeholders::_1, newSocket));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void I2CPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
 | 
			
		||||
	void I2CPServer::HandleAccept(const boost::system::error_code& ecode,
 | 
			
		||||
		std::shared_ptr<I2CPSession::proto::socket> socket)
 | 
			
		||||
	{
 | 
			
		||||
		if (!ecode && socket)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								I2CP.h
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								I2CP.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -99,7 +99,14 @@ namespace client
 | 
			
		|||
	{
 | 
			
		||||
		public:
 | 
			
		||||
 | 
			
		||||
			I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
 | 
			
		||||
#ifdef ANDROID 
 | 
			
		||||
			typedef boost::asio::local::stream_protocol proto;
 | 
			
		||||
#else
 | 
			
		||||
			typedef boost::asio::ip::tcp proto;
 | 
			
		||||
#endif		
 | 
			
		||||
 | 
			
		||||
			I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket);
 | 
			
		||||
 | 
			
		||||
			~I2CPSession ();
 | 
			
		||||
 | 
			
		||||
			void Start ();
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +151,7 @@ namespace client
 | 
			
		|||
		private:
 | 
			
		||||
 | 
			
		||||
			I2CPServer& m_Owner;
 | 
			
		||||
			std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
 | 
			
		||||
			std::shared_ptr<proto::socket> m_Socket;
 | 
			
		||||
			uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload;
 | 
			
		||||
			size_t m_PayloadLen;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +180,8 @@ namespace client
 | 
			
		|||
			void Run ();
 | 
			
		||||
 | 
			
		||||
			void Accept ();
 | 
			
		||||
			void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
 | 
			
		||||
 | 
			
		||||
			void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<I2CPSession::proto::socket> socket);
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +191,7 @@ namespace client
 | 
			
		|||
			bool m_IsRunning;
 | 
			
		||||
			std::thread * m_Thread;	
 | 
			
		||||
			boost::asio::io_service m_Service;
 | 
			
		||||
			boost::asio::ip::tcp::acceptor m_Acceptor;
 | 
			
		||||
			I2CPSession::proto::acceptor m_Acceptor;
 | 
			
		||||
 | 
			
		||||
		public:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ endif
 | 
			
		|||
 | 
			
		||||
# UPNP Support (miniupnpc 1.5 or 1.6)
 | 
			
		||||
ifeq ($(USE_UPNP),1)
 | 
			
		||||
  LDFLAGS += -ldl
 | 
			
		||||
  LDFLAGS += -lminiupnpc
 | 
			
		||||
  CXXFLAGS += -DUSE_UPNP
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,10 +6,15 @@ NEEDED_CXXFLAGS = -std=c++11
 | 
			
		|||
BOOST_SUFFIX = -mt
 | 
			
		||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
 | 
			
		||||
LDFLAGS = -Wl,-rpath,/usr/local/lib \
 | 
			
		||||
  -L/usr/local/lib \
 | 
			
		||||
  -L/c/dev/openssl \
 | 
			
		||||
  -L/c/dev/boost/lib
 | 
			
		||||
LDLIBS = \
 | 
			
		||||
  -L/usr/local/lib 
 | 
			
		||||
 | 
			
		||||
# UPNP Support 
 | 
			
		||||
ifeq ($(USE_UPNP),1)
 | 
			
		||||
  CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
 | 
			
		||||
  LDLIBS = -Wl,-Bstatic -lminiupnpc	
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
LDLIBS += \
 | 
			
		||||
  -Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
 | 
			
		||||
  -Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
 | 
			
		||||
  -Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -394,9 +394,12 @@ namespace stream
 | 
			
		|||
			}
 | 
			
		||||
		}	
 | 
			
		||||
		if (packets.size () > 0)
 | 
			
		||||
		{
 | 
			
		||||
			if (m_SavedPackets.empty ()) // no NACKS
 | 
			
		||||
			{	
 | 
			
		||||
				m_IsAckSendScheduled = false;	
 | 
			
		||||
				m_AckSendTimer.cancel ();
 | 
			
		||||
			}	
 | 
			
		||||
			bool isEmpty = m_SentPackets.empty ();
 | 
			
		||||
			auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 | 
			
		||||
			for (auto it: packets)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										78
									
								
								UPnP.cpp
									
										
									
									
									
								
							
							
						
						
									
										78
									
								
								UPnP.cpp
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -6,13 +6,6 @@
 | 
			
		|||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/bind.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#define dlsym GetProcAddress
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "Log.h"
 | 
			
		||||
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -24,32 +17,11 @@
 | 
			
		|||
#include <miniupnpc/miniupnpc.h>
 | 
			
		||||
#include <miniupnpc/upnpcommands.h>
 | 
			
		||||
 | 
			
		||||
// These are per-process and are safe to reuse for all threads
 | 
			
		||||
decltype(upnpDiscover) *upnpDiscoverFunc;
 | 
			
		||||
decltype(UPNP_AddPortMapping) *UPNP_AddPortMappingFunc;
 | 
			
		||||
decltype(UPNP_GetValidIGD) *UPNP_GetValidIGDFunc;
 | 
			
		||||
decltype(UPNP_GetExternalIPAddress) *UPNP_GetExternalIPAddressFunc;
 | 
			
		||||
decltype(UPNP_DeletePortMapping) *UPNP_DeletePortMappingFunc;
 | 
			
		||||
decltype(freeUPNPDevlist) *freeUPNPDevlistFunc;
 | 
			
		||||
decltype(FreeUPNPUrls) *FreeUPNPUrlsFunc;
 | 
			
		||||
 | 
			
		||||
// Nice approach http://stackoverflow.com/a/21517513/673826
 | 
			
		||||
template<class M, typename F>
 | 
			
		||||
F GetKnownProcAddressImpl(M hmod, const char *name, F) {
 | 
			
		||||
    auto proc = reinterpret_cast<F>(dlsym(hmod, name));
 | 
			
		||||
    if (!proc) {
 | 
			
		||||
        LogPrint(eLogError, "UPnP: Error resolving ", name, " from library, version mismatch?");
 | 
			
		||||
    }
 | 
			
		||||
    return proc;
 | 
			
		||||
}
 | 
			
		||||
#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
 | 
			
		||||
    UPnP::UPnP () : m_Thread (nullptr)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,33 +37,6 @@ namespace transport
 | 
			
		|||
 | 
			
		||||
    void UPnP::Start()
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_IsModuleLoaded) {
 | 
			
		||||
#ifdef MAC_OSX
 | 
			
		||||
            m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
 | 
			
		||||
#elif _WIN32
 | 
			
		||||
            m_Module = LoadLibrary ("miniupnpc.dll"); // official prebuilt binary, e.g., in upnpc-exe-win32-20140422.zip
 | 
			
		||||
#else
 | 
			
		||||
            m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
 | 
			
		||||
#endif
 | 
			
		||||
            if (m_Module == NULL)
 | 
			
		||||
            {
 | 
			
		||||
                LogPrint (eLogError, "UPnP: Error loading UPNP library, version mismatch?");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover);
 | 
			
		||||
                UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD);
 | 
			
		||||
                UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress);
 | 
			
		||||
                UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping);
 | 
			
		||||
                UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping);
 | 
			
		||||
                freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist);
 | 
			
		||||
                FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls);
 | 
			
		||||
                if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc &&
 | 
			
		||||
                    UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc)
 | 
			
		||||
                    m_IsModuleLoaded = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        m_Thread = new std::thread (std::bind (&UPnP::Run, this));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -123,16 +68,16 @@ namespace transport
 | 
			
		|||
    {
 | 
			
		||||
        int nerror = 0;
 | 
			
		||||
#if MINIUPNPC_API_VERSION >= 14
 | 
			
		||||
        m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
 | 
			
		||||
        m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
 | 
			
		||||
#else
 | 
			
		||||
        m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
 | 
			
		||||
        m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        int r;
 | 
			
		||||
        r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
 | 
			
		||||
        r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
 | 
			
		||||
        if (r == 1)
 | 
			
		||||
        {
 | 
			
		||||
            r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
 | 
			
		||||
            r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
 | 
			
		||||
            if(r != UPNPCOMMAND_SUCCESS)
 | 
			
		||||
            {
 | 
			
		||||
                LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress () returned ", r);
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +116,7 @@ namespace transport
 | 
			
		|||
        std::string strDesc = "I2Pd";
 | 
			
		||||
        try {
 | 
			
		||||
            for (;;) {
 | 
			
		||||
                r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
 | 
			
		||||
                r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
 | 
			
		||||
                if (r!=UPNPCOMMAND_SUCCESS)
 | 
			
		||||
                {
 | 
			
		||||
                    LogPrint (eLogError, "UPnP: AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r);
 | 
			
		||||
| 
						 | 
				
			
			@ -208,20 +153,15 @@ namespace transport
 | 
			
		|||
                strType = "UDP";
 | 
			
		||||
        }
 | 
			
		||||
        int r = 0;
 | 
			
		||||
        r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
 | 
			
		||||
        r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
 | 
			
		||||
        LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r, "\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::Close ()
 | 
			
		||||
    {
 | 
			
		||||
        freeUPNPDevlistFunc (m_Devlist);
 | 
			
		||||
        freeUPNPDevlist (m_Devlist);
 | 
			
		||||
        m_Devlist = 0;
 | 
			
		||||
        FreeUPNPUrlsFunc (&m_upnpUrls);
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
        dlclose (m_Module);
 | 
			
		||||
#else
 | 
			
		||||
        FreeLibrary (m_Module);
 | 
			
		||||
#endif
 | 
			
		||||
        FreeUPNPUrls (&m_upnpUrls);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								UPnP.h
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								UPnP.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -48,12 +48,6 @@ namespace transport
 | 
			
		|||
        struct UPNPDev * m_Devlist = 0;
 | 
			
		||||
        char m_NetworkAddr[64];
 | 
			
		||||
        char m_externalIPAddress[40];
 | 
			
		||||
        bool m_IsModuleLoaded;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
        void *m_Module;
 | 
			
		||||
#else
 | 
			
		||||
        HINSTANCE m_Module;
 | 
			
		||||
#endif
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								debian/tunnels.conf
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								debian/tunnels.conf
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,33 @@
 | 
			
		|||
[IRC]
 | 
			
		||||
type = client
 | 
			
		||||
address = 127.0.0.1
 | 
			
		||||
port = 6668
 | 
			
		||||
destination = irc.postman.i2p
 | 
			
		||||
destinationport = 6667
 | 
			
		||||
keys = irc-keys.dat
 | 
			
		||||
 | 
			
		||||
#[SMTP]
 | 
			
		||||
#type = client
 | 
			
		||||
#address = 127.0.0.1
 | 
			
		||||
#port = 7659
 | 
			
		||||
#destination = smtp.postman.i2p
 | 
			
		||||
#destinationport = 25
 | 
			
		||||
#keys = smtp-keys.dat
 | 
			
		||||
 | 
			
		||||
#[POP3]
 | 
			
		||||
#type = client
 | 
			
		||||
#address = 127.0.0.1
 | 
			
		||||
#port = 7660
 | 
			
		||||
#destination = pop.postman.i2p
 | 
			
		||||
#destinationport = 110
 | 
			
		||||
#keys = pop3-keys.dat
 | 
			
		||||
 | 
			
		||||
#[MTN]
 | 
			
		||||
#type = client
 | 
			
		||||
#address = 127.0.0.1
 | 
			
		||||
#port = 8998
 | 
			
		||||
#destination = mtn.i2p-projekt.i2p
 | 
			
		||||
#destinationport = 4691
 | 
			
		||||
#keys = mtn-keys.dat
 | 
			
		||||
 | 
			
		||||
# see more examples in /usr/share/doc/i2pd/configuration.md.gz
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,6 +164,13 @@ folder name included in downloaded archive.
 | 
			
		|||
Note that you might need to build DLL yourself for 64-bit systems
 | 
			
		||||
using msys2 as 64-bit DLLs are not provided by the project.
 | 
			
		||||
 | 
			
		||||
You can also install it through the MSYS2
 | 
			
		||||
and build with USE_UPNP key.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
pacman -S mingw-w64-i686-miniupnpc  
 | 
			
		||||
make USE_UPNP=1  
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Creating Visual Studio project
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ All options below still possible in cmdline, but better write it in config file:
 | 
			
		|||
* --http.pass=          - Password for basic auth (default: random, see logs)
 | 
			
		||||
 | 
			
		||||
* --httpproxy.address=  - The address to listen on (HTTP Proxy)
 | 
			
		||||
* --httpproxy.port=     - The port to listen on (HTTP Proxy) 4446 by default
 | 
			
		||||
* --httpproxy.port=     - The port to listen on (HTTP Proxy) 4444 by default
 | 
			
		||||
* --httpproxy.keys=     - optional keys file for proxy local destination (both HTTP and SOCKS)
 | 
			
		||||
* --httpproxy.enabled=  - If HTTP proxy is enabled. true by default 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,9 +60,9 @@ All options below still possible in cmdline, but better write it in config file:
 | 
			
		|||
* --bob.port=           - Port of BOB command channel. Usually 2827. BOB is off if not specified
 | 
			
		||||
* --bob.enabled=        - If BOB is enabled. false by default 
 | 
			
		||||
 | 
			
		||||
* --i2cp.address=        - The address to listen on 
 | 
			
		||||
* --i2cp.port=           - Port of I2CP server. Usually 7654. IPCP is off if not specified
 | 
			
		||||
* --i2cp.enabled=        - If I2CP is enabled. false by default. Other services don't requeire I2CP 
 | 
			
		||||
* --i2cp.address=        - The address to listen on or an abstract address for Android LocalSocket
 | 
			
		||||
* --i2cp.port=           - Port of I2CP server. Usually 7654. Ignored for Andorid
 | 
			
		||||
* --i2cp.enabled=        - If I2CP is enabled. false by default. Other services don't require I2CP 
 | 
			
		||||
 | 
			
		||||
* --i2pcontrol.address= - The address to listen on (I2P control service)
 | 
			
		||||
* --i2pcontrol.port=    - Port of I2P control service. Usually 7650. I2PControl is off if not specified
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,14 +42,18 @@
 | 
			
		|||
## Port to listen for connections
 | 
			
		||||
## By default i2pd picks random port. You MUST pick a random number too,
 | 
			
		||||
## don't just uncomment this
 | 
			
		||||
# port = 4321
 | 
			
		||||
# port = 4567
 | 
			
		||||
 | 
			
		||||
## Enable communication through ipv4
 | 
			
		||||
ipv4 = true
 | 
			
		||||
 | 
			
		||||
## Enable communication through ipv6
 | 
			
		||||
ipv6 = true
 | 
			
		||||
ipv6 = false
 | 
			
		||||
 | 
			
		||||
## Bandwidth configuration
 | 
			
		||||
## L limit bandwidth to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited
 | 
			
		||||
## Default is P for floodfill, L for regular node
 | 
			
		||||
## L limit bandwidth to 32Kbs/sec, O - to 256Kbs/sec, P - to 2048Kbs/sec,
 | 
			
		||||
## X - unlimited
 | 
			
		||||
## Default is X for floodfill, L for regular node
 | 
			
		||||
# bandwidth = L
 | 
			
		||||
 | 
			
		||||
## Router will not accept transit tunnels at startup
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +62,15 @@ ipv6 = true
 | 
			
		|||
## Router will be floodfill
 | 
			
		||||
# floodfill = true
 | 
			
		||||
 | 
			
		||||
[limits]
 | 
			
		||||
## Maximum active transit sessions (default:2500)
 | 
			
		||||
# transittunnels = 2500
 | 
			
		||||
 | 
			
		||||
[precomputation]
 | 
			
		||||
## Enable or disable elgamal precomputation table
 | 
			
		||||
## By default, enabled on i386 hosts
 | 
			
		||||
# elgamal = true
 | 
			
		||||
 | 
			
		||||
[http]
 | 
			
		||||
## Uncomment and set to 'false' to disable Web Console
 | 
			
		||||
# enabled = true
 | 
			
		||||
| 
						 | 
				
			
			@ -78,10 +91,11 @@ port = 4444
 | 
			
		|||
## Uncomment and set to 'false' to disable SOCKS Proxy
 | 
			
		||||
# enabled = true
 | 
			
		||||
## Address and port service will listen on
 | 
			
		||||
# address = 127.0.0.1
 | 
			
		||||
# port = 4447
 | 
			
		||||
address = 127.0.0.1
 | 
			
		||||
port = 4447
 | 
			
		||||
## Optional keys file for proxy local destination
 | 
			
		||||
# keys = socks-proxy-keys.dat
 | 
			
		||||
 | 
			
		||||
## Socks outproxy. Example below is set to use Tor for all connections except i2p
 | 
			
		||||
## Address and port of outproxy
 | 
			
		||||
# outproxy = 127.0.0.1
 | 
			
		||||
| 
						 | 
				
			
			@ -101,6 +115,13 @@ port = 4444
 | 
			
		|||
# address = 127.0.0.1
 | 
			
		||||
# port = 2827
 | 
			
		||||
 | 
			
		||||
[i2cp]
 | 
			
		||||
## Uncomment and set to 'true' to enable I2CP protocol
 | 
			
		||||
# enabled = false
 | 
			
		||||
## Address and port service will listen on
 | 
			
		||||
# address = 127.0.0.1
 | 
			
		||||
# port = 7654
 | 
			
		||||
 | 
			
		||||
[i2pcontrol]
 | 
			
		||||
## Uncomment and set to 'true' to enable I2PControl protocol
 | 
			
		||||
# enabled = false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<?xml version="1.0"?>
 | 
			
		||||
<manifest package="org.purplei2p.i2pd" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.8.0" android:versionCode="1" android:installLocation="auto">
 | 
			
		||||
<manifest package="org.purplei2p.i2pd" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.8.0" android:versionCode="2" android:installLocation="auto">
 | 
			
		||||
    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="23"/>
 | 
			
		||||
        <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
 | 
			
		||||
    <!-- <application android:hardwareAccelerated="true" -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,14 +11,17 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 | 
			
		|||
TARGET = i2pd_qt
 | 
			
		||||
TEMPLATE = app
 | 
			
		||||
QMAKE_CXXFLAGS *= -std=c++11
 | 
			
		||||
DEFINES += USE_UPNP
 | 
			
		||||
 | 
			
		||||
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git
 | 
			
		||||
# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git
 | 
			
		||||
# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git
 | 
			
		||||
# git clone https://github.com/PurpleI2P/android-ifaddrs.git
 | 
			
		||||
# change to your own
 | 
			
		||||
BOOST_PATH = /mnt/media/android/Boost-for-Android-Prebuilt
 | 
			
		||||
OPENSSL_PATH = /mnt/media/android/OpenSSL-for-Android-Prebuilt
 | 
			
		||||
IFADDRS_PATH = /mnt/media/android/android-ifaddrs
 | 
			
		||||
BOOST_PATH = /home/rebby/andp/Boost-for-Android-Prebuilt
 | 
			
		||||
OPENSSL_PATH = /home/rebby/andp/OpenSSL-for-Android-Prebuilt
 | 
			
		||||
MINIUPNP_PATH = /home/rebby/andp/MiniUPnP-for-Android-Prebuilt
 | 
			
		||||
IFADDRS_PATH = /home/rebby/andp/android-ifaddrs
 | 
			
		||||
 | 
			
		||||
# Steps in Android SDK manager:
 | 
			
		||||
# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +30,7 @@ IFADDRS_PATH = /mnt/media/android/android-ifaddrs
 | 
			
		|||
 | 
			
		||||
SOURCES += DaemonQT.cpp\
 | 
			
		||||
        mainwindow.cpp \
 | 
			
		||||
        ../../HTTPServer.cpp ../../I2PControl.cpp ../../UPnP.cpp ../../Daemon.cpp ../../Config.cpp \
 | 
			
		||||
        ../../HTTPServer.cpp ../../I2PControl.cpp ../../Daemon.cpp ../../Config.cpp \
 | 
			
		||||
    ../../AddressBook.cpp \
 | 
			
		||||
    ../../api.cpp \
 | 
			
		||||
    ../../Base.cpp \
 | 
			
		||||
| 
						 | 
				
			
			@ -69,9 +72,9 @@ SOURCES += DaemonQT.cpp\
 | 
			
		|||
    ../../TunnelEndpoint.cpp \
 | 
			
		||||
    ../../TunnelGateway.cpp \
 | 
			
		||||
    ../../TunnelPool.cpp \
 | 
			
		||||
    ../../UPnP.cpp \
 | 
			
		||||
    ../../util.cpp \
 | 
			
		||||
     ../../i2pd.cpp \
 | 
			
		||||
    $$IFADDRS_PATH/ifaddrs.c
 | 
			
		||||
     ../../i2pd.cpp
 | 
			
		||||
 | 
			
		||||
HEADERS  += DaemonQT.h mainwindow.h \
 | 
			
		||||
        ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \
 | 
			
		||||
| 
						 | 
				
			
			@ -122,9 +125,9 @@ HEADERS  += DaemonQT.h mainwindow.h \
 | 
			
		|||
    ../../TunnelEndpoint.h \
 | 
			
		||||
    ../../TunnelGateway.h \
 | 
			
		||||
    ../../TunnelPool.h \
 | 
			
		||||
    ../../UPnP.h \
 | 
			
		||||
    ../../util.h \
 | 
			
		||||
    ../../version.h \
 | 
			
		||||
    $$IFADDRS_PATH/ifaddrs.h 
 | 
			
		||||
    ../../version.h
 | 
			
		||||
 | 
			
		||||
FORMS    += mainwindow.ui
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,14 +141,19 @@ android {
 | 
			
		|||
message("Using Android settings")
 | 
			
		||||
DEFINES += ANDROID=1
 | 
			
		||||
DEFINES += __ANDROID__
 | 
			
		||||
 | 
			
		||||
INCLUDEPATH +=  $$BOOST_PATH/boost_1_53_0/include \
 | 
			
		||||
                $$OPENSSL_PATH/openssl-1.0.2/include \
 | 
			
		||||
                $$MINIUPNP_PATH/miniupnp-2.0/include \
 | 
			
		||||
                $$IFADDRS_PATH
 | 
			
		||||
DISTFILES += \
 | 
			
		||||
    android/AndroidManifest.xml
 | 
			
		||||
 | 
			
		||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
 | 
			
		||||
 | 
			
		||||
SOURCES += $$IFADDRS_PATH/ifaddrs.c ../../UPnP.cpp
 | 
			
		||||
HEADERS += $$IFADDRS_PATH/ifaddrs.h
 | 
			
		||||
 | 
			
		||||
equals(ANDROID_TARGET_ARCH, armeabi-v7a){
 | 
			
		||||
 | 
			
		||||
DEFINES += ANDROID_ARM7A
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +164,8 @@ LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \
 | 
			
		|||
-lboost_date_time-gcc-mt-1_53 \
 | 
			
		||||
-lboost_filesystem-gcc-mt-1_53 \
 | 
			
		||||
-lboost_program_options-gcc-mt-1_53 \
 | 
			
		||||
-L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl
 | 
			
		||||
-L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \
 | 
			
		||||
-L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc
 | 
			
		||||
 | 
			
		||||
PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \
 | 
			
		||||
                  $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +173,8 @@ PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \
 | 
			
		|||
DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include
 | 
			
		||||
 | 
			
		||||
ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \
 | 
			
		||||
                      $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so
 | 
			
		||||
                      $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \
 | 
			
		||||
                      $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so
 | 
			
		||||
}
 | 
			
		||||
equals(ANDROID_TARGET_ARCH, x86){
 | 
			
		||||
# http://stackoverflow.com/a/30235934/529442
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +183,8 @@ LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \
 | 
			
		|||
-lboost_date_time-gcc-mt-1_53 \
 | 
			
		||||
-lboost_filesystem-gcc-mt-1_53 \
 | 
			
		||||
-lboost_program_options-gcc-mt-1_53 \
 | 
			
		||||
-L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl
 | 
			
		||||
-L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \
 | 
			
		||||
-L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc
 | 
			
		||||
 | 
			
		||||
PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \
 | 
			
		||||
                  $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a
 | 
			
		||||
| 
						 | 
				
			
			@ -181,12 +192,13 @@ PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \
 | 
			
		|||
DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include
 | 
			
		||||
 | 
			
		||||
ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \
 | 
			
		||||
                      $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so
 | 
			
		||||
                      $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \
 | 
			
		||||
                      $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
linux:!android {
 | 
			
		||||
message("Using Linux settings")
 | 
			
		||||
LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
 | 
			
		||||
LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
#include "mainwindow.h"
 | 
			
		||||
//#include "ui_mainwindow.h"
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
#include "../../RouterContext.h"
 | 
			
		||||
 | 
			
		||||
MainWindow::MainWindow(QWidget *parent) :
 | 
			
		||||
    QMainWindow(parent)/*,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,20 +24,29 @@ MainWindow::MainWindow(QWidget *parent) :
 | 
			
		|||
    verticalLayout1->setContentsMargins(0, 0, 0, 0);
 | 
			
		||||
    quitButton = new QPushButton(verticalLayoutWidget);
 | 
			
		||||
    quitButton->setObjectName(QStringLiteral("quitButton"));
 | 
			
		||||
    QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
 | 
			
		||||
    sizePolicy.setHorizontalStretch(0);
 | 
			
		||||
    sizePolicy.setVerticalStretch(0);
 | 
			
		||||
    QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
 | 
			
		||||
    sizePolicy.setHorizontalStretch(1);
 | 
			
		||||
    //sizePolicy.setVerticalStretch(1);
 | 
			
		||||
    sizePolicy.setHeightForWidth(quitButton->sizePolicy().hasHeightForWidth());
 | 
			
		||||
    quitButton->setSizePolicy(sizePolicy);
 | 
			
		||||
 | 
			
		||||
    verticalLayout1->addWidget(quitButton);
 | 
			
		||||
    gracefulQuitButton = new QPushButton(verticalLayoutWidget);
 | 
			
		||||
    gracefulQuitButton->setObjectName(QStringLiteral("gracefulQuitButton"));
 | 
			
		||||
    QSizePolicy sizePolicy2(QSizePolicy::Maximum, QSizePolicy::Maximum);
 | 
			
		||||
    sizePolicy2.setHorizontalStretch(1);
 | 
			
		||||
    //sizePolicy2.setVerticalStretch(1);
 | 
			
		||||
    sizePolicy2.setHeightForWidth(gracefulQuitButton->sizePolicy().hasHeightForWidth());
 | 
			
		||||
    gracefulQuitButton->setSizePolicy(sizePolicy2);
 | 
			
		||||
    verticalLayout1->addWidget(gracefulQuitButton);
 | 
			
		||||
 | 
			
		||||
    setCentralWidget(centralWidget);
 | 
			
		||||
 | 
			
		||||
    setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0));
 | 
			
		||||
    quitButton->setText(QApplication::translate("MainWindow", "Quit", 0));
 | 
			
		||||
    gracefulQuitButton->setText(QApplication::translate("MainWindow", "Graceful Quit", 0));
 | 
			
		||||
 | 
			
		||||
    QObject::connect(quitButton, SIGNAL(released()), this, SLOT(handleQuitButton()));
 | 
			
		||||
    QObject::connect(gracefulQuitButton, SIGNAL(released()), this, SLOT(handleGracefulQuitButton()));
 | 
			
		||||
 | 
			
		||||
    //QMetaObject::connectSlotsByName(this);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +57,23 @@ void MainWindow::handleQuitButton() {
 | 
			
		|||
    QApplication::instance()->quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWindow::handleGracefulQuitButton() {
 | 
			
		||||
    qDebug("Graceful Quit pressed.");
 | 
			
		||||
    gracefulQuitButton->setText(QApplication::translate("MainWindow", "Graceful quit is in progress", 0));
 | 
			
		||||
    gracefulQuitButton->setEnabled(false);
 | 
			
		||||
    gracefulQuitButton->adjustSize();
 | 
			
		||||
    verticalLayoutWidget->adjustSize();
 | 
			
		||||
    i2p::context.SetAcceptsTunnels (false); // stop accpting tunnels
 | 
			
		||||
    QTimer::singleShot(10*60*1000/*millis*/, this, SLOT(handleGracefulQuitTimerEvent()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainWindow::handleGracefulQuitTimerEvent() {
 | 
			
		||||
    qDebug("Hiding the main window");
 | 
			
		||||
    close();
 | 
			
		||||
    qDebug("Performing quit");
 | 
			
		||||
    QApplication::instance()->quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MainWindow::~MainWindow()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("Destroying main window");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,12 +27,15 @@ public:
 | 
			
		|||
 | 
			
		||||
private slots:
 | 
			
		||||
    void handleQuitButton();
 | 
			
		||||
    void handleGracefulQuitButton();
 | 
			
		||||
    void handleGracefulQuitTimerEvent();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QWidget *centralWidget;
 | 
			
		||||
    QWidget *verticalLayoutWidget;
 | 
			
		||||
    QVBoxLayout *verticalLayout1;
 | 
			
		||||
    QPushButton *quitButton;
 | 
			
		||||
    QPushButton *gracefulQuitButton;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // MAINWINDOW_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,13 @@
 | 
			
		|||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QPushButton" name="gracefulShutdownButton">
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>Graceful Quit</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </widget>
 | 
			
		||||
  </widget>
 | 
			
		||||
| 
						 | 
				
			
			@ -60,8 +67,25 @@
 | 
			
		|||
    </hint>
 | 
			
		||||
   </hints>
 | 
			
		||||
  </connection>
 | 
			
		||||
  <connection>
 | 
			
		||||
   <sender>gracefulShutdownButton</sender>
 | 
			
		||||
   <signal>released()</signal>
 | 
			
		||||
   <receiver>MainWindow</receiver>
 | 
			
		||||
   <slot>handleGracefulQuitButton()</slot>
 | 
			
		||||
   <hints>
 | 
			
		||||
    <hint type="sourcelabel">
 | 
			
		||||
     <x>395</x>
 | 
			
		||||
     <y>319</y>
 | 
			
		||||
    </hint>
 | 
			
		||||
    <hint type="destinationlabel">
 | 
			
		||||
     <x>399</x>
 | 
			
		||||
     <y>239</y>
 | 
			
		||||
    </hint>
 | 
			
		||||
   </hints>
 | 
			
		||||
  </connection>
 | 
			
		||||
 </connections>
 | 
			
		||||
 <slots>
 | 
			
		||||
  <slot>handleQuitButton()</slot>
 | 
			
		||||
  <slot>handleGracefulQuitButton()</slot>
 | 
			
		||||
 </slots>
 | 
			
		||||
</ui>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue