123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- #include "api__ssdp.h"
- #include "SSDPAPI.h"
- #include "foundation/error.h"
- #include "jnetlib/jnetlib.h"
- #include "nx/nxsleep.h"
- #include "nswasabi/AutoCharNX.h"
- #include "nswasabi/ReferenceCounted.h"
- #include <time.h>
- #include <stdio.h>
- #ifdef __ANDROID__
- #include <android/log.h>
- #endif
- #ifdef _WIN32
- #define snprintf _snprintf
- #endif
- SSDPAPI::SSDPAPI()
- {
- listener=0;
- user_agent=0;
- }
- SSDPAPI::~SSDPAPI()
- {
- jnl_udp_release(listener);
- for (ServiceList::iterator itr=services.begin();itr!=services.end();itr++)
- {
- NXURIRelease(itr->location);
- NXStringRelease(itr->type);
- NXStringRelease(itr->usn);
- }
- for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
- {
- NXURIRelease(itr->location);
- NXStringRelease(itr->type);
- NXStringRelease(itr->usn);
- }
- }
- int SSDPAPI::Initialize()
- {
- user_agent=WASABI2_API_APP->GetUserAgent();
- NXThreadCreate(&ssdp_thread, SSDPThread, this);
- return NErr_Success;
- }
- int SSDPAPI::SSDP_RegisterService(nx_uri_t location, nx_string_t type, nx_string_t usn)
- {
- nu::AutoLock auto_lock(services_lock);
- Service service = {0};
- service.location = NXURIRetain(location);
- service.type = NXStringRetain(type);
- service.usn = NXStringRetain(usn);
- services.push_back(service);
- return NErr_Success;
- }
- int SSDPAPI::SSDP_RegisterCallback(cb_ssdp *callback)
- {
- if (!callback)
- {
- return NErr_BadParameter;
- }
- threadloop_node_t *node = thread_loop.GetAPC();
- if (!node)
- {
- return NErr_OutOfMemory;
- }
- node->func = APC_RegisterCallback;
- node->param1 = this;
- node->param2 = callback;
- callback->Retain();
- thread_loop.Schedule(node);
- return NErr_Success;
- }
- int SSDPAPI::SSDP_UnregisterCallback(cb_ssdp *callback)
- {
- if (!callback)
- {
- return NErr_BadParameter;
- }
- threadloop_node_t *node = thread_loop.GetAPC();
- if (!node)
- {
- return NErr_OutOfMemory;
- }
-
- node->func = APC_UnregisterCallback;
- node->param1 = this;
- node->param2 = callback;
- // since we don't actually know if the callback is registered until the other thread runs
- // we can't guarantee that we have a reference. so we'll add one!
- callback->Retain();
- thread_loop.Schedule(node);
- return NErr_Success;
- }
- int SSDPAPI::SSDP_Search(nx_string_t type)
- {
- if (!type)
- {
- return NErr_BadParameter;
- }
- threadloop_node_t *node = thread_loop.GetAPC();
- if (!node)
- {
- return NErr_OutOfMemory;
- }
-
- node->func = APC_Search;
- node->param1 = this;
- node->param2 = NXStringRetain(type);
- thread_loop.Schedule(node);
- return NErr_Success;
- }
- int SSDPAPI::FindUSN(ServiceList &service_list, const char *usn, Service *&service)
- {
- for (ServiceList::iterator itr = service_list.begin(); itr != service_list.end(); itr++)
- {
- if (!NXStringKeywordCompareWithCString(itr->usn, usn))
- {
- service = &(* itr);
- return NErr_Success;
- }
- }
- size_t num_services = service_list.size();
- Service new_service = {0};
- NXStringCreateWithUTF8(&new_service.usn, usn);
- service_list.push_back(new_service);
- service = &service_list.at(num_services);
- return NErr_Empty;
- }
- nx_thread_return_t SSDPAPI::SSDPThread(nx_thread_parameter_t parameter)
- {
- return ((SSDPAPI *)parameter)->Internal_Run();
- }
- void SSDPAPI::Internal_RegisterCallback(cb_ssdp *callback)
- {
- callbacks.push_back(callback);
- nu::AutoLock auto_lock(services_lock);
- for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
- {
- callback->OnServiceConnected(itr->location, itr->type, itr->usn);
- }
- }
- void SSDPAPI::Internal_UnregisterCallback(cb_ssdp *callback)
- {
- // TODO: verify that our list actually contains the callback?
- //callbacks.eraseObject(callback);
- auto it = std::find(callbacks.begin(), callbacks.end(), callback);
- if (it != callbacks.end())
- {
- callbacks.erase(it);
- }
- callback->Release(); // release the reference retained on the other thread
- callback->Release(); // release _our_ reference
- }
- nx_thread_return_t SSDPAPI::Internal_Run()
- {
- addrinfo *addr = 0;
- jnl_dns_resolve_now("239.255.255.250", 1900, &addr, SOCK_DGRAM);
- int ret = jnl_udp_create_multicast_listener(&listener, "239.255.255.250", 1900);
- if (ret != NErr_Success || !addr)
- return (nx_thread_return_t)ret;
- jnl_httpu_request_t httpu;
- ret = jnl_httpu_request_create(&httpu);
- if (ret != NErr_Success)
- {
- return (nx_thread_return_t)ret;
- }
- time_t last_notify = 0;
- for (;;)
- {
- time_t now = time(0);
- if ((now - last_notify) > 15)
- {
- Internal_Notify(0, addr->ai_addr, (socklen_t)addr->ai_addrlen);
- if (ret != NErr_Success)
- {
- // TODO: ?
- }
- last_notify = time(0);
- #if 0
- #if defined(_DEBUG) && defined(_WIN32)
- FILE *f = fopen("c:/services.txt", "wb");
- if (f)
- {
- for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
- {
-
- fprintf(f, "-----\r\n");
- fprintf(f, "ST: %s\r\n", AutoCharPrintfUTF8(itr->type));
- fprintf(f, "Location: %s\r\n", AutoCharPrintfUTF8(itr->location));
- fprintf(f, "USN: %s\r\n", AutoCharPrintfUTF8(itr->usn));
- }
- fclose(f);
- }
- #elif defined(__ANDROID__)
- for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
- {
-
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "-----\r\n");
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "ST: %s\r\n", AutoCharPrintfUTF8(itr->type));
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "Location: %s\r\n", AutoCharPrintfUTF8(itr->location));
- __android_log_print(ANDROID_LOG_INFO, "libreplicant", "USN: %s\r\n", AutoCharPrintfUTF8(itr->usn));
- }
- #endif
- #endif
- }
- thread_loop.Step(100);
- for (;;)
- {
- size_t bytes_received=0;
- jnl_udp_run(listener, 0, 8192, 0, &bytes_received);
- if (bytes_received)
- {
- jnl_httpu_request_process(httpu, listener);
- const char *method = jnl_httpu_request_get_method(httpu);
- if (method)
- {
- if (!strcmp(method, "NOTIFY"))
- {
- unsigned int max_age=0;
- const char *cache_control = jnl_httpu_request_get_header(httpu, "cache-control");
- if (cache_control)
- {
- if (strncasecmp(cache_control, "max-age=", 8) == 0)
- {
- max_age = strtoul(cache_control+8, 0, 10);
- }
- }
- const char *location = jnl_httpu_request_get_header(httpu, "location");
- const char *nt = jnl_httpu_request_get_header(httpu, "nt");
- const char *nts = jnl_httpu_request_get_header(httpu, "nts");
- const char *usn = jnl_httpu_request_get_header(httpu, "usn");
- // a hack to work with older android wifi sync protocol
- if (!usn)
- usn = jnl_httpu_request_get_header(httpu, "id");
- if (usn && nt && location && nts && !strcmp(nts, "ssdp:alive"))
- {
- Service *service = 0;
- int ret = FindUSN(found_services, usn, service);
- if (ret == NErr_Success) // found an existing one
- {
- // update the last seen time and expiration date
- // TODO: should we double check that the location didn't change?
- bool changed=false;
- ReferenceCountedNXString old_location;
-
- if (service->location)
- NXURIGetNXString(&old_location, service->location);
- if (!service->location || NXStringKeywordCompareWithCString(old_location, location))
- {
- NXURIRelease(service->location);
- NXURICreateWithUTF8(&service->location, location);
- changed=true;
- }
- service->last_seen = time(0);
- if (max_age)
- service->expiry = service->last_seen + max_age;
- else
- service->expiry = (time_t)-1;
- if (changed)
- {
- // notify clients
- for (CallbackList::iterator itr=callbacks.begin();itr!=callbacks.end();itr++)
- {
- (*itr)->OnServiceDisconnected(service->usn);
- (*itr)->OnServiceConnected(service->location, service->type, service->usn);
- }
- }
- }
- else if (ret == NErr_Empty) // new one
- {
- NXURICreateWithUTF8(&service->location, location);
- NXStringCreateWithUTF8(&service->type, nt);
- service->last_seen = time(0);
- if (max_age)
- service->expiry = service->last_seen + max_age;
- else
- service->expiry = (time_t)-1;
- // notify clients
- for (CallbackList::iterator itr=callbacks.begin();itr!=callbacks.end();itr++)
- {
- (*itr)->OnServiceConnected(service->location, service->type, service->usn);
- }
- }
- }
- }
- else if (!strcmp(method, "M-SEARCH"))
- {
- sockaddr *addr; socklen_t addr_length;
- jnl_udp_get_address(listener, &addr, &addr_length);
- const char *st = jnl_httpu_request_get_header(httpu, "st");
- Internal_Notify(st, addr, addr_length);
- }
- }
- }
- else
- {
- break;
- }
- }
- // check for expirations
- again:
- nu::AutoLock auto_lock(services_lock);
- for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
- {
- if (itr->expiry < now)
- {
- for (CallbackList::iterator itr2=callbacks.begin();itr2!=callbacks.end();itr2++)
- {
- (*itr2)->OnServiceDisconnected(itr->usn);
- }
-
- NXURIRelease(itr->location);
- NXStringRelease(itr->usn);
- NXStringRelease(itr->type);
- found_services.erase(itr);
- goto again;
- }
- }
- }
- return 0;
- }
- static char notify_request[] = "NOTIFY * HTTP/1.1\r\n";
- static char notify_host[] = "HOST: 239.255.255.250:1900\r\n";
- static char notify_cache_control[] = "CACHE-CONTROL:max-age=600\r\n";
- static char notify_nts[] = "NTS:ssdp:alive\r\n";
- static char search_request[] = "NOTIFY * HTTP/1.1\r\n";
- static char search_man[] = "MAN:\"ssdp:discover\"\r\n";
- ns_error_t SSDPAPI::Internal_Notify(const char *st, sockaddr *addr, socklen_t addr_length)
- {
- jnl_udp_set_peer_address(listener, addr, addr_length);
- nu::AutoLock auto_lock(services_lock);
- for (ServiceList::iterator itr=services.begin();itr!=services.end();itr++)
- {
- if (st && NXStringKeywordCompareWithCString(itr->type, st))
- continue;
- jnl_udp_send(listener, notify_request, strlen(notify_request));
- jnl_udp_send(listener, notify_host, strlen(notify_host));
- jnl_udp_send(listener, notify_cache_control, strlen(notify_cache_control));
- jnl_udp_send(listener, notify_nts, strlen(notify_nts));
- char header[512] = {0};
- snprintf(header, 511, "LOCATION:%s\r\n", AutoCharPrintfUTF8(itr->location));
- header[511]=0;
- jnl_udp_send(listener, header, strlen(header));
- if (itr->usn)
- {
- snprintf(header, 511, "USN:%s\r\n", AutoCharPrintfUTF8(itr->usn));
- header[511]=0;
- jnl_udp_send(listener, header, strlen(header));
- }
- if (itr->type)
- {
- snprintf(header, 511, "NT:%s\r\n", AutoCharPrintfUTF8(itr->type));
- header[511]=0;
- jnl_udp_send(listener, header, strlen(header));
- }
- if (user_agent)
- {
- snprintf(header, 511, "SERVER:%s\r\n",user_agent);
- header[511]=0;
- jnl_udp_send(listener, header, strlen(header));
- }
- jnl_udp_send(listener, "\r\n", 2);
- size_t bytes_sent=0;
- do
- {
- jnl_udp_run(listener, 8192, 0, &bytes_sent, 0); // TODO: error check
- if (bytes_sent == 0)
- NXSleep(100);
- } while (bytes_sent == 0);
- }
- return NErr_Success;
- }
- void SSDPAPI::Internal_Search(nx_string_t st)
- {
- addrinfo *addr;
- jnl_dns_resolve_now("239.255.255.250", 1900, &addr, SOCK_DGRAM);
- jnl_udp_set_peer_address(listener, addr->ai_addr, (socklen_t)addr->ai_addrlen);
- jnl_udp_send(listener, notify_request, strlen(notify_request));
- jnl_udp_send(listener, search_request, strlen(search_request));
- jnl_udp_send(listener, notify_host, strlen(notify_host));
- jnl_udp_send(listener, search_man, strlen(search_man));
- char header[512] = {0};
- sprintf(header, "ST:%s\r\n", AutoCharPrintfUTF8(st));
- header[511]=0;
- jnl_udp_send(listener, header, strlen(header));
- size_t bytes_sent=0;
- do
- {
- jnl_udp_run(listener, 8192, 0, &bytes_sent, 0); // TODO: error check
- if (bytes_sent == 0)
- NXSleep(100);
- } while (bytes_sent == 0);
- }
|