diff --git a/src/ssdp.h b/src/ssdp.h
index a53c8aa..9dff17a 100644
--- a/src/ssdp.h
+++ b/src/ssdp.h
@@ -63,6 +63,10 @@
# define LOCATION_PORT 8080
#endif
#define LOCATION_DESC "/description.xml"
+#define LOCATION_ICON "/icon.png"
+
+#define MIME_XML "text/xml"
+#define MIME_PNG "image/png"
#define SSDP_ST_ALL "ssdp:all"
@@ -108,10 +112,12 @@ extern int log_level;
extern int log_opts;
extern char uuid[42];
extern char url[128];
+extern char deviceType[64];
extern char fname[128];
extern char model[128];
extern char modelNumber[128];
extern char serialNumber[128];
+extern char iconFile[256];
extern char mfrurl[128];
extern char mfrnm[128];
extern int ttl;
diff --git a/src/ssdpd.c b/src/ssdpd.c
index bd33034..44001af 100644
--- a/src/ssdpd.c
+++ b/src/ssdpd.c
@@ -29,10 +29,12 @@ int ttl = MC_TTL_DEFAULT;
char *cachefn = NULL;
char *ver = NULL;
char *os = NULL;
+char deviceType[64] = "Basic";
char fname[128];
char model[128];
char modelNumber[128];
char serialNumber[128];
+char iconFile[256];
#ifdef MANUFACTURER_URL
char mfrurl[128] = MANUFACTURER_URL;
#else
@@ -561,28 +563,31 @@ static void signal_init(void)
static int usage(int code)
{
- printf("Usage: %s [-hnsvw] [-c FILE] [-D MODEL] [-N MODELN] [-S SERIAL] [-d URL] [-i SEC] [-l LEVEL] [-m NAME] [-M URL]\n"
+ printf("Usage: %s [-hnsvw] [-c FILE] [-D MODEL] [-N MODELN] [-S SERIAL] [-d URL] [-i SEC] [-I ICON] [-T DTYPE] [-l LEVEL] [-m NAME] [-M URL]\n"
" [-p URL] [-P FILE] [-r SEC] [-R NUM] [-t TTL] [-u UUID]\n"
" [IFACE [IFACE ...]]\n"
"\n"
- " -c FILE Path to alternate ssdpd.cache to store and/or read the UUID\n"
- " -d URL Override UPnP description.xml URL in announcements. The '%%s' in\n"
- " the URL is replaced with the IP, e.g. https://%%s:1901/main.xml\n"
+ " SSDP Params:\n"
+ " -T DTYPE Override deviceType in the default description.xml\n"
" -D MODEL Override modelName in the default description.xml\n"
" -N MODELN Override modelNumber in the default description.xml\n"
" -S SERIAL Override serialNumber in the default description.xml\n"
" -f FNAME Override friendlyName in the default description.xml\n"
- " -h This help text\n"
- " -i SEC SSDP notify interval (30-900), default %d sec\n"
- " -l LVL Set log level: none, err, notice (default), info, debug\n"
- " -m NAME Override manufacturer in the default description.xml\n"
+ " -m NAME Override manufacturer in the default description.xml\n"
" -M URL Override manufacturerURL in the default description.xml\n"
+ " -I ICON Icon file (only png file, required 128x128px)\n"
+ " -p URL Override presentationURL (WebUI) in the default description.xml\n"
+ " The '%%s' is replaced with the IP address. Default: http://%%s/\n"
+ " -c FILE Path to alternate ssdpd.cache to store and/or read the UUID\n"
+ " -d URL Override UPnP description.xml URL in announcements. The '%%s' in\n"
+ " the URL is replaced with the IP, e.g. https://%%s:1901/main.xml\n"
+ " -h This help text\n"
+ " -i SEC SSDP notify interval (30-900), default %d sec\n"
+ " -l LVL Set log level: none, err, notice (default), info, debug\n"
" -n Run in foreground, do not daemonize by default\n"
" -r SEC Interface refresh interval (5-1800), default %d sec\n"
" -R NUM Initial retries, using 10 sec refresh interval, default 3 times\n"
" Note: unused on systems with netlink interface monitoring.\n"
- " -p URL Override presentationURL (WebUI) in the default description.xml\n"
- " The '%%s' is replaced with the IP address. Default: http://%%s/\n"
" -P FILE Override PID file location, absolute path required\n"
" -s Use syslog, default unless running in foreground, -n\n"
" -t TTL TTL for multicast frames, default 2, according to the UDA\n"
@@ -614,7 +619,7 @@ int main(int argc, char *argv[])
int nlmon = 0;
int c;
- while ((c = getopt(argc, argv, "c:d:D:f:hi:l:m:M:np:P:r:R:st:u:vwN:S:")) != EOF) {
+ while ((c = getopt(argc, argv, "c:d:D:f:hi:l:m:M:np:P:r:R:st:u:vwN:S:I:T:")) != EOF) {
switch (c) {
case 'c':
cachefn = strdup(optarg);
@@ -627,13 +632,20 @@ int main(int argc, char *argv[])
case 'D':
strlcpy(model, optarg, sizeof(model));
break;
-
+
+ case 'T':
+ strlcpy(deviceType, optarg, sizeof(deviceType));
+ break;
case 'N':
- strlcpy(modelNumber, optarg, sizeof(model));
+ strlcpy(modelNumber, optarg, sizeof(modelNumber));
break;
case 'S':
- strlcpy(serialNumber, optarg, sizeof(model));
+ strlcpy(serialNumber, optarg, sizeof(serialNumber));
+ break;
+
+ case 'I':
+ strlcpy(iconFile, optarg, sizeof(iconFile));
break;
case 'f':
diff --git a/src/web.c b/src/web.c
index c11781b..cf8922b 100644
--- a/src/web.c
+++ b/src/web.c
@@ -46,7 +46,7 @@ const char *xml =
" 0\r\n"
" \r\n"
" \r\n"
- " urn:schemas-upnp-org:device:Basic:1\r\n"
+ " urn:schemas-upnp-org:device:%s:1\r\n"
" %s\r\n"
" %s\r\n%s"
" %s\r\n"
@@ -54,23 +54,16 @@ const char *xml =
" %s"
" %s\r\n"
" %s\r\n"
+ " "
+ " "
+ " image/png"
+ " 120"
+ " 120"
+ " 24"
+ " icon.png"
+ " "
+ " "
" \r\n"
- //""
- //""
- //"image/png"
- //"48"
- //"48"
- //"24"
- //"icon48.png"
- //""
- //""
- //"image/png"
- //"120"
- //"120"
- //"24"
- //"icon120.png"
- //""
- //""
"\r\n"
"\r\n";
@@ -142,6 +135,12 @@ static int validate_http(int sd, char *http)
return 0;
}
+static int errorNotFound(int sd){
+ if (write(sd, "HTTP/1.1 404 Not Found\r\n", 24) < 0)
+ logit(LOG_WARNING, "Failed returning status 404 to client: %s", strerror(errno));
+ return -1;
+}
+
static int respond(int sd, struct sockaddr_in *sin)
{
struct pollfd pfd = {
@@ -151,7 +150,7 @@ static int respond(int sd, struct sockaddr_in *sin)
const char *head = "HTTP/1.1 200 OK\r\n"
"Date: %s\r\n"
"Server: ssdp-responder/%s\r\n"
- "Content-Type: text/xml\r\n"
+ "Content-Type: %s\r\n"
"Connection: close\r\n"
"\r\n";
char manufacturer_url[192] = "";
@@ -187,7 +186,7 @@ static int respond(int sd, struct sockaddr_in *sin)
if (validate_http(sd, reqline[2]))
return -1;
- snprintf(mesg, sizeof(mesg), head, compose_time(), VERSION);
+ snprintf(mesg, sizeof(mesg), head, compose_time(), VERSION, MIME_XML);
if (send(sd, mesg, strlen(mesg), 0) < 0)
return -1;
} else if (strncmp(reqline[0], "GET", 4) == 0) {
@@ -195,11 +194,57 @@ static int respond(int sd, struct sockaddr_in *sin)
return -1;
/* XXX: Add support for icon as well */
- if (!reqline[1] || !strstr(reqline[1], LOCATION_DESC)) {
- if (write(sd, "HTTP/1.1 404 Not Found\r\n", 24) < 0)
- logit(LOG_WARNING, "Failed returning status 404 to client: %s", strerror(errno));
- return -1;
+ if (!reqline[1] || (!strstr(reqline[1], LOCATION_DESC) && !strstr(reqline[1], LOCATION_ICON))) {
+ return errorNotFound(sd);
+ }
+
+ if(strstr(reqline[1], LOCATION_ICON)){
+ if(!strlen(iconFile)){
+ return errorNotFound(sd);
+ }
+
+ FILE *fp = fopen(iconFile, "r"); //open file
+ if(fp)
+ {
+ logit(LOG_DEBUG, "Sending Icon reply ...");
+ //Calculate file size
+ int64_t _file_size = 0;
+ fseek(fp, 0, SEEK_END);
+ _file_size = ftello(fp);
+
+ //malloc buffer for image
+ uint8_t *buffer = (uint8_t*)malloc(_file_size + 1);
+ if(buffer == NULL){
+ logit(LOG_WARNING, "Failed malloc()");
+ return errorNotFound(sd);
+ }
+ fseek(fp, 0, SEEK_SET); //move to start file
+
+ //Read image file in buffer
+ if(!fread(buffer, _file_size, 1, fp)){
+ logit(LOG_DEBUG, "Error reading image file");
+ return errorNotFound(sd);
+ }
+
+ snprintf(mesg, sizeof(mesg), head, compose_time(), VERSION, MIME_PNG); //Generate head string
+ //Send HTTP Headers
+ if (send(sd, mesg, strlen(mesg), 0) < 0) {
+ logit(LOG_WARNING, "Failed sending file to client: %s", strerror(errno));
+ }
+ //Send Image buffer
+ if (send(sd, buffer, _file_size, 0) < 0) {
+ logit(LOG_WARNING, "Failed sending file to client: %s", strerror(errno));
+ }
+ fclose(fp); //Close file
+ free(buffer); //Free buffer
+ }
+ else{
+ logit(LOG_DEBUG, "Icon file not found ...");
+ return errorNotFound(sd);
+ }
+ return 0;
}
+
if(!strlen(fname))
gethostname(fname, sizeof(fname));
if (mfrurl[0])
@@ -207,8 +252,9 @@ static int respond(int sd, struct sockaddr_in *sin)
" %s\r\n", mfrurl);
logit(LOG_DEBUG, "Sending XML reply ...");
- rc = snprintf(mesg, sizeof(mesg), head, compose_time(), VERSION);
+ rc = snprintf(mesg, sizeof(mesg), head, compose_time(), VERSION, MIME_XML);
snprintf(&mesg[rc], sizeof(mesg) - rc, xml,
+ deviceType,
fname,
mfrnm,
manufacturer_url,