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