MINOR: ssl: directories are loaded like crt-list

Generate a directory cache with the crtlist and crtlist_entry structures.

With this new model, directories are a special case of the crt-lists.
A directory is a crt-list which allows only one occurence of each file,
without SSL configuration (ssl_bind_conf) and without filters.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 7fa4154..f1b0ba9 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -4429,10 +4429,15 @@
 	return errcode;
 }
 
-/* Read a directory and open its certificates
- * Returns a set of ERR_* flags possibly with an error in <err>. */
-static int ssl_sock_load_cert_dir(char *path, struct bind_conf *bind_conf, char **err)
+
+/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
+ * Fill the <crtlist> argument with a pointer to a new crtlist struct
+ *
+ * This function tries to open and store certificate files.
+ */
+static int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
 {
+	struct crtlist *dir;
 	struct dirent **de_list;
 	int i, n;
 	struct stat buf;
@@ -4449,6 +4454,14 @@
 	for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
 		*end = 0;
 
+	dir = malloc(sizeof(*dir) + strlen(path) + 1);
+	if (dir == NULL) {
+		memprintf(err, "not enough memory");
+		return ERR_ALERT | ERR_FATAL;
+	}
+	memcpy(dir->node.key, path, strlen(path) + 1);
+	dir->entries = EB_ROOT_UNIQUE; /* it's a directory, files are unique */
+
 	n = scandir(path, &de_list, 0, alphasort);
 	if (n < 0) {
 		memprintf(err, "%sunable to scan directory '%s' : %s.\n",
@@ -4457,13 +4470,20 @@
 	}
 	else {
 		for (i = 0; i < n; i++) {
+			struct crtlist_entry *entry;
 			struct dirent *de = de_list[i];
-			struct ckch_inst *ckch_inst = NULL;
 
 			end = strrchr(de->d_name, '.');
 			if (end && (!strcmp(end, ".issuer") || !strcmp(end, ".ocsp") || !strcmp(end, ".sctl") || !strcmp(end, ".key")))
 				goto ignore_entry;
 
+			entry = malloc(sizeof(*entry));
+			if (entry == NULL) {
+				memprintf(err, "not enough memory '%s'", fp);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto ignore_entry;
+			}
+
 			snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
 			if (stat(fp, &buf) != 0) {
 				memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
@@ -4503,31 +4523,64 @@
 					}
 
 					snprintf(fp, sizeof(fp), "%s/%.*s", path, dp_len, de->d_name);
-					if ((ckchs = ckchs_lookup(fp)) == NULL)
-						ckchs =  ckchs_load_cert_file(fp, 1,  err);
-					if (!ckchs)
+					ckchs = ckchs_lookup(fp);
+					if (ckchs == NULL)
+						ckchs = ckchs_load_cert_file(fp, 1,  err);
+					if (ckchs == NULL) {
+						free(de);
+						free(entry);
 						cfgerr |= ERR_ALERT | ERR_FATAL;
-					else
-						cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err);
+						goto end;
+					}
+
+					entry->node.key = ckchs;
+					entry->ssl_conf = NULL; /* directories don't use ssl_conf */
+					ebpt_insert(&dir->entries, &entry->node);
+
 					/* Successfully processed the bundle */
 					goto ignore_entry;
 				}
 			}
 
 #endif
-			if ((ckchs = ckchs_lookup(fp)) == NULL)
-				ckchs =  ckchs_load_cert_file(fp, 0,  err);
-			if (!ckchs)
+			ckchs = ckchs_lookup(fp);
+			if (ckchs == NULL)
+				ckchs = ckchs_load_cert_file(fp, 0,  err);
+			if (ckchs == NULL) {
+				free(de);
+				free(entry);
 				cfgerr |= ERR_ALERT | ERR_FATAL;
-			else
-				cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err);
+				goto end;
+			}
+			entry->node.key = ckchs;
+			entry->ssl_conf = NULL; /* directories don't use ssl_conf */
+			ebpt_insert(&dir->entries, &entry->node);
 
 ignore_entry:
 			free(de);
 		}
+end:
 		free(de_list);
 	}
+
+	if (cfgerr & ERR_CODE) {
+		/* free the dir and entries on error */
+		struct ebpt_node *node;
+
+		node = ebpt_first(&dir->entries);
+		while (node) {
+			struct crtlist_entry *entry;
+
+			entry = ebpt_entry(node, typeof(*entry), node);
+			node = ebpt_next(node);
+			ebpt_delete(&entry->node);
+			free(entry);
+		}
+		free(dir);
+	}
+
 	return cfgerr;
+
 }
 
 
@@ -4835,7 +4888,7 @@
  *
  *  Returns a set of ERR_* flags possibly with an error in <err>.
  */
-int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
+int ssl_sock_load_cert_list_file(char *file, int dir, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
 {
 	struct crtlist *crtlist = NULL;
 	struct ebmb_node *eb;
@@ -4849,7 +4902,12 @@
 	if (eb) {
 		crtlist = ebmb_entry(eb, struct crtlist, node);
 	} else {
-		cfgerr |= crtlist_parse_file(file, bind_conf, curproxy, &crtlist, err);
+		/* load a crt-list OR a directory */
+		if (dir)
+			cfgerr |= crtlist_load_cert_dir(file, bind_conf, &crtlist, err);
+		else
+			cfgerr |= crtlist_parse_file(file, bind_conf, curproxy, &crtlist, err);
+
 		if (!(cfgerr & ERR_CODE))
 			ebst_insert(&crtlists_tree, &crtlist->node);
 	}
@@ -4917,7 +4975,7 @@
 
 			return ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err);
 		} else {
-			return ssl_sock_load_cert_dir(path, bind_conf, err);
+			return ssl_sock_load_cert_list_file(path, 1, bind_conf, bind_conf->frontend, err);
 		}
 	} else {
 		/* stat failed, could be a bundle */
@@ -9021,7 +9079,7 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	err_code = ssl_sock_load_cert_list_file(args[cur_arg + 1], conf, px, err);
+	err_code = ssl_sock_load_cert_list_file(args[cur_arg + 1], 0, conf, px, err);
 	if (err_code)
 		memprintf(err, "'%s' : %s", args[cur_arg], *err);