mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2026-07-02 17:52:41 +00:00
413 lines
11 KiB
C++
413 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2025, The PurpleI2P Project
|
|
*
|
|
* This file is part of Purple i2pd project and licensed under BSD3
|
|
*
|
|
* See full license text in LICENSE file at top of project tree
|
|
*/
|
|
|
|
#if defined(__HAIKU__)
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <MenuItem.h>
|
|
#include <MenuBar.h>
|
|
#include <StringView.h>
|
|
#include <Font.h>
|
|
#include <MessageRunner.h>
|
|
#include<StringItem.h>
|
|
#include<ListView.h>
|
|
#include<TabView.h>
|
|
#include<ScrollView.h>
|
|
#include<Button.h>
|
|
#include <Window.h>
|
|
#include <Application.h>
|
|
#include <Alert.h>
|
|
#include<sstream>
|
|
//#include<Roster.h>
|
|
|
|
#include "version.h"
|
|
#include "Log.h"
|
|
#include "Config.h"
|
|
#include "RouterContext.h"
|
|
#include "Tunnel.h"
|
|
#include "Transports.h"
|
|
#include "Daemon.h"
|
|
#include "ClientContext.h"
|
|
enum
|
|
{
|
|
M_GRACEFUL_SHUTDOWN = 1,
|
|
M_RUN_PEER_TEST,
|
|
M_DUMMY_COMMAND,
|
|
C_GRACEFUL_SHUTDOWN_UPDATE,
|
|
C_MAIN_VIEW_UPDATE,
|
|
M_SHOW_TUNNEL,
|
|
C_OPENHTTP,
|
|
M_SHOWMAIN,
|
|
M_CLOSETUNNEL
|
|
};
|
|
constexpr bigtime_t GRACEFUL_SHUTDOWN_UPDATE_INTERVAL = 1000*1100; // in microseconds, ~ 1 sec
|
|
constexpr int GRACEFUL_SHUTDOWN_UPDATE_COUNT = 600; // 10 minutes
|
|
constexpr bigtime_t MAIN_VIEW_UPDATE_INTERVAL = 5000*1000; // in miscroseconds, 5 secs
|
|
|
|
class MainWindow: public BWindow
|
|
{
|
|
public:
|
|
MainWindow ();
|
|
|
|
private:
|
|
void MessageReceived (BMessage * msg) override;
|
|
bool QuitRequested () override;
|
|
void GetInfoAboutTunnel(BMessage * msg);
|
|
void UpdateMainView ();
|
|
void OpenHTTPInterface(void);
|
|
void DrawTunnel(std::stringstream & s);
|
|
size_t m_counter_streams = 0;
|
|
private:
|
|
BMessenger m_Messenger;
|
|
BTabView * m_tab_view;
|
|
BStringView * m_MainView;
|
|
std::unique_ptr<BMessageRunner> m_MainViewUpdateTimer, m_GracefulShutdownTimer;
|
|
bool m_IsGracefulShutdownComplete = false;
|
|
bool m_IsDrawTunnel = false;
|
|
i2p::data::IdentHash m_IdentHash;
|
|
};
|
|
|
|
class I2PApp: public BApplication
|
|
{
|
|
public:
|
|
|
|
I2PApp ();
|
|
void CreateMainWindow ();
|
|
};
|
|
|
|
MainWindow::MainWindow ():
|
|
BWindow (BRect(100, 100, 500, 400), "i2pd " VERSION, B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE),
|
|
m_Messenger (nullptr, this)
|
|
{
|
|
auto r = Bounds (); r.bottom = 20;
|
|
auto menuBar = new BMenuBar (r, "menubar");
|
|
AddChild (menuBar);
|
|
auto runMenu = new BMenu ("Run");
|
|
runMenu->AddItem (new BMenuItem ("Open web interface", new BMessage(C_OPENHTTP)));
|
|
runMenu->AddItem (new BMenuItem ("Graceful shutdown", new BMessage (M_GRACEFUL_SHUTDOWN), 'G'));
|
|
runMenu->AddItem (new BMenuItem ("Show Main page", new BMessage (M_SHOWMAIN), 'M'));
|
|
runMenu->AddItem (new BMenuItem ("Quit", new BMessage (B_QUIT_REQUESTED), 'Q'));
|
|
menuBar->AddItem (runMenu);
|
|
auto commandsMenu = new BMenu ("Commands");
|
|
commandsMenu->AddItem (new BMenuItem ("Run peer test", new BMessage (M_RUN_PEER_TEST), 'P'));
|
|
menuBar->AddItem (commandsMenu);
|
|
auto tunnelsMenu = new BMenu ("Tunnels");
|
|
for (auto& it: i2p::client::context.GetClientTunnels ())
|
|
{
|
|
auto msg = new BMessage{M_SHOW_TUNNEL};
|
|
msg->AddString("tunnel_name", BString(it.second->GetName()));
|
|
msg->AddBool("is_client_tunnel", true);
|
|
tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), msg));
|
|
}
|
|
for (auto& it: i2p::client::context.GetServerTunnels ())
|
|
{
|
|
auto msg = new BMessage{M_SHOW_TUNNEL};
|
|
msg->AddString("tunnel_name", BString(it.second->GetName()));
|
|
msg->AddBool("is_client_tunnel", false);
|
|
tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), msg));
|
|
}
|
|
menuBar->AddItem (tunnelsMenu);
|
|
|
|
m_tab_view = new BTabView(Bounds().OffsetByCopy(0, 20), "tabview");
|
|
AddChild(m_tab_view);
|
|
|
|
BRect tabRect = m_tab_view->ContainerView()->Bounds();
|
|
auto bview = new BView(tabRect, "main_tab", B_FOLLOW_ALL, B_WILL_DRAW);
|
|
bview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
|
|
m_MainView = new BStringView(BRect(10, 10, tabRect.right - 10, tabRect.bottom - 10),"info_view", "Starting...", B_FOLLOW_ALL);
|
|
bview->AddChild(m_MainView);
|
|
|
|
m_tab_view->AddTab(bview);
|
|
m_tab_view->TabAt(m_counter_streams)->SetLabel("Info");
|
|
|
|
m_MainView->SetViewColor (255, 255, 255);
|
|
m_MainView->SetHighColor (0xD4, 0x3B, 0x69);
|
|
BFont font = *be_plain_font;
|
|
font.SetSize (12);
|
|
m_MainView->SetFont (&font);
|
|
m_MainViewUpdateTimer = std::make_unique<BMessageRunner>(m_Messenger,
|
|
BMessage (C_MAIN_VIEW_UPDATE), MAIN_VIEW_UPDATE_INTERVAL);
|
|
}
|
|
|
|
void MainWindow::UpdateMainView ()
|
|
{
|
|
std::stringstream s;
|
|
if(!m_IsDrawTunnel)
|
|
{
|
|
i2p::util::PrintMainWindowText (s);
|
|
}
|
|
else
|
|
{
|
|
DrawTunnel(s);
|
|
}
|
|
////std::cout << s.str() << std::endl;
|
|
m_MainView->SetText (s.str ().c_str ());
|
|
}
|
|
/*
|
|
* Логика - создать вектор с streamID + vector dest.
|
|
* не перерисовывать каждый раз а лишь добавлять которых нет
|
|
* и удалять те которые есть
|
|
* кнопка close работает
|
|
* */
|
|
void MainWindow :: DrawTunnel(std::stringstream & s)
|
|
{
|
|
|
|
s << "\n";
|
|
s << "I2P Address: " << i2p::client::context.GetAddressBook().ToAddress(m_IdentHash) << ".b32.i2p\r\n";
|
|
|
|
auto dest = i2p::client::context.FindLocalDestination (m_IdentHash);
|
|
while(m_tab_view->CountTabs() > 1)
|
|
{
|
|
m_tab_view->RemoveTab(m_tab_view->CountTabs() - 1);
|
|
}
|
|
m_counter_streams = 0;
|
|
//std::cout << "Draw List View" << std::endl;;
|
|
if(!dest)
|
|
{
|
|
s << "Can't found local dest\r\n";
|
|
}
|
|
else
|
|
{
|
|
s << "Streams: \n";
|
|
for (auto stream : dest->GetAllStreams())
|
|
{
|
|
std::stringstream s1;
|
|
auto streamDest = i2p::client::context.GetAddressBook().ToAddress(stream->GetRemoteIdentity());
|
|
std::string streamDestShort = streamDest.substr(0,12) + ".b32.i2p";
|
|
s1 << streamDestShort << "|out|" << stream->GetNumSentBytes() << "|in:" << stream->GetNumReceivedBytes() <<"|";
|
|
|
|
BRect tabRect = m_tab_view->ContainerView()->Bounds();
|
|
auto bview = new BView(tabRect, streamDestShort.c_str(), B_FOLLOW_ALL, B_WILL_DRAW);
|
|
bview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
|
|
auto tunnel_view = new BStringView(BRect(0, 0, tabRect.right - 100, tabRect.bottom - 100),"tunnel_view", "tunnel_view...", B_FOLLOW_ALL);
|
|
|
|
tunnel_view->SetText (s1.str ().c_str ());
|
|
BRect btnRect{10,10,90,35};//10+32,10+12);
|
|
auto msg = new BMessage(M_CLOSETUNNEL);
|
|
msg->AddUInt32("tunnel_val", stream->GetRecvStreamID());
|
|
BButton * closeButton = new BButton(
|
|
btnRect,"close leaseset", "close", msg
|
|
);
|
|
bview->AddChild(closeButton);
|
|
bview->AddChild(tunnel_view);
|
|
|
|
|
|
m_tab_view->AddTab(bview);
|
|
m_tab_view->TabAt(++m_counter_streams)->SetLabel(streamDestShort.c_str());
|
|
|
|
|
|
/*
|
|
* void ShowLocalDestination in daemon/HTTPServer.cpp
|
|
*/
|
|
s << s1.str() << "\n";
|
|
}
|
|
//s << "\r\n";
|
|
}
|
|
}
|
|
|
|
void MainWindow :: GetInfoAboutTunnel(BMessage * msg)
|
|
{
|
|
if(!msg) return;
|
|
bool isClient = false;
|
|
BString name{};
|
|
msg->FindString("tunnel_name", &name);
|
|
msg->FindBool("is_client_tunnel", &isClient);
|
|
if (isClient)
|
|
{
|
|
for(auto it :i2p::client::context.GetClientTunnels () )
|
|
{
|
|
if( BString(it.second->GetName()) == name )
|
|
{
|
|
auto & ident = it.second->GetLocalDestination()->GetIdentHash();
|
|
m_IdentHash = ident;
|
|
m_IsDrawTunnel = true;
|
|
return UpdateMainView();
|
|
//TODO:
|
|
}
|
|
}
|
|
}else {
|
|
for(auto it: i2p::client::context.GetServerTunnels())
|
|
{
|
|
if( BString(it.second->GetName()) == name)
|
|
{
|
|
auto & ident = it.second->GetLocalDestination()->GetIdentHash();
|
|
m_IdentHash = ident;
|
|
m_IsDrawTunnel = true;
|
|
return UpdateMainView();
|
|
//TODO:
|
|
}
|
|
}
|
|
}// if not client tunnel
|
|
}
|
|
|
|
void MainWindow :: OpenHTTPInterface()
|
|
{
|
|
bool enabled; i2p::config::GetOption("http.enabled", enabled);
|
|
uint16_t port; i2p::config::GetOption("http.port", port);
|
|
std::string address; i2p::config::GetOption("http.address", address);
|
|
if(!enabled)
|
|
{
|
|
return m_MainView->SetText("Not enabled http server");
|
|
}
|
|
std::ostringstream com;
|
|
com << "/bin/open 'http://" << address << ":" << port<<"'";
|
|
//char * argv[] = {(char*)url.str().c_str() , NULL, NULL};
|
|
//BRoster{}.Launch( "application/x-vnd.Haiku-WebPositive", 2, argv);
|
|
std::string com_s{com.str()};
|
|
std::thread ([com_s]() {
|
|
system(com_s.c_str());
|
|
}).detach();
|
|
}
|
|
|
|
void MainWindow::MessageReceived (BMessage * msg)
|
|
{
|
|
if (!msg) return;
|
|
uint32_t tunnel_val;// streamID (to method)
|
|
switch (msg->what)
|
|
{
|
|
// to method
|
|
case M_CLOSETUNNEL:
|
|
//std::cout << "Close Tunnel (DEBUG NOT WORKS)" << std::endl;
|
|
msg->FindUInt32("tunnel_val", &tunnel_val);
|
|
if(tunnel_val)
|
|
{
|
|
auto dest =
|
|
i2p::client::context.FindLocalDestination
|
|
(m_IdentHash);
|
|
if(dest)
|
|
{
|
|
dest->DeleteStream(tunnel_val);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case M_SHOWMAIN:
|
|
m_IsDrawTunnel = false;
|
|
m_IdentHash = {};
|
|
UpdateMainView();
|
|
break;
|
|
case C_MAIN_VIEW_UPDATE:
|
|
UpdateMainView ();
|
|
break;
|
|
case C_OPENHTTP:
|
|
OpenHTTPInterface();
|
|
|
|
break;
|
|
case M_SHOW_TUNNEL:
|
|
GetInfoAboutTunnel(msg);
|
|
break;
|
|
case M_GRACEFUL_SHUTDOWN:
|
|
if (!m_GracefulShutdownTimer)
|
|
{
|
|
i2p::context.SetAcceptsTunnels (false);
|
|
Daemon.gracefulShutdownInterval = GRACEFUL_SHUTDOWN_UPDATE_COUNT;
|
|
m_MainViewUpdateTimer = nullptr;
|
|
m_GracefulShutdownTimer = std::make_unique<BMessageRunner>(m_Messenger,
|
|
BMessage (C_GRACEFUL_SHUTDOWN_UPDATE), GRACEFUL_SHUTDOWN_UPDATE_INTERVAL);
|
|
}
|
|
break;
|
|
case C_GRACEFUL_SHUTDOWN_UPDATE:
|
|
if (Daemon.gracefulShutdownInterval > 0)
|
|
{
|
|
UpdateMainView ();
|
|
Daemon.gracefulShutdownInterval--;
|
|
}
|
|
if (!Daemon.gracefulShutdownInterval || i2p::tunnel::tunnels.CountTransitTunnels () <= 0)
|
|
{
|
|
m_GracefulShutdownTimer = nullptr;
|
|
Daemon.gracefulShutdownInterval = 0;
|
|
m_IsGracefulShutdownComplete = true;
|
|
m_Messenger.SendMessage (B_QUIT_REQUESTED);
|
|
}
|
|
break;
|
|
case M_RUN_PEER_TEST:
|
|
i2p::transport::transports.PeerTest ();
|
|
break;
|
|
default:
|
|
BWindow::MessageReceived (msg);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::QuitRequested ()
|
|
{
|
|
bool isQuit = true;
|
|
if (!m_IsGracefulShutdownComplete)
|
|
{
|
|
auto alert = new BAlert (nullptr, "This will stop i2pd. Are you sure?", "Cancel", "Yes", "No",
|
|
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
|
|
alert->SetShortcut (0, B_ESCAPE);
|
|
isQuit = alert->Go () == 1; // Yes
|
|
}
|
|
if (isQuit)
|
|
{
|
|
m_MainViewUpdateTimer = nullptr;
|
|
m_GracefulShutdownTimer = nullptr;
|
|
}
|
|
return isQuit;
|
|
}
|
|
|
|
I2PApp::I2PApp (): BApplication("application/x-vnd.purplei2p-i2pd")
|
|
{
|
|
}
|
|
|
|
void I2PApp::CreateMainWindow ()
|
|
{
|
|
auto mainWindow = new MainWindow ();
|
|
mainWindow->Show ();
|
|
}
|
|
|
|
namespace i2p
|
|
{
|
|
namespace util
|
|
{
|
|
bool DaemonHaiku::start ()
|
|
{
|
|
I2PApp * app = nullptr;
|
|
if (!isDaemon)
|
|
{
|
|
app = new I2PApp(); // set be_app
|
|
i2p::log::SetThrowFunction ([](const std::string& s)
|
|
{
|
|
auto alert = new BAlert (nullptr, s.c_str (), "Quit", nullptr, nullptr,
|
|
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT);
|
|
alert->Go ();
|
|
});
|
|
}
|
|
bool ret = false;
|
|
if (app)
|
|
{
|
|
ret = Daemon_Singleton::start ();
|
|
if (ret)
|
|
app->CreateMainWindow ();
|
|
}
|
|
else
|
|
ret = DaemonUnix::start ();
|
|
return ret;
|
|
}
|
|
|
|
void DaemonHaiku::run ()
|
|
{
|
|
if (be_app)
|
|
{
|
|
be_app->Run ();
|
|
delete be_app;
|
|
}
|
|
else
|
|
DaemonUnix::run ();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|