wifi for printer config dialog

Enumerating wifi ssid and psk for win, apple and linux.
Win automatically loads password if possible.
Apple retrieves password from keychain on button.
Apple lists only connected network in some cases (ARM or latest OS wdk).
Cmake linking CoreWLAN lib on apple.
Result is written on usb drive.
This commit is contained in:
David Kocik 2023-10-16 16:09:02 +02:00
parent 12f735652b
commit 0638b3057f
13 changed files with 939 additions and 4 deletions

View File

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

View File

@ -95,6 +95,8 @@
#include "DesktopIntegrationDialog.hpp"
#include "SendSystemInfoDialog.hpp"
#include "Downloader.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "WifiConfigDialog.hpp"
#include "BitmapCache.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
// 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) {
switch (event.GetId() - config_id_base) {
@ -2544,6 +2547,16 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
case ConfigMenuFlashFirmware:
FirmwareDialog::run(mainframe);
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:
break;
}

View File

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

View File

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

View File

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

View File

@ -1095,4 +1095,11 @@ void RemovableDriveManager::eject_thread_finish()
}
#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

View File

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