piso/main.cc
#include <libgen.h>
#ifdef _WIN32
#define _UNICODE = 1
#define UNICODE = 1
#include <winsock2.h>
#include <windows.h>
#include <strings.h>
#endif
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "wx/xrc/xmlres.h"
#include "gui.cpp"
#include <wx/listctrl.h>
#include <wx/spinctrl.h>
#include <wx/hyperlink.h>
#include <wx/filepicker.h>
#include <wx/utils.h>
#include <wx/filefn.h>
#include <wx/platinfo.h>
#include <wx/stdpaths.h>
#include <wx/socket.h>
#include <wx/protocol/http.h>
#include <wx/fileconf.h>
#include <wx/aboutdlg.h>
#include "crow_all.h"
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
#include <wx/base64.h>
#include "miniupnpc/miniupnpc.h"
#include "miniupnpc/upnpcommands.h"
#include "icon.xpm"
#include "html.h"
const wxString version("0.1");
const wxString mainAuthor("Fabio Di Matteo (fadimatteo@gmail.com)");
class simple_upnpc : public wxThread
{
public:
char iaddr[30];
char eport[20];
char iport[20];
char leaseDuration[20];
char description[200];
std::string getGateway();
int addPortMapping();
int deletePortMapping();
int op=0;
private:
virtual void *Entry();
};
void *simple_upnpc::Entry()
{
if (op==0) addPortMapping();
if (op==1) deletePortMapping();
return (wxThread::ExitCode)0;
}
int simple_upnpc::addPortMapping()
{
struct UPNPDev *devlist ;
int err;
devlist = upnpDiscover(2000, NULL, NULL,
UPNP_LOCAL_PORT_SAME, 0, 2, &err);
if (devlist==NULL) return -1;
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[20]={0};
sprintf(lanaddr,"%s",getGateway().c_str());
UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), nullptr, 0);
char proto[]="TCP";
int res;
res=UPNP_AddPortMapping(devlist->descURL, data.first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
return res;
}
int simple_upnpc::deletePortMapping()
{
struct UPNPDev *devlist ;
int err;
devlist = upnpDiscover(2000, NULL, NULL,
UPNP_LOCAL_PORT_SAME, 0, 2, &err);
if (devlist==NULL) return -1;
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[20]={0};
sprintf(lanaddr,"%s",getGateway().c_str());
UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr),nullptr, 0);
char proto[]="TCP";
int res;
res=UPNP_DeletePortMapping(devlist->descURL, data.first.servicetype,
eport, proto, iaddr);
return res;
}
std::string simple_upnpc::getGateway()
{
struct UPNPDev *devlist ;
int err;
devlist = upnpDiscover(2000, NULL, NULL, UPNP_LOCAL_PORT_SAME, 0, 2, &err);
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[]={0};
UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr),nullptr, 0);
std::string sub ;
std::string fullUrl(urls.ipcondescURL);
std::string gw("192.168.1.1");
if (fullUrl.length()>4)
{
int pos = fullUrl.find(":");
sub = fullUrl.substr(pos + 3);
pos = sub.find(":");
sub = sub.substr(0, pos);
gw=sub;
}
return gw;
}
class CustomLogger : public crow::ILogHandler
{
public:
wxListCtrl *weblog;
CustomLogger() {}
void log(std::string message, crow::LogLevel /*level*/)
{
std::cerr << message << std::endl;
wxTheApp->CallAfter( [=] {
if (this->weblog)
{
int n=this->weblog->GetItemCount();
this->weblog->InsertItem(n+1,wxNow() +" "+ message);
this->weblog->EnsureVisible(n);
}
});
}
};
class webServer : public wxThread
{
public:
crow::SimpleApp app;
CustomLogger logger;
wxListCtrl *log;
int port;
std::string root_folder;
std::string prefixUrl;
bool upload=false;
webServer();
private:
virtual void *Entry();
};
webServer::webServer()
{
}
void *webServer::Entry()
{
extern const char* p;
logger.weblog=log;
crow::logger::setHandler(&logger);
if (prefixUrl!="/") prefixUrl=prefixUrl+"/";
const char* route=prefixUrl.c_str();
app.route_dynamic(route)([=](const crow::request& req){
std::string curr_folder;
char *f;
f=req.url_params.get("f");
auto page = crow::mustache::compile(p);
crow::mustache::context ctx;
if (f!=nullptr)
{
curr_folder=root_folder+"/"+f;
std::string t=f;
if (t.find("../")!=std::string::npos)
{
printf("ATTACK\n");
ctx["rows"] = "<li>Attack!</li>";
return page.render(ctx);
}
}else{
curr_folder=root_folder;
}
std::string files;
std::string e;
for (const auto & entry : fs::directory_iterator(curr_folder))
{
if (is_directory(entry.path()) )
{
e=entry.path().filename().u8string();
if (f!=nullptr)
{
files=files+"<li>[+]📁 <a href=\""+route+"?f="+f+"/"+e+"\">"+e+"</a></li>";
}else{
files=files+"<li>[+]📁 <a href=\""+route+"?f="+e+"\">"+e+"</a></li>";
}
}
}
std::string relPath;
for (const auto & entry : fs::directory_iterator(curr_folder))
{
if (!is_directory(entry.path()) )
{
if (f!=nullptr)
{
std::string sep="/";
relPath=f+sep+entry.path().filename().u8string();
}else{
relPath=entry.path().filename().u8string();
}
files=files+"<li>📄 <a href=\""+route+"download?f="+relPath+"\">"+entry.path().filename().u8string()+"</a></li>";
}
}
const char* rows=files.c_str();
if (f!=nullptr)
{
ctx["curr_folder"] = f;
}else{
ctx["curr_folder"] ="";
}
ctx["rows"] = rows;
char homeUrl[200];
char parentUrl[200];
sprintf(homeUrl,"<a href=\"%s\" >🏦 Home</a>",route);
sprintf(parentUrl,"<a href=\"%s?f=%s\" >↑ UP</a>",route,dirname(f));
ctx["home"] = homeUrl;
ctx["parent"] = parentUrl;
if (upload==true)
{
ctx["upload"] ="<a href=\"upload\">💾 Upload</a>";
}
return page.render(ctx);
});
std::string droute=prefixUrl+"download";
app.route_dynamic(droute.c_str())([=]( const crow::request& req,crow::response& res){
chdir(root_folder.c_str());
const char *f=req.url_params.get("f");
char myfile[512];
sprintf(myfile,"%s",f);
res.set_static_file_info(myfile);
std::string basname=basename(myfile);
std::string header="filename=\""+basname+"\"";
res.set_header("Content-Disposition",header);
res.end();
});
CROW_ROUTE(app, "/ping")([=](const crow::request& req){
const char* r="1";
return r;
});
if (upload==true)
{
std::string drouteFormUpload=prefixUrl+"upload";
app.route_dynamic(drouteFormUpload.c_str())([=](const crow::request& req ){
extern const char* pu;
auto page = crow::mustache::compile(pu);
crow::mustache::context ctx;
std::string u(prefixUrl+"save");
ctx["action"] = u.c_str();;
return page.render(ctx);
});
std::string drouteSaveUpload=prefixUrl+"save";
app.route_dynamic(drouteSaveUpload.c_str()).methods("POST"_method,"GET"_method)([=](const crow::request& req ){
crow::multipart::message msg(req);
crow::multipart::header myheader = msg.parts[0].get_header_object("Content-Disposition");
//std::string myheader_value=myheader.value;
std::string outfile_name=myheader.params["filename"];
CROW_LOG_ERROR << "File Name ->"+outfile_name;
std::ofstream out_file(root_folder+"/"+outfile_name, std::ios::binary);
if (!out_file)
{
CROW_LOG_ERROR << " Write to file failed\n";
return "Write to file failed!";
}
out_file << msg.parts[0].body;
out_file.close();
extern const char* uploadSuccess;
return uploadSuccess;
});
} //if (upload)
app.server_name("Piso web server");
app.port(port).multithreaded().run();
return (wxThread::ExitCode)0;
}
class MainFrame{
public:
wxFrame frame0;
wxMenuBar *menubar;
wxButton *btStart, *btStop, *btSettings;
wxSpinCtrl *spinPort;
wxListCtrl *log;
wxDirPickerCtrl *dirPckr;
wxTextCtrl *txtPrefixUrl;
wxHyperlinkCtrl *lblLocalIp, *lblPublicIp;
wxCheckBox *chkExpose;
wxCheckBox *chkPermitUpload;
void ShowFrame();
void OnClickStart(wxCommandEvent& event);
void OnClickStop(wxCommandEvent& event);
void OnCheckExpose(wxCommandEvent& event);
void OnCheckPermitUpload(wxCommandEvent& event);
void OnMenuItemExitSelection(wxCommandEvent& event);
void OnMenuItemAboutSelection(wxCommandEvent& event);
void ClosePort();
void ExposePort();
void Quit(wxCommandEvent& event);
void OnClose(wxCloseEvent& event);
wxString GetPublicIp();
std::string GetLocalIp();
private:
bool upload=false;
webServer *thWebServer;
void LoadIni();
void SaveIni();
void CreateMenuBar();
void TerminateApp();
} ;
void MainFrame::TerminateApp()
{
int dialog_return_value = wxID_NO;
wxMessageDialog* dial = new wxMessageDialog(NULL, _("Close Piso?"), _("Exit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dialog_return_value = dial->ShowModal();
if (dialog_return_value== wxID_YES)
{
frame0.Destroy();
}
}
void MainFrame::OnClose(wxCloseEvent& event)
{
TerminateApp();
}
void MainFrame::Quit(wxCommandEvent& event)
{
TerminateApp();
}
void MainFrame::OnMenuItemAboutSelection(wxCommandEvent& event)
{
wxAboutDialogInfo info;
info.SetName(_("Piso Web Server"));
info.SetVersion(version);
info.SetDescription(_("Piso allows you to share files across the network."));
info.SetCopyright(mainAuthor);
info.SetWebSite("https://www.freemedialab.org/code/project.php?p=piso");
info.SetIcon(frame0_icon);
wxAboutBox(info);
}
void MainFrame::LoadIni()
{
#ifdef __linux__
const wxString homefolder=wxStandardPaths::Get().GetUserConfigDir();
const wxString inifilepath=homefolder+"/.config/piso/piso.conf";
#endif
#ifdef _WIN32
const wxString homefolder=wxStandardPaths::Get().GetUserConfigDir();
const wxString inifilepath=homefolder+"\\piso\\piso.conf";
#endif
if (wxFileExists(inifilepath))
{
wxFileConfig *ConfigINI = new wxFileConfig(wxEmptyString,wxEmptyString,inifilepath);
ConfigINI->SetPath("/settings");
wxString folder=ConfigINI->Read("folder",homefolder);
int port=ConfigINI->Read("port",1024);
upload=ConfigINI->Read("upload",false);
wxString webroot=ConfigINI->Read("webroot","/");
dirPckr->SetPath(folder);
spinPort->SetValue(port);
chkPermitUpload->SetValue(upload);
txtPrefixUrl->SetValue(webroot);
}
}
void MainFrame::SaveIni()
{
#ifdef __linux__
const wxString homefolder=wxStandardPaths::Get().GetUserConfigDir();
const wxString configfolder=wxStandardPaths::Get().GetUserConfigDir()+"/.config/piso/";
const wxString inifilepath=homefolder+"/.config/piso/piso.conf";
#endif
#ifdef _WIN32
const wxString homefolder=wxStandardPaths::Get().GetUserConfigDir();
const wxString configfolder=wxStandardPaths::Get().GetUserConfigDir()+"\\piso\\";
const wxString inifilepath=homefolder+"\\piso\\piso.conf";
#endif
if (!wxDirExists(configfolder)) wxMkdir(configfolder);
wxFileConfig *ConfigINI = new wxFileConfig(wxEmptyString,wxEmptyString,inifilepath);
ConfigINI->SetPath("/settings");
ConfigINI->Write("folder",dirPckr->GetPath());
ConfigINI->Write("port",spinPort->GetValue());
ConfigINI->Write("upload",chkPermitUpload->GetValue());
ConfigINI->Write("webroot",txtPrefixUrl->GetValue());
ConfigINI->Flush();
}
std::string MainFrame::GetLocalIp()
{
unsigned short port = 53;
const char* GoogleDnsIp = "8.8.8.8";
/*Riempo le strutture dati per il server DNS*/
int sd;
struct sockaddr_in server;
struct hostent *hp;
hp = gethostbyname(GoogleDnsIp);
//bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr =((struct in_addr*)(hp->h_addr))->s_addr;
/*Riempo le strutture dati per il client*/
struct sockaddr_in client;
struct hostent *cp;
cp = gethostbyname("127.0.0.1");
//bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(port);
client.sin_addr.s_addr =((struct in_addr*)(cp->h_addr))->s_addr;
/* Creo il socket */
if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("Errore nella creazione del socket!\n");
}
/* connettiamoci all'host */
if (connect(sd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Errore di connessione!\n");
}
/*Individuo il mio indirizzo ip pubblico*/
socklen_t* clientlen = (socklen_t* )sizeof(client);
getsockname(sd, (struct sockaddr *) &client, (socklen_t*) &clientlen);
/*Lo converto in stringa e lo scrivo sulla variabile*/
char MyLocalAddress[50];
inet_ntop(AF_INET, &client.sin_addr.s_addr, MyLocalAddress, 50);
/*Chiudiamo il socket*/
close(sd);
std::string r= MyLocalAddress;
return r;
}
wxString MainFrame::GetPublicIp()
{
/*wxFileSystem* fileSystem = new wxFileSystem;
wxFSFile* file = fileSystem->OpenFile("https://www.freemedialab.org/myip/rawip.php");
wxString r="no";
if (file)
{
wxInputStream* strm=file->GetStream();
char buf[50];
strm->ReadAll( buf,50);
r=buf;
}
delete fileSystem;
return r;*/
wxArrayString output;
wxArrayString errors;
wxString r("127.0.0.1");
int res=wxExecute ("curl https://www.freemedialab.org/myip/rawip.php", output, errors, wxEXEC_HIDE_CONSOLE);
if (res==0)
{
return output[0];
}else {
return r;
}
}
void MainFrame::CreateMenuBar()
{
enum
{
ID_QUIT = 1,
ID_ABOUT = 2
};
wxMenu *menuFile = new wxMenu;
menuFile->Append(wxID_EXIT);
wxMenu *menuInfo = new wxMenu;
menuInfo->Append(ID_ABOUT, "About...","Authors");
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuInfo, "&Info");
frame0.SetMenuBar(menuBar);
frame0.Bind(wxEVT_MENU, &MainFrame::OnMenuItemAboutSelection, this, ID_ABOUT);
frame0.Bind(wxEVT_MENU, &MainFrame::Quit, this, wxID_EXIT);
}
void MainFrame::ShowFrame()
{
wxXmlResource::Get()->LoadFrame(&frame0, NULL, wxT("frame0"));
frame0.SetIcon(frame0_icon);
frame0.Bind(wxEVT_CLOSE_WINDOW, &MainFrame::OnClose, this);
spinPort = XRCCTRL(frame0, "spinPort", wxSpinCtrl);
log = XRCCTRL(frame0, "log", wxListCtrl);
dirPckr = XRCCTRL(frame0, "dirPckr", wxDirPickerCtrl);
dirPckr->SetPath(wxStandardPaths::Get().GetUserConfigDir());
txtPrefixUrl=XRCCTRL(frame0, "txtPrefixUrl", wxTextCtrl);
btStart=XRCCTRL(frame0, "btStart", wxButton);
btStart->Bind(wxEVT_BUTTON, &MainFrame::OnClickStart, this);
btStop=XRCCTRL(frame0, "btStop", wxButton);
btStop->Bind(wxEVT_BUTTON, &MainFrame::OnClickStop, this);
chkExpose = XRCCTRL(frame0, "chkExpose", wxCheckBox);
chkExpose->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &MainFrame::OnCheckExpose, this);
chkPermitUpload = XRCCTRL(frame0, "chkPermitUpload", wxCheckBox);
chkPermitUpload->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &MainFrame::OnCheckPermitUpload, this);
lblLocalIp=XRCCTRL(frame0, "lblLocalIp", wxHyperlinkCtrl);
lblPublicIp=XRCCTRL(frame0, "lblPubliclIp", wxHyperlinkCtrl);
log->AppendColumn ( "Log",wxLIST_FORMAT_LEFT, 550);
//log->SetColumnWidth(0,2600);
int n=log->GetItemCount();
log->InsertItem(n+1,"Piso ready.");
lblLocalIp->Hide();
lblPublicIp->Hide();
this->LoadIni();
CreateMenuBar();
frame0.SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
frame0.Show();
}
void MainFrame::ExposePort()
{
/*int port = spinPort->GetValue();
wxString cmd="upnpc -r "+wxString::Format(wxT("%i"),port)+ " TCP";
wxArrayString output;
wxArrayString errors;
wxExecute (cmd, output, errors, wxEXEC_HIDE_CONSOLE);*/
wxString publicIp=GetPublicIp();
if (publicIp!="127.0.0.1")
{
auto piso_upnp = new simple_upnpc();
sprintf(piso_upnp->eport,"%d",spinPort->GetValue());
sprintf(piso_upnp->iport,"%d",spinPort->GetValue());
sprintf(piso_upnp->iaddr,"%s",GetLocalIp().c_str());
sprintf(piso_upnp->leaseDuration,"%s","86400");
sprintf(piso_upnp->description,"%s","Piso web server");
piso_upnp->op=0;
piso_upnp->Create();
piso_upnp->Run();
}
}
void MainFrame::ClosePort()
{
/*int port = spinPort->GetValue();
wxString cmd="upnpc -d "+wxString::Format(wxT("%i"),port)+ " TCP";
wxArrayString output;
wxArrayString errors;
wxExecute (cmd, output, errors, wxEXEC_HIDE_CONSOLE);*/
wxString publicIp=GetPublicIp();
if (publicIp!="127.0.0.1")
{
auto piso_upnp = new simple_upnpc();
sprintf(piso_upnp->eport,"%d",spinPort->GetValue());
sprintf(piso_upnp->iport,"%d",spinPort->GetValue());
sprintf(piso_upnp->iaddr,"%s",GetLocalIp().c_str());
sprintf(piso_upnp->leaseDuration,"%s","86400");
sprintf(piso_upnp->description,"%s","Piso web server");
piso_upnp->op=1;
piso_upnp->Create();
piso_upnp->Run();
}
}
void MainFrame::OnCheckPermitUpload(wxCommandEvent& event)
{
bool s=chkPermitUpload->GetValue();
if (s==true)
{
upload=true;
}else{
upload=false;
}
}
void MainFrame::OnCheckExpose(wxCommandEvent& event)
{
bool s=chkExpose->GetValue();
if (s==true)
{
lblPublicIp->Show();
ExposePort();
int n=log->GetItemCount();
log->InsertItem(n+1,"Try to expose on internet");
log->EnsureVisible(n);
wxString publicIp=GetPublicIp();
if (publicIp!="no")
{
wxString strPort = wxString::Format(wxT("%i"),spinPort->GetValue());
lblPublicIp->SetURL("http://"+publicIp+":"+strPort+txtPrefixUrl->GetValue());
lblPublicIp->SetLabel("http://"+publicIp+":"+strPort+txtPrefixUrl->GetValue());
}else{
lblPublicIp->SetLabel("No public ip!");
}
}else{
int n=log->GetItemCount();
log->InsertItem(n+1,"Try to close expose on internet");
log->EnsureVisible(n);
ClosePort();
}
}
void MainFrame::OnClickStart(wxCommandEvent& event)
{
lblLocalIp->Show();
dirPckr->Enable(false);
spinPort->Enable(false);
btStart->Enable(false);
txtPrefixUrl->Enable(false);
chkPermitUpload->Enable(false);
int port = spinPort->GetValue();
wxString ip="http://"+GetLocalIp()+":"+wxString::Format(wxT("%i"),port)+txtPrefixUrl->GetValue();
lblLocalIp->SetLabel(ip);
lblLocalIp->SetURL(ip);
wxString publicIp=GetPublicIp();
wxString strPort = wxString::Format(wxT("%i"),spinPort->GetValue());
lblPublicIp->SetURL("http://"+publicIp+":"+strPort+txtPrefixUrl->GetValue());
lblPublicIp->SetLabel("http://"+publicIp+":"+strPort+txtPrefixUrl->GetValue());
int n=log->GetItemCount();
log->InsertItem(n+1,"Starting webserver...");
thWebServer = new webServer();
thWebServer->port=spinPort->GetValue();
thWebServer->log=log;
thWebServer->root_folder=dirPckr->GetPath();
thWebServer->prefixUrl=txtPrefixUrl->GetValue();
thWebServer->upload=upload;
thWebServer->Create();
thWebServer->Run();
}
void MainFrame::OnClickStop(wxCommandEvent& event)
{
wxHTTP pingHttp;
int port = spinPort->GetValue();
pingHttp.Connect("localhost",port);
pingHttp.GetInputStream("/ping");
int resp=pingHttp.GetResponse();
if (resp==200)
{
wxTheApp->CallAfter( [=] {
thWebServer->app.stop();
thWebServer->app.signal_clear();
});
dirPckr->Enable(true);
spinPort->Enable(true);
btStart->Enable(true);
txtPrefixUrl->Enable(true);
chkPermitUpload->Enable(true);
int n=log->GetItemCount();
log->InsertItem(n+1,"Stop webserver");
log->EnsureVisible(n);
SaveIni();
}
}
class MyApp: public wxApp
{
virtual bool OnInit();
};
bool MyApp::OnInit()
{
wxXmlResource::Get()->InitAllHandlers();
InitXmlResource();
MainFrame *MainWin = new MainFrame();
MainWin->ShowFrame();
return true;
}
IMPLEMENT_APP(MyApp)