diff --git a/AddressBook.cpp b/AddressBook.cpp index f66387db..3b3f85b4 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -56,7 +56,7 @@ namespace client f.seekg (0,std::ios::end); size_t len = f.tellg (); if (len < i2p::data::DEFAULT_IDENTITY_SIZE) { - LogPrint (eLogError, "Addresbook: File ", filename, " is too short: ", len); + LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len); return nullptr; } f.seekg(0, std::ios::beg); @@ -72,7 +72,7 @@ namespace client std::string path = storage.Path( address->GetIdentHash().ToBase32() ); std::ofstream f (path, std::ofstream::binary | std::ofstream::out); if (!f.is_open ()) { - LogPrint (eLogError, "Addresbook: can't open file ", path); + LogPrint (eLogError, "Addressbook: can't open file ", path); return; } size_t len = address->GetFullLen (); @@ -174,17 +174,17 @@ namespace client } if (m_IsDownloading) { - LogPrint (eLogInfo, "Addresbook: subscriptions is downloading, abort"); + LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort"); for (int i = 0; i < 30; i++) { if (!m_IsDownloading) { - LogPrint (eLogInfo, "Addresbook: subscriptions download complete"); + LogPrint (eLogInfo, "Addressbook: subscriptions download complete"); break; } std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds } - LogPrint (eLogError, "Addresbook: subscription download timeout"); + LogPrint (eLogError, "Addressbook: subscription download timeout"); m_IsDownloading = false; } if (m_Storage) @@ -303,10 +303,10 @@ namespace client numAddresses++; } else - LogPrint (eLogError, "Addresbook: malformed address ", addr, " for ", name); + LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name); } } - LogPrint (eLogInfo, "Addresbook: ", numAddresses, " addresses processed"); + LogPrint (eLogInfo, "Addressbook: ", numAddresses, " addresses processed"); if (numAddresses > 0) { m_IsLoaded = true; @@ -331,7 +331,7 @@ namespace client LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); } else - LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found in datadir"); + LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir"); } else LogPrint (eLogError, "Addressbook: subscriptions already loaded"); @@ -340,15 +340,18 @@ namespace client void AddressBook::DownloadComplete (bool success) { m_IsDownloading = false; - if (success && m_DefaultSubscription) + int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; + if (success) { - m_DefaultSubscription.reset (nullptr); - m_IsLoaded = true; + if (m_DefaultSubscription) m_DefaultSubscription.reset (nullptr); + if (m_IsLoaded) + nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; + else + m_IsLoaded = true; } if (m_SubscriptionsUpdateTimer) { - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes( - success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(nextUpdateTimeout)); m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, this, std::placeholders::_1)); } @@ -368,7 +371,7 @@ namespace client this, std::placeholders::_1)); } else - LogPrint (eLogError, "Addresbook: can't start subscriptions: missing shared local destination"); + LogPrint (eLogError, "Addressbook: can't start subscriptions: missing shared local destination"); } void AddressBook::StopSubscriptions () @@ -429,7 +432,7 @@ namespace client void AddressBookSubscription::Request () { // must be run in separate thread - LogPrint (eLogInfo, "Addresbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); + LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); bool success = false; i2p::util::http::url u (m_Link); i2p::data::IdentHash ident; @@ -488,7 +491,7 @@ namespace client 30); // wait for 30 seconds std::unique_lock l(newDataReceivedMutex); if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) - LogPrint (eLogError, "Addresbook: subscriptions request timeout expired"); + LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); } // process remaining buffer while (size_t len = stream->ReadSome (buf, 4096)) diff --git a/Config.cpp b/Config.cpp index 0de4d0aa..15c8ad67 100644 --- a/Config.cpp +++ b/Config.cpp @@ -125,6 +125,7 @@ namespace config { ("bandwidth", value()->default_value('-'), "Bandwidth limiting: L - 32kbps, O - 256Kbps, P - unlimited") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") + ("insomnia", value()->zero_tokens()->default_value(false), "Prevent system from sleeping") #endif ; diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp index 02c602f0..b28cf2cd 100644 --- a/DaemonWin32.cpp +++ b/DaemonWin32.cpp @@ -1,56 +1,113 @@ -#include "Config.h" -#include "Daemon.h" -#include "util.h" -#include "Log.h" - -#ifdef _WIN32 - +#include +#include "Config.h" +#include "Daemon.h" +#include "util.h" +#include "Log.h" + +#ifdef _WIN32 + +#include "Win32/Win32Service.h" +#ifdef WIN32_APP #include "Win32/Win32App.h" - -namespace i2p -{ - namespace util - { - bool DaemonWin32::init(int argc, char* argv[]) - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - setlocale(LC_ALL, "Russian"); - return Daemon_Singleton::init(argc, argv); +#endif + +namespace i2p +{ + namespace util + { + bool DaemonWin32::init(int argc, char* argv[]) + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + + if (!Daemon_Singleton::init(argc, argv)) + return false; + + std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl); + if (serviceControl == "install") + { + LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service"); + InstallService( + SERVICE_NAME, // Name of service + SERVICE_DISPLAY_NAME, // Name to display + SERVICE_START_TYPE, // Service start type + SERVICE_DEPENDENCIES, // Dependencies + SERVICE_ACCOUNT, // Service running account + SERVICE_PASSWORD // Password of the account + ); + return false; + } + else if (serviceControl == "remove") + { + LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service"); + UninstallService(SERVICE_NAME); + return false; + } + + if (isDaemon == 1) + { + LogPrint(eLogDebug, "Daemon: running as service"); + I2PService service(SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); + return false; + } + return false; + } + else + LogPrint(eLogDebug, "Daemon: running as user"); + + return true; } - - bool DaemonWin32::start() - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - setlocale(LC_ALL, "Russian"); + + bool DaemonWin32::start() + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); +#ifdef WIN32_APP if (!i2p::win32::StartWin32App ()) return false; // override log - i2p::config::SetOption("log", std::string ("file")); - bool ret = Daemon_Singleton::start(); - if (ret && IsLogToFile ()) - { - // TODO: find out where this garbage to console comes from - SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); - SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); - } - return ret; - } - - bool DaemonWin32::stop() + i2p::config::SetOption("log", std::string ("file")); +#endif + bool ret = Daemon_Singleton::start(); + if (ret && IsLogToFile ()) + { + // TODO: find out where this garbage to console comes from + SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); + SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); + } + bool insomnia; i2p::config::GetOption("insomnia", insomnia); + if (insomnia) + SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + return ret; + } + + bool DaemonWin32::stop() { - i2p::win32::StopWin32App (); - return Daemon_Singleton::stop(); +#ifdef WIN32_APP + i2p::win32::StopWin32App (); +#endif + return Daemon_Singleton::stop(); } void DaemonWin32::run () { +#ifdef WIN32_APP i2p::win32::RunWin32App (); - } - } -} - -#endif +#else + while (running) + { + std::this_thread::sleep_for (std::chrono::seconds(1)); + } + +#endif + } + } +} + +#endif diff --git a/FS.cpp b/FS.cpp index e84ff1d4..19963195 100644 --- a/FS.cpp +++ b/FS.cpp @@ -9,7 +9,7 @@ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #endif @@ -108,7 +108,7 @@ namespace fs { bool HashedStorage::Init(const char * chars, size_t count) { if (!boost::filesystem::exists(root)) { - boost::filesystem::create_directory(root); + boost::filesystem::create_directories(root); } for (size_t i = 0; i < count; i++) { diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index f0166a4a..160c27f6 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -12,6 +12,7 @@ #include "ClientContext.h" #include "I2PEndian.h" #include "I2PTunnel.h" +#include "Config.h" namespace i2p { @@ -36,6 +37,7 @@ namespace proxy void Terminate(); void AsyncSockRead(); void HTTPRequestFailed(/*std::string message*/); + void RedirectToJumpService(); void ExtractRequest(); bool ValidateHTTPRequest(); void HandleJumpServices(); @@ -95,6 +97,17 @@ namespace proxy std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } + void HTTPProxyHandler::RedirectToJumpService(/*HTTPProxyHandler::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 << "/?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)); + } + void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) { m_state = nstate; @@ -168,6 +181,13 @@ namespace proxy ExtractRequest(); //TODO: parse earlier if (!ValidateHTTPRequest()) return false; HandleJumpServices(); + + i2p::data::IdentHash identHash; + if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){ + RedirectToJumpService(); + return false; + } + m_request = m_method; m_request.push_back(' '); m_request += m_path; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 3b92d4bc..ddfa6478 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -203,6 +203,9 @@ namespace util const char HTTP_COMMAND_SAM_SESSION[] = "sam_session"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_COMMAND_I2P_TUNNELS[] = "i2p_tunnels"; + const char HTTP_COMMAND_JUMPSERVICES[] = "jumpservices="; + const char HTTP_PARAM_ADDRESS[] = "address"; + namespace misc_strings { @@ -393,6 +396,7 @@ namespace util else s << "Start accepting tunnels
\r\n
\r\n"; s << "Run peer test
\r\n
\r\n"; + s << "Jump services
\r\n
\r\n"; s << "
"; if (address.length () > 1) HandleCommand (address.substr (2), s); @@ -464,7 +468,13 @@ namespace util ShowTransports (s); else if (cmd == HTTP_COMMAND_TUNNELS) ShowTunnels (s); - else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) + else if (cmd == HTTP_COMMAND_JUMPSERVICES) + { + std::map params; + ExtractParams (command.substr (paramsPos), params); + auto address = params[HTTP_PARAM_ADDRESS]; + ShowJumpServices (address, s); + } else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) ShowTransitTunnels (s); else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) StartAcceptingTunnels (s); @@ -494,6 +504,16 @@ namespace util ShowI2PTunnels (s); } + void HTTPConnection::ShowJumpServices (const std::string& address, std::stringstream& s) + { + s << "
"; + s << ""; + s << "

\r\n"; + s << "Jump services for " << address << ""; + s << ""; + } + void HTTPConnection::ShowLocalDestinations (std::stringstream& s) { s << "Local Destinations:
\r\n
\r\n"; diff --git a/HTTPServer.h b/HTTPServer.h index 702e3191..f70e27dc 100644 --- a/HTTPServer.h +++ b/HTTPServer.h @@ -62,6 +62,7 @@ namespace util void HandleRequest (const std::string& address); void HandleCommand (const std::string& command, std::stringstream& s); + void ShowJumpServices (const std::string& address, std::stringstream& s); void ShowTransports (std::stringstream& s); void ShowTunnels (std::stringstream& s); void ShowTransitTunnels (std::stringstream& s); diff --git a/Identity.cpp b/Identity.cpp index 9d4162bf..0ca9567a 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -244,21 +244,20 @@ namespace data size_t IdentityEx::FromBase64(const std::string& s) { const size_t slen = s.length(); - uint8_t buf[slen]; // binary data can't exceed base64 - const size_t len = Base64ToByteStream (s.c_str(), slen, buf, slen); - return FromBuffer (buf, len); + std::vector buf(slen); // binary data can't exceed base64 + const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen); + return FromBuffer (buf.data(), len); } std::string IdentityEx::ToBase64 () const { const size_t bufLen = GetFullLen(); const size_t strLen = Base64EncodingBufferSize(bufLen); - uint8_t buf[bufLen]; - char str[strLen]; - size_t l = ToBuffer (buf, bufLen); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, strLen); - str[l1] = 0; - return std::string (str); + std::vector buf(bufLen); + std::vector str(strLen); + size_t l = ToBuffer (buf.data(), bufLen); + size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen); + return std::string (str.data(), l1); } size_t IdentityEx::GetSigningPublicKeyLen () const diff --git a/Makefile b/Makefile index 8f1847f3..d9d62b4c 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ else ifeq ($(UNAME),Linux) DAEMON_SRC += DaemonLinux.cpp include Makefile.linux else # win32 mingw - DAEMON_SRC += DaemonWin32.cpp Win32/Win32App.cpp + DAEMON_SRC += DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp include Makefile.mingw endif diff --git a/Makefile.mingw b/Makefile.mingw index d752e48c..1b500757 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,10 +1,11 @@ +USE_WIN32_APP=yes CXX = g++ WINDRES = windres -CXXFLAGS = -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN +CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN NEEDED_CXXFLAGS = -std=c++11 BOOST_SUFFIX = -mt INCFLAGS = -I/usr/include/ -I/usr/local/include/ -LDFLAGS = -mwindows -Wl,-rpath,/usr/local/lib \ +LDFLAGS = -Wl,-rpath,/usr/local/lib \ -L/usr/local/lib \ -L/c/dev/openssl \ -L/c/dev/boost/lib @@ -24,8 +25,13 @@ LDLIBS = \ -static-libgcc -static-libstdc++ \ -Wl,-Bstatic -lstdc++ \ -Wl,-Bstatic -lpthread -DAEMON_RC += Win32/Resource.rc -DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) + +ifeq ($(USE_WIN32_APP), yes) + CXXFLAGS += -DWIN32_APP + LDFLAGS += -mwindows -s + DAEMON_RC += Win32/Resource.rc + DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) +endif ifeq ($(USE_AESNI),1) CPU_FLAGS = -maes -DAESNI diff --git a/Transports.cpp b/Transports.cpp index acffd865..5fcce0fc 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -261,7 +261,7 @@ namespace transport { peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); - if (address) + if (address && m_NTCPServer) { #if BOOST_VERSION >= 104900 if (!address->host.is_unspecified ()) // we have address now diff --git a/UPnP.cpp b/UPnP.cpp index f8c038ff..c4e5e04c 100644 --- a/UPnP.cpp +++ b/UPnP.cpp @@ -19,27 +19,19 @@ #include "UPnP.h" #include "NetDb.h" #include "util.h" +#include "RouterInfo.h" #include #include // These are per-process and are safe to reuse for all threads -#ifndef UPNPDISCOVER_SUCCESS -/* miniupnpc 1.5 */ -UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int); -int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, - const char *, const char *, const char *, const char *); -#else -/* miniupnpc 1.6 */ -UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int, int, int *); -int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, - const char *, const char *, const char *, const char *, const char *); -#endif -int (*UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int); -int (*UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *); -int (*UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *); -void (*freeUPNPDevlistFunc) (struct UPNPDev *); -void (*FreeUPNPUrlsFunc) (struct UPNPUrls *); +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 @@ -109,7 +101,8 @@ namespace transport void UPnP::Run () { - for (auto& address : context.GetRouterInfo ().GetAddresses ()) + std::vector a = context.GetRouterInfo().GetAddresses(); + for (auto& address : a) { if (!address.host.is_v6 ()) { @@ -128,12 +121,10 @@ namespace transport void UPnP::Discover () { -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0); -#else - /* miniupnpc 1.6 */ int nerror = 0; +#if MINIUPNPC_API_VERSION >= 14 + m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror); +#else m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror); #endif @@ -180,13 +171,7 @@ namespace transport std::string strDesc = "I2Pd"; try { for (;;) { -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - 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); -#else - /* miniupnpc 1.6 */ 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"); -#endif if (r!=UPNPCOMMAND_SUCCESS) { LogPrint (eLogError, "UPnP: AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r); diff --git a/Win32/Resource.rc b/Win32/Resource.rc index 56868181..e10ec496 100644 --- a/Win32/Resource.rc +++ b/Win32/Resource.rc @@ -53,7 +53,6 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. MAINICON ICON "ictoopie.ico" -IDI_ICON1 ICON "ictoopie_16.ico" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index c997b266..cd9a8f34 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -1,12 +1,14 @@ #include #include #include -//#include "../Daemon.h" +#include "../Config.h" #include "resource.h" #include "Win32App.h" #define ID_ABOUT 2000 #define ID_EXIT 2001 +#define ID_CONSOLE 2002 +#define ID_APP 2003 #define ID_TRAY_ICON 2050 #define WM_TRAYICON (WM_USER + 1) @@ -18,10 +20,12 @@ namespace win32 static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) { HMENU hPopup = CreatePopupMenu(); - InsertMenu (hPopup, 0, MF_BYPOSITION | MF_STRING, ID_ABOUT, "About..."); - InsertMenu (hPopup, 1, MF_BYPOSITION | MF_STRING, ID_EXIT , "Exit"); - SetMenuDefaultItem (hPopup, ID_ABOUT, FALSE); - SetFocus (hWnd); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); + SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); POINT p; @@ -44,10 +48,11 @@ namespace win32 nid.cbSize = sizeof(nid); nid.hWnd = hWnd; nid.uID = ID_TRAY_ICON; - nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; nid.uCallbackMessage = WM_TRAYICON; - nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (IDI_ICON1)); + nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); strcpy (nid.szTip, "i2pd"); + strcpy (nid.szInfo, "i2pd is running"); Shell_NotifyIcon(NIM_ADD, &nid ); } @@ -88,14 +93,39 @@ namespace win32 PostMessage (hWnd, WM_CLOSE, 0, 0); return 0; } + case ID_CONSOLE: + { + char buf[30]; + std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + std::snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); + ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); + return 0; + } + case ID_APP: + { + ShowWindow(hWnd, SW_SHOW); + return 0; + } } break; } + case WM_SYSCOMMAND: + { + switch (wParam) + { + case SC_MINIMIZE: + { + ShowWindow(hWnd, SW_HIDE); + return 0; + } + } + } case WM_TRAYICON: { - SetForegroundWindow (hWnd); switch (lParam) { + case WM_LBUTTONUP: case WM_RBUTTONUP: { SetForegroundWindow (hWnd); @@ -127,15 +157,14 @@ namespace win32 wclx.cbClsExtra = 0; wclx.cbWndExtra = 0; wclx.hInstance = hInst; - wclx.hIcon = LoadIcon (hInst, IDI_APPLICATION); - wclx.hIconSm = LoadIcon (hInst, IDI_APPLICATION); + wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); wclx.hCursor = LoadCursor (NULL, IDC_ARROW); wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wclx.lpszMenuName = NULL; wclx.lpszClassName = I2PD_WIN32_CLASSNAME; RegisterClassEx (&wclx); // create new window - if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 250, 150, NULL, NULL, hInst, NULL)) + if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 250, 150, NULL, NULL, hInst, NULL)) { MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); return false; diff --git a/Win32/ictoopie_16.ico b/Win32/ictoopie_16.ico deleted file mode 100644 index 525646c3..00000000 Binary files a/Win32/ictoopie_16.ico and /dev/null differ diff --git a/Win32/resource.h b/Win32/resource.h index 7bb73d38..a8309c8b 100644 --- a/Win32/resource.h +++ b/Win32/resource.h @@ -2,7 +2,7 @@ // Microsoft Visual C++ generated include file. // Used by Resource.rc // -#define IDI_ICON1 101 +#define MAINICON 101 // Next default values for new objects // diff --git a/appveyor.yml b/appveyor.yml index e190b949..6600714d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -142,9 +142,9 @@ install: - if not defined msvc ( C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy bash pacman pacman-mirrors msys2-runtime msys2-runtime-devel" && if "%x64%" == "1" ( - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc" + C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-openssl mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc" ) else ( - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-i686-boost mingw-w64-i686-miniupnpc" + C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-i686-openssl mingw-w64-i686-boost mingw-w64-i686-miniupnpc" ) ) cache: @@ -167,7 +167,7 @@ build_script: echo "bitness=%bitness%; static=%static%; dll=%dll%; type=%type%; generator=%generator%; variant=%variant%; cmake=%cmake%; cmake_extra=%cmake_extra%" - if not defined msvc ( - C:\msys64\usr\bin\bash -lc "export PATH=/mingw%bitness%/bin:/usr/bin && cd /c/projects/build && cmake /c/projects/i2pd/build -G 'Unix Makefiles' -DWITH_AESNI=ON -DWITH_UPNP=ON %cmake% %cmake_extra% -DWITH_STATIC=%static% -DWITH_HARDENING=ON -DCMAKE_INSTALL_PREFIX:PATH=/c/projects/instdir -DCMAKE_FIND_ROOT_PATH=/mingw%bitness% && make install" + C:\msys64\usr\bin\bash -lc "export PATH=/mingw%bitness%/bin:/usr/bin && cd /c/projects/build && CC=/mingw%bitness%/bin/gcc.exe CXX=/mingw%bitness%/bin/g++.exe /usr/bin/cmake /c/projects/i2pd/build -G 'Unix Makefiles' -DWITH_AESNI=ON -DWITH_UPNP=ON %cmake% %cmake_extra% -DWITH_STATIC=%static% -DWITH_HARDENING=ON -DCMAKE_INSTALL_PREFIX:PATH=/c/projects/instdir -DCMAKE_FIND_ROOT_PATH=/mingw%bitness% && make install" && 7z a -tzip -mx9 -mmt C:\projects\i2pd\i2pd-mingw-win%bitness%-%type%.zip C:\projects\instdir\* C:\msys64\mingw%bitness%\bin\zlib1.dll C:\msys64\mingw%bitness%\bin\*eay32.dll ) - rem We are fine with multiple generated configurations in MS solution. Will use later diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 577dcc66..76963026 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -11,6 +11,7 @@ option(WITH_BINARY "Build binary" ON) option(WITH_STATIC "Static build" OFF) option(WITH_UPNP "Include support for UPnP client" OFF) option(WITH_PCH "Use precompiled header" OFF) +option(WITH_GUI "Include GUI (currently MS Windows only)" ON) # paths set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) @@ -56,7 +57,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS) endif () add_library(libi2pd ${LIBI2PD_SRC}) -set_target_properties(libi2pd PROPERTIES OUTPUT_NAME "i2pd") +set_target_properties(libi2pd PROPERTIES PREFIX "") install(TARGETS libi2pd EXPORT libi2pd ARCHIVE DESTINATION lib @@ -95,7 +96,7 @@ endif () # compiler flags customization (by vendor) if (MSVC) - add_definitions( -D_WIN32_WINNT=_WIN32_WINNT_WINXP -DWIN32_LEAN_AND_MEAN -DNOMINMAX ) #-DOPENSSL_NO_SSL2 -DOPENSSL_USE_DEPRECATED + add_definitions( -DWIN32_LEAN_AND_MEAN -DNOMINMAX ) # TODO Check & report to Boost dev, there should be no need for these two add_definitions( -DBOOST_THREAD_NO_LIB -DBOOST_CHRONO_NO_LIB ) set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL" ) @@ -157,6 +158,11 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonLinux.cpp") elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS) list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonWin32.cpp") + if (WITH_GUI) + list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/Win32/Win32App.cpp") + set_source_files_properties("${CMAKE_SOURCE_DIR}/DaemonWin32.cpp" + PROPERTIES COMPILE_DEFINITIONS WIN32_APP) + endif () list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/Win32/Win32Service.cpp") list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/Win32/Resource.rc") endif () @@ -309,11 +315,14 @@ include(GNUInstallDirs) if (WITH_BINARY) add_executable ( "${PROJECT_NAME}" ${DAEMON_SRC} ) - if(NOT MSVC) # FIXME: incremental linker file name (.ilk) collision for dll & exe - if (WITH_STATIC) - set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static" ) - endif () - endif() + if (WIN32 AND WITH_GUI) + set_target_properties("${PROJECT_NAME}" PROPERTIES WIN32_EXECUTABLE TRUE ) + endif() + if(NOT MSVC) + if (WITH_STATIC) + set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static" ) + endif () + endif() if (WITH_PCH) if (MSVC) diff --git a/i2pd.cpp b/i2pd.cpp index 6167f10e..f3ac6b3f 100644 --- a/i2pd.cpp +++ b/i2pd.cpp @@ -3,10 +3,25 @@ int main( int argc, char* argv[] ) { - Daemon.init(argc, argv); - if (Daemon.start()) - Daemon.run (); - Daemon.stop(); + if (Daemon.init(argc, argv)) + { + if (Daemon.start()) + Daemon.run (); + Daemon.stop(); + } return EXIT_SUCCESS; } +#ifdef _WIN32 +#include + +int CALLBACK WinMain( + _In_ HINSTANCE hInstance, + _In_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ int nCmdShow + ) +{ + return main(__argc, __argv); +} +#endif