Merge branch 'dk_wifi'

This commit is contained in:
David Kocik 2023-10-16 16:09:32 +02:00
commit 306374129d
13 changed files with 939 additions and 4 deletions

View File

@ -201,6 +201,8 @@ set(SLIC3R_GUI_SOURCES
GUI/FirmwareDialog.hpp GUI/FirmwareDialog.hpp
GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.cpp
GUI/PrintHostDialogs.hpp GUI/PrintHostDialogs.hpp
GUI/WifiConfigDialog.cpp
GUI/WifiConfigDialog.hpp
GUI/Jobs/Job.hpp GUI/Jobs/Job.hpp
GUI/Jobs/Worker.hpp GUI/Jobs/Worker.hpp
GUI/Jobs/BoostThreadWorker.hpp GUI/Jobs/BoostThreadWorker.hpp
@ -304,6 +306,8 @@ set(SLIC3R_GUI_SOURCES
Utils/WinRegistry.hpp Utils/WinRegistry.hpp
Utils/WxFontUtils.cpp Utils/WxFontUtils.cpp
Utils/WxFontUtils.hpp Utils/WxFontUtils.hpp
Utils/WifiScanner.hpp
Utils/WifiScanner.cpp
) )
find_package(NanoSVG REQUIRED) find_package(NanoSVG REQUIRED)
@ -313,6 +317,8 @@ if (APPLE)
Utils/RetinaHelperImpl.mm Utils/RetinaHelperImpl.mm
Utils/MacDarkMode.mm Utils/MacDarkMode.mm
Utils/MacUtils.mm Utils/MacUtils.mm
Utils/WifiScannerMac.h
Utils/WifiScannerMac.mm
GUI/RemovableDriveManagerMM.mm GUI/RemovableDriveManagerMM.mm
GUI/RemovableDriveManagerMM.h GUI/RemovableDriveManagerMM.h
GUI/Mouse3DHandlerMac.mm GUI/Mouse3DHandlerMac.mm
@ -320,6 +326,7 @@ if (APPLE)
GUI/InstanceCheckMac.h GUI/InstanceCheckMac.h
) )
FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
FIND_LIBRARY(COREWLAN_LIBRARY CoreWLAN)
endif () endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
@ -339,7 +346,7 @@ if (MSVC)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES})
elseif (APPLE) elseif (APPLE)
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY} ${COREWLAN_LIBRARY})
endif() endif()
if (SLIC3R_STATIC) if (SLIC3R_STATIC)

View File

@ -95,6 +95,8 @@
#include "DesktopIntegrationDialog.hpp" #include "DesktopIntegrationDialog.hpp"
#include "SendSystemInfoDialog.hpp" #include "SendSystemInfoDialog.hpp"
#include "Downloader.hpp" #include "Downloader.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "WifiConfigDialog.hpp"
#include "BitmapCache.hpp" #include "BitmapCache.hpp"
#include "Notebook.hpp" #include "Notebook.hpp"
@ -2446,6 +2448,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
// TODO: for when we're able to flash dictionaries // TODO: for when we're able to flash dictionaries
// local_menu->Append(config_id_base + FirmwareMenuDict, _L("Flash Language File"), _L("Upload a language dictionary file into a Prusa printer")); // local_menu->Append(config_id_base + FirmwareMenuDict, _L("Flash Language File"), _L("Upload a language dictionary file into a Prusa printer"));
} }
local_menu->Append(config_id_base + ConfigMenuWifiConfigFile, _L("Wi-Fi Configuration File"), _L("Generate a file to be loaded by a Prusa printer to configure a Wi-Fi connection."));
local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
switch (event.GetId() - config_id_base) { switch (event.GetId() - config_id_base) {
@ -2544,6 +2547,16 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
case ConfigMenuFlashFirmware: case ConfigMenuFlashFirmware:
FirmwareDialog::run(mainframe); FirmwareDialog::run(mainframe);
break; break;
case ConfigMenuWifiConfigFile:
{
std::string file_path;
WifiConfigDialog dialog(mainframe, file_path, removable_drive_manager());
if (dialog.ShowModal() == wxID_OK)
{
plater_->get_notification_manager()->push_exporting_finished_notification(file_path, boost::filesystem::path(file_path).parent_path().string(), true);
}
}
break;
default: default:
break; break;
} }

View File

@ -101,6 +101,7 @@ enum ConfigMenuIDs {
ConfigMenuLanguage, ConfigMenuLanguage,
ConfigMenuFlashFirmware, ConfigMenuFlashFirmware,
ConfigMenuCnt, ConfigMenuCnt,
ConfigMenuWifiConfigFile
}; };
class Tab; class Tab;

View File

@ -838,5 +838,4 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
update_host_type(true); update_host_type(true);
} }
}} // namespace Slic3r::GUI }} // namespace Slic3r::GUI

View File

@ -105,7 +105,6 @@ protected:
}; };
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -1095,4 +1095,11 @@ void RemovableDriveManager::eject_thread_finish()
} }
#endif // __APPLE__ #endif // __APPLE__
std::vector<DriveData> RemovableDriveManager::get_drive_list()
{
{
std::lock_guard<std::mutex> guard(m_drives_mutex);
return m_current_drives;
}
}
}} // namespace Slic3r::GUI }} // namespace Slic3r::GUI

View File

@ -92,7 +92,8 @@ public:
// Called by Win32 Volume arrived / detached callback. // Called by Win32 Volume arrived / detached callback.
void volumes_changed(); void volumes_changed();
#endif // _WIN32 #endif // _WIN32
// returns copy of m_current_drives (protected by mutex)
std::vector<DriveData> get_drive_list();
private: private:
bool m_initialized { false }; bool m_initialized { false };
wxEvtHandler* m_callback_evt_handler { nullptr }; wxEvtHandler* m_callback_evt_handler { nullptr };

View File

@ -0,0 +1,249 @@
#include "WifiConfigDialog.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "I18N.hpp"
#include "format.hpp"
#include "RemovableDriveManager.hpp"
#include "MsgDialog.hpp"
#include <wx/stattext.h>
#include <wx/button.h>
#include <boost/nowide/convert.hpp>
#include <boost/log/trivial.hpp>
#include <boost/filesystem.hpp>
namespace Slic3r {
namespace GUI {
WifiConfigDialog::WifiConfigDialog(wxWindow* parent, std::string& file_path, RemovableDriveManager* removable_manager)
: DPIDialog(parent, wxID_ANY, _L("Physical Printer Instalation"), wxDefaultPosition, wxDefaultSize/*wxSize(25 * wxGetApp().em_unit(), 20 * wxGetApp().em_unit())*/, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_wifi_scanner(new WifiScanner())
, out_file_path(file_path)
, m_removable_manager(removable_manager)
{
const int em = GUI::wxGetApp().em_unit();
wxPanel* panel = new wxPanel(this);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(vsizer);
auto* ssid_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN SSID of WiFi network.
wxStaticText* ssid_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("SSID")));
m_ssid_combo = new wxComboBox(panel, wxID_ANY);
#if __APPLE__
m_ssid_combo->SetToolTip(_L("On some versions of MacOS, this only loads SSID of connencted network."));
#endif // __APPLE__
rescan_networks(false);
wxButton* ssid_button = new wxButton(panel, wxID_ANY, _(L("Rescan")));
ssid_sizer->Add(m_ssid_combo, 1, wxALIGN_CENTER_VERTICAL, 10);
ssid_sizer->Add(ssid_button, 0);
auto* pass_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN Password of WiFi network.
wxStaticText* password_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Password")));
m_pass_textctrl = new wxTextCtrl(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
pass_sizer->Add(m_pass_textctrl, 1, wxALIGN_CENTER_VERTICAL, 10);
#if __APPLE__
wxButton* pass_button = new wxButton(panel, wxID_ANY, _(L("Retrieve")));
pass_sizer->Add(pass_button, 0);
pass_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_retrieve_password, this);
#endif // __APPLE__
// show password if current ssid was selected already
fill_password();
auto* drive_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN description of Combo Box with path to USB drive.
wxStaticText* drive_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Drive")));
m_drive_combo = new wxComboBox(panel, wxID_ANY);
rescan_drives();
wxButton* drive_button = new wxButton(panel, wxID_ANY, _(L("Rescan")));
drive_sizer->Add(m_drive_combo, 1, wxALIGN_CENTER_VERTICAL, 10);
drive_sizer->Add(drive_button, 0);
wxButton* ok_button = new wxButton(panel, wxID_OK, _L("Write"));
wxButton* cancel_button = new wxButton(panel, wxID_CANCEL);
auto* grid = new wxFlexGridSizer(2, 15, 15);
grid->AddGrowableCol(1);
grid->Add(ssid_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(ssid_sizer, 0, wxEXPAND);
grid->Add(password_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(pass_sizer, 0, wxEXPAND);
grid->Add(drive_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(drive_sizer, 0, wxEXPAND);
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, 15);
wxBoxSizer* buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
buttons_sizer->Add(ok_button, 1, wxLEFT);
buttons_sizer->AddStretchSpacer();
buttons_sizer->Add(cancel_button, 1, wxRIGHT);
vsizer->Add(buttons_sizer, 0, wxEXPAND);
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
topsizer->Add(panel, 1, wxEXPAND | wxALL, 15);
SetSizerAndFit(topsizer);
ok_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_ok, this);
m_ssid_combo->Bind(wxEVT_TEXT, &WifiConfigDialog::on_combo, this);
drive_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_drives, this);
ssid_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_networks, this);
}
WifiConfigDialog::~WifiConfigDialog()
{
if (m_wifi_scanner)
delete m_wifi_scanner;
}
void WifiConfigDialog::on_combo(wxCommandEvent& e)
{
fill_password();
}
void WifiConfigDialog::fill_password()
{
assert(m_ssid_combo && m_pass_textctrl && m_wifi_scanner);
if (auto it = m_wifi_scanner->get_map().find(m_ssid_combo->GetValue()); it != m_wifi_scanner->get_map().end())
m_pass_textctrl->SetValue(boost::nowide::widen(it->second));
}
void WifiConfigDialog::on_retrieve_password(wxCommandEvent& e)
{
if (m_ssid_combo->GetValue().empty()) {
return;
}
std::string psk = m_wifi_scanner->get_psk(boost::nowide::narrow(m_ssid_combo->GetValue()));
if (psk.empty()) {
// TRN Alert message when retrieving password for wifi from keychain. Probably will display only on Apple so keychain is MacOS term.
wxString msg = _L("No password in the keychain for given SSID.");
show_info(nullptr, msg);
return;
}
m_pass_textctrl->SetValue(boost::nowide::widen(psk));
}
void WifiConfigDialog::on_rescan_drives(wxCommandEvent& e)
{
rescan_drives();
}
void WifiConfigDialog::rescan_drives()
{
assert(m_drive_combo && m_removable_manager);
m_drive_combo->Clear();
std::vector<DriveData> ddata = m_removable_manager->get_drive_list();
for (const auto& data : ddata) {
m_drive_combo->Append(boost::nowide::widen(data.path));
}
if (m_drive_combo->GetCount() > 0) {
m_drive_combo->Select(0);
}
}
void WifiConfigDialog::on_rescan_networks(wxCommandEvent& e)
{
rescan_networks(true);
}
void WifiConfigDialog::rescan_networks(bool select)
{
assert(m_ssid_combo && m_wifi_scanner);
m_wifi_scanner->scan();
std::string current = m_wifi_scanner->get_current_ssid();
const auto& map = m_wifi_scanner->get_map();
m_ssid_combo->Clear();
for (const auto pair : map) {
m_ssid_combo->Append(pair.first);
// select ssid of current network (if connected)
if (current == pair.first)
m_ssid_combo->Select(m_ssid_combo->GetCount() - 1);
}
if (m_ssid_combo->GetSelection() == -1 && m_ssid_combo->GetCount() > 0)
m_ssid_combo->Select(0);
if (select && m_ssid_combo->GetSelection() != -1)
fill_password();
}
void WifiConfigDialog::on_ok(wxCommandEvent& e)
{
assert(m_ssid_combo && m_pass_textctrl);
if (m_ssid_combo->GetValue().empty()) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("SSID field is empty.");
show_info(nullptr, msg);
return;
}
std::string selected_path = boost::nowide::narrow(m_drive_combo->GetValue());
if (selected_path.empty()) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("Drive field is empty.");
show_info(nullptr, msg);
return;
}
boost::filesystem::path file_path = boost::filesystem::path(selected_path) / "prusa_printer_settings.ini";
bool path_on_removable_media = m_removable_manager->set_and_verify_last_save_path(file_path.string());
if (!path_on_removable_media) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("Selected path is not on removable media.");
show_info(nullptr, msg);
return;
}
if (boost::filesystem::exists(file_path))
{
wxString msg_text = GUI::format_wxstr("%1% already exists. Do you want to rewrite it?", file_path.string());
WarningDialog dialog(m_parent, msg_text, _L("Warning"), wxYES | wxNO);
if (dialog.ShowModal() == wxID_NO)
{
return;
}
}
wxString ssid = m_ssid_combo->GetValue();
wxString pass = m_pass_textctrl->GetValue();
wxString data = GUI::format(
"[wifi]\n"
"ssid=%1%\n"
"psk=%2%\n"
, ssid
, pass
);
FILE* file;
file = fopen(file_path.string().c_str(), "w");
if (file == NULL) {
BOOST_LOG_TRIVIAL(error) << "Failed to write to file " << file_path;
// TODO show error
show_error(nullptr, _L("Failed to open file for writing."));
return;
}
fwrite(data.c_str(), 1, data.size(), file);
fclose(file);
out_file_path = file_path.string();
this->EndModal(wxID_OK);
}
void WifiConfigDialog::on_dpi_changed(const wxRect& suggested_rect)
{
SetFont(wxGetApp().normal_font());
const int em = em_unit();
//msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL });
Fit();
Refresh();
}
}}// Slicer::GUI

View File

@ -0,0 +1,45 @@
#ifndef slic3r_WifiConfigDialog_hpp_
#define slic3r_WifiConfigDialog_hpp_
#include "GUI_Utils.hpp"
#include "../Utils/WifiScanner.hpp"
#include <wx/event.h>
#include <wx/dialog.h>
#include <wx/combobox.h>
#include <wx/textctrl.h>
namespace Slic3r {
namespace GUI {
class RemovableDriveManager;
class WifiConfigDialog : public DPIDialog
{
public:
WifiConfigDialog(wxWindow* parent, std::string& file_path, RemovableDriveManager* removable_manager);
~WifiConfigDialog();
private:
wxComboBox* m_ssid_combo {nullptr};
wxTextCtrl* m_pass_textctrl {nullptr};
wxComboBox* m_drive_combo {nullptr};
void on_ok(wxCommandEvent& e);
void on_combo(wxCommandEvent& e);
void on_rescan_drives(wxCommandEvent& e);
void on_rescan_networks(wxCommandEvent& e);
void on_retrieve_password(wxCommandEvent& e);
void rescan_drives();
void rescan_networks(bool select);
void fill_password();
// reference to string that is filled after ShowModal is called from owner
std::string& out_file_path;
WifiScanner* m_wifi_scanner;
RemovableDriveManager* m_removable_manager;
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_sys_color_changed() override {}
};
}} // Slicer::GUI
#endif

View File

@ -0,0 +1,459 @@
#include "WifiScanner.hpp"
#include <boost/log/trivial.hpp>
#include <boost/nowide/system.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/filesystem.hpp>
#ifdef _WIN32
#include <windows.h>
#include <wlanapi.h>
#include <objbase.h>
#include <wtypes.h>
// Need to link with Wlanapi.lib and Ole32.lib
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
#elif __APPLE_
#include "WifiScannerMac.h"
#endif
#if __linux__
#include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
#endif //__linux__
namespace {
bool ptree_get_value(const boost::property_tree::ptree& pt, const std::string& target, std::string& result)
{
// Check if the current node has the target element
if (pt.find(target) != pt.not_found()) {
result = pt.get<std::string>(target);
return true;
}
// Recursively search child nodes
for (const auto& child : pt) {
if (ptree_get_value(child.second, target, result)) {
return true;
}
}
return false; // Element not found in this subtree
}
#ifdef _WIN32
// Fill SSID map. Implementation from Raspberry Pi imager and Win32 Api examples.
// https://github.com/raspberrypi/rpi-imager/blob/qml/src/windows/winwlancredentials.cpp
// https://learn.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlangetavailablenetworklist
void fill_wifi_map(Slic3r::WifiSsidPskMap& wifi_map, std::string& connected_ssid)
{
HANDLE handle;
DWORD supported_version = 0;
DWORD client_version = 2;
PWLAN_INTERFACE_INFO_LIST interface_list = NULL;
if (WlanOpenHandle(client_version, NULL, &supported_version, &handle) != ERROR_SUCCESS)
return;
if (WlanEnumInterfaces(handle, NULL, &interface_list) != ERROR_SUCCESS)
return;
for (DWORD i = 0; i < interface_list->dwNumberOfItems; i++)
{
if (interface_list->InterfaceInfo[i].isState == wlan_interface_state_connected)
{
PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES);
WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid;
if (WlanQueryInterface(handle, &interface_list->InterfaceInfo[i].InterfaceGuid,
wlan_intf_opcode_current_connection, NULL,
&connectInfoSize, (PVOID*)&pConnectInfo, &opCode) == ERROR_SUCCESS && pConnectInfo && pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength)
{
connected_ssid = std::string((const char*)pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID,
pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength);
}
WlanFreeMemory(pConnectInfo);
}
PWLAN_PROFILE_INFO_LIST profile_list = NULL;
PWLAN_INTERFACE_INFO interface_info_entry = NULL;
PWLAN_AVAILABLE_NETWORK_LIST available_network_list = NULL;
WCHAR guid[39] = { 0 };
// Get all available networks.
interface_info_entry = (WLAN_INTERFACE_INFO*)&interface_list->InterfaceInfo[i];
int iRet = StringFromGUID2(interface_info_entry->InterfaceGuid, (LPOLESTR)&guid,
sizeof(guid) / sizeof(*guid));
if (WlanGetAvailableNetworkList(handle,
&interface_info_entry->InterfaceGuid,
0,
NULL,
&available_network_list)
!= ERROR_SUCCESS)
{
continue;
}
for (unsigned int j = 0; j < available_network_list->dwNumberOfItems; j++)
{
PWLAN_AVAILABLE_NETWORK available_network_entry = NULL;
wxString ssid;
// Store SSID into the map.
available_network_entry =
(WLAN_AVAILABLE_NETWORK*)&available_network_list->Network[j];
if (available_network_entry->dot11Ssid.uSSIDLength != 0)
ssid = wxString(available_network_entry->dot11Ssid.ucSSID,
available_network_entry->dot11Ssid.uSSIDLength);
if (ssid.empty())
continue;
if (wifi_map.find(ssid) != wifi_map.end())
continue;
wifi_map[ssid] = std::string();
if (WlanGetProfileList(handle, &interface_list->InterfaceInfo[i].InterfaceGuid,
NULL, &profile_list) != ERROR_SUCCESS)
{
continue;
}
// enmurate all stored profiles, take password from matching one.
for (DWORD k = 0; k < profile_list->dwNumberOfItems; k++)
{
DWORD flags = WLAN_PROFILE_GET_PLAINTEXT_KEY;
DWORD access = 0;
DWORD ret = 0;
LPWSTR xmlstr = NULL;
wxString s(profile_list->ProfileInfo[k].strProfileName);
BOOST_LOG_TRIVIAL(debug) << "Enumerating wlan profiles, SSID found:" << s << " looking for:" << ssid;
if (s != ssid)
continue;
if ((ret = WlanGetProfile(handle, &interface_list->InterfaceInfo[i].InterfaceGuid, profile_list->ProfileInfo[k].strProfileName,
NULL, &xmlstr, &flags, &access)) == ERROR_SUCCESS && xmlstr)
{
wxString xml(xmlstr);
boost::property_tree::ptree pt;
std::stringstream ss(boost::nowide::narrow(xml));
BOOST_LOG_TRIVIAL(error) << ss.str();
boost::property_tree::read_xml(ss, pt);
std::string password;
std::string psk_protected;
BOOST_LOG_TRIVIAL(debug) << "XML wlan profile:" << xml;
// break if password is not readable
// TODO: what if there is other line "protected" in the XML?
if (ptree_get_value(pt, "protected", psk_protected) && psk_protected != "false")
break;
if (ptree_get_value(pt, "keyMaterial", password))
wifi_map[ssid] = password;
WlanFreeMemory(xmlstr);
break;
}
}
if (profile_list) {
WlanFreeMemory(profile_list);
}
}
}
if (interface_list)
WlanFreeMemory(interface_list);
WlanCloseHandle(handle, NULL);
}
#elif __APPLE__
void get_connected_ssid(std::string& connected_ssid)
{
std::string program = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I";
std::string regexpstr = "[ \t]+SSID: (.+)";
std::ostringstream output;
std::string line;
// Run the process and capture its output
FILE* pipe = popen(program.c_str(), "r");
if (!pipe) {
BOOST_LOG_TRIVIAL(error) << "Error executing airport command." << std::endl;
return;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
line = buffer;
output << line;
}
BOOST_LOG_TRIVIAL(error) << output.str();
pclose(pipe);
// Process the captured output using regular expressions
std::regex rx(regexpstr);
std::smatch match;
std::istringstream outputStream(output.str());
while (std::getline(outputStream, line)) {
BOOST_LOG_TRIVIAL(error) << line;
if (std::regex_search(line, match, rx)) {
connected_ssid = match[1].str();
// airport -I gets data only about current connection, so only 1 network.
return;
}
}
}
#else
DBusMessage* dbus_query(DBusConnection* connection, const char* service, const char* object, const char* interface, const char* method)
{
DBusError error;
dbus_error_init(&error);
DBusMessage* msg = dbus_message_new_method_call(service, object, interface, method);
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error);
dbus_message_unref(msg);
if (dbus_error_is_set(&error)) {
// todo (debug)
BOOST_LOG_TRIVIAL(debug) << "D-Bus method call error: " << error.message << std::endl;
dbus_error_free(&error);
return nullptr;
}
return reply;
}
// On each access point call method Get on interface org.freedesktop.DBus.Properties to get the ssid
void iter_access_points(DBusConnection* connection, DBusMessage* access_points, Slic3r::WifiSsidPskMap& wifi_map)
{
DBusError error;
dbus_error_init(&error);
DBusMessageIter iter;
dbus_message_iter_init(access_points, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
BOOST_LOG_TRIVIAL(debug) << "Error iterating access points reply - not an array.";
return;
}
DBusMessageIter array_iter;
dbus_message_iter_recurse(&iter, &array_iter);
while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_OBJECT_PATH) {
const char* object_path;
dbus_message_iter_get_basic(&array_iter, &object_path);
DBusMessage* msg = dbus_message_new_method_call(
"org.freedesktop.NetworkManager",
object_path,
"org.freedesktop.DBus.Properties",
"Get");
const char* arg1 = "org.freedesktop.NetworkManager.AccessPoint";
const char* arg2 = "Ssid";
dbus_message_append_args(
msg,
DBUS_TYPE_STRING, &arg1,
DBUS_TYPE_STRING, &arg2,
DBUS_TYPE_INVALID
);
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error);
dbus_message_unref(msg);
if (dbus_error_is_set(&error)) {
BOOST_LOG_TRIVIAL(debug) << "D-Bus method call error: " << error.message << std::endl;
dbus_error_free(&error);
dbus_message_iter_next(&array_iter);
continue;
}
// process reply
DBusMessageIter rep_iter;
dbus_message_iter_init(reply, &rep_iter);
dbus_message_unref(reply);
if (dbus_message_iter_get_arg_type(&rep_iter) != DBUS_TYPE_VARIANT) {
BOOST_LOG_TRIVIAL(debug) << "Reply does not contain a Variant";
dbus_message_iter_next(&array_iter);
continue;
}
DBusMessageIter variant_iter;
dbus_message_iter_recurse(&rep_iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) {
BOOST_LOG_TRIVIAL(debug) << "Variant does not contain an array";
dbus_message_iter_next(&array_iter);
continue;
}
DBusMessageIter var_array_iter;
dbus_message_iter_recurse(&variant_iter, &var_array_iter);
if (dbus_message_iter_get_arg_type(&var_array_iter) != DBUS_TYPE_BYTE) {
BOOST_LOG_TRIVIAL(debug) << "Array does not contain bytes";
dbus_message_iter_next(&array_iter);
continue;
}
unsigned char *result;
int result_len;
// Get the array of bytes and its length
dbus_message_iter_get_fixed_array(&var_array_iter, &result, &result_len);
wifi_map[result] = std::string();
dbus_message_iter_next(&array_iter);
}
}
// For each device call method GetAllAccessPoints to get object path to all Access Point objects
void iter_devices(DBusConnection* connection, DBusMessage* devices, Slic3r::WifiSsidPskMap& wifi_map)
{
DBusError error;
dbus_error_init(&error);
DBusMessageIter iter;
dbus_message_iter_init(devices, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
BOOST_LOG_TRIVIAL(debug) << "Error iterating devices reply - not an array.";
return;
}
DBusMessageIter array_iter;
dbus_message_iter_recurse(&iter, &array_iter);
while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_OBJECT_PATH) {
const char* object_path;
dbus_message_iter_get_basic(&array_iter, &object_path);
// Create a new message to get all access points for this device
DBusMessage* reply = dbus_query(
connection,
"org.freedesktop.NetworkManager", // Service name
object_path, // Object path (device path)
"org.freedesktop.NetworkManager.Device.Wireless", // Interface for Wi-Fi devices
"GetAllAccessPoints" // Method name
);
if (reply) {
iter_access_points(connection, reply, wifi_map);
dbus_message_unref(reply);
}
dbus_message_iter_next(&array_iter);
}
}
// Query NetworkManager for available Wi-Fi.
// On org.freedesktop.NetworkManager call method GetAllDevices to get object paths to all device objects.
// For each device call method GetAllAccessPoints to get object path to all Access Point objects (iter_devices function here).
// On each access point call method Get on interface org.freedesktop.DBus.Properties to get the ssid (iter_access_points function here).
void fill_wifi_map(Slic3r::WifiSsidPskMap& wifi_map)
{
DBusConnection* connection;
DBusError error;
dbus_error_init(&error);
// Connect to the system bus
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if (dbus_error_is_set(&error)) {
BOOST_LOG_TRIVIAL(debug) << "D-Bus connection error: " << error.message << std::endl;
dbus_error_free(&error);
}
//
DBusMessage* reply = dbus_query(
connection,
"org.freedesktop.NetworkManager", // Service name
"/org/freedesktop/NetworkManager", // Object path
"org.freedesktop.NetworkManager", // Interface
"GetAllDevices" // Method name
);
if (reply) {
iter_devices(connection, reply, wifi_map);
dbus_message_unref(reply);
}
dbus_connection_unref(connection);
}
#endif //__linux__
}
namespace Slic3r
{
WifiScanner::WifiScanner()
{}
WifiScanner::~WifiScanner()
{}
void WifiScanner::scan()
{
m_map.clear();
#ifdef _WIN32
fill_wifi_map(m_map, m_current_ssid);
#elif __APPLE__
std::vector<std::string> ssids;
try
{
// Objective-c implementation using CoreWLAN library
// This failed to get data at ARM Sonoma
get_ssids_mac(ssids);
}
catch (const std::exception&)
{
BOOST_LOG_TRIVIAL(error) << "Exception caught: Getting SSIDs failed.";
}
for ( const std::string& ssid : ssids)
{
m_map[boost::nowide::widen(ssid)] = {};
}
if (m_map.empty()) {
try
{
// Second implementation calling "airport" system command
get_connected_ssid(m_current_ssid);
m_map[m_current_ssid] = std::string();
}
catch (const std::exception&)
{
BOOST_LOG_TRIVIAL(error) << "Exception caught: get_connected_ssid failed.";
}
} else {
try
{
m_current_ssid = get_current_ssid_mac();
}
catch (const std::exception&)
{
BOOST_LOG_TRIVIAL(error) << "Exception caught: Getting current SSID failed.";
}
}
#else
fill_wifi_map(m_map);
#endif
}
std::string WifiScanner::get_psk(const std::string& ssid)
{
#ifdef __APPLE__
return get_psk_mac(ssid);
#endif
if (m_map.find(ssid) != m_map.end())
{
return m_map[ssid];
}
return {};
}
} // Slic3r

View File

@ -0,0 +1,40 @@
#ifndef slic3r_WifiScanner_hpp_
#define slic3r_WifiScanner_hpp_
#include <map>
#include <vector>
#include <string>
#include <wx/string.h>
namespace Slic3r {
typedef std::map<wxString, std::string> WifiSsidPskMap;
class WifiScanner
{
public:
WifiScanner();
~WifiScanner();
const WifiSsidPskMap& get_map() const { return m_map; }
// returns psk for given ssid
// used on APPLE where each psk query requires user to give their password
std::string get_psk(const std::string& ssid);
const std::string get_current_ssid() { return m_current_ssid; }
// fills map with ssid psk couples (or only ssid if no psk)
// on APPLE only ssid
void scan();
private:
WifiSsidPskMap m_map;
std::string m_current_ssid;
#if __APPLE__
void get_ssids_mac(std::vector<std::string>& ssids);
std::string get_psk_mac(const std::string& ssid);
std::string get_current_ssid_mac();
void* m_impl_osx { nullptr };
#endif
};
} // Slic3r
#endif

View File

@ -0,0 +1,21 @@
#ifndef WiFiScanner_h
#define WiFiScanner_h
#import <Foundation/Foundation.h>
extern "C" {
#import <CoreWLAN/CoreWLAN.h>
}
@interface WifiScannerMac : NSObject
- (instancetype)init;
- (void)dealloc;
- (NSArray<NSString *> *)scan_ssids;
- (NSString *)retrieve_password_for_ssid:(NSString *)ssid;
- (NSString *)current_ssid;
@property (strong, nonatomic) CWInterface *wifiInterface;
@end
#endif /* WiFiScanner_h */

View File

@ -0,0 +1,94 @@
#import "WifiScanner.hpp"
#import "WifiScannerMac.h"
@implementation WifiScannerMac
- (instancetype)init {
self = [super init];
if (self) {
// Create a CWInterface object to work with Wi-Fi interfaces
self->_wifiInterface = [CWInterface interface];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (NSArray *)scan_ssids {
NSMutableArray *ssids = [NSMutableArray array];
NSError *error = nil;
// Retrieve the list of available Wi-Fi networks
NSSet<CWNetwork *> *networksSet = [self->_wifiInterface scanForNetworksWithName:nil error:&error];
if (error) {
return ssids;
}
NSArray<CWNetwork *> *availableNetworks = [networksSet allObjects];
// Loop through the list of available networks and store their SSIDs
for (CWNetwork *network in availableNetworks) {
if (network.ssid != nil)
{
[ssids addObject:network.ssid];
}
}
return ssids;
}
- (NSString *)retrieve_password_for_ssid:(NSString *)ssid {
NSString * psk;
OSStatus status = CWKeychainFindWiFiPassword(kCWKeychainDomainSystem, [ssid dataUsingEncoding:NSUTF8StringEncoding], &psk);
if (status == errSecSuccess) {
return psk;
}
return @""; // Password not found or an error occurred
}
- (NSString *)current_ssid
{
if (self->_wifiInterface && self->_wifiInterface.ssid != nil) {
return self->_wifiInterface.ssid;
}
return @"";
}
@end
namespace Slic3r {
void WifiScanner::get_ssids_mac(std::vector<std::string>& ssids)
{
if (!m_impl_osx)
m_impl_osx = [[WifiScannerMac alloc] init];
if (m_impl_osx) {
NSArray *arr = [(id)m_impl_osx scan_ssids];
for (NSString* ssid in arr)
ssids.push_back(std::string([ssid UTF8String]));
}
}
std::string WifiScanner::get_psk_mac(const std::string &ssid)
{
if (!m_impl_osx)
m_impl_osx = [[WifiScannerMac alloc] init];
if (m_impl_osx) {
NSString *ns_ssid = [NSString stringWithCString:ssid.c_str() encoding:[NSString defaultCStringEncoding]];
NSString *psk = [(id)m_impl_osx retrieve_password_for_ssid:ns_ssid];
return std::string([psk UTF8String]);
}
return {};
}
std::string WifiScanner::get_current_ssid_mac()
{
if (!m_impl_osx)
m_impl_osx = [[WifiScannerMac alloc] init];
if (m_impl_osx) {
NSString *ssid = [(id)m_impl_osx current_ssid];
return std::string([ssid UTF8String]);
}
return {};
}
}