Initialize VP to NULL.
[freeradius.git] / src / modules / rlm_rest / rest.c
index 5762695..22ad177 100644 (file)
@@ -1,9 +1,4 @@
-/** Functions and datatypes for the REST (HTTP) transport.
- *
- * @file rest.c
- *
- * Version:    $Id$
- *
+/*
  *   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
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+/*
+ * $Id$
+ *
+ * @brief Functions and datatypes for the REST (HTTP) transport.
+ * @file rest.c
  *
- * Copyright 2012  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
+ * @copyright 2012-2013  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
  */
 
 #include <freeradius-devel/ident.h>
@@ -47,7 +49,7 @@ const http_body_type_t http_body_type_supported[HTTP_BODY_NUM_ENTRIES] = {
        HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNSUPPORTED
        HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_INVALID
        HTTP_BODY_POST,         // HTTP_BODY_POST
-#ifdef WITH_JSON
+#ifdef HAVE_JSON
        HTTP_BODY_JSON,         // HTTP_BODY_JSON
 #else
        HTTP_BODY_UNAVAILABLE,
@@ -183,6 +185,7 @@ const FR_NAME_NUMBER http_content_type_table[] = {
        {  NULL , -1 }
 };
 
+#ifdef HAVE_JSON
 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
  *
  * These fields are set when parsing the expanded format for value pairs in
@@ -192,12 +195,11 @@ const FR_NAME_NUMBER http_content_type_table[] = {
  * @see json_pairmake
  * @see json_pairmake_leaf
  */
-#ifdef WITH_JSON
 typedef struct json_flags {
-       boolean do_xlat;        //!< If TRUE value will be expanded with xlat.
-       boolean is_json;        //!< If TRUE value will be inserted as raw JSON
+       int do_xlat;            //!< If TRUE value will be expanded with xlat.
+       int is_json;            //!< If TRUE value will be inserted as raw JSON
                                // (multiple values not supported).
-       FR_TOKEN operator;      //!< The operator that determines how the new VP
+       FR_TOKEN op;            //!< The operator that determines how the new VP
                                // is processed. @see fr_tokens
 } json_flags_t;
 #endif
@@ -379,7 +381,7 @@ int rest_socket_alive(void *instance, void *handle)
        long last_socket;
        CURLcode ret;
 
-       curl_easy_getinfo(candle, CURLINFO_LASTSOCKET, &last_socket);
+       ret = curl_easy_getinfo(candle, CURLINFO_LASTSOCKET, &last_socket);
        if (ret != CURLE_OK) {
                radlog(L_ERR,
                       "rlm_rest (%s): Couldn't determine socket"
@@ -476,11 +478,11 @@ static size_t rest_encode_post(void *ptr, size_t size, size_t nmemb,
                        goto end_chunk;
                }
 
-               RDEBUG2("Encoding attribute \"%s\"", current[0]->name);
+               RDEBUG2("Encoding attribute \"%s\"", current[0]->da->name);
 
                if (ctx->state == READ_STATE_ATTR_BEGIN) {
-                       escaped = curl_escape(current[0]->name,
-                                             strlen(current[0]->name));
+                       escaped = curl_escape(current[0]->da->name,
+                                             strlen(current[0]->da->name));
                        len = strlen(escaped);
 
                        if (s < (1 + len)) {
@@ -579,7 +581,7 @@ static size_t rest_encode_post(void *ptr, size_t size, size_t nmemb,
  * successive calls will return additional encoded VALUE_PAIRs.
  *
  * Only complete attribute headers
- * @verbatim "<name>":{"type":"<type>","value":['</pre> @endverbatim
+ * @verbatim "<name>":{"type":"<type>","value":[' @endverbatim
  * and complete attribute values will be written to ptr.
  *
  * If an attribute occurs multiple times in the request the attribute values
@@ -655,18 +657,18 @@ static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
                 *      New attribute, write name, type, and beginning of
                 *      value array.
                 */
-               RDEBUG2("Encoding attribute \"%s\"", current[0]->name);
+               RDEBUG2("Encoding attribute \"%s\"", current[0]->da->name);
                if (ctx->state == READ_STATE_ATTR_BEGIN) {
-                       type = fr_int2str(dict_attr_types, current[0]->type,
+                       type = fr_int2str(dict_attr_types, current[0]->da->type,
                                          "¿Unknown?");
 
                        len  = strlen(type);
-                       len += strlen(current[0]->name);
+                       len += strlen(current[0]->da->name);
 
                        if (s < (23 + len)) goto no_space;
 
                        len = sprintf(p, "\"%s\":{\"type\":\"%s\",\"value\":[" ,
-                                     current[0]->name, type);
+                                     current[0]->da->name, type);
                        p += len;
                        s -= len;
 
@@ -701,9 +703,8 @@ static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
                        /* 
                         *      Multivalued attribute
                         */
-                       if (current[1] && 
-                           ((current[0]->attribute == current[1]->attribute) &&
-                            (current[0]->vendor == current[1]->vendor))) {
+                       if (current[1] &&
+                           (current[0]->da == current[1]->da)) {
                                *p++ = ',';
                                current++;
 
@@ -848,7 +849,7 @@ static ssize_t rest_read_wrapper(char **buffer, rest_read_t func,
  * @see rest_read_ctx_free
  *
  * @param[in] request Current request.
- * @param[in] read to initialise.
+ * @param[in] ctx to initialise.
  * @param[in] sort If TRUE VALUE_PAIRs will be sorted within the VALUE_PAIR
  *     pointer array.
  */
@@ -892,14 +893,8 @@ static void rest_read_ctx_init(REQUEST *request,
        /* TODO: Quicksort would be faster... */
        do {
                for(i = 1; i < count; i++) {
-                       assert(current[i-1]->attribute &&
-                              current[i]->attribute);
-
                        swap = 0;
-                       if ((current[i-1]->vendor > current[i]->vendor) ||
-                           ((current[i-1]->vendor == current[i]->vendor) &&
-                            (current[i-1]->attribute > current[i]->attribute)
-                           )) {
+                       if (current[i-1]->da > current[i]->da) {
                                tmp          = current[i];
                                current[i]   = current[i-1];
                                current[i-1] = tmp;
@@ -916,7 +911,7 @@ static void rest_read_ctx_init(REQUEST *request,
  *
  * @see rest_read_ctx_init
  *
- * @param[in] read to free.
+ * @param[in] ctx to free.
  */
 static void rest_read_ctx_free(rlm_rest_read_t *ctx)
 {
@@ -925,29 +920,6 @@ static void rest_read_ctx_free(rlm_rest_read_t *ctx)
        }
 }
 
-/** Verify that value wasn't truncated when it was converted to a VALUE_PAIR
- *
- * Certain values may be truncated when they're converted into VALUE_PAIRs
- * for example 64bit integers converted to 32bit integers. Warn the user
- * when this happens.
- * 
- * @param[in] raw string from decoder.
- * @param[in] vp containing parsed value.
- */
-static void rest_check_truncation(REQUEST *request, const char *raw,
-                                 VALUE_PAIR *vp)
-{
-       char cooked[1024];
-
-       vp_prints_value(cooked, sizeof(cooked), vp, 0);
-       if (strcmp(raw, cooked) != 0) {
-               RDEBUG("WARNING: Value-Pair does not match POST value, "
-                      "truncation may have occurred");
-               RDEBUG("\tValue (pair) : \"%s\"", cooked);
-               RDEBUG("\tValue (post) : \"%s\"", raw);
-       }
-}
-
 /** Converts POST response into VALUE_PAIRs and adds them to the request
  *
  * Accepts VALUE_PAIRS in the same format as rest_encode_post, but with the
@@ -982,14 +954,16 @@ static int rest_decode_post(rlm_rest_t *instance,
        const char *attribute;
        char *name  = NULL;
        char *value = NULL;
+       
+       char buffer[1024];
 
        const DICT_ATTR *da;
        VALUE_PAIR *vp;
 
        const DICT_ATTR **current, *processed[REST_BODY_MAX_ATTRS + 1];
-       VALUE_PAIR *tmp;
 
-       pair_lists_t list;
+       pair_lists_t list_name;
+       request_refs_t request_name;
        REQUEST *reference = request;
        VALUE_PAIR **vps;
 
@@ -1004,8 +978,7 @@ static int rest_decode_post(rlm_rest_t *instance,
         * Empty response?
         */
        while (isspace(*p)) p++;
-
-       if (p == NULL) return FALSE;
+       if (*p == '\0') return FALSE;
 
        while (((q = strchr(p, '=')) != NULL) &&
               (count < REST_BODY_MAX_ATTRS)) {
@@ -1016,9 +989,18 @@ static int rest_decode_post(rlm_rest_t *instance,
                p = (q + 1);
 
                RDEBUG("Decoding attribute \"%s\"", name);
+               
+               request_name = radius_request_name(&attribute, REQUEST_CURRENT);
+               if (request_name == REQUEST_UNKNOWN) {
+                       RDEBUGW("Invalid request qualifier, skipping");
+
+                       curl_free(name);
+
+                       continue;
+               }
 
-               if (!radius_ref_request(&reference, &attribute)) {
-                       RDEBUG("WARNING: Attribute name refers to outer request"
+               if (!radius_request(&reference, request_name)) {
+                       RDEBUGW("Attribute name refers to outer request"
                               " but not in a tunnel, skipping");
 
                        curl_free(name);
@@ -1026,9 +1008,9 @@ static int rest_decode_post(rlm_rest_t *instance,
                        continue;
                }
 
-               list = radius_list_name(&attribute, PAIR_LIST_REPLY);
-               if (list == PAIR_LIST_UNKNOWN) {
-                       RDEBUG("WARNING: Invalid list qualifier, skipping");
+               list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
+               if (list_name == PAIR_LIST_UNKNOWN) {
+                       RDEBUGW("Invalid list qualifier, skipping");
 
                        curl_free(name);
 
@@ -1037,7 +1019,7 @@ static int rest_decode_post(rlm_rest_t *instance,
 
                da = dict_attrbyname(attribute);
                if (!da) {
-                       RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
+                       RDEBUGW("Attribute \"%s\" unknown, skipping",
                               attribute);
 
                        curl_free(name);
@@ -1045,7 +1027,7 @@ static int rest_decode_post(rlm_rest_t *instance,
                        continue;
                }
 
-               vps = radius_list(reference, list);
+               vps = radius_list(reference, list_name);
 
                assert(vps);
 
@@ -1066,16 +1048,23 @@ static int rest_decode_post(rlm_rest_t *instance,
 
                RDEBUG2("\tLength : %i", curl_len);
                RDEBUG2("\tValue  : \"%s\"", value);
+               
+               RDEBUG("Performing xlat expansion of response value");
+               
+               if (!radius_xlat(buffer, sizeof(buffer),
+                                value, request, NULL, NULL)) {
+                       goto skip;
+               }
 
-               vp = paircreate(da->attr, da->vendor, da->type);
+               vp = pairalloc(NULL, da);
                if (!vp) {
                        radlog(L_ERR, "rlm_rest (%s): Failed creating"
-                              " value-pair", instance->xlat_name);
+                              " valuepair", instance->xlat_name);
 
                        goto error;
                }
 
-               vp->operator = T_OP_SET;
+               vp->op = T_OP_SET;
  
                /*
                 *      Check to see if we've already processed an
@@ -1084,32 +1073,22 @@ static int rest_decode_post(rlm_rest_t *instance,
                 */
                current = processed;
                while (*current++) {
-                       if ((current[0]->attr == da->attr) &&
-                           (current[0]->vendor == da->vendor)) {
-                               vp->operator = T_OP_ADD;
+                       if (current[0] == da) {
+                               vp->op = T_OP_ADD;
                                break;
                        }
                }
                
-               if (vp->operator != T_OP_ADD) {
+               if (vp->op != T_OP_ADD) {
                        current[0] = da;
                        current[1] = NULL;
                }
 
-               tmp = pairparsevalue(vp, value);
-               if (tmp == NULL) {
+               if (!pairparsevalue(vp, buffer)) {
                        RDEBUG("Incompatible value assignment, skipping");
                        pairbasicfree(vp);
                        goto skip;
                }
-               vp = tmp;
-
-               rest_check_truncation(request, value, vp);
-
-               vp->flags.do_xlat = 1;
-
-               RDEBUG("Performing xlat expansion of response value", value);
-               pairxlatmove(request, vps, &vp);
 
                if (++count == REST_BODY_MAX_ATTRS) {
                        radlog(L_ERR, "rlm_rest (%s): At maximum"
@@ -1141,6 +1120,7 @@ static int rest_decode_post(rlm_rest_t *instance,
 
 }
 
+#ifdef HAVE_JSON
 /** Converts JSON "value" key into VALUE_PAIR.
  *
  * If leaf is not in fact a leaf node, but contains JSON data, the data will
@@ -1149,20 +1129,21 @@ static int rest_decode_post(rlm_rest_t *instance,
  * @param[in] instance configuration data.
  * @param[in] section configuration data.
  * @param[in] request Current request.
- * @param[in] attribute name without qualifiers.
+ * @param[in] da Attribute to create.
  * @param[in] flags containing the operator other flags controlling value
  *     expansion.
  * @param[in] leaf object containing the VALUE_PAIR value.
  * @return The VALUE_PAIR just created, or NULL on error.
  */
-#ifdef WITH_JSON
 static VALUE_PAIR *json_pairmake_leaf(rlm_rest_t *instance,
                                      UNUSED rlm_rest_section_t *section,
                                      REQUEST *request, const DICT_ATTR *da,
                                      json_flags_t *flags, json_object *leaf)
 {
-       const char *value;
-       VALUE_PAIR *vp, *tmp;
+       const char *value, *to_parse;
+       char buffer[1024];
+       
+       VALUE_PAIR *vp;
 
        /*
         *      Should encode any nested JSON structures into JSON strings.
@@ -1176,26 +1157,31 @@ static VALUE_PAIR *json_pairmake_leaf(rlm_rest_t *instance,
        RDEBUG2("\tLength : %i", strlen(value));
        RDEBUG2("\tValue  : \"%s\"", value);
 
-       vp = paircreate(da->attr, da->vendor, da->type);
+       if (flags->do_xlat) {
+               if (!radius_xlat(buffer, sizeof(buffer), value,
+                                request, NULL, NULL)) {
+                       return NULL;
+               }
+               
+               to_parse = buffer;
+       } else {
+               to_parse = value;
+       }
+
+       vp = paircreate(da->attr, da->vendor);
        if (!vp) {
-               radlog(L_ERR, "rlm_rest (%s): Failed creating value-pair",
+               radlog(L_ERR, "rlm_rest (%s): Failed creating valuepair",
                       instance->xlat_name);
                return NULL;
        }
 
-       vp->operator = flags->operator;
-
-       tmp = pairparsevalue(vp, value);
-       if (tmp == NULL) {
+       vp->op = flags->op;
+       
+       if (!pairparsevalue(vp, to_parse)) {
                RDEBUG("Incompatible value assignment, skipping");
                pairbasicfree(vp);
                return NULL;
        }
-       vp = tmp;
-
-       rest_check_truncation(request, value, vp);
-
-       if (flags->do_xlat) vp->flags.do_xlat = 1;
 
        return vp;
 }
@@ -1247,7 +1233,7 @@ static VALUE_PAIR *json_pairmake_leaf(rlm_rest_t *instance,
  * @param[in] object containing root node, or parent node.
  * @param[in] level Current nesting level.
  * @param[in] max_attrs counter, decremented after each VALUE_PAIR is created,
- * when 0 no more attributes will be processed.
+ *           when 0 no more attributes will be processed.
  * @return VALUE_PAIR or NULL on error.
  */
 static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
@@ -1265,9 +1251,10 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
        json_flags_t flags;
 
        const DICT_ATTR *da;
-       VALUE_PAIR *vp;
+       VALUE_PAIR *vp = NULL;
        
-       pair_lists_t list;
+       request_refs_t request_name;
+       pair_lists_t list_name;
        REQUEST *reference = request;
        VALUE_PAIR **vps;
 
@@ -1285,7 +1272,7 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
         */
        entry = json_object_get_object(object)->head;
        while (entry) {
-               flags.operator = T_OP_SET;
+               flags.op = T_OP_SET;
                flags.do_xlat  = 1;
                flags.is_json  = 0;
 
@@ -1313,30 +1300,37 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                 *      pairlist.
                 */
                RDEBUG2("Decoding attribute \"%s\"", name);
+               
+               request_name = radius_request_name(&attribute, REQUEST_CURRENT);
+               if (request_name == REQUEST_UNKNOWN) {
+                       RDEBUGW("Request qualifier unknown, skipping");
 
-               if (!radius_ref_request(&reference, &attribute)) {
-                       RDEBUG("WARNING: Attribute name refers to outer request"
+                       continue;
+               }
+
+               if (!radius_request(&reference, request_name)) {
+                       RDEBUGW("Attribute name refers to outer request"
                               " but not in a tunnel, skipping");
 
                        continue;
                }
 
-               list = radius_list_name(&attribute, PAIR_LIST_REPLY);
-               if (list == PAIR_LIST_UNKNOWN) {
-                       RDEBUG("WARNING: Invalid list qualifier, skipping");
+               list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
+               if (list_name == PAIR_LIST_UNKNOWN) {
+                       RDEBUGW("Invalid list qualifier, skipping");
 
                        continue;
                }
 
                da = dict_attrbyname(attribute);
                if (!da) {
-                       RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
+                       RDEBUGW("Attribute \"%s\" unknown, skipping",
                               attribute);
 
                        continue;
                }
 
-               vps = radius_list(reference, list);
+               vps = radius_list(reference, list_name);
 
                assert(vps);
 
@@ -1362,10 +1356,8 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                         */
                        tmp = json_object_object_get(value, "op");
                        if (tmp) {
-                               flags.operator = fr_str2int(fr_tokens,
-                                                           json_object_get_string(tmp), 0);
-
-                               if (!flags.operator) {
+                               flags.op = fr_str2int(fr_tokens, json_object_get_string(tmp), 0);
+                               if (!flags.op) {
                                        RDEBUG("Invalid operator value \"%s\","
                                               " skipping", tmp);
                                        continue;
@@ -1399,62 +1391,55 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                        }
                }
 
-       /*
-        *      Setup pairmake / recursion loop.
-        */
-       if (!flags.is_json &&
-           json_object_is_type(value, json_type_array)) {
-               len = json_object_array_length(value);
-               if (!len) {
-                       RDEBUG("Zero length value array, skipping", value);
-                       continue;
-               }
-               idx = json_object_array_get_idx(value, 0);
-       } else {
-               len = 1;
-               idx = value;
-       }
-
-       i = 0;
-       do {
-               if (!(*max_attrs)--) {
-                               radlog(L_ERR, "rlm_rest (%s): At maximum"
-                                      " attribute limit", instance->xlat_name);
-                               return NULL;
-               }
+               /*
+                *      Setup pairmake / recursion loop.
+                */
+               if (!flags.is_json &&
+                   json_object_is_type(value, json_type_array)) {
+                       len = json_object_array_length(value);
+                       if (!len) {
+                               RDEBUG("Zero length value array, skipping",
+                                      value);
+                               continue;
+                       }
+                       idx = json_object_array_get_idx(value, 0);
+               } else {
+                       len = 1;
+                       idx = value;
+               }
 
-               /*
-                *      Automagically switch the op for multivalued
-                *      attributes.
-                */
-               if (((flags.operator == T_OP_SET) ||
-                    (flags.operator == T_OP_EQ)) && (len > 1)) {
-                       flags.operator = T_OP_ADD;
-               }
+               i = 0;
+               do {
+                       if (!(*max_attrs)--) {
+                                       radlog(L_ERR, "rlm_rest (%s): At "
+                                              "maximum attribute limit",
+                                              instance->xlat_name);
+                                       return NULL;
+                       }
 
-               if (!flags.is_json &&
-                   json_object_is_type(value, json_type_object)) {
-                       /* TODO: Insert nested VP into VP structure...*/
-                       RDEBUG("Found nested VP", value);
-                       vp = json_pairmake(instance, section,
-                                          request, value,
-                                          level + 1, max_attrs);
-               } else {
-                       vp = json_pairmake_leaf(instance, section,
-                                               request, da, &flags,
-                                               idx);
-
-                       if (vp != NULL) {
-                               if (vp->flags.do_xlat) {
-                                       RDEBUG("Performing xlat"
-                                              " expansion of response"
-                                              " value", value);
-                               }
+                       /*
+                        *      Automagically switch the op for multivalued
+                        *      attributes.
+                        */
+                       if (((flags.op == T_OP_SET) ||
+                            (flags.op == T_OP_EQ)) && (len > 1)) {
+                               flags.op = T_OP_ADD;
+                       }
 
-                               pairxlatmove(request, vps, &vp);
+                       if (!flags.is_json &&
+                           json_object_is_type(value, json_type_object)) {
+                               /* TODO: Insert nested VP into VP structure...*/
+                               RDEBUG("Found nested VP", value);
+                               vp = json_pairmake(instance, section,
+                                                  request, value,
+                                                  level + 1, max_attrs);
+                       } else {
+                               vp = json_pairmake_leaf(instance, section,
+                                                       request, da, &flags,
+                                                       idx);
                        }
-               }
-       } while ((++i < len) && (idx = json_object_array_get_idx(value, i)));
+               } while ((++i < len) &&
+                        (idx = json_object_array_get_idx(value, i)));
    }
 
    return vp;
@@ -1464,7 +1449,7 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
  * 
  * Converts the raw JSON string into a json-c object tree and passes it to
  * json_pairmake. After the tree has been parsed json_object_put is called
- * which decrements the reference, count to the root node by one, and frees
+ * which decrements the reference count of the root node by one, and frees
  * the entire tree.
  *
  * @see rest_encode_json
@@ -1472,15 +1457,15 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
  *
  * @param[in] instance configuration data.
  * @param[in] section configuration data.
- * @param[in] g to use.
- * @param[in] request Current request.
+ * @param[in,out] request Current request.
+ * @param[in] handle REST handle.
  * @param[in] raw buffer containing JSON data.
  * @param[in] rawlen Length of data in raw buffer.
  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
  */
 static int rest_decode_json(rlm_rest_t *instance,
                            UNUSED rlm_rest_section_t *section,
-                           UNUSED REQUEST *request, UNUSED void *handle,
+                           REQUEST *request, UNUSED void *handle,
                            char *raw, UNUSED size_t rawlen)
 {
        const char *p = raw;
@@ -1493,7 +1478,7 @@ static int rest_decode_json(rlm_rest_t *instance,
         *      Empty response?
         */
        while (isspace(*p)) p++;
-       if (p == NULL) return FALSE;
+       if (*p == '\0') return FALSE;
 
        json = json_tokener_parse(p);
        if (!json) {
@@ -1589,7 +1574,7 @@ static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
                        ctx->code = atoi(p);
 
                        /*
-                        * Process reason_phrase (if present).
+                        *      Process reason_phrase (if present).
                         */
                        if (p[3] == ' ') {
                                p += 4;
@@ -1776,7 +1761,7 @@ static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
  * @see rest_write_header
  * 
  * @param[in] request Current request.
- * @param[in] data to initialise.
+ * @param[in] ctx data to initialise.
  * @param[in] type Default http_body_type to use when decoding raw data, may be
  * overwritten by rest_write_header.
  */
@@ -1793,7 +1778,7 @@ static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx,
 
 /** Frees the intermediary buffer created by rest_write.
  *
- * @param[in] data to be freed.
+ * @param[in] ctx data to be freed.
  */
 static void rest_write_free(rlm_rest_write_t *ctx)
 {
@@ -1812,7 +1797,7 @@ static void rest_write_free(rlm_rest_write_t *ctx)
  * @param[in] section configuration data.
  * @param[in] handle rlm_rest_handle_t to configure.
  * @param[in] func to pass to libcurl for chunked.
- *     transfers (NULL if not using chunked mode).
+ *           transfers (NULL if not using chunked mode).
  * @return TRUE on success FALSE on error.
  */
 static int rest_request_config_body(rlm_rest_t *instance,
@@ -1983,6 +1968,7 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
                                               CURLOPT_CUSTOMREQUEST,
                                               section->method);
                        if (ret != CURLE_OK) goto error;
+                       break;
 
                default:
                        assert(0);
@@ -2147,7 +2133,7 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
 
                        switch (type)
                        {
-#ifdef WITH_JSON
+#ifdef HAVE_JSON
                                case HTTP_BODY_JSON:
                                        rest_read_ctx_init(request,
                                                           &ctx->read, 1);
@@ -2253,7 +2239,7 @@ int rest_request_decode(rlm_rest_t *instance,
                return FALSE;
        }
 
-       RDEBUG("Processing body", ret);
+       RDEBUG("Processing body");
 
        switch (ctx->write.type)
        {
@@ -2262,7 +2248,7 @@ int rest_request_decode(rlm_rest_t *instance,
                                               handle, ctx->write.buffer,
                                               ctx->write.used);
                        break;
-#ifdef WITH_JSON
+#ifdef HAVE_JSON
                case HTTP_BODY_JSON:
                        ret = rest_decode_json(instance, section, request,
                                               handle, ctx->write.buffer,
@@ -2330,9 +2316,11 @@ void rest_request_cleanup(UNUSED rlm_rest_t *instance,
  * 
  * Encode special chars as per RFC 3986 section 4.
  *
+ * @param[in] request Current request.
  * @param[out] out Where to write escaped string.
  * @param[in] outlen Size of out buffer.
  * @param[in] raw string to be urlencoded.
+ * @param[in] arg pointer, gives context for escaping.
  * @return length of data written to out (excluding NULL).
  */
 static size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen,
@@ -2375,7 +2363,16 @@ ssize_t rest_uri_build(rlm_rest_t *instance, rlm_rest_section_t *section,
 
        p = section->uri;
 
-       while ((q = strchr(p, '/')) && (count++ < 3)) p = (q + 1);
+       /*
+        *      All URLs must contain at least <scheme>://<server>/
+        */
+       while ((q = strchr(p, '/'))) {
+               p = q + 1;
+               
+               if (++count == 3) {
+                       break;
+               }
+       }
 
        if (count != 3) {
                radlog(L_ERR, "rlm_rest (%s): Error URI is malformed,"