MINOR: ssl: split the line parsing of the crt-list

In order to reuse the crt-list line parsing in "add ssl crt-list",
the line parsing was extracted from crtlist_parse_file() to a new
function crtlist_parse_line().

This function fills a crtlist_entry structure with a bind_ssl_conf and
the list of filters.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index adc95da..198d0dd 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -4689,6 +4689,137 @@
 
 }
 
+/*
+ *  Read a single crt-list line. /!\ alter the <line> string.
+ *  Fill <crt_path> and <crtlist_entry>
+ *  <crtlist_entry> must be alloc and free by the caller
+ *  <crtlist_entry->ssl_conf> is alloc by the function
+ *  <crtlist_entry->filters> is alloc by the function
+ *  <crt_path> is a ptr in <line>
+ *  Return an error code
+ */
+static int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, char **err)
+{
+	int cfgerr = 0;
+	int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
+	char *end;
+	char *args[MAX_CRT_ARGS + 1];
+	struct ssl_bind_conf *ssl_conf = NULL;
+
+	if (!line || !crt_path || !entry)
+		return ERR_ALERT | ERR_FATAL;
+
+	end = line + strlen(line);
+	if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
+		/* Check if we reached the limit and the last char is not \n.
+		 * Watch out for the last line without the terminating '\n'!
+		 */
+		memprintf(err, "line %d too long in file '%s', limit is %d characters",
+		          linenum, file, CRT_LINESIZE-1);
+		cfgerr |= ERR_ALERT | ERR_FATAL;
+		goto error;
+	}
+	arg = 0;
+	newarg = 1;
+	while (*line) {
+		if (isspace((unsigned char)*line)) {
+			newarg = 1;
+			*line = 0;
+		} else if (*line == '[') {
+			if (ssl_b) {
+				memprintf(err, "too many '[' on line %d in file '%s'.", linenum, file);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto error;
+			}
+			if (!arg) {
+				memprintf(err, "file must start with a cert on line %d in file '%s'", linenum, file);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto error;
+			}
+			ssl_b = arg;
+			newarg = 1;
+			*line = 0;
+		} else if (*line == ']') {
+			if (ssl_e) {
+				memprintf(err, "too many ']' on line %d in file '%s'.", linenum, file);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto error;
+			}
+			if (!ssl_b) {
+				memprintf(err, "missing '[' in line %d in file '%s'.", linenum, file);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto error;
+			}
+			ssl_e = arg;
+			newarg = 1;
+			*line = 0;
+		} else if (newarg) {
+			if (arg == MAX_CRT_ARGS) {
+				memprintf(err, "too many args on line %d in file '%s'.", linenum, file);
+				cfgerr |= ERR_ALERT | ERR_FATAL;
+				goto error;
+			}
+			newarg = 0;
+			args[arg++] = line;
+		}
+		line++;
+	}
+	args[arg++] = line;
+
+	/* empty line */
+	if (!*args[0]) {
+		cfgerr |= ERR_NONE;
+		goto error;
+	}
+
+	*crt_path = args[0];
+
+	ssl_conf = calloc(1, sizeof *ssl_conf);
+	if (!ssl_conf) {
+		memprintf(err, "not enough memory!");
+		cfgerr |= ERR_ALERT | ERR_FATAL;
+		goto error;
+	}
+	cur_arg = ssl_b ? ssl_b : 1;
+	while (cur_arg < ssl_e) {
+		newarg = 0;
+		for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
+			if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
+				newarg = 1;
+				cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, NULL, ssl_conf, err);
+				if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
+					memprintf(err, "ssl args out of '[]' for %s on line %d in file '%s'",
+					          args[cur_arg], linenum, file);
+					cfgerr |= ERR_ALERT | ERR_FATAL;
+					goto error;
+				}
+				cur_arg += 1 + ssl_bind_kws[i].skip;
+				break;
+			}
+		}
+		if (!cfgerr && !newarg) {
+			memprintf(err, "unknown ssl keyword %s on line %d in file '%s'.",
+				  args[cur_arg], linenum, file);
+			cfgerr |= ERR_ALERT | ERR_FATAL;
+			goto error;
+		}
+	}
+
+	entry->ssl_conf = ssl_conf;
+	entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
+	entry->fcount = arg - cur_arg - 1;
+
+	return cfgerr;
+
+error:
+	crtlist_free_filters(entry->filters);
+	entry->filters = NULL;
+	ssl_sock_free_ssl_conf(entry->ssl_conf);
+	free(entry->ssl_conf);
+	entry->ssl_conf = NULL;
+	return cfgerr;
+}
+
 /* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
  * Fill the <crtlist> argument with a pointer to a new crtlist struct
  *
@@ -4697,6 +4828,7 @@
 static int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
 {
 	struct crtlist *newlist;
+	struct crtlist_entry *entry = NULL;
 	char thisline[CRT_LINESIZE];
 	char path[MAXPATHLEN+1];
 	FILE *f;
@@ -4718,13 +4850,11 @@
 	memcpy(newlist->node.key, file, strlen(file) + 1);
 	newlist->entries = EB_ROOT;
 	newlist->bind_conf = NULL;
+	newlist->node.node.leaf_p = NULL;
 	LIST_INIT(&newlist->ord_entries);
 
 	while (fgets(thisline, sizeof(thisline), f) != NULL) {
-		struct crtlist_entry *entry;
-		int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
 		char *end;
-		char *args[MAX_CRT_ARGS + 1];
 		char *line = thisline;
 		char *crt_path;
 		struct ssl_bind_conf *ssl_conf = NULL;
@@ -4741,100 +4871,45 @@
 			cfgerr |= ERR_ALERT | ERR_FATAL;
 			break;
 		}
-		arg = 0;
-		newarg = 1;
-		while (*line) {
-			if (*line == '#' || *line == '\n' || *line == '\r') {
-				/* end of string, end of loop */
-				*line = 0;
-				break;
-			} else if (isspace((unsigned char)*line)) {
-				newarg = 1;
-				*line = 0;
-			} else if (*line == '[') {
-				if (ssl_b) {
-					memprintf(err, "too many '[' on line %d in file '%s'.", linenum, file);
-					cfgerr |= ERR_ALERT | ERR_FATAL;
-					break;
-				}
-				if (!arg) {
-					memprintf(err, "file must start with a cert on line %d in file '%s'", linenum, file);
-					cfgerr |= ERR_ALERT | ERR_FATAL;
-					break;
-				}
-				ssl_b = arg;
-				newarg = 1;
-				*line = 0;
-			} else if (*line == ']') {
-				if (ssl_e) {
-					memprintf(err, "too many ']' on line %d in file '%s'.", linenum, file);
-					cfgerr |= ERR_ALERT | ERR_FATAL;
-					break;
-				}
-				if (!ssl_b) {
-					memprintf(err, "missing '[' in line %d in file '%s'.", linenum, file);
-					cfgerr |= ERR_ALERT | ERR_FATAL;
-					break;
-				}
-				ssl_e = arg;
-				newarg = 1;
-				*line = 0;
-			} else if (newarg) {
-				if (arg == MAX_CRT_ARGS) {
-					memprintf(err, "too many args on line %d in file '%s'.", linenum, file);
-					cfgerr |= ERR_ALERT | ERR_FATAL;
-					break;
-				}
-				newarg = 0;
-				args[arg++] = line;
-			}
-			line++;
+
+		if (*line == '#' || *line == '\n' || *line == '\r')
+			continue;
+
+		entry = malloc(sizeof(*entry));
+		if (entry == NULL) {
+			memprintf(err, "Not enough memory!");
+			cfgerr |= ERR_ALERT | ERR_FATAL;
+			goto error;
 		}
+		entry->fcount = 0;
+		entry->filters = NULL;
+		entry->ssl_conf = NULL;
+		entry->node.key = NULL;
+		LIST_INIT(&entry->by_crtlist);
+
+		*(end - 1) = '\0'; /* line parser mustn't receive any \n */
+		cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, err);
 		if (cfgerr)
-			break;
-		args[arg++] = line;
+			goto error;
 
 		/* empty line */
-		if (!*args[0])
+		if (!crt_path || !*crt_path) {
+			free(entry);
+			entry = NULL;
 			continue;
+		}
 
-		crt_path = args[0];
 		if (*crt_path != '/' && global_ssl.crt_base) {
 			if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > MAXPATHLEN) {
 				memprintf(err, "'%s' : path too long on line %d in file '%s'",
 					  crt_path, linenum, file);
 				cfgerr |= ERR_ALERT | ERR_FATAL;
-				break;
+				goto error;
 			}
 			snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, crt_path);
 			crt_path = path;
 		}
 
-		ssl_conf = calloc(1, sizeof *ssl_conf);
-		cur_arg = ssl_b ? ssl_b : 1;
-		while (cur_arg < ssl_e) {
-			newarg = 0;
-			for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
-				if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
-					newarg = 1;
-					cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, curproxy, ssl_conf, err);
-					if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
-						memprintf(err, "ssl args out of '[]' for %s on line %d in file '%s'",
-							  args[cur_arg], linenum, file);
-						cfgerr |= ERR_ALERT | ERR_FATAL;
-					}
-					cur_arg += 1 + ssl_bind_kws[i].skip;
-					break;
-				}
-			}
-			if (!cfgerr && !newarg) {
-				memprintf(err, "unknown ssl keyword %s on line %d in file '%s'.",
-					  args[cur_arg], linenum, file);
-				cfgerr |= ERR_ALERT | ERR_FATAL;
-				break;
-			}
-		}
-
 		/* Look for a ckch_store or create one */
 		ckchs = ckchs_lookup(crt_path);
 		if (ckchs == NULL) {
@@ -4845,31 +4920,19 @@
 		}
 		if (ckchs == NULL)
 			cfgerr |= ERR_ALERT | ERR_FATAL;
-
-		entry = malloc(sizeof(*entry));
-		if (entry == NULL) {
-			memprintf(err, "Not enough memory!");
-			cfgerr |= ERR_ALERT | ERR_FATAL;
-		}
 
-		if (cfgerr & ERR_CODE) {
-			free(entry);
-			entry = NULL;
-			ssl_sock_free_ssl_conf(ssl_conf);
-			free(ssl_conf);
-			ssl_conf = NULL;
+		if (cfgerr & ERR_CODE)
 			goto error;
-		}
+
 		entry->node.key = ckchs;
 		entry->ssl_conf = ssl_conf;
 		entry->crtlist = newlist;
 		LIST_INIT(&entry->ckch_inst);
-		/* filters */
-		entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
-		entry->fcount = arg - cur_arg - 1;
 		ebpt_insert(&newlist->entries, &entry->node);
 		LIST_ADDQ(&newlist->ord_entries, &entry->by_crtlist);
 		LIST_ADDQ(&ckchs->crtlist_entry, &entry->by_ckch_store);
+
+		entry = NULL;
 	}
 	if (cfgerr & ERR_CODE)
 		goto error;
@@ -4879,6 +4942,15 @@
 
 	return cfgerr;
 error:
+	if (entry) {
+		crtlist_free_filters(entry->filters);
+		entry->filters = NULL;
+		ssl_sock_free_ssl_conf(entry->ssl_conf);
+		free(entry->ssl_conf);
+		entry->ssl_conf = NULL;
+		free(entry);
+	}
+
 	fclose(f);
 	crtlist_free(newlist);
 	return cfgerr;