diff mbox series

[v2,2/5] corelib: Rework dictionary to support multiple values per key

Message ID 1516199646-5607-2-git-send-email-stefan@herbrechtsmeier.net
State Accepted
Headers show
Series [v2,1/5] dict: Rename dictionary struct and its key to distinguish it from simple lists | expand

Commit Message

Stefan Herbrechtsmeier Jan. 17, 2018, 2:34 p.m. UTC
From: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>

Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>

---

Changes in v3:
- Add const to all dict key and value parameters
- Update swuforward to iterate over the value list

Changes in v2: None

 bootloader/grub.c             |  20 +++----
 corelib/installer.c           |   9 +--
 corelib/swupdate_dict.c       | 130 ++++++++++++++++++++++++++++++++----------
 handlers/swuforward_handler.c |  11 +---
 include/swupdate_dict.h       |  22 +++++--
 parser/parser.c               |   2 +-
 suricatta/server_hawkbit.c    |  14 +++--
 7 files changed, 143 insertions(+), 65 deletions(-)

Comments

Stefano Babic Jan. 17, 2018, 6:17 p.m. UTC | #1
On 17/01/2018 15:34, stefan@herbrechtsmeier.net wrote:
> From: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
> 
> Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
> 
> ---
> 
> Changes in v3:
> - Add const to all dict key and value parameters
> - Update swuforward to iterate over the value list
> 
> Changes in v2: None
> 
>  bootloader/grub.c             |  20 +++----
>  corelib/installer.c           |   9 +--
>  corelib/swupdate_dict.c       | 130 ++++++++++++++++++++++++++++++++----------
>  handlers/swuforward_handler.c |  11 +---
>  include/swupdate_dict.h       |  22 +++++--
>  parser/parser.c               |   2 +-
>  suricatta/server_hawkbit.c    |  14 +++--
>  7 files changed, 143 insertions(+), 65 deletions(-)
> 
> diff --git a/bootloader/grub.c b/bootloader/grub.c
> index 2f6172e..dc35718 100644
> --- a/bootloader/grub.c
> +++ b/bootloader/grub.c
> @@ -153,8 +153,10 @@ static inline void grubenv_update_size(struct grubenv_t *grubenv)
>  
>  	/* lengths of strings + '=' and '\n' characters */
>  	LIST_FOREACH(grubvar, &grubenv->vars, next) {
> -		size = size + strlen(grubvar->key) +
> -						strlen(grubvar->value) + 2;
> +		char *key = dict_entry_get_key(grubvar);
> +		char *value = dict_entry_get_value(grubvar);
> +
> +		size = size + strlen(key) + strlen(value) + 2;
>  	}
>  	size += strlen(GRUBENV_HEADER);
>  	grubenv->size = size;
> @@ -194,10 +196,12 @@ static int grubenv_write(struct grubenv_t *grubenv)
>  	strncpy(buf, GRUBENV_HEADER, strlen(GRUBENV_HEADER) + 1);
>  
>  	LIST_FOREACH(grubvar, &grubenv->vars, next) {
> -		llen = strlen(grubvar->key) + strlen(grubvar->value) + 2;
> +		char *key = dict_entry_get_key(grubvar);
> +		char *value = dict_entry_get_value(grubvar);
> +
> +		llen = strlen(key) + strlen(value) + 2;
>  		/* +1 for null termination */
> -		snprintf(line, llen + 1, "%s=%s\n", grubvar->key,
> -						grubvar->value);
> +		snprintf(line, llen + 1, "%s=%s\n", key, value);
>  		strncat(buf, line, llen);
>  	}
>  
> @@ -237,11 +241,7 @@ cleanup:
>   * allocation */
>  static inline void grubenv_close(struct grubenv_t *grubenv)
>  {
> -	struct dict_entry *grubvar;
> -
> -	LIST_FOREACH(grubvar, &grubenv->vars, next) {
> -		dict_remove(&grubenv->vars, grubvar->key);
> -	}
> +	dict_drop_db(&grubenv->vars);
>  }
>  
>  /* I feel that '#' and '=' characters should be forbidden. Although it's not
> diff --git a/corelib/installer.c b/corelib/installer.c
> index e5bf895..f778d8b 100644
> --- a/corelib/installer.c
> +++ b/corelib/installer.c
> @@ -206,11 +206,12 @@ static int prepare_boot_script(struct swupdate_cfg *cfg, const char *script)
>  		return -1;
>  
>  	LIST_FOREACH(bootvar, &cfg->bootloader, next) {
> -		if (!bootvar->key || !bootvar->value)
> +		char *key = dict_entry_get_key(bootvar);
> +		char *value = dict_entry_get_value(bootvar);
> +
> +		if (!key || !value)
>  			continue;
> -		snprintf(buf, sizeof(buf), "%s %s\n",
> -			bootvar->key,
> -			bootvar->value);
> +		snprintf(buf, sizeof(buf), "%s %s\n", key, value);
>  		if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
>  			  TRACE("Error saving temporary file");
>  			  ret = -1;
> diff --git a/corelib/swupdate_dict.c b/corelib/swupdate_dict.c
> index 2cdad47..be21ff2 100644
> --- a/corelib/swupdate_dict.c
> +++ b/corelib/swupdate_dict.c
> @@ -2,6 +2,9 @@
>   * (C) Copyright 2016
>   * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
>   *
> + * Copyright (C) 2018 Weidmüller Interface GmbH & Co. KG
> + * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
> + *
>   * SPDX-License-Identifier:     GPL-2.0-or-later
>   */
>  
> @@ -19,7 +22,52 @@
>  #include "util.h"
>  #include "swupdate_dict.h"
>  
> -static struct dict_entry *get_entry(struct dict *dictionary, char *key)
> +static int insert_list_elem(struct dict_list *list, const char *value)
> +{
> +	struct dict_list_elem *elem = (struct dict_list_elem *)malloc(sizeof(*elem));
> +
> +	if (!elem)
> +		return -ENOMEM;
> +
> +	memset(elem, 0, sizeof(*elem));
> +	elem->value = strdup(value);
> +
> +	LIST_INSERT_HEAD(list, elem, next);
> +
> +	return 0;
> +}
> +
> +static void remove_list_elem(struct dict_list_elem *elem)
> +{
> +	LIST_REMOVE(elem, next);
> +	free(elem->value);
> +	free(elem);
> +}
> +
> +static void remove_list(struct dict_list *list)
> +{
> +	struct dict_list_elem *elem;
> +
> +	LIST_FOREACH(elem, list, next) {
> +		remove_list_elem(elem);
> +	}
> +}
> +
> +static struct dict_entry *insert_entry(struct dict *dictionary, const char *key)
> +{
> +	struct dict_entry *entry = (struct dict_entry *)malloc(sizeof(*entry));
> +	if (!entry)
> +		return NULL;
> +
> +	memset(entry, 0, sizeof(*entry));
> +	entry->key = strdup(key);
> +
> +	LIST_INSERT_HEAD(dictionary, entry, next);
> +
> +	return entry;
> +}
> +
> +static struct dict_entry *get_entry(struct dict *dictionary, const char *key)
>  {
>  	struct dict_entry *entry;
>  
> @@ -31,72 +79,92 @@ static struct dict_entry *get_entry(struct dict *dictionary, char *key)
>  	return NULL;
>  }
>  
> -int dict_insert_entry(struct dict *dictionary, char *key, char *value)
> +static void remove_entry(struct dict_entry *entry)
>  {
> -	struct dict_entry *entry = (struct dict_entry *)malloc(sizeof(*entry));
> +	LIST_REMOVE(entry, next);
> +	free(entry->key);
> +	remove_list(&entry->list);
> +	free(entry);
> +}
>  
> +char *dict_entry_get_key(struct dict_entry *entry)
> +{
>  	if (!entry)
> -		return -ENOMEM;
> +		return NULL;
>  
> -	memset(entry, 0, sizeof(*entry));
> -	entry->key = strdup(key);
> -	entry->value = strdup(value);
> +	return entry->key;
> +}
>  
> -	LIST_INSERT_HEAD(dictionary, entry, next);
> +char *dict_entry_get_value(struct dict_entry *entry)
> +{
> +	if (!entry || !LIST_FIRST(&entry->list))
> +		return NULL;
>  
> -	return 0;
> +	return LIST_FIRST(&entry->list)->value;
>  }
>  
> -char *dict_get_value(struct dict *dictionary, char *key)
> +struct dict_list *dict_get_list(struct dict *dictionary, const char *key)
>  {
>  	struct dict_entry *entry = get_entry(dictionary, key);
>  
>  	if (!entry)
>  		return NULL;
>  
> -	return entry->value;
> +	return &entry->list;
>  }
>  
> -int dict_set_value(struct dict *dictionary, char *key, char *value)
> +char *dict_get_value(struct dict *dictionary, const char *key)
>  {
>  	struct dict_entry *entry = get_entry(dictionary, key);
>  
> -	/*
> -	 * Set to new value if key is already in
> -	 * dictionary
> -	 */
> -	if (entry) {
> -		LIST_REMOVE(entry, next);
> -		free(entry);
> -	}
> +	if (!entry)
> +		return NULL;
>  
> -	return dict_insert_entry(dictionary, key, value);
> +	return dict_entry_get_value(entry);
>  }
>  
> -void dict_remove_entry(struct dict_entry *entry)
> +int dict_insert_value(struct dict *dictionary, const char *key, const char *value)
>  {
> -	LIST_REMOVE(entry, next);
> -	free(entry->key);
> -	free(entry->value);
> -	free(entry);
> +	struct dict_entry *entry = get_entry(dictionary, key);
> +
> +	if (!entry) {
> +		entry = insert_entry(dictionary, key);
> +		if (!entry)
> +			return -ENOMEM;
> +	}
> +
> +	return insert_list_elem(&entry->list, value);
>  }
>  
> -void dict_remove(struct dict *dictionary, char *key)
> +int dict_set_value(struct dict *dictionary, const char *key, const char *value)
>  {
> +	struct dict_entry *entry = get_entry(dictionary, key);
> +
> +	if (entry)
> +		remove_entry(entry);
>  
> +	entry = insert_entry(dictionary, key);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	return insert_list_elem(&entry->list, value);
> +}
> +
> +void dict_remove(struct dict *dictionary, const char *key)
> +{
>  	struct dict_entry *entry = get_entry(dictionary, key);
>  
>  	if (!entry)
>  		return;
>  
> -	dict_remove_entry(entry);
> +	remove_entry(entry);
>  }
>  
>  void dict_drop_db(struct dict *dictionary)
>  {
> -	struct dict_entry *var;
> +	struct dict_entry *entry;
>  
> -	LIST_FOREACH(var, dictionary, next) {
> -		dict_remove_entry(var);
> +	LIST_FOREACH(entry, dictionary, next) {
> +		remove_entry(entry);
>  	}
>  }
> diff --git a/handlers/swuforward_handler.c b/handlers/swuforward_handler.c
> index c804628..8b0c17f 100644
> --- a/handlers/swuforward_handler.c
> +++ b/handlers/swuforward_handler.c
> @@ -292,7 +292,7 @@ static int install_remote_swu(struct img_type *img,
>  	struct hnd_priv priv;
>  	struct curlconn *conn;
>  	int ret, still_running = 0;
> -	struct dict_entry *url;
> +	struct dict_list_elem *url;
>  	struct curl_slist *headerlist;
>  	CURLMsg *msg = NULL;
>  
> @@ -320,16 +320,9 @@ static int install_remote_swu(struct img_type *img,
>  	priv.maxwaitms = MAX_WAIT_MS;
>  	priv.size = img->size;
>  
> -	/*
> -	 * Parse handler properties to get URLs for destination
> -	 *
> -	 */
> -	LIST_FOREACH(url, &img->properties, next) {
> +	LIST_FOREACH(url, dict_get_list(&img->properties, "url"), next) {
>  		char curlheader[SWUPDATE_GENERAL_STRING_SIZE + strlen(CUSTOM_HEADER)];
>  
> -		if (!url->key || !url->value || strcmp(url->key, "url"))
> -			continue;
> -
>  		conn = (struct curlconn *)calloc(1, sizeof(struct curlconn));
>  		if (!conn) {
>  			ERROR("FAULT: no memory");
> diff --git a/include/swupdate_dict.h b/include/swupdate_dict.h
> index 2cae5ca..82988ac 100644
> --- a/include/swupdate_dict.h
> +++ b/include/swupdate_dict.h
> @@ -10,19 +10,29 @@
>  
>  #include <bsdqueue.h>
>  
> +struct dict_list_elem {
> +	char *value;
> +	LIST_ENTRY(dict_list_elem) next;
> +};
> +
> +LIST_HEAD(dict_list, dict_list_elem);
> +
>  struct dict_entry {
>  	char *key;
> -	char *value;
> +	struct dict_list list;
>  	LIST_ENTRY(dict_entry) next;
>  };
>  
>  LIST_HEAD(dict, dict_entry);
>  
> -char *dict_get_value(struct dict *dictionary, char *key);
> -int dict_set_value(struct dict *dictionary, char *key, char *value);
> -int dict_insert_entry(struct dict *dictionary, char *key, char *value);
> -void dict_remove(struct dict *dictionary, char *key);
> -void dict_remove_entry(struct dict_entry *entry);
> +char *dict_entry_get_key(struct dict_entry *entry);
> +char *dict_entry_get_value(struct dict_entry *entry);
> +
> +struct dict_list *dict_get_list(struct dict *dictionary, const char *key);
> +char *dict_get_value(struct dict *dictionary, const char *key);
> +int dict_set_value(struct dict *dictionary, const char *key, const char *value);
> +int dict_insert_value(struct dict *dictionary, const char *key, const char *value);
> +void dict_remove(struct dict *dictionary, const char *key);
>  void dict_drop_db(struct dict *dictionary);
>  
>  #endif
> diff --git a/parser/parser.c b/parser/parser.c
> index 04cd549..2e9208d 100644
> --- a/parser/parser.c
> +++ b/parser/parser.c
> @@ -162,7 +162,7 @@ static void add_properties(parsertype p, void *node, struct img_type *image)
>  				key,
>  				value
>  			);
> -			if (dict_insert_entry(&image->properties, key, value))
> +			if (dict_insert_value(&image->properties, key, value))
>  				ERROR("Property not stored, skipping...");
>  
>  		}
> diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
> index c84de65..02ffb3a 100644
> --- a/suricatta/server_hawkbit.c
> +++ b/suricatta/server_hawkbit.c
> @@ -1336,7 +1336,10 @@ int get_target_data_length(void)
>  	struct dict_entry *entry;
>  
>  	LIST_FOREACH(entry, &server_hawkbit.configdata, next) {
> -		len += strlen(entry->key) + strlen(entry->value) + strlen (" : ") + 6;
> +		char *key = dict_entry_get_key(entry);
> +		char *value = dict_entry_get_value(entry);
> +
> +		len += strlen(key) + strlen(value) + strlen (" : ") + 6;
>  	}
>  
>  	return len;
> @@ -1367,17 +1370,20 @@ server_op_res_t server_send_target_data(void)
>  
>  	char *keyvalue = NULL;
>  	LIST_FOREACH(entry, &server_hawkbit.configdata, next) {
> +		char *key = dict_entry_get_key(entry);
> +		char *value = dict_entry_get_value(entry);
> +
>  		if (ENOMEM_ASPRINTF ==
>  		    asprintf(&keyvalue, config_data,
>  				((first) ? ' ' : ','),
> -				entry->key,
> -				entry->value)) {
> +				key,
> +				value)) {
>  			ERROR("hawkBit server reply cannot be sent because of OOM.\n");
>  			result = SERVER_EINIT;
>  			goto cleanup;
>  		}
>  		first = false;
> -		TRACE("KEYVALUE=%s %s %s", keyvalue, entry->key, entry->value);
> +		TRACE("KEYVALUE=%s %s %s", keyvalue, key, value);
>  		strcat(configData, keyvalue);
>  		free(keyvalue);
>  
> 

Reviewed-by: Stefano Babic <sbabic@denx.de>
Tested-by: Stefano Babic <sbabic@denx.de>

Best regards,
Stefano Babic
Stefano Babic Jan. 19, 2018, 9:34 a.m. UTC | #2
On 17/01/2018 15:34, stefan@herbrechtsmeier.net wrote:
> From: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
> 
> Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
> 
> ---
> 

Applied to -master, thanks !

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/bootloader/grub.c b/bootloader/grub.c
index 2f6172e..dc35718 100644
--- a/bootloader/grub.c
+++ b/bootloader/grub.c
@@ -153,8 +153,10 @@  static inline void grubenv_update_size(struct grubenv_t *grubenv)
 
 	/* lengths of strings + '=' and '\n' characters */
 	LIST_FOREACH(grubvar, &grubenv->vars, next) {
-		size = size + strlen(grubvar->key) +
-						strlen(grubvar->value) + 2;
+		char *key = dict_entry_get_key(grubvar);
+		char *value = dict_entry_get_value(grubvar);
+
+		size = size + strlen(key) + strlen(value) + 2;
 	}
 	size += strlen(GRUBENV_HEADER);
 	grubenv->size = size;
@@ -194,10 +196,12 @@  static int grubenv_write(struct grubenv_t *grubenv)
 	strncpy(buf, GRUBENV_HEADER, strlen(GRUBENV_HEADER) + 1);
 
 	LIST_FOREACH(grubvar, &grubenv->vars, next) {
-		llen = strlen(grubvar->key) + strlen(grubvar->value) + 2;
+		char *key = dict_entry_get_key(grubvar);
+		char *value = dict_entry_get_value(grubvar);
+
+		llen = strlen(key) + strlen(value) + 2;
 		/* +1 for null termination */
-		snprintf(line, llen + 1, "%s=%s\n", grubvar->key,
-						grubvar->value);
+		snprintf(line, llen + 1, "%s=%s\n", key, value);
 		strncat(buf, line, llen);
 	}
 
@@ -237,11 +241,7 @@  cleanup:
  * allocation */
 static inline void grubenv_close(struct grubenv_t *grubenv)
 {
-	struct dict_entry *grubvar;
-
-	LIST_FOREACH(grubvar, &grubenv->vars, next) {
-		dict_remove(&grubenv->vars, grubvar->key);
-	}
+	dict_drop_db(&grubenv->vars);
 }
 
 /* I feel that '#' and '=' characters should be forbidden. Although it's not
diff --git a/corelib/installer.c b/corelib/installer.c
index e5bf895..f778d8b 100644
--- a/corelib/installer.c
+++ b/corelib/installer.c
@@ -206,11 +206,12 @@  static int prepare_boot_script(struct swupdate_cfg *cfg, const char *script)
 		return -1;
 
 	LIST_FOREACH(bootvar, &cfg->bootloader, next) {
-		if (!bootvar->key || !bootvar->value)
+		char *key = dict_entry_get_key(bootvar);
+		char *value = dict_entry_get_value(bootvar);
+
+		if (!key || !value)
 			continue;
-		snprintf(buf, sizeof(buf), "%s %s\n",
-			bootvar->key,
-			bootvar->value);
+		snprintf(buf, sizeof(buf), "%s %s\n", key, value);
 		if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
 			  TRACE("Error saving temporary file");
 			  ret = -1;
diff --git a/corelib/swupdate_dict.c b/corelib/swupdate_dict.c
index 2cdad47..be21ff2 100644
--- a/corelib/swupdate_dict.c
+++ b/corelib/swupdate_dict.c
@@ -2,6 +2,9 @@ 
  * (C) Copyright 2016
  * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
  *
+ * Copyright (C) 2018 Weidmüller Interface GmbH & Co. KG
+ * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
+ *
  * SPDX-License-Identifier:     GPL-2.0-or-later
  */
 
@@ -19,7 +22,52 @@ 
 #include "util.h"
 #include "swupdate_dict.h"
 
-static struct dict_entry *get_entry(struct dict *dictionary, char *key)
+static int insert_list_elem(struct dict_list *list, const char *value)
+{
+	struct dict_list_elem *elem = (struct dict_list_elem *)malloc(sizeof(*elem));
+
+	if (!elem)
+		return -ENOMEM;
+
+	memset(elem, 0, sizeof(*elem));
+	elem->value = strdup(value);
+
+	LIST_INSERT_HEAD(list, elem, next);
+
+	return 0;
+}
+
+static void remove_list_elem(struct dict_list_elem *elem)
+{
+	LIST_REMOVE(elem, next);
+	free(elem->value);
+	free(elem);
+}
+
+static void remove_list(struct dict_list *list)
+{
+	struct dict_list_elem *elem;
+
+	LIST_FOREACH(elem, list, next) {
+		remove_list_elem(elem);
+	}
+}
+
+static struct dict_entry *insert_entry(struct dict *dictionary, const char *key)
+{
+	struct dict_entry *entry = (struct dict_entry *)malloc(sizeof(*entry));
+	if (!entry)
+		return NULL;
+
+	memset(entry, 0, sizeof(*entry));
+	entry->key = strdup(key);
+
+	LIST_INSERT_HEAD(dictionary, entry, next);
+
+	return entry;
+}
+
+static struct dict_entry *get_entry(struct dict *dictionary, const char *key)
 {
 	struct dict_entry *entry;
 
@@ -31,72 +79,92 @@  static struct dict_entry *get_entry(struct dict *dictionary, char *key)
 	return NULL;
 }
 
-int dict_insert_entry(struct dict *dictionary, char *key, char *value)
+static void remove_entry(struct dict_entry *entry)
 {
-	struct dict_entry *entry = (struct dict_entry *)malloc(sizeof(*entry));
+	LIST_REMOVE(entry, next);
+	free(entry->key);
+	remove_list(&entry->list);
+	free(entry);
+}
 
+char *dict_entry_get_key(struct dict_entry *entry)
+{
 	if (!entry)
-		return -ENOMEM;
+		return NULL;
 
-	memset(entry, 0, sizeof(*entry));
-	entry->key = strdup(key);
-	entry->value = strdup(value);
+	return entry->key;
+}
 
-	LIST_INSERT_HEAD(dictionary, entry, next);
+char *dict_entry_get_value(struct dict_entry *entry)
+{
+	if (!entry || !LIST_FIRST(&entry->list))
+		return NULL;
 
-	return 0;
+	return LIST_FIRST(&entry->list)->value;
 }
 
-char *dict_get_value(struct dict *dictionary, char *key)
+struct dict_list *dict_get_list(struct dict *dictionary, const char *key)
 {
 	struct dict_entry *entry = get_entry(dictionary, key);
 
 	if (!entry)
 		return NULL;
 
-	return entry->value;
+	return &entry->list;
 }
 
-int dict_set_value(struct dict *dictionary, char *key, char *value)
+char *dict_get_value(struct dict *dictionary, const char *key)
 {
 	struct dict_entry *entry = get_entry(dictionary, key);
 
-	/*
-	 * Set to new value if key is already in
-	 * dictionary
-	 */
-	if (entry) {
-		LIST_REMOVE(entry, next);
-		free(entry);
-	}
+	if (!entry)
+		return NULL;
 
-	return dict_insert_entry(dictionary, key, value);
+	return dict_entry_get_value(entry);
 }
 
-void dict_remove_entry(struct dict_entry *entry)
+int dict_insert_value(struct dict *dictionary, const char *key, const char *value)
 {
-	LIST_REMOVE(entry, next);
-	free(entry->key);
-	free(entry->value);
-	free(entry);
+	struct dict_entry *entry = get_entry(dictionary, key);
+
+	if (!entry) {
+		entry = insert_entry(dictionary, key);
+		if (!entry)
+			return -ENOMEM;
+	}
+
+	return insert_list_elem(&entry->list, value);
 }
 
-void dict_remove(struct dict *dictionary, char *key)
+int dict_set_value(struct dict *dictionary, const char *key, const char *value)
 {
+	struct dict_entry *entry = get_entry(dictionary, key);
+
+	if (entry)
+		remove_entry(entry);
 
+	entry = insert_entry(dictionary, key);
+	if (!entry)
+		return -ENOMEM;
+
+	return insert_list_elem(&entry->list, value);
+}
+
+void dict_remove(struct dict *dictionary, const char *key)
+{
 	struct dict_entry *entry = get_entry(dictionary, key);
 
 	if (!entry)
 		return;
 
-	dict_remove_entry(entry);
+	remove_entry(entry);
 }
 
 void dict_drop_db(struct dict *dictionary)
 {
-	struct dict_entry *var;
+	struct dict_entry *entry;
 
-	LIST_FOREACH(var, dictionary, next) {
-		dict_remove_entry(var);
+	LIST_FOREACH(entry, dictionary, next) {
+		remove_entry(entry);
 	}
 }
diff --git a/handlers/swuforward_handler.c b/handlers/swuforward_handler.c
index c804628..8b0c17f 100644
--- a/handlers/swuforward_handler.c
+++ b/handlers/swuforward_handler.c
@@ -292,7 +292,7 @@  static int install_remote_swu(struct img_type *img,
 	struct hnd_priv priv;
 	struct curlconn *conn;
 	int ret, still_running = 0;
-	struct dict_entry *url;
+	struct dict_list_elem *url;
 	struct curl_slist *headerlist;
 	CURLMsg *msg = NULL;
 
@@ -320,16 +320,9 @@  static int install_remote_swu(struct img_type *img,
 	priv.maxwaitms = MAX_WAIT_MS;
 	priv.size = img->size;
 
-	/*
-	 * Parse handler properties to get URLs for destination
-	 *
-	 */
-	LIST_FOREACH(url, &img->properties, next) {
+	LIST_FOREACH(url, dict_get_list(&img->properties, "url"), next) {
 		char curlheader[SWUPDATE_GENERAL_STRING_SIZE + strlen(CUSTOM_HEADER)];
 
-		if (!url->key || !url->value || strcmp(url->key, "url"))
-			continue;
-
 		conn = (struct curlconn *)calloc(1, sizeof(struct curlconn));
 		if (!conn) {
 			ERROR("FAULT: no memory");
diff --git a/include/swupdate_dict.h b/include/swupdate_dict.h
index 2cae5ca..82988ac 100644
--- a/include/swupdate_dict.h
+++ b/include/swupdate_dict.h
@@ -10,19 +10,29 @@ 
 
 #include <bsdqueue.h>
 
+struct dict_list_elem {
+	char *value;
+	LIST_ENTRY(dict_list_elem) next;
+};
+
+LIST_HEAD(dict_list, dict_list_elem);
+
 struct dict_entry {
 	char *key;
-	char *value;
+	struct dict_list list;
 	LIST_ENTRY(dict_entry) next;
 };
 
 LIST_HEAD(dict, dict_entry);
 
-char *dict_get_value(struct dict *dictionary, char *key);
-int dict_set_value(struct dict *dictionary, char *key, char *value);
-int dict_insert_entry(struct dict *dictionary, char *key, char *value);
-void dict_remove(struct dict *dictionary, char *key);
-void dict_remove_entry(struct dict_entry *entry);
+char *dict_entry_get_key(struct dict_entry *entry);
+char *dict_entry_get_value(struct dict_entry *entry);
+
+struct dict_list *dict_get_list(struct dict *dictionary, const char *key);
+char *dict_get_value(struct dict *dictionary, const char *key);
+int dict_set_value(struct dict *dictionary, const char *key, const char *value);
+int dict_insert_value(struct dict *dictionary, const char *key, const char *value);
+void dict_remove(struct dict *dictionary, const char *key);
 void dict_drop_db(struct dict *dictionary);
 
 #endif
diff --git a/parser/parser.c b/parser/parser.c
index 04cd549..2e9208d 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -162,7 +162,7 @@  static void add_properties(parsertype p, void *node, struct img_type *image)
 				key,
 				value
 			);
-			if (dict_insert_entry(&image->properties, key, value))
+			if (dict_insert_value(&image->properties, key, value))
 				ERROR("Property not stored, skipping...");
 
 		}
diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index c84de65..02ffb3a 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -1336,7 +1336,10 @@  int get_target_data_length(void)
 	struct dict_entry *entry;
 
 	LIST_FOREACH(entry, &server_hawkbit.configdata, next) {
-		len += strlen(entry->key) + strlen(entry->value) + strlen (" : ") + 6;
+		char *key = dict_entry_get_key(entry);
+		char *value = dict_entry_get_value(entry);
+
+		len += strlen(key) + strlen(value) + strlen (" : ") + 6;
 	}
 
 	return len;
@@ -1367,17 +1370,20 @@  server_op_res_t server_send_target_data(void)
 
 	char *keyvalue = NULL;
 	LIST_FOREACH(entry, &server_hawkbit.configdata, next) {
+		char *key = dict_entry_get_key(entry);
+		char *value = dict_entry_get_value(entry);
+
 		if (ENOMEM_ASPRINTF ==
 		    asprintf(&keyvalue, config_data,
 				((first) ? ' ' : ','),
-				entry->key,
-				entry->value)) {
+				key,
+				value)) {
 			ERROR("hawkBit server reply cannot be sent because of OOM.\n");
 			result = SERVER_EINIT;
 			goto cleanup;
 		}
 		first = false;
-		TRACE("KEYVALUE=%s %s %s", keyvalue, entry->key, entry->value);
+		TRACE("KEYVALUE=%s %s %s", keyvalue, key, value);
 		strcat(configData, keyvalue);
 		free(keyvalue);