Arduino DLNA Server
Loading...
Searching...
No Matches
Schedule.h
Go to the documentation of this file.
1#pragma once
2
6
7#define MAX_TMP_SIZE 400
8#define ALIVE_MS 0
9#define MAX_AGE (60 * 60 * 24)
10#define MULTI_MSG_DELAY_MS 50
11
12namespace tiny_dlna {
13
18struct Schedule {
19 // int id = 0;
20 // scheduled next execution time
21 uint64_t time = 0;
22 // repeat every n ms
23 uint32_t repeat_ms = 0;
24 // repeat until;
25 uint64_t end_time = 0;
26 // schedle is active
27 bool active = false;
28 // optional address associated with the schedule (e.g. requester)
30 // enables logging the address when the schedule is queued
31 bool report_ip = false;
32
33 virtual bool process(IUDPService& udp) { return false; }
34
35 virtual const char* name() { return "n/a"; };
36
37 virtual bool isValid() { return true; }
38
39 operator bool() { return active; }
40};
41
46class MSearchSchedule : public Schedule {
47 public:
48 MSearchSchedule(IPAddressAndPort addr, const char* searchTarget, int mx = 3) {
49 this->address = addr;
50 search_target = searchTarget;
51 max_age = mx;
52 }
53 const char* name() override { return "MSearch"; }
54
55 bool process(IUDPService& udp) override {
56 // we keep the data on the stack
57 DlnaLogger.log(DlnaLogLevel::Debug, "Sending %s for %s to %s", name(),
59
60 char buffer[MAX_TMP_SIZE] = {0};
61 const char* tmp =
62 "M-SEARCH * HTTP/1.1\r\n"
63 "HOST: %s\r\n"
64 "MAN: \"ssdp:discover\"\r\n"
65 "MX: %d\r\n"
66 "ST: %s\r\n\r\n";
67 int n = snprintf(buffer, MAX_TMP_SIZE, tmp, address.toString(), max_age,
69 assert(n < MAX_TMP_SIZE);
70 DlnaLogger.log(DlnaLogLevel::Debug, "sending: %s", buffer);
71 udp.send(address, (uint8_t*)buffer, n);
72 return true;
73 }
74
75 protected:
76 int max_age = 3;
77 const char* search_target = nullptr;
78};
79
85 public:
87 this->address = addr;
88 this->report_ip = true;
89 p_device = &device;
90 }
91 const char* name() override { return "MSearchReply"; }
92
93 bool process(IUDPService& udp) override {
94 // we keep the data on the stack
95 DlnaLogger.log(DlnaLogLevel::Info, "Sending %s for %s to %s", name(),
97
98 DLNADeviceInfo& device = *p_device;
99 const char* udn = device.getUDN();
100 int delay_ms = MULTI_MSG_DELAY_MS;
101
102 sendReply(udp, device, "upnp:rootdevice", udn);
103 delay(delay_ms);
104 sendReply(udp, device, p_device->getDeviceType(), udn);
105 // announce with service udn
106 for (auto& service : device.getServices()) {
107 delay(delay_ms);
108 sendReply(udp, device, service.service_type, udn);
109 }
110 return true;
111 }
112
113 bool isValid() override {
114 // check if the search target is valid for this device
116 return false;
117 }
118 return isValidIP();
119 }
120
123 int mx = 0;
124
125 protected:
127
129 bool isValidIP() {
130 IPAddress netmask = DLNA_DISCOVERY_NETMASK;
131 IPAddress localIP = p_device->getIPAddress();
132 IPAddress peerIP = address.address;
133 Str netmask_str = netmask.toString().c_str();
134 Str localIP_str = localIP.toString().c_str();
135
136 // Apply netmask to both local and peer IP addresses
137 for (int i = 0; i < 4; i++) {
138 if ((localIP[i] & netmask[i]) != (peerIP[i] & netmask[i])) {
139 DlnaLogger.log(DlnaLogLevel::Info,
140 "Discovery request from %s filtered (not in same subnet "
141 "as %s with mask %s)",
142 address.toString(), localIP_str.c_str(),
143 netmask_str.c_str());
144 return false;
145 }
146 }
147 return true;
148 }
149
150 // check requested search target
152 if (StrView(search_target).equals("ssdp:all")) {
153 return true;
154 }
155 if (StrView(search_target).equals("upnp:rootdevice")) {
156 return true;
157 }
158 if (StrView(search_target).equals(device.getDeviceType())) {
159 return true;
160 }
161 // accept discovery by UDN (uuid:...), used by some control points
162 // (e.g. BubbleUPnP, some Windows components)
163 if (StrView(search_target).equals(device.getUDN())) {
164 return true;
165 }
166 // check services
167 for (auto& service : device.getServices()) {
168 if (StrView(search_target).equals(service.service_type)) {
169 return true;
170 }
171 }
172 DlnaLogger.log(DlnaLogLevel::Info, "Ignoring M-SEARCH for %s",
174 return false;
175 }
176
177 bool sendReply(IUDPService& udp, DLNADeviceInfo& device, const char* target,
178 const char* udn) {
179 // construct usn
180 char usn_str[200];
181 snprintf(usn_str, 200, "%s::%s", device.getUDN(), target);
182 // construct message
183 char buffer[MAX_TMP_SIZE] = {0};
184 const char* tmp =
185 "HTTP/1.1 200 OK\r\n"
186 "CACHE-CONTROL: max-age=%d\r\n"
187 "EXT:\r\n"
188 "LOCATION: %s\r\n"
189 "SERVER: Arduino-DLNA/1.0 UPnP/1.1 DLNA/1.5\r\n"
190 "ST: %s\r\n"
191 "USN: %s\r\n"
192 "CONTENT-LENGTH: 0\r\n\r\n";
193 int n = snprintf(buffer, MAX_TMP_SIZE, tmp, max_age,
194 device.getDeviceURL().url(), target, usn_str);
195 assert(n < MAX_TMP_SIZE);
196 DlnaLogger.log(DlnaLogLevel::Info, "- %s: %s", name(), target);
197 DlnaLogger.log(DlnaLogLevel::Debug, "%s", buffer);
198 if (!udp.send(address, (uint8_t*)buffer, n)) {
199 DlnaLogger.log(DlnaLogLevel::Warning, "Failed to send MSearchReply to %s",
200 address.toString());
201 return false;
202 }
203 return true;
204 }
205};
206
212class MSearchReplyCP : public Schedule {
213 public:
214 const char* name() override { return "MSearchReplyCP"; }
218
219 bool process(IUDPService& udp) override {
220 DlnaLogger.log(DlnaLogLevel::Debug, "-> %s not processed",
222 return true;
223 }
224};
225
254 public:
255 const char* name() override { return "NotifyReplyCP"; }
256 Str nts{80};
262
263 // callback invoked by the scheduler when processing this notification.
264 // Return true if the notification was handled; false otherwise.
265 std::function<bool(NotifyReplyCP& ref)> callback;
266
267 bool process(IUDPService& udp) override {
268 if (callback(*this)) {
269 DlnaLogger.log(DlnaLogLevel::Debug, "%s -> %s", name(), nts.c_str());
270 return true;
271 }
272
273 DlnaLogger.log(DlnaLogLevel::Debug, "-> %s not processed", nts.c_str());
274 return true;
275 }
276};
277
283 public:
284 PostAliveSchedule(DLNADeviceInfo& device, uint32_t repeatMs) {
285 p_device = &device;
286 this->repeat_ms = repeatMs;
287 }
288 const char* name() override { return "PostAlive"; }
289
290 void setRepeatMs(uint32_t ms) { this->repeat_ms = ms; }
291
292 bool process(IUDPService& udp) override {
293 DlnaLogger.log(DlnaLogLevel::Debug, "Sending %s to %s", name(),
294 DLNABroadcastAddress.toString());
295 DLNADeviceInfo& device = *p_device;
296 char nt[100];
297
298 const char* device_url = device.getDeviceURL().url();
299 const char* udn = device.getUDN();
300 int max_age = repeat_ms / 1000 + 10;
301 int delay_ms = MULTI_MSG_DELAY_MS;
302
303 // announce with udn
304 sendData(udn, udn, device_url, max_age, udp);
305 // NT: upnp:rootdevice\r\n
306 sendData("upnp:rootdevice", udn, device_url, max_age, udp);
307 delay(delay_ms);
308 // NT: urn:schemas-upnp-org:device:MediaRenderer:1\r\n
309 sendData(device.getDeviceType(), udn, device_url, max_age, udp);
310
311 // announce with service udn
312 for (auto& service : device.getServices()) {
313 delay(delay_ms);
314 sendData(service.service_type, udn, device_url, max_age, udp);
315 }
316 return true;
317 }
318
319 protected:
321
322 bool sendData(const char* nt, const char* udn, const char* device_url,
323 int max_age, IUDPService& udp) {
324 char usn[200];
325 if (StrView(nt).equals(udn)) {
326 strcpy(usn, nt);
327 } else {
328 snprintf(usn, 200, "%s::%s", udn, nt);
329 }
330
331 // we keep the data on the stack
332 char buffer[MAX_TMP_SIZE] = {0};
333 const char* tmp =
334 "NOTIFY * HTTP/1.1\r\n"
335 "HOST:%s\r\n"
336 "CACHE-CONTROL: max-age=%d\r\n"
337 "LOCATION: %s\r\n"
338 "NT: %s\r\n"
339 "NTS: ssdp:alive\r\n"
340 "USN: %s\r\n\r\n";
341 int n = snprintf(buffer, MAX_TMP_SIZE, tmp, DLNABroadcastAddress.toString(),
342 max_age, device_url, nt, usn);
343
344 assert(n < MAX_TMP_SIZE);
345 DlnaLogger.log(DlnaLogLevel::Info, "sending: ssdp:alive %s", nt);
346 DlnaLogger.log(DlnaLogLevel::Debug, "%s", buffer);
347 udp.send(DLNABroadcastAddress, (uint8_t*)buffer, n);
348 return true;
349 }
350};
351
356class PostByeSchedule : public Schedule {
357 public:
359 const char* name() override { return "ByeBye"; }
360 bool process(IUDPService& udp) override {
361 DlnaLogger.log(DlnaLogLevel::Debug, "Sending %s to %s", name(),
362 DLNABroadcastAddress.toString());
363 // we keep the data on the stack
364 char buffer[MAX_TMP_SIZE] = {0};
365 const char* tmp =
366 "NOTIFY * HTTP/1.1\r\n"
367 "HOST: %s\r\n"
368 "CACHE-CONTROL: max-age = %d\r\n"
369 "LOCATION: *\r\n"
370 "NT: %s\r\n"
371 "NTS: ssdp:byebye\r\n"
372 "USN: %s\r\n\r\n";
373 int n = snprintf(buffer, MAX_TMP_SIZE, tmp, DLNABroadcastAddress.toString(),
375
376 DlnaLogger.log(DlnaLogLevel::Debug, "sending: %s", buffer);
377 udp.send(DLNABroadcastAddress, (uint8_t*)buffer, n);
378 return true;
379 }
380
381 protected:
382 int max_age = 1800;
384};
385
390class PostSubscribe : public Schedule {
391 public:
392 PostSubscribe(IPAddressAndPort addr, const char* path, uint32_t sec) {
393 setDestination(addr, path);
394 setDuration(sec);
395 }
396 const char* name() override { return "Subscribe"; }
397
398 bool process(IUDPService& udp) override {
400 DlnaLogger.log(DlnaLogLevel::Debug, "Sending Subscribe to %s",
401 address.toString());
402
403 char buffer[MAX_TMP_SIZE] = {0};
404 const char* tmp =
405 "SUBSCRIBE %s HTTP/1.1\r\n"
406 "HOST: %s\r\n"
407 "CALLBACK: %s"
408 "NT: upnp-event\r\n"
409 "TIMEOUT: Second-%d\r\n\r\n";
410 int n = snprintf(buffer, MAX_TMP_SIZE, tmp, path, address.toString(),
412 assert(n < MAX_TMP_SIZE);
413 DlnaLogger.log(DlnaLogLevel::Debug, "sending: %s", buffer);
414 udp.send(address, (uint8_t*)buffer, n);
415 return true;
416 }
417
418 protected:
419 const char* path;
420 int durationSec = 0;
421
422 void setDestination(IPAddressAndPort addr, const char* path) {
423 this->address = addr;
424 this->path = path;
425 }
426
427 void setDuration(uint32_t sec) { durationSec = sec; }
428};
429
435 public:
436 CallbackSchedule(std::function<bool(void* ref)> cb, void* ref) {
437 callback = cb;
438 reference = ref;
439 }
440 const char* name() override { return "Callback"; }
441 void* reference = nullptr;
442
443 bool process(IUDPService& udp) override {
444 if (callback) {
445 return callback(reference);
446 }
447 return false;
448 }
449
450 protected:
451 std::function<bool(void* ref)> callback;
452};
453
454} // namespace tiny_dlna
#define MAX_TMP_SIZE
Definition: Schedule.h:7
#define MAX_AGE
Definition: Schedule.h:9
#define MULTI_MSG_DELAY_MS
Definition: Schedule.h:10
Generic Schedule that invokes a callback function.
Definition: Schedule.h:434
CallbackSchedule(std::function< bool(void *ref)> cb, void *ref)
Definition: Schedule.h:436
void * reference
Definition: Schedule.h:441
std::function< bool(void *ref)> callback
Definition: Schedule.h:451
const char * name() override
Definition: Schedule.h:440
bool process(IUDPService &udp) override
Definition: Schedule.h:443
Device Attributes and generation of XML using urn:schemas-upnp-org:device-1-0. We could just return a...
Definition: DLNADeviceInfo.h:28
Vector< DLNAServiceInfo > & getServices()
Provides all service definitions.
Definition: DLNADeviceInfo.h:212
const char * getUDN()
Provide the udn uuid.
Definition: DLNADeviceInfo.h:92
Url & getDeviceURL()
This method returns base url/device.xml.
Definition: DLNADeviceInfo.h:133
IPAddress getIPAddress()
Provides the local IP address.
Definition: DLNADeviceInfo.h:148
const char * getDeviceType()
Definition: DLNADeviceInfo.h:86
Abstract Interface for UDP API.
Definition: IUDPService.h:33
virtual bool send(uint8_t *data, int len)=0
Send data to the default destination.
Processing at control point to handle a MSearchReply from the device.
Definition: Schedule.h:212
Str usn
Definition: Schedule.h:216
const char * name() override
Definition: Schedule.h:214
Str location
Definition: Schedule.h:215
Str search_target
Definition: Schedule.h:217
bool process(IUDPService &udp) override
Definition: Schedule.h:219
Answer from device to MSearch request by sending the related replies.
Definition: Schedule.h:84
Str search_target
Definition: Schedule.h:121
bool isValid() override
Definition: Schedule.h:113
int mx
Definition: Schedule.h:123
bool process(IUDPService &udp) override
Definition: Schedule.h:93
bool isValidIP()
check netmask
Definition: Schedule.h:129
MSearchReplySchedule(DLNADeviceInfo &device, IPAddressAndPort addr)
Definition: Schedule.h:86
DLNADeviceInfo * p_device
Definition: Schedule.h:122
const char * name() override
Definition: Schedule.h:91
bool isValidSearchTarget(DLNADeviceInfo &device, const char *search_target)
Definition: Schedule.h:151
bool sendReply(IUDPService &udp, DLNADeviceInfo &device, const char *target, const char *udn)
Definition: Schedule.h:177
int max_age
Definition: Schedule.h:126
Send MSearch request.
Definition: Schedule.h:46
const char * search_target
Definition: Schedule.h:77
bool process(IUDPService &udp) override
Definition: Schedule.h:55
const char * name() override
Definition: Schedule.h:53
MSearchSchedule(IPAddressAndPort addr, const char *searchTarget, int mx=3)
Definition: Schedule.h:48
int max_age
Definition: Schedule.h:76
Represents a notification/notify reply scheduled for control-point processing.
Definition: Schedule.h:253
std::function< bool(NotifyReplyCP &ref)> callback
Definition: Schedule.h:265
Str delivery_host_and_port
Definition: Schedule.h:257
Str xml
Definition: Schedule.h:261
Str event_key
Definition: Schedule.h:260
Str subscription_id
Definition: Schedule.h:259
Str delivery_path
Definition: Schedule.h:258
const char * name() override
Definition: Schedule.h:255
bool process(IUDPService &udp) override
Definition: Schedule.h:267
Str nts
Definition: Schedule.h:256
Send out PostAlive messages: Repeated every 5 seconds.
Definition: Schedule.h:282
bool sendData(const char *nt, const char *udn, const char *device_url, int max_age, IUDPService &udp)
Definition: Schedule.h:322
DLNADeviceInfo * p_device
Definition: Schedule.h:320
PostAliveSchedule(DLNADeviceInfo &device, uint32_t repeatMs)
Definition: Schedule.h:284
const char * name() override
Definition: Schedule.h:288
bool process(IUDPService &udp) override
Definition: Schedule.h:292
void setRepeatMs(uint32_t ms)
Definition: Schedule.h:290
Send out ByeBye message.
Definition: Schedule.h:356
DLNADeviceInfo * p_device
Definition: Schedule.h:383
PostByeSchedule(DLNADeviceInfo &device)
Definition: Schedule.h:358
bool process(IUDPService &udp) override
Definition: Schedule.h:360
int max_age
Definition: Schedule.h:382
const char * name() override
Definition: Schedule.h:359
Send SUBSCRIBE message via UDP unicast.
Definition: Schedule.h:390
const char * name() override
Definition: Schedule.h:396
void setDuration(uint32_t sec)
Definition: Schedule.h:427
void setDestination(IPAddressAndPort addr, const char *path)
Definition: Schedule.h:422
PostSubscribe(IPAddressAndPort addr, const char *path, uint32_t sec)
Definition: Schedule.h:392
const char * path
Definition: Schedule.h:419
int durationSec
Definition: Schedule.h:420
bool process(IUDPService &udp) override
Definition: Schedule.h:398
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:18
Heap-backed string utility used throughout tiny_dlna.
Definition: Str.h:27
const char * c_str() const
C-string pointer to internal buffer.
Definition: Str.h:88
const char * url()
Definition: Url.h:39
#define DLNA_DISCOVERY_NETMASK
Define the netmask for discovery filtering.
Definition: dlna_config.h:203
Definition: Allocator.h:13
IP Adress including Port information.
Definition: IPAddressAndPort.h:20
const char * toString()
Definition: IPAddressAndPort.h:26
IPAddress address
Definition: IPAddressAndPort.h:23
An individual Schedule (to send out UDP messages)
Definition: Schedule.h:18
uint64_t end_time
Definition: Schedule.h:25
IPAddressAndPort address
Definition: Schedule.h:29
bool active
Definition: Schedule.h:27
bool report_ip
Definition: Schedule.h:31
virtual const char * name()
Definition: Schedule.h:35
virtual bool isValid()
Definition: Schedule.h:37
virtual bool process(IUDPService &udp)
Definition: Schedule.h:33
uint64_t time
Definition: Schedule.h:21
uint32_t repeat_ms
Definition: Schedule.h:23