Ensure we leave room for the trailing NUL
[freeradius.git] / src / main / conffile.c
index 07da6ac..ee47afb 100644 (file)
@@ -447,6 +447,42 @@ static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
        return cs;
 }
 
+/*
+ *     Replace pair in a given section with a new pair,
+ *     of the given value.
+ */
+int cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, const char *value)
+{
+       CONF_PAIR *newp;
+       CONF_ITEM *ci, *cn, **last;
+
+       newp = cf_pair_alloc(cp->attr, value, cp->operator, cp->value_type,
+                            cs);
+       if (!newp) return -1;
+
+       ci = cf_pairtoitem(cp);
+       cn = cf_pairtoitem(newp);
+
+       /*
+        *      Find the old one from the linked list, and replace it
+        *      with the new one.
+        */
+       for (last = &cs->children; (*last) != NULL; last = &(*last)->next) {
+               if (*last == ci) {
+                       cn->next = (*last)->next;
+                       *last = cn;
+                       ci->next = NULL;
+                       break;
+               }
+       }
+
+       rbtree_deletebydata(cs->pair_tree, ci);
+
+       rbtree_insert(cs->pair_tree, cn);
+
+       return 0;
+}
+
 
 /*
  *     Add an item to a configuration section.
@@ -481,11 +517,13 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci)
 
                                if (!cs->section_tree) {
                                        cs->section_tree = rbtree_create(section_cmp, NULL, 0);
-                                       /* ignore any errors */
+                                       if (!cs->section_tree) {
+                                               radlog(L_ERR, "Out of memory");
+                                               _exit(1);
+                                       }
                                }
 
-                               if (cs->section_tree) {
-                                       rbtree_insert(cs->section_tree, cs_new);                                }
+                               rbtree_insert(cs->section_tree, cs_new);
 
                                /*
                                 *      Two names: find the named instance.
@@ -564,6 +602,8 @@ CONF_ITEM *cf_reference_item(const CONF_SECTION *parentcs,
                 *      "foo.bar.baz" means "from the root"
                 */
        } else if (strchr(p, '.') != NULL) {
+               if (!parentcs) goto no_such_item;
+
                cs = parentcs;
        }
 
@@ -634,7 +674,7 @@ CONF_ITEM *cf_reference_item(const CONF_SECTION *parentcs,
        /*
         *      "foo" is "in the current section, OR in main".
         */
-       if ((p == name) && (cs != parentcs)) {
+       if ((p == name) && (parentcs != NULL) && (cs != parentcs)) {
                cs = parentcs;
                goto retry;
        }
@@ -728,6 +768,11 @@ static const char *cf_expand_variables(const char *cf, int *lineno,
                         *  Substitute the value of the variable.
                         */
                        cp = cf_itemtopair(ci);
+                       if (!cp->value) {
+                               radlog(L_ERR, "%s[%d]: Reference \"%s\" has no value",
+                                      cf, *lineno, input);
+                               return NULL;
+                       }
                        strcpy(p, cp->value);
                        p += strlen(p);
                        ptr = end + 1;
@@ -806,10 +851,10 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
        char **q;
        const char *value;
        fr_ipaddr_t ipaddr;
-       const CONF_PAIR *cp;
+       const CONF_PAIR *cp = NULL;
        char ipbuf[128];
 
-       cp = cf_pair_find(cs, name);
+       if (cs) cp = cf_pair_find(cs, name);
        if (cp) {
                value = cp->value;
 
@@ -863,7 +908,9 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                if (value == dflt) {
                        char buffer[8192];
 
-                       int lineno = cs->item.lineno;
+                       int lineno = 0;
+
+                       if (cs) lineno = cs->item.lineno;
 
                        /*
                         *      FIXME: sizeof(buffer)?
@@ -897,7 +944,9 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                if (value == dflt) {
                        char buffer[8192];
 
-                       int lineno = cs->item.lineno;
+                       int lineno = 0;
+
+                       if (cs) lineno = cs->item.lineno;
 
                        /*
                         *      FIXME: sizeof(buffer)?
@@ -1119,6 +1168,7 @@ static int condition_looks_ok(const char **ptr)
                                 *      Parse error.
                                 */
                                if (*q != '{') {
+                                       DEBUG2("Expected open brace '{' after condition at %s", p);
                                        return 0;
                                }
 
@@ -1142,6 +1192,7 @@ static int condition_looks_ok(const char **ptr)
                }
        }
 
+       DEBUG3("Unexpected error");
        return 0;
 }
 
@@ -1194,12 +1245,12 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
         *      Read, checking for line continuations ('\\' at EOL)
         */
        for (;;) {
-               int eof;
+               int at_eof;
 
                /*
                 *      Get data, and remember if we are at EOF.
                 */
-               eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
+               at_eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
                (*lineno)++;
 
                /*
@@ -1221,14 +1272,14 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                 *      conditions.
                 */
                if (cbuf == buf) {
-                       if (eof) break;
+                       if (at_eof) break;
                        
                        ptr = buf;
                        while (*ptr && isspace((int) *ptr)) ptr++;
 
                        if (!*ptr || (*ptr == '#')) continue;
 
-               } else if (eof || (len == 0)) {
+               } else if (at_eof || (len == 0)) {
                        radlog(L_ERR, "%s[%d]: Continuation at EOF is illegal",
                               filename, *lineno);
                        return -1;
@@ -1256,10 +1307,15 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                 */
                while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
 
-               if ((ptr[0] == '%') && (ptr[1] == '{')) {
+               if (((ptr[0] == '%') && (ptr[1] == '{')) ||
+                   (ptr[0] == '`')) {
                        int hack;
 
-                       hack = rad_copy_variable(buf1, ptr);
+                       if (ptr[0] == '%') {
+                               hack = rad_copy_variable(buf1, ptr);
+                       } else {
+                               hack = rad_copy_string(buf1, ptr);
+                       }
                        if (hack < 0) {
                                radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
                                       filename, *lineno, ptr);
@@ -1269,7 +1325,6 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                        t1 = T_BARE_WORD;
                        ptr += hack;
 
-
                        t2 = gettoken(&ptr, buf2, sizeof(buf2));
                        switch (t2) {
                        case T_EOL:
@@ -1409,12 +1464,19 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
 
               if (strcasecmp(buf1, "$template") == 0) {
                       CONF_ITEM *ci;
-                      CONF_SECTION *parentcs;
+                      CONF_SECTION *parentcs, *templatecs;
                       t2 = getword(&ptr, buf2, sizeof(buf2));
 
                       parentcs = cf_top_section(current);
 
-                      ci = cf_reference_item(parentcs, this, buf2);
+                      templatecs = cf_section_sub_find(parentcs, "templates");
+                      if (!templatecs) {
+                               radlog(L_ERR, "%s[%d]: No \"templates\" section for reference \"%s\"",
+                                      filename, *lineno, buf2);
+                               return -1;
+                      }
+
+                      ci = cf_reference_item(parentcs, templatecs, buf2);
                       if (!ci || (ci->type != CONF_ITEM_SECTION)) {
                                radlog(L_ERR, "%s[%d]: Reference \"%s\" not found",
                                       filename, *lineno, buf2);
@@ -1449,6 +1511,7 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                case T_EOL:
                case T_HASH:
                do_bare_word:
+                       t3 = t2;
                        t2 = T_OP_EQ;
                        value = NULL;
                        goto do_set;
@@ -1458,6 +1521,7 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                case T_OP_SUB:
                case T_OP_LE:
                case T_OP_GE:
+               case T_OP_CMP_FALSE:
                        if (!this || (strcmp(this->name1, "update") != 0)) {
                                radlog(L_ERR, "%s[%d]: Invalid operator in assignment",
                                       filename, *lineno);
@@ -1466,8 +1530,13 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
 
                case T_OP_EQ:
                case T_OP_SET:
-               do_set:
                        t3 = getstring(&ptr, buf3, sizeof(buf3));
+                       if (t3 == T_OP_INVALID) {
+                               radlog(L_ERR, "%s[%d]: Parse error: %s",
+                                      filename, *lineno,
+                                      fr_strerror());
+                               return -1;
+                       }
 
                        /*
                         *      Handle variable substitution via ${foo}
@@ -1487,6 +1556,7 @@ static int cf_section_read(const char *filename, int *lineno, FILE *fp,
                        /*
                         *      Add this CONF_PAIR to our CONF_SECTION
                         */
+               do_set:
                        cpn = cf_pair_alloc(buf1, value, t2, t3, this);
                        cpn->item.filename = filename;
                        cpn->item.lineno = *lineno;
@@ -1639,6 +1709,12 @@ int cf_file_include(const char *filename, CONF_SECTION *cs)
                return -1;
        }
 
+       if (cf_data_find_internal(cs, filename, PW_TYPE_FILENAME)) {
+               radlog(L_ERR, "Cannot include the same file twice: \"%s\"",
+                      filename);
+               return -1;
+       }
+
        /*
         *      Add the filename to the section
         */
@@ -1648,7 +1724,7 @@ int cf_file_include(const char *filename, CONF_SECTION *cs)
        if (cf_data_add_internal(cs, filename, mtime, free,
                                 PW_TYPE_FILENAME) < 0) {
                fclose(fp);
-               radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+               radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
                       filename);
                return -1;
        }
@@ -1656,7 +1732,7 @@ int cf_file_include(const char *filename, CONF_SECTION *cs)
        cd = cf_data_find_internal(cs, filename, PW_TYPE_FILENAME);
        if (!cd) {
                fclose(fp);
-               radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+               radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
                       filename);
                return -1;
        }
@@ -1761,7 +1837,7 @@ const char *cf_pair_value(CONF_PAIR *pair)
 /*
  *     Copied here for error reporting.
  */
-extern void librad_log(const char *, ...);
+extern void fr_strerror_printf(const char *, ...);
 
 /*
  * Turn a CONF_PAIR into a VALUE_PAIR
@@ -1772,12 +1848,12 @@ VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
        VALUE_PAIR *vp;
 
        if (!pair) {
-               librad_log("Internal error");
+               fr_strerror_printf("Internal error");
                return NULL;
        }
 
        if (!pair->value) {
-               librad_log("No value given for attribute %s", pair->attr);
+               fr_strerror_printf("No value given for attribute %s", pair->attr);
                return NULL;
        }
 
@@ -1895,10 +1971,12 @@ CONF_SECTION *cf_section_sub_find(const CONF_SECTION *cs, const char *name)
 {
        CONF_ITEM *ci;
 
+       if (!name) return NULL; /* can't find an un-named section */
+
        /*
         *      Do the fast lookup if possible.
         */
-       if (name && cs->section_tree) {
+       if (cs->section_tree) {
                CONF_SECTION mycs;
 
                mycs.name1 = name;
@@ -2387,25 +2465,25 @@ void cf_log_err(CONF_ITEM *ci, const char *fmt, ...)
 }
 
 
-void cf_log_info(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_info(CONF_SECTION *cs, const char *fmt, ...)
 {
        va_list ap;
 
        va_start(ap, fmt);
-       if (debug_flag > 1 && cf_log_config) vradlog(L_DBG, fmt, ap);
+       if (debug_flag > 1 && cf_log_config && cs) vradlog(L_DBG, fmt, ap);
        va_end(ap);
 }
 
 /*
  *     Wrapper to simplify the code.
  */
-void cf_log_module(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_module(CONF_SECTION *cs, const char *fmt, ...)
 {
        va_list ap;
        char buffer[256];
 
        va_start(ap, fmt);
-       if (debug_flag > 1 && cf_log_modules) {
+       if (debug_flag > 1 && cf_log_modules && cs) {
                vsnprintf(buffer, sizeof(buffer), fmt, ap);
 
                radlog(L_DBG, " Module: %s", buffer);
@@ -2413,6 +2491,12 @@ void cf_log_module(UNUSED CONF_SECTION *cs, const char *fmt, ...)
        va_end(ap);
 }
 
+const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs)
+{
+       if (!cs) return NULL;
+
+       return cs->variables;
+}
 
 #if 0
 /*
@@ -2464,3 +2548,159 @@ int dump_config(CONF_SECTION *cs)
        return dump_config_section(cs, 0);
 }
 #endif
+
+static const char *cf_pair_print_value(const CONF_PAIR *cp,
+                                      char *buffer, size_t buflen)
+{
+       char *p;
+
+       if (!cp->value) return "";
+
+       switch (cp->value_type) {
+       default:
+       case T_BARE_WORD:
+               snprintf(buffer, buflen, "%s", cp->value);
+               break;
+
+       case T_SINGLE_QUOTED_STRING:
+               snprintf(buffer, buflen, "'%s'", cp->value);
+               break;
+
+       case T_DOUBLE_QUOTED_STRING:
+               buffer[0] = '"';
+               fr_print_string(cp->value, strlen(cp->value),
+                               buffer + 1, buflen - 3);
+               p = buffer + strlen(buffer); /* yuck... */
+               p[0] = '"';
+               p[1] = '\0';
+               break;
+       }
+
+       return buffer;
+}
+
+
+int cf_pair2xml(FILE *fp, const CONF_PAIR *cp)
+{
+       fprintf(fp, "<%s>", cp->attr);
+       if (cp->value) {
+               char buffer[2048];
+
+               char *p = buffer;
+               const char *q = cp->value;
+
+               while (*q && (p < (buffer + sizeof(buffer) - 1))) {
+                       if (q[0] == '&') {
+                               memcpy(p, "&amp;", 4);
+                               p += 5;
+
+                       } else if (q[0] == '<') {
+                               memcpy(p, "&lt;", 4);
+                               p += 4;
+
+                       } else if (q[0] == '>') {
+                               memcpy(p, "&gt;", 4);
+                               p += 4;
+
+                       } else {
+                               *(p++) = *q;
+                       }
+                       q++;
+               }
+
+               *p = '\0';
+               fprintf(fp, "%s", buffer);
+       }
+
+       fprintf(fp, "</%s>\n", cp->attr);
+
+       return 1;
+}
+
+int cf_section2xml(FILE *fp, const CONF_SECTION *cs)
+{
+       CONF_ITEM *ci, *next;
+
+       /*
+        *      Section header
+        */
+       fprintf(fp, "<%s>\n", cs->name1);
+       if (cs->name2) {
+               fprintf(fp, "<_name2>%s</_name2>\n", cs->name2);
+       }
+
+       /*
+        *      Loop over contents.
+        */
+       for (ci = cs->children; ci; ci = next) {
+               next = ci->next;
+
+               switch (ci->type) {
+               case CONF_ITEM_PAIR:
+                       if (!cf_pair2xml(fp, (CONF_PAIR *) ci)) return 0;
+                       break;
+
+               case CONF_ITEM_SECTION:
+                       if (!cf_section2xml(fp, (CONF_SECTION *) ci)) return 0;
+                       break;
+
+               default:        /* should really be an error. */
+                       break;
+               
+               }
+       }
+
+       fprintf(fp, "</%s>\n", cs->name1);
+
+       return 1;               /* success */
+}
+
+int cf_pair2file(FILE *fp, const CONF_PAIR *cp)
+{
+       char buffer[2048];
+
+       fprintf(fp, "\t%s = %s\n", cp->attr,
+               cf_pair_print_value(cp, buffer, sizeof(buffer)));
+
+       return 1;
+}
+
+int cf_section2file(FILE *fp, const CONF_SECTION *cs)
+{
+       const CONF_ITEM *ci, *next;
+
+       /*
+        *      Section header
+        */
+       if (!cs->name2) {
+               fprintf(fp, "%s {\n", cs->name1);
+       } else {
+               fprintf(fp, "%s %s {\n",
+                       cs->name1, cs->name2);
+       }
+
+       /*
+        *      Loop over contents.
+        */
+       for (ci = cs->children; ci; ci = next) {
+               next = ci->next;
+
+               switch (ci->type) {
+               case CONF_ITEM_PAIR:
+                       if (!cf_pair2file(fp, (const CONF_PAIR *) ci)) return 0;
+                       break;
+
+               case CONF_ITEM_SECTION:
+                       if (!cf_section2file(fp, (const CONF_SECTION *) ci)) return 0;
+                       break;
+
+               default:        /* should really be an error. */
+                       break;
+               
+               }
+       }
+
+       fprintf(fp, "}\n");
+
+       return 1;               /* success */
+}