mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 06:05:56 -05:00
Finish (mostly) upnp discovery
This commit is contained in:
parent
0eba96d92a
commit
648297e253
@ -15,18 +15,27 @@
|
||||
<modelURL>http://www.fireflymediaserver.org/</modelURL>
|
||||
<serialNumber>SERIAL-001</serialNumber>
|
||||
<UDN>uuid:@upnp@</UDN>
|
||||
<iconList>
|
||||
<icon>
|
||||
<mimetype>image/png</mimetype>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
<depth>32</depth>
|
||||
<url>/upnp/ff_logo_32.png</url>
|
||||
</icon>
|
||||
</iconList>
|
||||
<serviceList>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
|
||||
<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
|
||||
<SCPDURL>/upnp/cm/scpdurl</SCPDURL>
|
||||
<SCPDURL>/upnp-cm.xml</SCPDURL>
|
||||
<controlURL>/upnp/cm/control</controlURL>
|
||||
<eventSubURL>/upnp/cm/event</eventSubURL>
|
||||
</service>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>
|
||||
<serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>
|
||||
<SCPDURL>/upnp/cd/scpdurl</SCPDURL>
|
||||
<SCPDURL>/upnp-cd.xml</SCPDURL>
|
||||
<controlURL>/upnp/cd/control</controlURL>
|
||||
<eventSubURL>/upnp/cd/event</eventSubURL>
|
||||
</service>
|
||||
|
178
admin-root/upnp-cd.xml
Normal file
178
admin-root/upnp-cd.xml
Normal file
@ -0,0 +1,178 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<actionList>
|
||||
<action>
|
||||
<name>Browse</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ObjectID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>BrowseFlag</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Filter</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>StartingIndex</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>RequestedCount</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>SortCriteria</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Result</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>NumberReturned</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>TotalMatches</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>UpdateID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>DestroyObject</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ObjectID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetSystemUpdateID</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>Id</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SystemUpdateID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetSearchCapabilities</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>SearchCaps</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SearchCapabilities</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetSortCapabilities</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>SortCaps</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SortCapabilities</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>UpdateObject</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ObjectID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>CurrentTagValue</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>NewTagValue</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
</actionList>
|
||||
<serviceStateTable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_BrowseFlag</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>BrowseMetadata</allowedValue>
|
||||
<allowedValue>BrowseDirectChildren</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SystemUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Count</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_SortCriteria</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>SortCapabilities</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Index</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ObjectID</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_UpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_TagValueList</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Result</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>SearchCapabilities</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Filter</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
</serviceStateTable>
|
||||
</scpd>
|
132
admin-root/upnp-cm.xml
Normal file
132
admin-root/upnp-cm.xml
Normal file
@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<actionList>
|
||||
<action>
|
||||
<name>GetCurrentConnectionInfo</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ConnectionID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>RcsID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>AVTransportID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>ProtocolInfo</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>PeerConnectionManager</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>PeerConnectionID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Direction</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Status</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetProtocolInfo</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>Source</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SourceProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Sink</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SinkProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetCurrentConnectionIDs</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ConnectionIDs</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
</actionList>
|
||||
<serviceStateTable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionStatus</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>OK</allowedValue>
|
||||
<allowedValue>ContentFormatMismatch</allowedValue>
|
||||
<allowedValue>InsufficientBandwidth</allowedValue>
|
||||
<allowedValue>UnreliableChannel</allowedValue>
|
||||
<allowedValue>Unknown</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_AVTransportID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_RcsID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionManager</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SourceProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SinkProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Direction</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>Input</allowedValue>
|
||||
<allowedValue>Output</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>CurrentConnectionIDs</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
</serviceStateTable>
|
||||
</scpd>
|
BIN
admin-root/upnp/ff_logo_16.png
Normal file
BIN
admin-root/upnp/ff_logo_16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 362 B |
BIN
admin-root/upnp/ff_logo_32.png
Normal file
BIN
admin-root/upnp/ff_logo_32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 606 B |
BIN
admin-root/upnp/ff_logo_48.png
Normal file
BIN
admin-root/upnp/ff_logo_48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 838 B |
12
src/conf.c
12
src/conf.c
@ -1226,6 +1226,7 @@ int conf_write(void) {
|
||||
FILE *fp;
|
||||
|
||||
if(!conf_main_file) {
|
||||
DPRINTF(E_DBG,L_CONF,"Config file apparently not loaded\n");
|
||||
return CONF_E_NOCONF;
|
||||
}
|
||||
|
||||
@ -1233,10 +1234,13 @@ int conf_write(void) {
|
||||
if((fp = fopen(conf_main_file,"w+")) != NULL) {
|
||||
retval = _conf_write(fp,conf_main,0,NULL);
|
||||
fclose(fp);
|
||||
} else {
|
||||
DPRINTF(E_LOG,L_CONF,"Error opening config file for write: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
util_mutex_unlock(l_conf);
|
||||
|
||||
return retval ? CONF_E_SUCCESS : CONF_E_NOTWRITABLE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1299,8 +1303,10 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
|
||||
}
|
||||
fprintf(fp,"\n");
|
||||
|
||||
if(!_conf_write(fp, pli->value.as_ll, 1, pli->key))
|
||||
return FALSE;
|
||||
if(!_conf_write(fp, pli->value.as_ll, 1, pli->key)) {
|
||||
DPRINTF(E_DBG,L_CONF,"Error writing key %s:%s\n",pli->key);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -531,7 +531,7 @@ int config_auth(WS_CONNINFO *pwsc, char *user, char *password) {
|
||||
}
|
||||
|
||||
void config_emit_upnp(WS_CONNINFO *pwsc, void *value, char *arg) {
|
||||
ws_writefd(pwsc,"%s",UPNP_UUID);
|
||||
ws_writefd(pwsc,"%s",upnp_uuid());
|
||||
}
|
||||
|
||||
|
||||
|
@ -589,10 +589,6 @@ int main(int argc, char *argv[]) {
|
||||
config.reload = 1; /* force a reload on start */
|
||||
|
||||
while(!config.stop) {
|
||||
#ifdef UPNP
|
||||
upnp_tick(); /* run the upnp loop */
|
||||
#endif
|
||||
|
||||
if((conf_get_int("general","rescan_interval",0) &&
|
||||
(rescan_counter > conf_get_int("general","rescan_interval",0)))) {
|
||||
if((conf_get_int("general","always_scan",0)) ||
|
||||
|
174
src/upnp.c
174
src/upnp.c
@ -40,6 +40,7 @@
|
||||
#include "upnp.h"
|
||||
|
||||
#define UPNP_MAX_PACKET 1500
|
||||
#define UPNP_CACHE_DURATION 1800
|
||||
#define UPNP_ADDR "239.255.255.250"
|
||||
#define UPNP_PORT 1900
|
||||
|
||||
@ -47,7 +48,9 @@
|
||||
#define UPNP_TYPE_BYEBYE 2
|
||||
#define UPNP_TYPE_RESPONSE 3
|
||||
|
||||
#define UPNP_SELECT_TIMEOUT 1 /* update loop for discover replies */
|
||||
#define UPNP_UUID "12345678-1234-1234-1234-123456789013"
|
||||
|
||||
#define UPNP_SELECT_TIMEOUT 1
|
||||
|
||||
typedef struct upnp_adinfo_t {
|
||||
int type; /** one of the UPNP_AD_ values */
|
||||
@ -67,16 +70,24 @@ typedef struct upnp_packetinfo_t {
|
||||
typedef struct upnp_disco_t {
|
||||
int seconds_remaining;
|
||||
char *query;
|
||||
struct sockaddr to;
|
||||
struct sockaddr_in to;
|
||||
struct upnp_disco_t *next;
|
||||
} UPNP_DISCO;
|
||||
|
||||
/* Globals */
|
||||
UPNP_PACKETINFO upnp_packetlist;
|
||||
OS_SOCKETTYPE upnp_socket;
|
||||
UPNP_DISCO upnp_disco;
|
||||
|
||||
pthread_t upnp_listener_tid;
|
||||
int upnp_quitflag = 0;
|
||||
int upnp_thread_started = 0;
|
||||
char *upnp_broadcast_types[] = {
|
||||
"none",
|
||||
"SSDP Alive",
|
||||
"SSDP Byebye",
|
||||
"SSDP Discovery Response"
|
||||
};
|
||||
|
||||
/* Forwards */
|
||||
int upnp_strcat(char *what, char *where, int bytes_left);
|
||||
@ -85,6 +96,15 @@ void upnp_build_packet(char *packet, int len, int type, UPNP_PACKETINFO *pi,
|
||||
void upnp_broadcast(int type, UPNP_DISCO *pdisco);
|
||||
void *upnp_listener(void *arg);
|
||||
void upnp_process_packet(void);
|
||||
void upnp_process_queries(void);
|
||||
|
||||
/**
|
||||
* return the current upnp uuid
|
||||
*/
|
||||
char *upnp_uuid(void) {
|
||||
return UPNP_UUID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add a upnp packet too the root list.
|
||||
@ -178,11 +198,11 @@ void upnp_build_packet(char *packet, int len, int type,
|
||||
|
||||
/* USN & NT */
|
||||
switch(pi->padinfo->type) {
|
||||
case UPNP_AD_BARE:
|
||||
snprintf(buffer,sizeof(buffer),"USN: uuid:%s\r\n",UPNP_UUID);
|
||||
case UPNP_AD_UUID:
|
||||
snprintf(buffer,sizeof(buffer),"USN: uuid:%s\r\n",upnp_uuid());
|
||||
len=upnp_strcat(buffer,packet,len);
|
||||
if(type != UPNP_TYPE_RESPONSE) { /* no NT for responses */
|
||||
snprintf(buffer,sizeof(buffer),"NT: uuid:%s\r\n",UPNP_UUID);
|
||||
snprintf(buffer,sizeof(buffer),"NT: uuid:%s\r\n",upnp_uuid());
|
||||
len=upnp_strcat(buffer,packet,len);
|
||||
}
|
||||
break;
|
||||
@ -190,7 +210,7 @@ void upnp_build_packet(char *packet, int len, int type,
|
||||
case UPNP_AD_DEVICE:
|
||||
case UPNP_AD_SERVICE:
|
||||
case UPNP_AD_ROOT:
|
||||
snprintf(buffer,sizeof(buffer),"USN: uuid:%s::%s:%s",UPNP_UUID,
|
||||
snprintf(buffer,sizeof(buffer),"USN: uuid:%s::%s:%s",upnp_uuid(),
|
||||
pi->padinfo->namespace, pi->padinfo->name);
|
||||
len=upnp_strcat(buffer,packet,len);
|
||||
if(pi->padinfo->version) {
|
||||
@ -214,7 +234,9 @@ void upnp_build_packet(char *packet, int len, int type,
|
||||
break;
|
||||
}
|
||||
|
||||
len=upnp_strcat("CACHE-CONTROL: max-age=1800\r\n",packet,len);
|
||||
snprintf(buffer,sizeof(buffer),"CACHE-CONTROL: max-age=%d\r\n",
|
||||
UPNP_CACHE_DURATION);
|
||||
len=upnp_strcat(buffer,packet,len);
|
||||
|
||||
if((pi->padinfo->body) && (type != UPNP_TYPE_RESPONSE)) {
|
||||
snprintf(buffer,sizeof(buffer),"Content-Length: %d\r\n\r\n",
|
||||
@ -234,41 +256,79 @@ void upnp_broadcast(int type, UPNP_DISCO *pdisco) {
|
||||
struct sockaddr_in sin;
|
||||
char packet[UPNP_MAX_PACKET];
|
||||
int pass;
|
||||
char passes;
|
||||
int send_packet;
|
||||
int elements=0;
|
||||
int recognized=0;
|
||||
char **argv = NULL;
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(UPNP_PORT);
|
||||
sin.sin_addr.s_addr = inet_addr(UPNP_ADDR);
|
||||
if(type == UPNP_TYPE_RESPONSE) {
|
||||
memcpy((void*)&sin,(void*)&pdisco->to,sizeof(struct sockaddr_in));
|
||||
} else {
|
||||
sin.sin_addr.s_addr = inet_addr(UPNP_ADDR);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(UPNP_PORT);
|
||||
}
|
||||
|
||||
util_mutex_lock(l_upnp);
|
||||
|
||||
DPRINTF(E_DBG,L_MISC,"Sending upnp broadcast of type: %d\n",type);
|
||||
DPRINTF(E_DBG,L_MISC,"Sending upnp broadcast of type: %d (%s)\n",type,
|
||||
upnp_broadcast_types[type]);
|
||||
|
||||
for(pass=0; pass < 2; pass++) {
|
||||
passes = 2;
|
||||
if(type == UPNP_TYPE_RESPONSE) {
|
||||
passes = 1;
|
||||
if(strcasecmp(pdisco->query,"ssdp:all")==0)
|
||||
passes = 3;
|
||||
|
||||
elements = util_split(pdisco->query,":",&argv);
|
||||
}
|
||||
|
||||
for(pass=0; pass < passes; pass++) {
|
||||
pi=upnp_packetlist.next;
|
||||
while(pi) {
|
||||
upnp_build_packet(packet, UPNP_MAX_PACKET, type, pi, pdisco);
|
||||
sendto(upnp_socket,packet,strlen(packet),0,
|
||||
(struct sockaddr *)&sin, sizeof(sin));
|
||||
send_packet = 1;
|
||||
if(type == UPNP_TYPE_RESPONSE) {
|
||||
send_packet = 0;
|
||||
/* if it's a request for rootdevice, only respond
|
||||
with root device. */
|
||||
if(strcasecmp(pdisco->query,"ssdp:all")==0) {
|
||||
send_packet = 1;
|
||||
} else if((strcasecmp(pdisco->query,"upnp:rootdevice")==0) &&
|
||||
(pi->padinfo->type == UPNP_AD_ROOT)) {
|
||||
send_packet = 1;
|
||||
}
|
||||
else if((strncasecmp(pdisco->query,"uuid:",5) == 0) &&
|
||||
(!strcasecmp((char*)&pdisco->query[5],upnp_uuid())) &&
|
||||
(pi->padinfo->type == UPNP_AD_UUID)) {
|
||||
send_packet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(send_packet) {
|
||||
usleep(100);
|
||||
recognized = 1;
|
||||
upnp_build_packet(packet, UPNP_MAX_PACKET, type, pi, pdisco);
|
||||
sendto(upnp_socket,packet,strlen(packet),0,
|
||||
(struct sockaddr *)&sin, sizeof(sin));
|
||||
}
|
||||
|
||||
pi = pi->next;
|
||||
}
|
||||
}
|
||||
|
||||
util_mutex_unlock(l_upnp);
|
||||
}
|
||||
|
||||
|
||||
int upnp_tick(void) {
|
||||
static time_t last_broadcast = 0;
|
||||
|
||||
if((time(NULL) - last_broadcast) > 60) {
|
||||
upnp_broadcast(UPNP_TYPE_ALIVE,NULL);
|
||||
last_broadcast = time(NULL);
|
||||
if(type == UPNP_TYPE_RESPONSE) {
|
||||
printf("%s respond to query %s\n", recognized ? "Did" : "Did not",
|
||||
pdisco->query);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
if(argv) {
|
||||
util_dispose_split(argv);
|
||||
}
|
||||
|
||||
util_mutex_unlock(l_upnp);
|
||||
}
|
||||
|
||||
/**
|
||||
* start UPnP broadcaster. We'll want to send at least a rootdevice
|
||||
@ -285,7 +345,9 @@ int upnp_init(void) {
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
memset(&upnp_packetlist,0,sizeof(upnp_packetlist));
|
||||
upnp_add_packet("base,", UPNP_AD_BARE, "/upnp-basic.xml",
|
||||
memset(&upnp_disco,0,sizeof(upnp_disco));
|
||||
|
||||
upnp_add_packet("base,", UPNP_AD_UUID, "/upnp-basic.xml",
|
||||
NULL, NULL, 0, NULL);
|
||||
upnp_add_packet("base,", UPNP_AD_DEVICE, "/upnp-basic.xml",
|
||||
"urn:schemas-upnp-org","MediaServer", 1, NULL);
|
||||
@ -345,11 +407,42 @@ int upnp_init(void) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* walk the query chain and see if there are any queries we need
|
||||
* to respond to.
|
||||
*/
|
||||
void upnp_process_queries(void) {
|
||||
UPNP_DISCO *plast, *pcurrent;
|
||||
|
||||
plast=&upnp_disco;
|
||||
pcurrent = upnp_disco.next;
|
||||
|
||||
/* don't need locks here since this ist he same thread as the listening
|
||||
* thread */
|
||||
|
||||
while(pcurrent) {
|
||||
if(pcurrent->seconds_remaining - UPNP_SELECT_TIMEOUT < 1) {
|
||||
/* do the broadcast, and cull it */
|
||||
upnp_broadcast(UPNP_TYPE_RESPONSE,pcurrent);
|
||||
pcurrent = pcurrent->next;
|
||||
free(plast->next->query);
|
||||
free(plast->next);
|
||||
plast->next = pcurrent;
|
||||
} else {
|
||||
pcurrent->seconds_remaining -= UPNP_SELECT_TIMEOUT;
|
||||
plast = pcurrent;
|
||||
pcurrent = pcurrent->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* listener thread for upnp query requests
|
||||
*/
|
||||
void *upnp_listener(void *arg) {
|
||||
int result;
|
||||
static time_t last_broadcast=0;
|
||||
fd_set readset;
|
||||
struct timeval timeout;
|
||||
|
||||
@ -379,7 +472,18 @@ void *upnp_listener(void *arg) {
|
||||
upnp_thread_started=0;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
upnp_process_packet();
|
||||
|
||||
if(FD_ISSET(upnp_socket,&readset)) {
|
||||
upnp_process_packet();
|
||||
}
|
||||
|
||||
if((time(NULL) - last_broadcast) > (UPNP_CACHE_DURATION/3)) {
|
||||
DPRINTF(E_DBG,L_MISC,"Time for alive message!\n");
|
||||
upnp_broadcast(UPNP_TYPE_ALIVE,NULL);
|
||||
last_broadcast = time(NULL);
|
||||
}
|
||||
|
||||
upnp_process_queries();
|
||||
}
|
||||
|
||||
upnp_thread_started=0;
|
||||
@ -464,19 +568,22 @@ void upnp_process_packet(void) {
|
||||
/* do we care? Let's check the query and see if it is interesting */
|
||||
discovery = 0;
|
||||
|
||||
|
||||
pdisco = (UPNP_DISCO *)malloc(sizeof(UPNP_DISCO));
|
||||
if(!pdisco)
|
||||
DPRINTF(E_FATAL,L_MISC,"malloc error");
|
||||
|
||||
memset(pdisco,0,sizeof(UPNP_DISCO));
|
||||
if(mx) {
|
||||
pdisco->seconds_remaining = 2;
|
||||
pdisco->seconds_remaining = (rand() / (RAND_MAX/mx)) + 1;
|
||||
DPRINTF(E_DBG,L_MISC,"Responding in %d (of %d) seconds\n",
|
||||
pdisco->seconds_remaining, mx);
|
||||
}
|
||||
|
||||
pdisco->query = query;
|
||||
query = NULL;
|
||||
memcpy((void*)&pdisco->to, (void*)&from, sizeof(struct sockaddr));
|
||||
memcpy((void*)&pdisco->to, (void*)&from,sizeof(struct sockaddr_in));
|
||||
pdisco->next = upnp_disco.next;
|
||||
upnp_disco.next = pdisco;
|
||||
}
|
||||
|
||||
if(query) {
|
||||
@ -510,6 +617,3 @@ int upnp_deinit(void) {
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -31,18 +31,16 @@
|
||||
#ifndef _UPNP_H_
|
||||
#define _UPNP_H_
|
||||
|
||||
#define UPNP_UUID "12345678-1234-1234-1234-123456789013"
|
||||
|
||||
#define UPNP_AD_DEVICE 1
|
||||
#define UPNP_AD_SERVICE 2
|
||||
#define UPNP_AD_ROOT 3
|
||||
#define UPNP_AD_BARE 4
|
||||
#define UPNP_AD_UUID 4
|
||||
|
||||
extern int upnp_init(void);
|
||||
extern int upnp_tick(void);
|
||||
extern int upnp_deinit(void);
|
||||
extern void upnp_add_packet(char *group_id, int type, char *location,
|
||||
char *namespace, char *name, int version,
|
||||
char *body);
|
||||
extern char *upnp_uuid();
|
||||
|
||||
#endif /* _UPNP_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user