Merge branch 'dk_resolve_search'
This commit is contained in:
commit
24fe505f00
@ -15,6 +15,7 @@
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
@ -233,7 +234,49 @@ void BonjourDialog::on_timer_process()
|
||||
}
|
||||
}
|
||||
|
||||
IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Multiple resolved IP addresses")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER))
|
||||
, m_selected_index (selected_index)
|
||||
{
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
m_list->SetMinSize(wxSize(40 * em, 30 * em));
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname));
|
||||
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||
|
||||
m_list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||
m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 40 * em);
|
||||
|
||||
for (size_t i = 0; i < ips.size(); i++)
|
||||
m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string()));
|
||||
|
||||
m_list->Select(0);
|
||||
|
||||
vsizer->Add(m_list, 1, wxEXPAND | wxALL, em);
|
||||
|
||||
wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
|
||||
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
GUI::wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
IPListDialog::~IPListDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void IPListDialog::EndModal(int retCode)
|
||||
{
|
||||
if (retCode == wxID_OK) {
|
||||
m_selected_index = (size_t)m_list->GetFirstSelected();
|
||||
}
|
||||
wxDialog::EndModal(retCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
@ -11,7 +12,7 @@ class wxListView;
|
||||
class wxStaticText;
|
||||
class wxTimer;
|
||||
class wxTimerEvent;
|
||||
|
||||
class address;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -41,12 +42,26 @@ private:
|
||||
unsigned timer_state;
|
||||
Slic3r::PrinterTechnology tech;
|
||||
|
||||
void on_reply(BonjourReplyEvent &);
|
||||
virtual void on_reply(BonjourReplyEvent &);
|
||||
void on_timer(wxTimerEvent &);
|
||||
void on_timer_process();
|
||||
};
|
||||
|
||||
class IPListDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index);
|
||||
IPListDialog(IPListDialog&&) = delete;
|
||||
IPListDialog(const IPListDialog&) = delete;
|
||||
IPListDialog& operator=(IPListDialog&&) = delete;
|
||||
IPListDialog& operator=(const IPListDialog&) = delete;
|
||||
~IPListDialog();
|
||||
|
||||
virtual void EndModal(int retCode) wxOVERRIDE;
|
||||
private:
|
||||
wxListView* m_list;
|
||||
size_t& m_selected_index;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -1823,7 +1823,7 @@ void NotificationManager::push_upload_job_notification(int id, float filesize,
|
||||
}
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize, filename, host), 0);
|
||||
}
|
||||
void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
|
||||
{
|
||||
@ -1832,6 +1832,21 @@ void NotificationManager::set_upload_job_notification_percentage(int id, const s
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->set_percentage(percentage);
|
||||
if (phun->get_host() != host)
|
||||
phun->set_host(host);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_upload_job_notification_host(int id, const std::string& host)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload) {
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->set_host(host);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
@ -1845,6 +1860,8 @@ void NotificationManager::upload_job_notification_show_canceled(int id, const st
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->cancel();
|
||||
if (phun->get_host() != host)
|
||||
phun->set_host(host);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
@ -1858,6 +1875,8 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if(phun->compare_job_id(id)) {
|
||||
phun->error();
|
||||
if (phun->get_host() != host)
|
||||
phun->set_host(host);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
|
@ -203,6 +203,7 @@ public:
|
||||
// print host upload
|
||||
void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void set_upload_job_notification_host(int id, const std::string& host);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
// Download App progress
|
||||
@ -505,10 +506,12 @@ private:
|
||||
PB_CANCELLED,
|
||||
PB_COMPLETED
|
||||
};
|
||||
PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize)
|
||||
PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize, const std::string& filename, const std::string& host)
|
||||
:ProgressBarNotification(n, id_provider, evt_handler)
|
||||
, m_job_id(job_id)
|
||||
, m_file_size(filesize)
|
||||
, m_filename(filename)
|
||||
, m_host(host)
|
||||
{
|
||||
m_has_cancel_button = true;
|
||||
set_percentage(percentage);
|
||||
@ -519,6 +522,8 @@ private:
|
||||
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); }
|
||||
bool compare_job_id(const int other_id) const { return m_job_id == other_id; }
|
||||
bool compare_text(const std::string& text) const override { return false; }
|
||||
void set_host(const std::string& host) { m_host = host; update({ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, get_upload_job_text(m_id, m_filename, m_host)}); }
|
||||
std::string get_host() const { return m_host; }
|
||||
protected:
|
||||
void init() override;
|
||||
void count_spaces() override;
|
||||
@ -536,6 +541,8 @@ private:
|
||||
float m_file_size;
|
||||
long m_hover_time{ 0 };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
|
||||
std::string m_filename;
|
||||
std::string m_host;
|
||||
};
|
||||
|
||||
class SlicingProgressNotification : public ProgressBarNotification
|
||||
|
@ -190,6 +190,7 @@ void PrintHostSendDialog::EndModal(int ret)
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event);
|
||||
|
||||
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||
: wxEvent(winid, eventType)
|
||||
@ -218,6 +219,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||
, on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this)
|
||||
, on_resolve_evt(this, EVT_PRINTHOST_RESOLVE, &PrintHostQueueDialog::on_resolve, this)
|
||||
{
|
||||
const auto em = GetTextExtent("m").x;
|
||||
|
||||
@ -450,6 +452,17 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
|
||||
wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_resolve(Event& evt)
|
||||
{
|
||||
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
// wxstring in event is called error, but it should contain new host string.
|
||||
wxVariant hst(evt.error);
|
||||
// todo: set variant
|
||||
job_list->SetValue(hst,evt.job_id,COL_HOST);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_host(evt.job_id + 1, boost::nowide::narrow(evt.error));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
|
||||
{
|
||||
int ic = job_list->GetItemCount();
|
||||
|
@ -108,6 +108,7 @@ private:
|
||||
EventGuard on_progress_evt;
|
||||
EventGuard on_error_evt;
|
||||
EventGuard on_cancel_evt;
|
||||
EventGuard on_resolve_evt;
|
||||
|
||||
JobState get_state(int idx);
|
||||
void set_state(int idx, JobState);
|
||||
@ -115,6 +116,7 @@ private:
|
||||
void on_progress(Event&);
|
||||
void on_error(Event&);
|
||||
void on_cancel(Event&);
|
||||
void on_resolve(Event&);
|
||||
// This vector keep adress and filename of uploads. It is used when checking for running uploads during exit.
|
||||
std::vector<std::pair<std::string, std::string>> upload_names;
|
||||
void save_user_data(int);
|
||||
@ -124,7 +126,7 @@ private:
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event);
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
@ -92,7 +92,7 @@ wxString AstroBox::get_test_failed_msg (wxString &msg) const
|
||||
% _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return true; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
@ -7,12 +7,11 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_duration.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
using boost::optional;
|
||||
using boost::system::error_code;
|
||||
@ -238,6 +237,7 @@ struct DnsRR_A
|
||||
enum { TAG = 0x1 };
|
||||
|
||||
asio::ip::address_v4 ip;
|
||||
std::string name;
|
||||
|
||||
static void decode(optional<DnsRR_A> &result, const DnsResource &rr)
|
||||
{
|
||||
@ -255,6 +255,7 @@ struct DnsRR_AAAA
|
||||
enum { TAG = 0x1c };
|
||||
|
||||
asio::ip::address_v6 ip;
|
||||
std::string name;
|
||||
|
||||
static void decode(optional<DnsRR_AAAA> &result, const DnsResource &rr)
|
||||
{
|
||||
@ -402,7 +403,7 @@ struct DnsMessage
|
||||
|
||||
DnsSDMap sdmap;
|
||||
|
||||
static optional<DnsMessage> decode(const std::vector<char> &buffer, const Bonjour::TxtKeys &txt_keys)
|
||||
static optional<DnsMessage> decode(const std::vector<char>& buffer, const Bonjour::TxtKeys& txt_keys)
|
||||
{
|
||||
const auto size = buffer.size();
|
||||
if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
|
||||
@ -426,30 +427,36 @@ struct DnsMessage
|
||||
auto rr = DnsResource::decode(buffer, offset, dataoffset);
|
||||
if (!rr) {
|
||||
return boost::none;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
res.parse_rr(buffer, std::move(*rr), dataoffset, txt_keys);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
private:
|
||||
void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys)
|
||||
void parse_rr(const std::vector<char>& buffer, DnsResource&& rr, size_t dataoffset, const Bonjour::TxtKeys& txt_keys)
|
||||
{
|
||||
switch (rr.type) {
|
||||
case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break;
|
||||
case DnsRR_AAAA::TAG: DnsRR_AAAA::decode(this->rr_aaaa, rr); break;
|
||||
case DnsRR_SRV::TAG: {
|
||||
auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset);
|
||||
if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); }
|
||||
break;
|
||||
}
|
||||
case DnsRR_TXT::TAG: {
|
||||
auto txt = DnsRR_TXT::decode(rr, txt_keys);
|
||||
if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
|
||||
break;
|
||||
}
|
||||
case DnsRR_A::TAG:
|
||||
DnsRR_A::decode(this->rr_a, rr);
|
||||
this->rr_a->name = rr.name;
|
||||
break;
|
||||
case DnsRR_AAAA::TAG:
|
||||
DnsRR_AAAA::decode(this->rr_aaaa, rr);
|
||||
this->rr_aaaa->name = rr.name;
|
||||
break;
|
||||
case DnsRR_SRV::TAG: {
|
||||
auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset);
|
||||
if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); }
|
||||
break;
|
||||
}
|
||||
case DnsRR_TXT::TAG: {
|
||||
auto txt = DnsRR_TXT::decode(rr, txt_keys);
|
||||
if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -480,24 +487,11 @@ std::ostream& operator<<(std::ostream &os, const DnsMessage &msg)
|
||||
return os << "])";
|
||||
}
|
||||
|
||||
|
||||
struct BonjourRequest
|
||||
{
|
||||
static const asio::ip::address_v4 MCAST_IP4;
|
||||
static const uint16_t MCAST_PORT;
|
||||
|
||||
std::vector<char> data;
|
||||
|
||||
static optional<BonjourRequest> make(const std::string &service, const std::string &protocol);
|
||||
|
||||
private:
|
||||
BonjourRequest(std::vector<char> &&data) : data(std::move(data)) {}
|
||||
};
|
||||
|
||||
const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb};
|
||||
const asio::ip::address_v4 BonjourRequest::MCAST_IP4{ 0xe00000fb };
|
||||
const asio::ip::address_v6 BonjourRequest::MCAST_IP6 = asio::ip::make_address_v6("ff02::fb");
|
||||
const uint16_t BonjourRequest::MCAST_PORT = 5353;
|
||||
|
||||
optional<BonjourRequest> BonjourRequest::make(const std::string &service, const std::string &protocol)
|
||||
optional<BonjourRequest> BonjourRequest::make_PTR(const std::string &service, const std::string &protocol)
|
||||
{
|
||||
if (service.size() > 15 || protocol.size() > 15) {
|
||||
return boost::none;
|
||||
@ -535,73 +529,227 @@ optional<BonjourRequest> BonjourRequest::make(const std::string &service, const
|
||||
return BonjourRequest(std::move(data));
|
||||
}
|
||||
|
||||
|
||||
// API - private part
|
||||
|
||||
struct Bonjour::priv
|
||||
optional<BonjourRequest> BonjourRequest::make_A(const std::string& hostname)
|
||||
{
|
||||
const std::string service;
|
||||
std::string protocol;
|
||||
std::string service_dn;
|
||||
TxtKeys txt_keys;
|
||||
unsigned timeout;
|
||||
unsigned retries;
|
||||
// todo: why is this and what is real max
|
||||
if (hostname.size() > 30) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<char> buffer;
|
||||
std::thread io_thread;
|
||||
Bonjour::ReplyFn replyfn;
|
||||
Bonjour::CompleteFn completefn;
|
||||
std::vector<char> data;
|
||||
data.reserve(hostname.size() + 18);
|
||||
|
||||
priv(std::string &&service);
|
||||
// Add metadata
|
||||
static const unsigned char rq_meta[] = {
|
||||
0x00, 0x00, // Query ID (zero for mDNS)
|
||||
0x00, 0x00, // Flags
|
||||
0x00, 0x01, // One query
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Zero Answer, Authority, and Additional RRs
|
||||
};
|
||||
std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data));
|
||||
|
||||
std::string strip_service_dn(const std::string &service_name) const;
|
||||
void udp_receive(udp::endpoint from, size_t bytes);
|
||||
void lookup_perform();
|
||||
};
|
||||
// Add hostname without .local
|
||||
data.push_back(hostname.size());
|
||||
data.insert(data.end(), hostname.begin(), hostname.end());
|
||||
|
||||
// Add the rest of A record
|
||||
static const unsigned char ptr_tail[] = {
|
||||
0x05, // length of "local"
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,// "local" string and terminator
|
||||
0x00, 0x01, // Type A
|
||||
0x00, 0xff, // Class - 01 is internet 0xff is any
|
||||
};
|
||||
std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data));
|
||||
|
||||
Bonjour::priv::priv(std::string &&service)
|
||||
: service(std::move(service))
|
||||
, protocol("tcp")
|
||||
, timeout(10)
|
||||
, retries(1)
|
||||
{
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
return BonjourRequest(std::move(data));
|
||||
}
|
||||
|
||||
std::string Bonjour::priv::strip_service_dn(const std::string &service_name) const
|
||||
optional<BonjourRequest> BonjourRequest::make_AAAA(const std::string& hostname)
|
||||
{
|
||||
// todo: why is this and what is real max
|
||||
if (hostname.size() > 30) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
data.reserve(hostname.size() + 18);
|
||||
|
||||
// Add metadata
|
||||
static const unsigned char rq_meta[] = {
|
||||
0x00, 0x00, // Query ID (zero for mDNS)
|
||||
0x00, 0x00, // Flags
|
||||
0x00, 0x01, // One query
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Zero Answer, Authority, and Additional RRs
|
||||
};
|
||||
std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data));
|
||||
|
||||
// Add hostname without .local
|
||||
data.push_back(hostname.size());
|
||||
data.insert(data.end(), hostname.begin(), hostname.end());
|
||||
|
||||
// Add the rest of A record
|
||||
static const unsigned char ptr_tail[] = {
|
||||
0x05, // length of "local"
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, // "local" string and terminator
|
||||
0x00, 0x1c, // Type AAAA
|
||||
0x00, 0xff, // Class - 01 is internet 0xff is any
|
||||
};
|
||||
std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data));
|
||||
|
||||
return BonjourRequest(std::move(data));
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string strip_service_dn(const std::string& service_name, const std::string& service_dn)
|
||||
{
|
||||
if (service_name.size() <= service_dn.size()) {
|
||||
return service_name;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
auto needle = service_name.rfind(service_dn);
|
||||
if (needle == service_name.size() - service_dn.size()) {
|
||||
return service_name.substr(0, needle - 1);
|
||||
} else {
|
||||
return service_name;
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UdpSession::UdpSession(Bonjour::ReplyFn rfn) : replyfn(rfn)
|
||||
{
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
}
|
||||
|
||||
UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: replyfn(replyfn)
|
||||
, multicast_address(multicast_address)
|
||||
, socket(*io_service)
|
||||
, io_service(io_service)
|
||||
{
|
||||
try {
|
||||
// open socket
|
||||
boost::asio::ip::udp::endpoint listen_endpoint(multicast_address.is_v4() ? udp::v4() : udp::v6(), BonjourRequest::MCAST_PORT);
|
||||
socket.open(listen_endpoint.protocol());
|
||||
// set socket to listen
|
||||
socket.set_option(udp::socket::reuse_address(true));
|
||||
socket.bind(listen_endpoint);
|
||||
if (interface_address.is_v4()) {
|
||||
// listen for multicast on given interface
|
||||
socket.set_option(boost::asio::ip::multicast::join_group(multicast_address.to_v4(), interface_address.to_v4()));
|
||||
// send to interface
|
||||
socket.set_option(asio::ip::multicast::outbound_interface(interface_address.to_v4()));
|
||||
} else {
|
||||
// listen for multicast on given interface
|
||||
socket.set_option(boost::asio::ip::multicast::join_group(multicast_address.to_v6(), interface_address.to_v6().scope_id()));
|
||||
// send to interface
|
||||
socket.set_option(asio::ip::multicast::outbound_interface(interface_address.to_v6().scope_id()));
|
||||
}
|
||||
mcast_endpoint = udp::endpoint(multicast_address, BonjourRequest::MCAST_PORT);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address << ". Interface: " << interface_address;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
||||
|
||||
UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: replyfn(replyfn)
|
||||
, multicast_address(multicast_address)
|
||||
, socket(*io_service)
|
||||
, io_service(io_service)
|
||||
{
|
||||
try {
|
||||
// open socket
|
||||
boost::asio::ip::udp::endpoint listen_endpoint(multicast_address.is_v4() ? udp::v4() : udp::v6(), BonjourRequest::MCAST_PORT);
|
||||
socket.open(listen_endpoint.protocol());
|
||||
// set socket to listen
|
||||
socket.set_option(udp::socket::reuse_address(true));
|
||||
socket.bind(listen_endpoint);
|
||||
socket.set_option(boost::asio::ip::multicast::join_group(multicast_address));
|
||||
mcast_endpoint = udp::endpoint(multicast_address, BonjourRequest::MCAST_PORT);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void UdpSocket::send()
|
||||
{
|
||||
try {
|
||||
for (const auto& request : requests)
|
||||
socket.send_to(asio::buffer(request.m_data), mcast_endpoint);
|
||||
|
||||
// Should we care if this is called while already receiving? (async_receive call from receive_handler)
|
||||
async_receive();
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void UdpSocket::async_receive()
|
||||
{
|
||||
try {
|
||||
// our session to hold the buffer + endpoint
|
||||
auto session = create_session();
|
||||
socket.async_receive_from(asio::buffer(session->buffer, session->buffer.size())
|
||||
, session->remote_endpoint
|
||||
, boost::bind(&UdpSocket::receive_handler, this, session, asio::placeholders::error, asio::placeholders::bytes_transferred));
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void UdpSocket::receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes)
|
||||
{
|
||||
// let io_service to handle the datagram on session
|
||||
// from boost documentation io_service::post:
|
||||
// The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked.
|
||||
io_service->post(boost::bind(&UdpSession::handle_receive, session, error, bytes));
|
||||
// immediately accept new datagrams
|
||||
async_receive();
|
||||
}
|
||||
|
||||
SharedSession LookupSocket::create_session() const
|
||||
{
|
||||
return std::shared_ptr< LookupSession >(new LookupSession(this, replyfn));
|
||||
}
|
||||
|
||||
|
||||
void LookupSession::handle_receive(const error_code& error, size_t bytes)
|
||||
{
|
||||
assert(socket);
|
||||
|
||||
if (error) {
|
||||
BOOST_LOG_TRIVIAL(error) << error.message();
|
||||
return;
|
||||
}
|
||||
if (bytes == 0 || !replyfn) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.resize(bytes);
|
||||
auto dns_msg = DnsMessage::decode(buffer, txt_keys);
|
||||
auto dns_msg = DnsMessage::decode(buffer, socket->get_txt_keys());
|
||||
if (dns_msg) {
|
||||
asio::ip::address ip = from.address();
|
||||
asio::ip::address ip = remote_endpoint.address();
|
||||
if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
|
||||
else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; }
|
||||
|
||||
for (auto &sdpair : dns_msg->sdmap) {
|
||||
if (! sdpair.second.srv) {
|
||||
for (auto& sdpair : dns_msg->sdmap) {
|
||||
if (!sdpair.second.srv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &srv = *sdpair.second.srv;
|
||||
auto service_name = strip_service_dn(sdpair.first);
|
||||
const auto& srv = *sdpair.second.srv;
|
||||
|
||||
auto service_name = strip_service_dn(sdpair.first, socket->get_service_dn());
|
||||
if (service_name.empty())
|
||||
continue;
|
||||
|
||||
std::string path;
|
||||
std::string version;
|
||||
@ -617,64 +765,283 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
||||
}
|
||||
}
|
||||
|
||||
SharedSession ResolveSocket::create_session() const
|
||||
{
|
||||
return std::shared_ptr< ResolveSession > (new ResolveSession(this, replyfn));
|
||||
}
|
||||
|
||||
|
||||
void ResolveSession::handle_receive(const error_code& error, size_t bytes)
|
||||
{
|
||||
assert(socket);
|
||||
if (error) {
|
||||
// todo: what level? do we even log? There might be callbacks when timer runs out
|
||||
BOOST_LOG_TRIVIAL(info) << error.message();
|
||||
return;
|
||||
}
|
||||
if (bytes == 0 || !replyfn) {
|
||||
// todo: log something?
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.resize(bytes);
|
||||
#if 0
|
||||
std::string str;
|
||||
char const hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
const char ch = buffer[i];
|
||||
str += hex_chars[(ch & 0xF0) >> 4];
|
||||
str += hex_chars[(ch & 0x0F) >> 0];
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << remote_endpoint.address()<< " " << str;
|
||||
#endif
|
||||
// decode buffer, txt keys are not needed for A / AAAA answer
|
||||
auto dns_msg = DnsMessage::decode(buffer, Bonjour::TxtKeys());
|
||||
if (dns_msg) {
|
||||
asio::ip::address ip;
|
||||
std::string answer_name;
|
||||
if (dns_msg->rr_a) {
|
||||
ip = dns_msg->rr_a->ip;
|
||||
answer_name = dns_msg->rr_a->name;
|
||||
}
|
||||
else if (dns_msg->rr_aaaa) {
|
||||
ip = dns_msg->rr_aaaa->ip;
|
||||
answer_name = dns_msg->rr_aaaa->name;
|
||||
}
|
||||
else
|
||||
return; // not matching query type with answer type
|
||||
|
||||
if (!answer_name.empty()) {
|
||||
// transform both strings to lower. Should we really do it?
|
||||
std::string name_tolower = answer_name;
|
||||
std::transform(name_tolower.begin(), name_tolower.end(), name_tolower.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
std::string hostname_tolower = socket->get_hostname();
|
||||
std::transform(hostname_tolower.begin(), hostname_tolower.end(), hostname_tolower.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
if (name_tolower == hostname_tolower) {
|
||||
BonjourReply reply(ip, 0, std::string(), answer_name, BonjourReply::TxtData());
|
||||
replyfn(std::move(reply));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API - private part
|
||||
|
||||
struct Bonjour::priv
|
||||
{
|
||||
const std::string service;
|
||||
std::string protocol;
|
||||
std::string service_dn;
|
||||
TxtKeys txt_keys;
|
||||
unsigned timeout;
|
||||
unsigned retries;
|
||||
std::string hostname;
|
||||
|
||||
// std::vector<BonjourReply> replies;
|
||||
|
||||
std::vector<char> buffer;
|
||||
std::thread io_thread;
|
||||
Bonjour::ReplyFn replyfn;
|
||||
Bonjour::CompleteFn completefn;
|
||||
Bonjour::ResolveFn resolvefn;
|
||||
|
||||
priv(std::string&& service);
|
||||
|
||||
// void udp_receive_lookup(udp::endpoint from, size_t bytes);
|
||||
void lookup_perform();
|
||||
void resolve_perform();
|
||||
};
|
||||
|
||||
Bonjour::priv::priv(std::string&& service)
|
||||
: service(std::move(service))
|
||||
, protocol("tcp")
|
||||
, timeout(10)
|
||||
, retries(1)
|
||||
{
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
}
|
||||
|
||||
void Bonjour::priv::lookup_perform()
|
||||
{
|
||||
service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str();
|
||||
|
||||
const auto brq = BonjourRequest::make(service, protocol);
|
||||
if (!brq) {
|
||||
return;
|
||||
std::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service);
|
||||
|
||||
std::vector<LookupSocket*> sockets;
|
||||
|
||||
// resolve intefaces - from PR#6646
|
||||
std::vector<boost::asio::ip::address> interfaces;
|
||||
asio::ip::udp::resolver resolver(*io_service);
|
||||
boost::system::error_code ec;
|
||||
// ipv4 interfaces
|
||||
auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), "", ec);
|
||||
if (!ec) {
|
||||
for (const auto & r : results) {
|
||||
const auto addr = r.endpoint().address();
|
||||
if (addr.is_loopback()) continue;
|
||||
interfaces.emplace_back(std::move(addr));
|
||||
}
|
||||
// create ipv4 socket for each interface
|
||||
// each will send to querry to for both ipv4 and ipv6
|
||||
for (const auto& intrfc : interfaces)
|
||||
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, intrfc, io_service));
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message();
|
||||
}
|
||||
|
||||
auto self = this;
|
||||
|
||||
if (sockets.empty())
|
||||
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, io_service));
|
||||
// ipv6 interfaces
|
||||
interfaces.clear();
|
||||
//udp::resolver::query query(host, PORT, boost::asio::ip::resolver_query_base::numeric_service);
|
||||
results = resolver.resolve(udp::v6(), asio::ip::host_name(), "", ec);
|
||||
if (!ec)
|
||||
{
|
||||
for (const auto& r : results) {
|
||||
const auto addr = r.endpoint().address();
|
||||
if (addr.is_loopback()) continue;
|
||||
interfaces.emplace_back(std::move(addr));
|
||||
}
|
||||
// create ipv6 socket for each interface
|
||||
// each will send to querry to for both ipv4 and ipv6
|
||||
for (const auto& intrfc : interfaces)
|
||||
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, intrfc, io_service));
|
||||
if (interfaces.empty())
|
||||
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, io_service));
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info)<< "Failed to resolve ipv6 interfaces: " << ec.message();
|
||||
}
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
udp::socket socket(io_service);
|
||||
socket.open(udp::v4());
|
||||
socket.set_option(udp::socket::reuse_address(true));
|
||||
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
|
||||
socket.send_to(asio::buffer(brq->data), mcast);
|
||||
// send first queries
|
||||
for (auto * socket : sockets)
|
||||
socket->send();
|
||||
|
||||
bool expired = false;
|
||||
bool retry = false;
|
||||
asio::deadline_timer timer(io_service);
|
||||
// timer settings
|
||||
asio::deadline_timer timer(*io_service);
|
||||
retries--;
|
||||
std::function<void(const error_code &)> timer_handler = [&](const error_code &error) {
|
||||
std::function<void(const error_code&)> timer_handler = [&](const error_code& error) {
|
||||
// end
|
||||
if (retries == 0 || error) {
|
||||
expired = true;
|
||||
if (self->completefn) {
|
||||
self->completefn();
|
||||
// is this correct ending?
|
||||
io_service->stop();
|
||||
if (completefn) {
|
||||
completefn();
|
||||
}
|
||||
// restart timer
|
||||
} else {
|
||||
retry = true;
|
||||
retries--;
|
||||
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||
timer.async_wait(timer_handler);
|
||||
// trigger another round of queries
|
||||
for (auto * socket : sockets)
|
||||
socket->send();
|
||||
}
|
||||
};
|
||||
|
||||
// start timer
|
||||
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||
timer.async_wait(timer_handler);
|
||||
// start io_service, it will run until it has something to do - so in this case until stop is called in timer
|
||||
io_service->run();
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
udp::endpoint recv_from;
|
||||
const auto recv_handler = [&](const error_code &error, size_t bytes) {
|
||||
if (!error) { self->udp_receive(recv_from, bytes); }
|
||||
};
|
||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||
void Bonjour::priv::resolve_perform()
|
||||
{
|
||||
// reply callback is shared to every UDPSession which is called on same thread as io_service->run();
|
||||
// thus no need to mutex replies in reply_callback, same should go with the timer
|
||||
std::vector<BonjourReply> replies;
|
||||
// examples would store [self] to the lambda (and the timer one), is it ok not to do it? (Should be c++03)
|
||||
const auto reply_callback = [&rpls = replies](BonjourReply&& reply)
|
||||
{
|
||||
if (std::find(rpls.begin(), rpls.end(), reply) == rpls.end())
|
||||
rpls.push_back(reply);
|
||||
};
|
||||
|
||||
while (io_service.run_one()) {
|
||||
if (expired) {
|
||||
socket.cancel();
|
||||
} else if (retry) {
|
||||
retry = false;
|
||||
socket.send_to(asio::buffer(brq->data), mcast);
|
||||
} else {
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||
}
|
||||
std::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service);
|
||||
|
||||
std::vector<ResolveSocket*> sockets;
|
||||
|
||||
// resolve intefaces - from PR#6646
|
||||
std::vector<boost::asio::ip::address> interfaces;
|
||||
asio::ip::udp::resolver resolver(*io_service);
|
||||
boost::system::error_code ec;
|
||||
// ipv4 interfaces
|
||||
auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), "", ec);
|
||||
if (!ec) {
|
||||
for (auto const& r : results) {
|
||||
auto const addr = r.endpoint().address();
|
||||
if (addr.is_loopback()) continue;
|
||||
interfaces.emplace_back(addr);
|
||||
}
|
||||
} catch (std::exception& /* e */) {
|
||||
// create ipv4 socket for each interface
|
||||
// each will send to querry to for both ipv4 and ipv6
|
||||
for (const auto& intrfc : interfaces)
|
||||
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service));
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message();
|
||||
}
|
||||
if (sockets.empty())
|
||||
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, io_service));
|
||||
|
||||
// ipv6 interfaces
|
||||
interfaces.clear();
|
||||
results = resolver.resolve(udp::v6(), asio::ip::host_name(), "", ec);
|
||||
if (!ec) {
|
||||
for (auto const& r : results) {
|
||||
auto const addr = r.endpoint().address();
|
||||
if (addr.is_loopback()) continue;
|
||||
interfaces.emplace_back(addr);
|
||||
}
|
||||
// create ipv6 socket for each interface
|
||||
// each will send to querry to for both ipv4 and ipv6
|
||||
for (const auto& intrfc : interfaces)
|
||||
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service));
|
||||
if (interfaces.empty())
|
||||
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, io_service));
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv6 interfaces: " << ec.message();
|
||||
}
|
||||
|
||||
try {
|
||||
// send first queries
|
||||
for (auto * socket : sockets)
|
||||
socket->send();
|
||||
|
||||
// timer settings
|
||||
asio::deadline_timer timer(*io_service);
|
||||
retries--;
|
||||
std::function<void(const error_code&)> timer_handler = [&](const error_code& error) {
|
||||
int replies_count = replies.size();
|
||||
// end
|
||||
if (retries == 0 || error || replies_count > 0) {
|
||||
// is this correct ending?
|
||||
io_service->stop();
|
||||
if (replies_count > 0 && resolvefn) {
|
||||
resolvefn(replies);
|
||||
}
|
||||
// restart timer
|
||||
} else {
|
||||
retries--;
|
||||
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||
timer.async_wait(timer_handler);
|
||||
// trigger another round of queries
|
||||
for (auto * socket : sockets)
|
||||
socket->send();
|
||||
}
|
||||
};
|
||||
// start timer
|
||||
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||
timer.async_wait(timer_handler);
|
||||
// start io_service, it will run until it has something to do - so in this case until stop is called in timer
|
||||
io_service->run();
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,6 +1137,12 @@ Bonjour& Bonjour::set_timeout(unsigned timeout)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::set_hostname(const std::string& hostname)
|
||||
{
|
||||
if (p) { p->hostname = hostname; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::set_retries(unsigned retries)
|
||||
{
|
||||
if (p && retries > 0) { p->retries = retries; }
|
||||
@ -788,6 +1161,12 @@ Bonjour& Bonjour::on_complete(CompleteFn fn)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::on_resolve(ResolveFn fn)
|
||||
{
|
||||
if (p) { p->resolvefn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour::Ptr Bonjour::lookup()
|
||||
{
|
||||
auto self = std::make_shared<Bonjour>(std::move(*this));
|
||||
@ -803,4 +1182,26 @@ Bonjour::Ptr Bonjour::lookup()
|
||||
}
|
||||
|
||||
|
||||
Bonjour::Ptr Bonjour::resolve()
|
||||
{
|
||||
auto self = std::make_shared<Bonjour>(std::move(*this));
|
||||
|
||||
if (self->p) {
|
||||
auto io_thread = std::thread([self]() {
|
||||
self->p->resolve_perform();
|
||||
});
|
||||
self->p->io_thread = std::move(io_thread);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void Bonjour::resolve_sync()
|
||||
{
|
||||
if (p)
|
||||
p->resolve_perform();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,17 @@
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
|
||||
struct BonjourReply
|
||||
{
|
||||
typedef std::unordered_map<std::string, std::string> TxtData;
|
||||
@ -40,7 +45,6 @@ struct BonjourReply
|
||||
|
||||
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||
|
||||
|
||||
/// Bonjour lookup performer
|
||||
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
||||
private:
|
||||
@ -49,6 +53,7 @@ public:
|
||||
typedef std::shared_ptr<Bonjour> Ptr;
|
||||
typedef std::function<void(BonjourReply &&)> ReplyFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
typedef std::function<void(const std::vector<BonjourReply>&)> ResolveFn;
|
||||
typedef std::set<std::string> TxtKeys;
|
||||
|
||||
Bonjour(std::string service);
|
||||
@ -65,15 +70,217 @@ public:
|
||||
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
|
||||
// Timeout is per one retry, ie. total time spent listening = retries * timeout.
|
||||
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
|
||||
|
||||
// sets hostname queried by resolve()
|
||||
Bonjour& set_hostname(const std::string& hostname);
|
||||
|
||||
Bonjour& on_reply(ReplyFn fn);
|
||||
Bonjour& on_complete(CompleteFn fn);
|
||||
|
||||
Bonjour& on_resolve(ResolveFn fn);
|
||||
// lookup all devices by given TxtKeys
|
||||
// each correct reply is passed back in ReplyFn, finishes with CompleteFn
|
||||
Ptr lookup();
|
||||
// performs resolving of hostname into vector of ip adresses passed back by ResolveFn
|
||||
// needs set_hostname and on_resolve to be called before.
|
||||
Ptr resolve();
|
||||
// resolve on the current thread
|
||||
void resolve_sync();
|
||||
private:
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
struct BonjourRequest
|
||||
{
|
||||
static const boost::asio::ip::address_v4 MCAST_IP4;
|
||||
static const boost::asio::ip::address_v6 MCAST_IP6;
|
||||
static const uint16_t MCAST_PORT;
|
||||
|
||||
std::vector<char> m_data;
|
||||
|
||||
static boost::optional<BonjourRequest> make_PTR(const std::string& service, const std::string& protocol);
|
||||
static boost::optional<BonjourRequest> make_A(const std::string& hostname);
|
||||
static boost::optional<BonjourRequest> make_AAAA(const std::string& hostname);
|
||||
private:
|
||||
BonjourRequest(std::vector<char>&& data) : m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
|
||||
class LookupSocket;
|
||||
class ResolveSocket;
|
||||
|
||||
// Session is created for each async_receive of socket. On receive, its handle_receive method is called (Thru io_service->post).
|
||||
// ReplyFn is called if correct datagram was received.
|
||||
class UdpSession
|
||||
{
|
||||
public:
|
||||
UdpSession(Bonjour::ReplyFn rfn);
|
||||
virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0;
|
||||
std::vector<char> buffer;
|
||||
boost::asio::ip::udp::endpoint remote_endpoint;
|
||||
protected:
|
||||
Bonjour::ReplyFn replyfn;
|
||||
};
|
||||
typedef std::shared_ptr<UdpSession> SharedSession;
|
||||
// Session for LookupSocket
|
||||
class LookupSession : public UdpSession
|
||||
{
|
||||
public:
|
||||
LookupSession(const LookupSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
|
||||
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
|
||||
protected:
|
||||
// const pointer to socket to get needed data as txt_keys etc.
|
||||
const LookupSocket* socket;
|
||||
};
|
||||
// Session for ResolveSocket
|
||||
class ResolveSession : public UdpSession
|
||||
{
|
||||
public:
|
||||
ResolveSession(const ResolveSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
|
||||
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
|
||||
protected:
|
||||
// const pointer to seocket to get hostname during handle_receive
|
||||
const ResolveSocket* socket;
|
||||
};
|
||||
|
||||
// Udp socket, starts receiving answers after first send() call until io_service is stopped.
|
||||
class UdpSocket
|
||||
{
|
||||
public:
|
||||
// Two constructors: 1st is with interface which must be resolved before calling this
|
||||
UdpSocket(Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, const boost::asio::ip::address& interface_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service);
|
||||
|
||||
UdpSocket(Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service);
|
||||
|
||||
void send();
|
||||
void async_receive();
|
||||
void cancel() { socket.cancel(); }
|
||||
protected:
|
||||
void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes);
|
||||
virtual SharedSession create_session() const = 0;
|
||||
|
||||
Bonjour::ReplyFn replyfn;
|
||||
boost::asio::ip::address multicast_address;
|
||||
boost::asio::ip::udp::socket socket;
|
||||
boost::asio::ip::udp::endpoint mcast_endpoint;
|
||||
std::shared_ptr< boost::asio::io_service > io_service;
|
||||
std::vector<BonjourRequest> requests;
|
||||
};
|
||||
|
||||
class LookupSocket : public UdpSocket
|
||||
{
|
||||
public:
|
||||
LookupSocket(Bonjour::TxtKeys txt_keys
|
||||
, std::string service
|
||||
, std::string service_dn
|
||||
, std::string protocol
|
||||
, Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, const boost::asio::ip::address& interface_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
|
||||
, txt_keys(txt_keys)
|
||||
, service(service)
|
||||
, service_dn(service_dn)
|
||||
, protocol(protocol)
|
||||
{
|
||||
assert(!service.empty() && replyfn);
|
||||
create_request();
|
||||
}
|
||||
|
||||
LookupSocket(Bonjour::TxtKeys txt_keys
|
||||
, std::string service
|
||||
, std::string service_dn
|
||||
, std::string protocol
|
||||
, Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: UdpSocket(replyfn, multicast_address, io_service)
|
||||
, txt_keys(txt_keys)
|
||||
, service(service)
|
||||
, service_dn(service_dn)
|
||||
, protocol(protocol)
|
||||
{
|
||||
assert(!service.empty() && replyfn);
|
||||
create_request();
|
||||
}
|
||||
|
||||
const Bonjour::TxtKeys get_txt_keys() const { return txt_keys; }
|
||||
const std::string get_service() const { return service; }
|
||||
const std::string get_service_dn() const { return service_dn; }
|
||||
|
||||
protected:
|
||||
SharedSession create_session() const override;
|
||||
void create_request()
|
||||
{
|
||||
requests.clear();
|
||||
// create PTR request
|
||||
if (auto rqst = BonjourRequest::make_PTR(service, protocol); rqst)
|
||||
requests.push_back(std::move(rqst.get()));
|
||||
}
|
||||
boost::optional<BonjourRequest> request;
|
||||
Bonjour::TxtKeys txt_keys;
|
||||
std::string service;
|
||||
std::string service_dn;
|
||||
std::string protocol;
|
||||
};
|
||||
|
||||
class ResolveSocket : public UdpSocket
|
||||
{
|
||||
public:
|
||||
ResolveSocket(const std::string& hostname
|
||||
, Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, const boost::asio::ip::address& interface_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
|
||||
, hostname(hostname)
|
||||
|
||||
{
|
||||
assert(!hostname.empty() && replyfn);
|
||||
create_requests();
|
||||
}
|
||||
|
||||
ResolveSocket(const std::string& hostname
|
||||
, Bonjour::ReplyFn replyfn
|
||||
, const boost::asio::ip::address& multicast_address
|
||||
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||
: UdpSocket(replyfn, multicast_address, io_service)
|
||||
, hostname(hostname)
|
||||
|
||||
{
|
||||
assert(!hostname.empty() && replyfn);
|
||||
create_requests();
|
||||
}
|
||||
|
||||
std::string get_hostname() const { return hostname; }
|
||||
protected:
|
||||
SharedSession create_session() const override;
|
||||
void create_requests()
|
||||
{
|
||||
requests.clear();
|
||||
// BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname.
|
||||
// If that is unsufficient, we need to change make_A / AAAA and pass full hostname.
|
||||
std::string trimmed_hostname = hostname;
|
||||
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
|
||||
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
|
||||
if (auto rqst = BonjourRequest::make_A(trimmed_hostname); rqst)
|
||||
requests.push_back(std::move(rqst.get()));
|
||||
|
||||
trimmed_hostname = hostname;
|
||||
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
|
||||
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
|
||||
if (auto rqst = BonjourRequest::make_AAAA(trimmed_hostname); rqst)
|
||||
requests.push_back(std::move(rqst.get()));
|
||||
}
|
||||
|
||||
std::string hostname;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||
% std::string(msg.ToUTF8())).str());
|
||||
}
|
||||
|
||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
wxString connect_msg;
|
||||
auto connectionType = connect(connect_msg);
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::StartSimulation; }
|
||||
|
@ -76,7 +76,7 @@ wxString FlashAir::get_test_failed_msg (wxString &msg) const
|
||||
% _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str());
|
||||
}
|
||||
|
||||
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
|
||||
|
@ -62,7 +62,7 @@ wxString MKS::get_test_failed_msg(wxString& msg) const
|
||||
% std::string(msg.ToUTF8())).str());
|
||||
}
|
||||
|
||||
bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
bool test(wxString& curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString& msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
@ -18,7 +19,8 @@
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
#include "Bonjour.hpp"
|
||||
#include "slic3r/GUI/BonjourDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
@ -91,6 +93,38 @@ std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string get_host_from_url(const std::string& url_in)
|
||||
{
|
||||
std::string url = url_in;
|
||||
// add http:// if there is no scheme
|
||||
size_t double_slash = url.find("//");
|
||||
if (double_slash == std::string::npos)
|
||||
url = "http://" + url;
|
||||
std::string out = url;
|
||||
CURLU* hurl = curl_url();
|
||||
if (hurl) {
|
||||
// Parse the input URL.
|
||||
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
// Replace the address.
|
||||
char* host;
|
||||
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
out = host;
|
||||
curl_free(host);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
|
||||
curl_url_cleanup(hurl);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
||||
return out;
|
||||
}
|
||||
} //namespace
|
||||
#endif // WIN32
|
||||
|
||||
@ -103,11 +137,62 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
|
||||
|
||||
const char* OctoPrint::get_name() const { return "OctoPrint"; }
|
||||
|
||||
bool OctoPrint::test(wxString &msg) const
|
||||
#ifdef WIN32
|
||||
bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
const char* name = get_name();
|
||||
bool res = true;
|
||||
// Msg contains ip string.
|
||||
auto url = substitute_host(make_url("api/version"), GUI::into_u8(msg));
|
||||
msg.Clear();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(url);//std::move(url));
|
||||
set_auth(http);
|
||||
http
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version at %2% : %3%, HTTP %4%, body: `%5%`") % name % url % error % status % body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
if (!ptree.get_optional<std::string>("api")) {
|
||||
res = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = ptree.get_optional<std::string>("text");
|
||||
res = validate_version_text(text);
|
||||
if (!res) {
|
||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
||||
}
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
res = false;
|
||||
msg = "Could not parse server response.";
|
||||
}
|
||||
})
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif //WIN32
|
||||
|
||||
bool OctoPrint::test(wxString& msg) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
|
||||
const char *name = get_name();
|
||||
|
||||
bool res = true;
|
||||
@ -147,8 +232,8 @@ bool OctoPrint::test(wxString &msg) const
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Remember resolved address to be reused at successive REST API call.
|
||||
msg = GUI::from_u8(address);
|
||||
@ -159,6 +244,7 @@ bool OctoPrint::test(wxString &msg) const
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
wxString OctoPrint::get_test_ok_msg () const
|
||||
{
|
||||
return _(L("Connection to OctoPrint works correctly."));
|
||||
@ -172,9 +258,130 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
#ifndef WIN32
|
||||
return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn);
|
||||
#else
|
||||
std::string host = get_host_from_url(m_host);
|
||||
|
||||
// decide what to do based on m_host - resolve hostname or upload to ip
|
||||
std::vector<boost::asio::ip::address> resolved_addr;
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
|
||||
if (!ec) {
|
||||
resolved_addr.push_back(host_ip);
|
||||
} else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1"){
|
||||
Bonjour("octoprint")
|
||||
.set_hostname(host)
|
||||
.set_retries(10) // number of rounds of queries send
|
||||
.set_timeout(1) // after each timeout, if there is any answer, the resolving will stop
|
||||
.on_resolve([&ra = resolved_addr](const std::vector<BonjourReply>& replies) {
|
||||
std::vector<boost::asio::ip::address> resolved_addr;
|
||||
for (const auto & rpl : replies) {
|
||||
boost::asio::ip::address ip(rpl.ip);
|
||||
ra.emplace_back(ip);
|
||||
BOOST_LOG_TRIVIAL(info) << "Resolved IP address: " << rpl.ip;
|
||||
}
|
||||
})
|
||||
.resolve_sync();
|
||||
}
|
||||
if (resolved_addr.empty()) {
|
||||
// no resolved addresses - try system resolving
|
||||
BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving.";
|
||||
return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn);
|
||||
} else if (resolved_addr.size() == 1) {
|
||||
// one address resolved - upload there
|
||||
return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front());
|
||||
} else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) {
|
||||
// there are just 2 addresses and 1 is ip_v4 and other is ip_v6
|
||||
// try sending to both. (Then if both fail, show both error msg after second try)
|
||||
wxString error_message;
|
||||
if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn
|
||||
, [&msg = error_message, resolved_addr](wxString error) { msg = GUI::format_wxstr("%1%: %2%", resolved_addr.front(), error); }
|
||||
, resolve_fn, resolved_addr.front())
|
||||
&&
|
||||
!upload_inner_with_resolved_ip(upload_data, prorgess_fn
|
||||
, [&msg = error_message, resolved_addr](wxString error) { msg += GUI::format_wxstr("\n%1%: %2%", resolved_addr.back(), error); }
|
||||
, resolve_fn, resolved_addr.back())
|
||||
) {
|
||||
|
||||
error_fn(error_message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// There are multiple addresses - user needs to choose which to use.
|
||||
size_t selected_index = resolved_addr.size();
|
||||
IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
|
||||
if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
|
||||
return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr[selected_index]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#endif // WIN32
|
||||
}
|
||||
#ifdef WIN32
|
||||
bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const
|
||||
{
|
||||
resolve_fn(boost::nowide::widen(resolved_addr.to_string()));
|
||||
|
||||
// If test fails, test_msg_or_host_ip contains the error message.
|
||||
// Otherwise on Windows it contains the resolved IP address of the host.
|
||||
// Test_msg already contains resolved ip and will be cleared on start of test().
|
||||
wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string());
|
||||
if (!test_with_resolved_ip(test_msg_or_host_ip)) {
|
||||
error_fn(std::move(test_msg_or_host_ip));
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* name = get_name();
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string());
|
||||
bool result = true;
|
||||
|
||||
resolve_fn(boost::nowide::widen(url));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
|
||||
% name
|
||||
% upload_data.source_path
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||
|
||||
auto http = Http::post(url);//std::move(url));
|
||||
set_auth(http);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % error % status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
result = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
result = false;
|
||||
}
|
||||
})
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.perform_sync();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif //WIN32
|
||||
|
||||
bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
@ -182,7 +389,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||
// If test fails, test_msg_or_host_ip contains the error message.
|
||||
// Otherwise on Windows it contains the resolved IP address of the host.
|
||||
wxString test_msg_or_host_ip;
|
||||
if (! test(test_msg_or_host_ip)) {
|
||||
if (!test(test_msg_or_host_ip)) {
|
||||
error_fn(std::move(test_msg_or_host_ip));
|
||||
return false;
|
||||
}
|
||||
@ -207,6 +414,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||
// This new address returns in "test_msg_or_host_ip" variable.
|
||||
// Solves troubles of uploades failing with name address.
|
||||
// in original address (m_host) replace host for resolved ip
|
||||
resolve_fn(test_msg_or_host_ip);
|
||||
url = substitute_host(make_url("api/files/local"), GUI::into_u8(test_msg_or_host_ip));
|
||||
BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
|
||||
}
|
||||
@ -233,7 +441,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include "PrintHost.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
@ -25,7 +26,7 @@ public:
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return true; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
@ -35,6 +36,11 @@ public:
|
||||
|
||||
protected:
|
||||
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
|
||||
#ifdef WIN32
|
||||
virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const;
|
||||
virtual bool test_with_resolved_ip(wxString& curl_msg) const;
|
||||
#endif
|
||||
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const;
|
||||
|
||||
private:
|
||||
std::string m_host;
|
||||
|
@ -93,10 +93,12 @@ struct PrintHostJobQueue::priv
|
||||
void emit_progress(int progress);
|
||||
void emit_error(wxString error);
|
||||
void emit_cancel(size_t id);
|
||||
void emit_resolve(wxString host);
|
||||
void start_bg_thread();
|
||||
void stop_bg_thread();
|
||||
void bg_thread_main();
|
||||
void progress_fn(Http::Progress progress, bool &cancel);
|
||||
void error_fn(wxString error);
|
||||
void remove_source(const fs::path &path);
|
||||
void remove_source();
|
||||
void perform_job(PrintHostJob the_job);
|
||||
@ -131,6 +133,13 @@ void PrintHostJobQueue::priv::emit_cancel(size_t id)
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_resolve(wxString host)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_RESOLVE, queue_dialog->GetId(), job_id, host);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
|
||||
void PrintHostJobQueue::priv::start_bg_thread()
|
||||
{
|
||||
if (bg_thread.joinable()) { return; }
|
||||
@ -233,6 +242,36 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::error_fn(wxString error)
|
||||
{
|
||||
// check if transfer was not canceled before error occured - than do not show the error
|
||||
bool do_emit_err = true;
|
||||
if (channel_cancels.size_hint() > 0) {
|
||||
// Lock both queues
|
||||
auto cancels = channel_cancels.lock_rw();
|
||||
auto jobs = channel_jobs.lock_rw();
|
||||
|
||||
for (size_t cancel_id : *cancels) {
|
||||
if (cancel_id == job_id) {
|
||||
do_emit_err = false;
|
||||
emit_cancel(job_id);
|
||||
}
|
||||
else if (cancel_id > job_id) {
|
||||
const size_t idx = cancel_id - job_id - 1;
|
||||
if (idx < jobs->size()) {
|
||||
jobs->at(idx).cancelled = true;
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue: Job id %1% cancelled") % cancel_id;
|
||||
emit_cancel(cancel_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
cancels->clear();
|
||||
}
|
||||
if (do_emit_err)
|
||||
emit_error(std::move(error));
|
||||
}
|
||||
|
||||
|
||||
void PrintHostJobQueue::priv::remove_source(const fs::path &path)
|
||||
{
|
||||
if (! path.empty()) {
|
||||
@ -255,10 +294,9 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||
emit_progress(0); // Indicate the upload is starting
|
||||
|
||||
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||
[this](wxString error) {
|
||||
emit_error(std::move(error));
|
||||
}
|
||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||
[this](wxString error) { this->error_fn(std::move(error)); },
|
||||
[this](wxString host) { emit_resolve(std::move(host)); }
|
||||
);
|
||||
|
||||
if (success) {
|
||||
|
@ -43,13 +43,14 @@ public:
|
||||
|
||||
typedef Http::ProgressFn ProgressFn;
|
||||
typedef std::function<void(wxString /* error */)> ErrorFn;
|
||||
typedef ErrorFn ResolveFn;
|
||||
|
||||
virtual const char* get_name() const = 0;
|
||||
|
||||
virtual bool test(wxString &curl_msg) const = 0;
|
||||
virtual wxString get_test_ok_msg () const = 0;
|
||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const = 0;
|
||||
virtual bool has_auto_discovery() const = 0;
|
||||
virtual bool can_test() const = 0;
|
||||
virtual PrintHostPostUploadActions get_post_upload_actions() const = 0;
|
||||
|
@ -92,7 +92,7 @@ wxString Repetier::get_test_failed_msg (wxString &msg) const
|
||||
% _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
Loading…
Reference in New Issue
Block a user