From d53b39a2807fb410f03f0caa888464377130c2fe Mon Sep 17 00:00:00 2001
From: Omar Kallel <omar.kallel@pivasoftware.com>
Date: Fri, 24 Jan 2020 14:38:17 +0100
Subject: [PATCH] miniupnpc: periodic desvoery, download description files,
 corresponding ubus

---
 net/miniupnpc/Makefile                        |  13 +-
 net/miniupnpc/files/periodic_ssdp.init        |  23 +
 .../patches/400-periodic_discovery_ubus.patch | 486 ++++++++++++++++++
 3 files changed, 521 insertions(+), 1 deletion(-)
 create mode 100644 net/miniupnpc/files/periodic_ssdp.init
 create mode 100644 net/miniupnpc/patches/400-periodic_discovery_ubus.patch

diff --git a/net/miniupnpc/Makefile b/net/miniupnpc/Makefile
index a458b29486..029eab271f 100644
--- a/net/miniupnpc/Makefile
+++ b/net/miniupnpc/Makefile
@@ -36,7 +36,7 @@ define Package/miniupnpc
   CATEGORY:=Network
   SUBMENU:=Firewall
   TITLE+= client
-  DEPENDS:=+libminiupnpc
+  DEPENDS:=+libminiupnpc +libubox +libubus +libjson-c +libblobmsg-json
 endef
 
 define Package/libminiupnpc
@@ -51,6 +51,13 @@ CMAKE_OPTIONS += -DUPNPC_BUILD_TESTS=OFF
 TARGET_CFLAGS += $(FPIC)
 TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
 
+USE_LOCAL=$(shell ls ./src/ 2>/dev/null >/dev/null && echo 1)
+ifneq ($(USE_LOCAL),)
+define Build/Prepare
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+endif
+
 define Build/InstallDev
 	$(INSTALL_DIR) $(1)/usr/include/miniupnpc
 	$(CP) \
@@ -63,9 +70,13 @@ define Build/InstallDev
 endef
 
 define Package/miniupnpc/install
+	echo "Helllo install"
 	$(INSTALL_DIR) $(1)/usr/bin
 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/upnpc-shared $(1)/usr/bin/upnpc
 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/listdevices $(1)/usr/bin/
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/periodic_ssdp $(1)/usr/bin/	
+	$(INSTALL_DIR) $(1)/etc/init.d  
+	$(INSTALL_BIN) ./files/periodic_ssdp.init $(1)/etc/init.d/periodic_ssdp
 endef
 
 define Package/libminiupnpc/install
diff --git a/net/miniupnpc/files/periodic_ssdp.init b/net/miniupnpc/files/periodic_ssdp.init
new file mode 100644
index 0000000000..a2b80f5af7
--- /dev/null
+++ b/net/miniupnpc/files/periodic_ssdp.init
@@ -0,0 +1,23 @@
+#!/bin/sh /etc/rc.common
+
+START=21
+STOP=89
+
+USE_PROCD=1
+NAME=periodic_ssdp
+
+start_service() {
+	procd_open_instance
+	procd_set_param command "/usr/bin/periodic_ssdp"
+	procd_set_param respawn
+	procd_close_instance
+}
+
+stop() {
+	service_stop /usr/bin/periodic_ssdp
+}
+
+reload_service() {
+	stop
+	start
+}
\ No newline at end of file
diff --git a/net/miniupnpc/patches/400-periodic_discovery_ubus.patch b/net/miniupnpc/patches/400-periodic_discovery_ubus.patch
new file mode 100644
index 0000000000..8420ca5671
--- /dev/null
+++ b/net/miniupnpc/patches/400-periodic_discovery_ubus.patch
@@ -0,0 +1,486 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -147,6 +147,9 @@
+     add_executable (listdevices listdevices.c)
+     target_link_libraries (listdevices PRIVATE libminiupnpc-shared)
+     target_include_directories(listdevices PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
++
++    add_executable (periodic_ssdp periodic_ssdp.c upnp_ubus.c)
++    target_link_libraries (periodic_ssdp PRIVATE libminiupnpc-shared ubox ubus json-c blobmsg_json)
+   endif ()
+ endif ()
+ 
+--- a/periodic_ssdp.c
++++ b/periodic_ssdp.c
+@@ -0,0 +1,95 @@
++/*
++*	This program is free software: you can redistribute it and/or modify
++*	it under the terms of the GNU General Public License as published by
++*	the Free Software Foundation, either version 2 of the License, or
++*	(at your option) any later version.
++*
++*	Copyright (C) 2019 iopsys Software Solutions AB
++*		Author: Omar Kallel <omar.kallel@pivasoftware.com>
++*/
++
++#include <stdio.h>
++#include "upnp_ubus.h"
++#include "miniupnpc.h"
++#include <libubox/uloop.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++
++LIST_HEAD(descriptions_list);
++
++static void ssdp_discover_devices(struct uloop_timeout *timeout);
++
++static struct uloop_timeout ssdp_timer = { .cb = ssdp_discover_devices };
++
++static void discover_upnp_connected_devices(){
++	char **urlparams, *desc_name = NULL, *filepath = NULL, *download_cmd = NULL;
++	struct UPNPDev *dev;
++	int error;
++	size_t length;
++	int is_device_desc;
++
++	/*
++	 * Discover devices
++	 */
++	devlist = upnpDiscoverAll(2000, NULL, NULL, 0, 0, 2, &error);
++
++	/*
++	 * Download description files
++	 */
++	check_create_dir("/etc/upnpc");
++	check_create_dir("/etc/upnpc/descriptions");
++
++	if(devlist) {
++		for(dev = devlist; dev != NULL; dev = dev->pNext) {
++			urlparams= strsplit(strstr(dev->descURL, "://")?strstr(dev->descURL, "://")+3:dev->descURL, "/", &length);
++			asprintf(&desc_name, "%s_%s", urlparams[0], urlparams[length-1]);
++			asprintf(&filepath, "/etc/upnpc/descriptions/%s", desc_name);
++			asprintf(&download_cmd, "wget -q -O %s %s", filepath, dev->descURL);
++			if(!is_desc_elt_exit_in_list(dev->descURL)) {
++				if(strstr(dev->st, ":rootdevice") != NULL || strstr(dev->usn, ":device:") != NULL)
++					is_device_desc = 1;
++				else
++					is_device_desc = 0;
++				add_desc_to_descriptions_list(filepath, dev->descURL, is_device_desc);
++			}
++			if (access(filepath, F_OK)) {
++				system(download_cmd);
++			}
++
++			FREE(desc_name);
++			FREE(filepath);
++			FREE(download_cmd);
++		}
++	}
++	uloop_timeout_set(&ssdp_timer, 10000);
++}
++
++static void ssdp_discover_devices(struct uloop_timeout *timeout) {
++	discover_upnp_connected_devices();
++}
++
++int main() {
++	struct ubus_context *ctx = NULL;
++	const char *ubus_socket = NULL;
++	int ret;
++
++	uloop_init();
++	discover_upnp_connected_devices();
++
++	ctx = ubus_connect(ubus_socket);
++	if (!ctx) {
++		fprintf(stderr, "Failed to connect to ubus\n");
++		return -1;
++	}
++	ubus_add_uloop(ctx);
++
++	ret = ubus_add_object(ctx, &upnpc_object);
++	if (ret)
++		fprintf(stderr, "Failed to add 'usp' ubus object: %s\n", ubus_strerror(ret));
++
++	uloop_run();
++	uloop_done();
++	return 0;
++}
+--- a/upnp_ubus.c
++++ b/upnp_ubus.c
+@@ -0,0 +1,300 @@
++/*
++*	This program is free software: you can redistribute it and/or modify
++*	it under the terms of the GNU General Public License as published by
++*	the Free Software Foundation, either version 2 of the License, or
++*	(at your option) any later version.
++*
++*	Copyright (C) 2019 iopsys Software Solutions AB
++*		Author: Omar Kallel <omar.kallel@pivasoftware.com>
++*
++*/
++
++#include <stdio.h>
++#include "miniupnpc.h"
++#include "upnp_ubus.h"
++#include "minixml.h"
++#include "igd_desc_parse.h"
++
++struct UPNPDev * devlist = NULL;
++
++const struct blobmsg_policy upnp_dev_policy[0] = {
++	//[0] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
++};
++
++struct ubus_method upnpc_methods[] = {
++	UBUS_METHOD("discovery", upnp_discovery_res, upnp_dev_policy),
++	UBUS_METHOD("description", upnp_description_res, upnp_dev_policy),
++};
++
++struct ubus_object_type upnpc_type = UBUS_OBJECT_TYPE("upnpc", upnpc_methods);
++
++struct ubus_object upnpc_object = {
++	.name = "upnpc",
++	.type = &upnpc_type,
++	.methods = upnpc_methods,
++	.n_methods = ARRAY_SIZE(upnpc_methods),
++};
++
++void xmplparsestartelt(void * d, const char * name, int l)
++{
++	struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d;
++	struct xml_parse_list_elt *xmlelt = NULL, *parentelt = NULL;
++
++	xmldata->node_name = (char *)malloc(l*sizeof(char));
++	memcpy(xmldata->node_name, name, l);
++	xmldata->node_name[l] = '\0';
++
++	if(memcmp(name, "service", l) == 0 || memcmp(name, "device", l) == 0) {
++		if(!list_empty(xmldata->xml_elts)) {
++			parentelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev;
++		}
++		xmlelt = calloc(1, sizeof(struct xml_parse_list_elt));
++		list_add_tail(&xmlelt->list, xmldata->xml_elts);
++		xmlelt->nodename = (char *)malloc(l*sizeof(char));
++		memcpy(xmlelt->nodename, name, l);
++		xmlelt->nodename[l] = '\0';
++		xmlelt->eltobj = json_object_new_object();
++		if (memcmp(name, "device", l) == 0) {
++			xmlelt->elt_type = DEVICE;
++		} else if (memcmp(name, "service", l) == 0) {
++			xmlelt->elt_type = SERVICE;
++		}
++		if(parentelt) {
++			json_object_object_add(xmlelt->eltobj, "parent_dev", json_object_new_string(parentelt->udn));
++		} else {
++			json_object_object_add(xmlelt->eltobj, "parent_dev", json_object_new_string(""));
++		}
++		xmlelt->udn = NULL;
++	}
++}
++
++void xmplparsedata(void * d, const char * data, int l)
++{
++	struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d;
++	struct xml_parse_list_elt *xmlelt = NULL;
++
++	int length = get_data_length(data, l);
++	char *value = (char *)malloc(length*sizeof(char));
++	memcpy(value, data, length);
++	value[length]='\0';
++
++	if(!list_empty(xmldata->xml_elts)) {
++		xmlelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev;
++	}
++	if (xmlelt && (xmlelt->elt_type == DEVICE || xmlelt->elt_type == SERVICE)) {
++		if(strcmp(xmldata->node_name, "UDN") == 0) {
++			xmlelt->udn = (char *)malloc(length*sizeof(char));
++			memcpy(xmlelt->udn, value, length);
++			xmlelt->udn[length] = '\0';
++		}
++		json_object_object_add(xmlelt->eltobj, xmldata->node_name, json_object_new_string(value));
++	}
++	//free(value);
++}
++
++void xmplparseendelt(void * d, const char * name, int l)
++{
++	struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d;
++	struct xml_parse_list_elt *xmlelt;
++
++	if(memcmp(name, "service", l) == 0 || memcmp(name, "device", l) == 0) {
++		xmlelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev;
++		if(memcmp(name, "device", l) == 0) {
++			json_object_array_add(xmldata->devicesarray, xmlelt->eltobj);
++		}
++		if(memcmp(name, "service", l) == 0) {
++			json_object_array_add(xmldata->servicesarray, xmlelt->eltobj);
++		}
++		xmlelt->eltobj = NULL;
++		list_del(&xmlelt->list);
++	}
++}
++
++int upnp_discovery_res(struct ubus_context *ctx, struct ubus_object *obj,
++		struct ubus_request_data *req, const char *method,
++		struct blob_attr *msg)
++{
++	struct blob_buf bb;
++	struct UPNPDev * dev;
++	int i;
++
++	memset(&bb,0,sizeof(struct blob_buf));
++	blob_buf_init(&bb, 0);
++
++
++	json_object *devices_array, *services_array, *device_obj, *service_obj;
++
++	devices_array = json_object_new_array();
++	services_array = json_object_new_array();
++
++	for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
++		if((dev->st && strstr(dev->st, ":rootdevice") != NULL) || (dev->usn && strstr(dev->usn, ":device:") != NULL)){
++			device_obj = json_object_new_object();
++			json_object_object_add(device_obj, "descurl", json_object_new_string(dev->descURL));
++			json_object_object_add(device_obj, "st", json_object_new_string(dev->st));
++			json_object_object_add(device_obj, "usn", json_object_new_string(dev->usn));
++			json_object_object_add(device_obj, "is_root_device", json_object_new_string(dev->st && strstr(dev->st, ":rootdevice") ? "1":"0"));
++			json_object_array_add(devices_array, device_obj);
++		}
++
++		if(dev->usn && strstr(dev->usn, ":service:") != NULL) {
++			service_obj = json_object_new_object();
++			json_object_object_add(service_obj, "descurl", json_object_new_string(dev->descURL));
++			json_object_object_add(service_obj, "st", json_object_new_string(dev->st));
++			json_object_object_add(service_obj, "usn", json_object_new_string(dev->usn));
++			json_object_array_add(services_array, service_obj);
++		}
++	}
++	blobmsg_add_json_element(&bb, "devices", devices_array);
++	blobmsg_add_json_element(&bb, "services", services_array);
++	json_object_put(device_obj);
++	json_object_put(service_obj);
++	json_object_put(devices_array);
++	json_object_put(services_array);
++	ubus_send_reply(ctx, req, bb.head);
++	blob_buf_free(&bb);
++	return 0;
++}
++
++int upnp_description_res(struct ubus_context *ctx, struct ubus_object *obj,
++		struct ubus_request_data *req, const char *method,
++		struct blob_attr *msg)
++{
++	struct blob_buf bb;
++	FILE *file = NULL;
++	char *buffer;
++	int len, r;
++	struct xmlparser parser;
++	struct xml_parse_strct xmldata;
++	struct descriptions_list_elt *desc_elt = NULL;
++	json_object *descriptions_array = NULL, *desc_obj = NULL;
++	memset(&bb,0,sizeof(struct blob_buf));
++	blob_buf_init(&bb, 0);
++
++	memset(&parser, 0, sizeof(struct xmlparser));
++	descriptions_array = json_object_new_array();
++	LIST_HEAD(tmp_elts);
++	xmldata.devicesarray = json_object_new_array();
++	xmldata.servicesarray = json_object_new_array();
++	xmldata.xml_elts = &tmp_elts;
++	parser.data = &xmldata;
++	parser.starteltfunc = xmplparsestartelt;
++	parser.endeltfunc = xmplparseendelt;
++	parser.datafunc = xmplparsedata;
++	int i = 0;
++	list_for_each_entry(desc_elt, &descriptions_list, list) {
++		i++;
++		desc_obj = json_object_new_object();
++		json_object_object_add(desc_obj, "desc_url", json_object_new_string(desc_elt->url));
++		json_object_object_add(desc_obj, "is_device_desc", json_object_new_int(desc_elt->is_device_desc));
++		json_object_array_add(descriptions_array, desc_obj);
++		file = fopen(desc_elt->sytem_file_path,"r");
++	    if (file == NULL){
++	            fprintf(stderr,"Error to open the file filetest\n");
++	    }
++	    fseek(file, 0, SEEK_END);
++	    len = ftell(file);
++	    fseek(file, 0, SEEK_SET);
++	    buffer = malloc(len);
++	    if(!buffer) {
++	            fprintf(stderr, "Memory allocation error.\n");
++	            fclose(file);
++	            return 1;
++	    }
++	    r = (int)fread(buffer, 1, len, file);
++		parser.xmlstart = buffer;
++		parser.xmlsize = len;
++		parsexml(&parser);
++
++		fclose(file);
++		file = NULL;
++		FREE(buffer);
++	}
++
++	blobmsg_add_json_element(&bb, "descriptions", descriptions_array);
++	blobmsg_add_json_element(&bb, "devicesinstances", xmldata.devicesarray);
++	blobmsg_add_json_element(&bb, "servicesinstances", xmldata.servicesarray);
++	ubus_send_reply(ctx, req, bb.head);
++	blob_buf_free(&bb);
++	return 0;
++}
++
++
++/*
++ * Other functions
++ */
++
++
++char **strsplit(const char* str, const char* delim, size_t* numtokens)
++{
++    // copy the original string so that we don't overwrite parts of it
++    // (don't do this if you don't need to keep the old line,
++    // as this is less efficient)
++    char *s = strdup(str);
++    // these three variables are part of a very common idiom to
++    // implement a dynamically-growing array
++    size_t tokens_alloc = 1;
++    size_t tokens_used = 0;
++    char **tokens = calloc(tokens_alloc, sizeof(char*));
++    char *token, *strtok_ctx;
++    for (token = strtok_r(s, delim, &strtok_ctx);
++            token != NULL;
++            token = strtok_r(NULL, delim, &strtok_ctx)) {
++        // check if we need to allocate more space for tokens
++        if (tokens_used == tokens_alloc) {
++            tokens_alloc *= 2;
++            tokens = realloc(tokens, tokens_alloc * sizeof(char*));
++        }
++        tokens[tokens_used++] = strdup(token);
++    }
++    // cleanup
++    if (tokens_used == 0) {
++        free(tokens);
++        tokens = NULL;
++    } else {
++        tokens = realloc(tokens, tokens_used * sizeof(char*));
++    }
++    *numtokens = tokens_used;
++    free(s);
++    return tokens;
++}
++
++void check_create_dir(char *dirpath) {
++	struct stat st = {0};
++
++	if (stat(dirpath, &st) == -1) {
++	    mkdir(dirpath, 0700);
++	}
++}
++
++int is_desc_elt_exit_in_list(char *desc_url)
++{
++	struct descriptions_list_elt *desc_elt = NULL;
++
++	list_for_each_entry(desc_elt, &descriptions_list, list) {
++		if(strcmp(desc_elt->url, desc_url) == 0)
++			return 1;
++	}
++	return 0;
++}
++
++void add_desc_to_descriptions_list(char *filepath, char *url, int is_device_desc) {
++	struct descriptions_list_elt *desc_elt;
++
++	desc_elt = calloc(1, sizeof(struct descriptions_list_elt));
++	list_add_tail(&desc_elt->list, &descriptions_list);
++	asprintf(&desc_elt->sytem_file_path, "%s", filepath);
++	asprintf(&desc_elt->url, "%s", url);
++	desc_elt->is_device_desc = is_device_desc;
++}
++
++int get_data_length(const char *data, int l)
++{
++	int i;
++
++	for (i=0; i<l; i++) {
++		if(data[i] == '\n' || data[i] == '\t')
++			return i;
++	}
++	return l;
++}
+diff -ruN orig/upnp_ubus.h new/upnp_ubus.h
+--- a/upnp_ubus.h
++++ b/upnp_ubus.h
+@@ -0,0 +1,69 @@
++/*
++*	This program is free software: you can redistribute it and/or modify
++*	it under the terms of the GNU General Public License as published by
++*	the Free Software Foundation, either version 2 of the License, or
++*	(at your option) any later version.
++*
++*	Copyright (C) 2019 iopsys Software Solutions AB
++*		Author: Omar Kallel <omar.kallel@pivasoftware.com>
++*
++*/
++
++#ifndef SRC_UPNP_UBUS_H_
++#define SRC_UPNP_UBUS_H_
++#include <libubus.h>
++#include <libubox/blobmsg.h>
++#include <libubox/blobmsg_json.h>
++#include <json-c/json.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++#define FREE(x) if(x!=NULL) { free(x); x=NULL; }
++
++enum elemtentTypes { DEVICE, SERVICE, NONE };
++
++struct descriptions_list_elt {
++	struct list_head list;
++	char *url;
++	char *sytem_file_path;
++	int is_device_desc;
++};
++
++struct xml_parse_list_elt {
++	struct list_head list;
++	json_object *eltobj;
++	enum elemtentTypes elt_type;
++	char *nodename;
++	char *udn;
++};
++
++struct xml_parse_strct {
++	json_object *devicesarray;
++	json_object *servicesarray;
++	char *node_name;
++	struct list_head *xml_elts;
++};
++
++extern struct UPNPDev * devlist;
++extern struct ubus_object upnpc_object;
++extern struct list_head descriptions_list;
++
++
++int upnp_discovery_res(struct ubus_context *ctx, struct ubus_object *obj,
++		struct ubus_request_data *req, const char *method,
++		struct blob_attr *msg);
++
++int upnp_description_res(struct ubus_context *ctx, struct ubus_object *obj,
++		struct ubus_request_data *req, const char *method,
++		struct blob_attr *msg);
++
++char **strsplit(const char* str, const char* delim, size_t* numtokens);
++void check_create_dir(char *dirpath);
++void IGDdata1(void *, const char *, int);
++void IGDstartelt1(void * d, const char * name, int l);
++void IGDendelt1(void * d, const char * name, int l);
++int is_desc_elt_exit_in_list(char *desc_url);
++void add_desc_to_descriptions_list(char *filepath, char *url, int is_device_desc);
++int get_data_length(const char *data, int l);
++#endif /* SRC_UPNP_UBUS_H_ */
-- 
GitLab