Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
SSDPClient.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <iostream>
7#include <mutex>
8
11#include "Stroika/Foundation/Execution/CommandLine.h"
12#include "Stroika/Foundation/Execution/SignalHandlers.h"
13#include "Stroika/Foundation/Execution/TimeOutException.h"
14#include "Stroika/Foundation/Execution/WaitableEvent.h"
15#include "Stroika/Foundation/IO/Network/Transfer/Connection.h"
16
17#include "Stroika/Frameworks/UPnP/DeviceDescription.h"
20
21using namespace std;
22
23using namespace Stroika::Foundation;
25using namespace Stroika::Frameworks::UPnP;
26using namespace Stroika::Frameworks::UPnP::SSDP;
27
29using Client::Search;
31
32namespace {
33 mutex kStdOutMutex_; // If the listener impl uses multiple listen threads, prevent display from getting messed up
34}
35
36namespace {
37 // Ignore if fails
38 void DoPrintDeviceDescription_ (const URI& deviceDescriptionURL)
39 {
40 try {
41 using namespace IO::Network::Transfer;
42 Connection::Ptr c = Connection::New ();
43 Response r = c.GET (deviceDescriptionURL);
44 if (r.GetSucceeded ()) {
45 DeviceDescription deviceInfo = DeSerialize (r.GetData ());
46 cout << "\t\tDevice-Decsciption: " << Characters::ToString (deviceInfo).AsNarrowSDKString () << endl;
47 }
48 }
49 catch (...) {
50 DbgTrace ("failed to fetch description: {}"_f, Characters::ToString (current_exception ()));
51 }
52 }
53}
54
55namespace {
56 void DoListening_ (Listener* l)
57 {
58 cout << "Listening..." << endl;
59 l->AddOnFoundCallback ([] (const SSDP::Advertisement& d) {
60 lock_guard<mutex> critSection{kStdOutMutex_};
61 cout << "\tFound device (NOTIFY):" << endl;
62 cout << "\t\tUSN: " << d.fUSN.AsUTF8<string> () << endl;
63 if (d.fAlive.has_value ()) {
64 cout << "\t\tAlive: " << Characters::ToString (d.fAlive).AsUTF8<string> () << endl;
65 }
66 cout << "\t\tST: " << d.fTarget.AsUTF8<string> () << endl;
67 cout << "\t\tLocation: " << Characters::ToString (d.fLocation).AsUTF8<string> () << endl;
68 if (not d.fServer.empty ()) {
69 cout << "\t\tServer: " << d.fServer.AsUTF8<string> () << endl;
70 }
71 DoPrintDeviceDescription_ (d.fLocation);
72 cout << endl;
73 });
74 l->Start ();
75 }
76}
77
78namespace {
79 void DoSearching_ (Search* searcher, const String& searchFor)
80 {
81 cout << "Searching for '" << searchFor.AsUTF8<string> () << "'..." << endl;
82 searcher->AddOnFoundCallback ([] (const SSDP::Advertisement& d) {
83 lock_guard<mutex> critSection{kStdOutMutex_};
84 cout << "\tFound device (MATCHED SEARCH):" << endl;
85 cout << "\t\tUSN: " << d.fUSN.AsUTF8<string> () << endl;
86 cout << "\t\tLocation: " << Characters::ToString (d.fLocation).AsUTF8<string> () << endl;
87 cout << "\t\tST: " << d.fTarget.AsUTF8<string> () << endl;
88 if (not d.fServer.empty ()) {
89 cout << "\t\tServer: " << d.fServer.AsUTF8<string> () << endl;
90 }
91 DoPrintDeviceDescription_ (d.fLocation);
92 cout << endl;
93 });
94 searcher->Start (searchFor);
95 }
96}
97
98int main (int argc, const char* argv[])
99{
101 Stroika_Foundation_Debug_OptionalizeTraceArgs ("main", "argv={}"_f, Characters::ToString (vector<const char*>{argv, argv + argc}))};
102#if qStroika_Foundation_Common_Platform_POSIX
104#endif
105 bool listen = false;
106 optional<String> searchFor;
107 Time::DurationSeconds quitAfter = Time::kInfinity;
108
109 const Execution::CommandLine::Option kListenO_{
110 .fSingleCharName = 'l',
111 };
112 const Execution::CommandLine::Option kSearchO_{
113 .fSingleCharName = 's', .fSupportsArgument = true, .fHelpArgName = "SEARCHFOR"sv, .fHelpOptionText = "Search for the argument UPNP name"sv};
114 const Execution::CommandLine::Option kQuitAfterO_{.fLongName = "quit-after"sv, .fSupportsArgument = true, .fHelpArgName = "NSECONDS"sv};
115
116 Execution::CommandLine cmdLine{argc, argv};
117 listen = cmdLine.Has (kListenO_);
118 searchFor = cmdLine.GetArgument (kSearchO_);
119 if (auto o = cmdLine.GetArgument (kQuitAfterO_)) {
120 quitAfter = Time::DurationSeconds{Characters::FloatConversion::ToFloat<Time::DurationSeconds::rep> (*o)};
121 }
122
123 if (not listen and not searchFor.has_value ()) {
124 cerr << "Usage: SSDPClient [-l] [-s SEARCHFOR] [--quit-after N]" << endl;
125 cerr << " e.g. SSDPClient -l" << endl;
126 cerr << " e.g. SSDPClient -s \"upnp:rootdevice\"" << endl;
127 return EXIT_FAILURE;
128 }
129
130 try {
131 Listener l;
132 if (listen) {
133 DoListening_ (&l);
134 }
135 Search s;
136 if (searchFor.has_value ()) {
137 DoSearching_ (&s, *searchFor);
138 }
139 if (listen or searchFor.has_value ()) {
140 Execution::WaitableEvent{}.Wait (quitAfter); // wait quitAfter seconds, or til user hits ctrl-c
141 }
142 else {
143 cerr << "Specify -l to listen or -s STRING to search" << endl;
144 return EXIT_FAILURE;
145 }
146 }
147 catch (const Execution::TimeOutException&) {
148 cerr << "Timed out - so - exiting..." << endl;
149 return EXIT_SUCCESS;
150 }
151 catch (...) {
152 String exceptMsg = Characters::ToString (current_exception ());
153 cerr << "Exception - " << exceptMsg.AsNarrowSDKString () << " - terminating..." << endl;
154 return EXIT_FAILURE;
155 }
156
157 return EXIT_SUCCESS;
158}
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
#define DbgTrace
Definition Trace.h:309
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:270
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual string AsNarrowSDKString() const
Definition String.inl:834
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void Wait(Time::DurationSeconds timeout=Time::kInfinity)
STL namespace.