Use of 'op' instead of 'operator', and declare it as an FR_TOKEN.
[freeradius.git] / src / modules / rlm_rest / rest.c
1 /*
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16  
17 /*
18  * $Id$
19  *
20  * @brief Functions and datatypes for the REST (HTTP) transport.
21  * @file rest.c
22  *
23  * @copyright 2012-2013  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
24  */
25
26 #include <freeradius-devel/ident.h>
27 RCSID("$Id$")
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <time.h>
33
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/libradius.h>
36 #include <freeradius-devel/connection.h>
37
38 #include "rest.h"
39
40 /** Table of encoder/decoder support.
41  *
42  * Indexes in this table match the http_body_type_t enum, and should be
43  * updated if additional enum values are added.
44  *
45  * @see http_body_type_t
46  */
47 const http_body_type_t http_body_type_supported[HTTP_BODY_NUM_ENTRIES] = {
48         HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNKOWN
49         HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNSUPPORTED
50         HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_INVALID
51         HTTP_BODY_POST,         // HTTP_BODY_POST
52 #ifdef HAVE_JSON
53         HTTP_BODY_JSON,         // HTTP_BODY_JSON
54 #else
55         HTTP_BODY_UNAVAILABLE,
56 #endif
57         HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_XML
58         HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_YAML
59         HTTP_BODY_INVALID,      // HTTP_BODY_HTML
60         HTTP_BODY_INVALID       // HTTP_BODY_PLAIN
61 };
62
63 /*
64  *      Lib CURL doesn't define symbols for unsupported auth methods
65  */
66 #ifndef CURLOPT_TLSAUTH_SRP
67 #define CURLOPT_TLSAUTH_SRP     0
68 #endif
69 #ifndef CURLAUTH_BASIC
70 #define CURLAUTH_BASIC          0
71 #endif
72 #ifndef CURLAUTH_DIGEST
73 #define CURLAUTH_DIGEST         0
74 #endif
75 #ifndef CURLAUTH_DIGEST_IE
76 #define CURLAUTH_DIGEST_IE      0
77 #endif
78 #ifndef CURLAUTH_GSSNEGOTIATE
79 #define CURLAUTH_GSSNEGOTIATE   0
80 #endif
81 #ifndef CURLAUTH_NTLM
82 #define CURLAUTH_NTLM           0
83 #endif
84 #ifndef CURLAUTH_NTLM_WB
85 #define CURLAUTH_NTLM_WB        0
86 #endif
87
88 const http_body_type_t http_curl_auth[HTTP_AUTH_NUM_ENTRIES] = {
89         0,                      // HTTP_AUTH_UNKNOWN
90         0,                      // HTTP_AUTH_NONE
91         CURLOPT_TLSAUTH_SRP,    // HTTP_AUTH_TLS_SRP
92         CURLAUTH_BASIC,         // HTTP_AUTH_BASIC
93         CURLAUTH_DIGEST,        // HTTP_AUTH_DIGEST
94         CURLAUTH_DIGEST_IE,     // HTTP_AUTH_DIGEST_IE
95         CURLAUTH_GSSNEGOTIATE,  // HTTP_AUTH_GSSNEGOTIATE
96         CURLAUTH_NTLM,          // HTTP_AUTH_NTLM
97         CURLAUTH_NTLM_WB,       // HTTP_AUTH_NTLM_WB
98         CURLAUTH_ANY,           // HTTP_AUTH_ANY
99         CURLAUTH_ANYSAFE        // HTTP_AUTH_ANY_SAFE
100 };
101
102
103 /** Conversion table for method config values.
104  * 
105  * HTTP verb strings for http_method_t enum values. Used by libcurl in the
106  * status line of the outgoing HTTP header, by rest_write_header for decoding
107  * incoming HTTP responses, and by the configuration parser.
108  *
109  * @see http_method_t
110  * @see fr_str2int
111  * @see fr_int2str
112  */
113 const FR_NAME_NUMBER http_method_table[] = {
114         { "GET",                HTTP_METHOD_GET         },
115         { "POST",               HTTP_METHOD_POST        },
116         { "PUT",                HTTP_METHOD_PUT         },
117         { "DELETE",             HTTP_METHOD_DELETE      },
118
119         {  NULL , -1 }
120 };
121
122 /** Conversion table for type config values.
123  *
124  * Textual names for http_body_type_t enum values, used by the
125  * configuration parser.
126  *
127  * @see http_body_Type_t
128  * @see fr_str2int
129  * @see fr_int2str
130  */
131 const FR_NAME_NUMBER http_body_type_table[] = {
132         { "unknown",            HTTP_BODY_UNKNOWN       },
133         { "unsupported",        HTTP_BODY_UNSUPPORTED   },
134         { "unavailable",        HTTP_BODY_UNAVAILABLE   },
135         { "invalid",            HTTP_BODY_INVALID       },
136         { "post",               HTTP_BODY_POST          },
137         { "json",               HTTP_BODY_JSON          },
138         { "xml",                HTTP_BODY_XML           },
139         { "yaml",               HTTP_BODY_YAML          },
140         { "html",               HTTP_BODY_HTML          },
141         { "plain",              HTTP_BODY_PLAIN         },
142
143         {  NULL , -1 }
144 };
145
146 const FR_NAME_NUMBER http_auth_table[] = {
147         { "none",               HTTP_AUTH_NONE          },
148         { "srp",                HTTP_AUTH_TLS_SRP       },
149         { "basic",              HTTP_AUTH_BASIC         },
150         { "digest",             HTTP_AUTH_DIGEST        },
151         { "digest-ie",          HTTP_AUTH_DIGEST_IE     },
152         { "gss-negotiate",      HTTP_AUTH_GSSNEGOTIATE  },
153         { "ntlm",               HTTP_AUTH_NTLM          },
154         { "ntlm-winbind",       HTTP_AUTH_NTLM_WB       },
155         { "any",                HTTP_AUTH_ANY           },
156         { "safe",               HTTP_AUTH_ANY_SAFE      },
157
158         {  NULL , -1 }
159 };
160
161 /** Conversion table for "Content-Type" header values.
162  *
163  * Used by rest_write_header for parsing incoming headers.
164  *
165  * Values we expect to see in the 'Content-Type:' header of the incoming
166  * response.
167  *
168  * Some data types (like YAML) do no have standard MIME types defined,
169  * so multiple types, are listed here.
170  *
171  * @see http_body_Type_t
172  * @see fr_str2int
173  * @see fr_int2str
174  */
175 const FR_NAME_NUMBER http_content_type_table[] = {
176         { "application/x-www-form-urlencoded", HTTP_BODY_POST },
177         { "application/json",   HTTP_BODY_JSON          },
178         { "text/html",          HTTP_BODY_HTML          },
179         { "text/plain",         HTTP_BODY_PLAIN         },
180         { "text/xml",           HTTP_BODY_XML           },
181         { "text/yaml",          HTTP_BODY_YAML          },
182         { "text/x-yaml",        HTTP_BODY_YAML          },
183         { "application/yaml",   HTTP_BODY_YAML          },
184         { "application/x-yaml", HTTP_BODY_YAML          },
185         {  NULL , -1 }
186 };
187
188 #ifdef HAVE_JSON
189 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
190  *
191  * These fields are set when parsing the expanded format for value pairs in
192  * JSON, and control how json_pairmake_leaf and json_pairmake convert the JSON
193  * value, and move the new VALUE_PAIR into an attribute list.
194  *
195  * @see json_pairmake
196  * @see json_pairmake_leaf
197  */
198 typedef struct json_flags {
199         boolean do_xlat;        //!< If TRUE value will be expanded with xlat.
200         boolean is_json;        //!< If TRUE value will be inserted as raw JSON
201                                 // (multiple values not supported).
202         FR_TOKEN op;            //!< The operator that determines how the new VP
203                                 // is processed. @see fr_tokens
204 } json_flags_t;
205 #endif
206
207 /** Initialises libcurl.
208  *
209  * Allocates global variables and memory required for libcurl to fundtion.
210  * MUST only be called once per module instance.
211  *
212  * rest_cleanup must not be called if rest_init fails.
213  *
214  * @see rest_cleanup
215  *
216  * @param[in] instance configuration data.
217  * @return TRUE if init succeeded FALSE if it failed.
218  */
219 int rest_init(rlm_rest_t *instance)
220 {
221         CURLcode ret;
222
223         ret = curl_global_init(CURL_GLOBAL_ALL);
224         if (ret != CURLE_OK) {
225                 radlog(L_ERR,
226                        "rlm_rest (%s): CURL init returned error: %i - %s",
227                        instance->xlat_name,
228                        ret, curl_easy_strerror(ret));
229
230                 curl_global_cleanup();
231                 return FALSE;
232         }
233
234         radlog(L_DBG, "rlm_rest (%s): CURL library version: %s",
235                instance->xlat_name,
236                curl_version());
237
238         return TRUE;
239 }
240
241 /** Cleans up after libcurl.
242  *
243  * Wrapper around curl_global_cleanup, frees any memory allocated by rest_init.
244  * Must only be called once per call of rest_init.
245  *
246  * @see rest_init
247  */
248 void rest_cleanup(void)
249 {
250         curl_global_cleanup();
251 }
252
253 /** Creates a new connection handle for use by the FR connection API.
254  *
255  * Matches the fr_connection_create_t function prototype, is passed to
256  * fr_connection_pool_init, and called when a new connection is required by the
257  * connection pool API.
258  *
259  * Creates an instances of rlm_rest_handle_t, and rlm_rest_curl_context_t
260  * which hold the context data required for generating requests and parsing
261  * responses. Calling rest_socket_delete will free this memory.
262  *
263  * If instance->connect_uri is not NULL libcurl will attempt to open a 
264  * TCP socket to the server specified in the URI. This is done so that when the
265  * socket is first used, there will already be a cached TCP connection to the
266  * REST server associated with the curl handle. 
267  *
268  * @see rest_socket_delete
269  * @see fr_connection_pool_init
270  * @see fr_connection_create_t
271  * @see connection.c
272  *
273  * @param[in] instance configuration data.
274  * @return connection handle or NULL if the connection failed or couldn't
275  *      be initialised.
276  */
277 void *rest_socket_create(void *instance) 
278 {
279         rlm_rest_t *inst = instance;
280
281         rlm_rest_handle_t       *randle;
282         rlm_rest_curl_context_t *ctx;
283
284         CURL *candle = curl_easy_init();
285         CURLcode ret;
286
287         if (!candle) {
288                 radlog(L_ERR, "rlm_rest (%s): Failed to create CURL handle", 
289                        inst->xlat_name);
290                 return NULL;
291         }
292
293         if (!*inst->connect_uri) {
294                 radlog(L_ERR, "rlm_rest (%s): Skipping pre-connect,"
295                        " connect_uri not specified", inst->xlat_name);
296                 return candle;
297         }
298
299         /*
300          *      Pre-establish TCP connection to webserver. This would usually be
301          *      done on the first request, but we do it here to minimise
302          *      latency.
303          */
304         ret = curl_easy_setopt(candle, CURLOPT_CONNECT_ONLY, 1);
305         if (ret != CURLE_OK) goto error;
306
307         ret = curl_easy_setopt(candle, CURLOPT_URL,
308                                inst->connect_uri);
309         if (ret != CURLE_OK) goto error;
310
311         radlog(L_DBG, "rlm_rest (%s): Connecting to \"%s\"",
312                inst->xlat_name,
313                inst->connect_uri);
314
315         ret = curl_easy_perform(candle);
316         if (ret != CURLE_OK) {
317                 radlog(L_ERR, "rlm_rest (%s): Connection failed: %i - %s",
318                         inst->xlat_name,
319                         ret, curl_easy_strerror(ret));
320
321                 goto connection_error;
322         }
323
324         /* 
325          *      Malloc memory for the connection handle abstraction.
326          */
327         randle = malloc(sizeof(*randle));
328         memset(randle, 0, sizeof(*randle));
329
330         ctx = malloc(sizeof(*ctx));
331         memset(ctx, 0, sizeof(*ctx));
332
333         ctx->headers = NULL; /* CURL needs this to be NULL */
334         ctx->read.instance = inst;
335
336         randle->ctx = ctx;
337         randle->handle = candle;
338
339         /*
340          *      Clear any previously configured options for the first request.
341          */
342         curl_easy_reset(candle);
343
344         return randle;
345
346         /*
347          *      Cleanup for error conditions.
348          */
349         error:
350
351         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
352                         inst->xlat_name,
353                         ret, curl_easy_strerror(ret));
354
355         /* 
356          *      So we don't leak CURL handles.
357          */
358         connection_error:
359
360         curl_easy_cleanup(candle);
361
362         return NULL;
363 }
364
365 /** Verifies that the last TCP socket associated with a handle is still active.
366  *
367  * Quieries libcurl to try and determine if the TCP socket associated with a
368  * connection handle is still viable.
369  *
370  * @param[in] instance configuration data.
371  * @param[in] handle to check.
372  * @returns FALSE if the last socket is dead, or if the socket state couldn't be
373  *      determined, else TRUE.
374  */
375 int rest_socket_alive(void *instance, void *handle)
376 {
377         rlm_rest_t *inst                = instance;
378         rlm_rest_handle_t *randle       = handle;
379         CURL *candle                    = randle->handle;
380
381         long last_socket;
382         CURLcode ret;
383
384         curl_easy_getinfo(candle, CURLINFO_LASTSOCKET, &last_socket);
385         if (ret != CURLE_OK) {
386                 radlog(L_ERR,
387                        "rlm_rest (%s): Couldn't determine socket"
388                        " state: %i - %s", inst->xlat_name, ret,
389                        curl_easy_strerror(ret));
390
391                 return FALSE;
392         }
393
394         if (last_socket == -1) {
395                 return FALSE;
396         }
397
398         return TRUE;
399 }
400
401 /** Frees a libcurl handle, and any additional memory used by context data.
402  * 
403  * @param[in] instance configuration data.
404  * @param[in] handle rlm_rest_handle_t to close and free.
405  * @return returns TRUE.
406  */
407 int rest_socket_delete(UNUSED void *instance, void *handle)
408 {   
409         rlm_rest_handle_t *randle       = handle;
410         CURL *candle                    = randle->handle;
411
412         curl_easy_cleanup(candle);
413
414         free(randle->ctx);
415         free(randle);
416
417         return TRUE;
418 }
419
420 /** Encodes VALUE_PAIR linked list in POST format
421  *
422  * This is a stream function matching the rest_read_t prototype. Multiple
423  * successive calls will return additional encoded VALUE_PAIRs. 
424  * Only complete attribute headers @verbatim '<name>=' @endverbatim and values
425  * will be written to the ptr buffer.
426  *
427  * POST request format is:
428  * @verbatim <attribute0>=<value0>&<attribute1>=<value1>&<attributeN>=<valueN>@endverbatim
429  *
430  * All attributes and values are url encoded. There is currently no support for
431  * nested attributes, or attribute qualifiers.
432  *
433  * Nested attributes may be added in the future using
434  * @verbatim <attribute-outer>:<attribute-inner>@endverbatim
435  * to denotate nesting.
436  *
437  * Requires libcurl for url encoding.
438  *
439  * @see rest_decode_post
440  *
441  * @param[out] ptr Char buffer to write encoded data to.
442  * @param[in] size Multiply by nmemb to get the length of ptr.
443  * @param[in] nmemb Multiply by size to get the length of ptr.
444  * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
445  * @return length of data (including NULL) written to ptr, or 0 if no more
446  *      data to write.
447  */
448 static size_t rest_encode_post(void *ptr, size_t size, size_t nmemb,
449                                void *userdata)
450 {
451         rlm_rest_read_t *ctx    = userdata;
452         REQUEST *request        = ctx->request; /* Used by RDEBUG */
453         VALUE_PAIR **current    = ctx->next;
454
455         char *p = ptr;  /* Position in buffer */
456         char *f = ptr;  /* Position in buffer of last fully encoded attribute or value */
457         char *escaped;  /* Pointer to current URL escaped data */
458
459         ssize_t len = 0;
460         ssize_t s = (size * nmemb) - 1;
461
462         /* Allow manual chunking */
463         if ((ctx->chunk) && (ctx->chunk <= s)) {
464                 s = (ctx->chunk - 1);
465         }
466
467         if (ctx->state == READ_STATE_END) return FALSE;
468
469         /* Post data requires no headers */
470         if (ctx->state == READ_STATE_INIT) {
471                 ctx->state = READ_STATE_ATTR_BEGIN;
472         }
473
474         while (s > 0) {
475                 if (!*current) {
476                         ctx->state = READ_STATE_END;
477
478                         goto end_chunk;
479                 }
480
481                 RDEBUG2("Encoding attribute \"%s\"", current[0]->name);
482
483                 if (ctx->state == READ_STATE_ATTR_BEGIN) {
484                         escaped = curl_escape(current[0]->name,
485                                               strlen(current[0]->name));
486                         len = strlen(escaped);
487
488                         if (s < (1 + len)) {
489                                 curl_free(escaped);
490                                 goto no_space;
491                         }
492
493                         len = sprintf(p, "%s=", escaped);
494
495                         curl_free(escaped);
496
497                         p += len;
498                         s -= len;
499
500                         /* 
501                          *      We wrote the attribute header, record progress.
502                          */
503                         f = p;
504                         ctx->state = READ_STATE_ATTR_CONT;
505                 }
506
507                 /*
508                  *      Write out single attribute string.
509                  */
510                 len = vp_prints_value(p , s, current[0], 0);
511                 escaped = curl_escape(p, len);
512                 len = strlen(escaped);
513
514                 if (s < len) {
515                         curl_free(escaped);
516                         goto no_space;
517                 }
518
519                 len = strlcpy(p, escaped, len + 1);
520
521                 curl_free(escaped);
522
523                 RDEBUG("\tLength : %i", len);
524                 RDEBUG("\tValue  : %s", p);
525
526                 p += len;
527                 s -= len;
528
529                 if (*++current) {
530                         if (!--s) goto no_space;
531                         *p++ = '&';
532                 }
533
534                 /* 
535                  *      We wrote one full attribute value pair, record progress.
536                  */
537                 f = p;
538                 ctx->next = current;
539                 ctx->state = READ_STATE_ATTR_BEGIN;
540         }
541
542         end_chunk:
543
544         *p = '\0';
545
546         len = p - (char*)ptr;
547
548         RDEBUG2("POST Data: %s", (char*) ptr);
549         RDEBUG2("Returning %i bytes of POST data", len);
550
551         return len;
552
553         /*
554          *      Cleanup for error conditions
555          */ 
556         no_space:
557
558         *f = '\0';
559
560         len = f - (char*)ptr;
561
562         RDEBUG2("POST Data: %s", (char*) ptr);
563
564         /*
565          *      The buffer wasn't big enough to encode a single attribute chunk.
566          */
567         if (!len) {
568                 radlog(L_ERR, "rlm_rest (%s): AVP exceeds buffer length" 
569                        " or chunk", ctx->instance->xlat_name);
570         } else {
571                 RDEBUG2("Returning %i bytes of POST data"
572                         " (buffer full or chunk exceeded)", len);
573         }
574
575         return len;
576 }
577
578 /** Encodes VALUE_PAIR linked list in JSON format
579  *
580  * This is a stream function matching the rest_read_t prototype. Multiple
581  * successive calls will return additional encoded VALUE_PAIRs.
582  *
583  * Only complete attribute headers
584  * @verbatim "<name>":{"type":"<type>","value":[' @endverbatim
585  * and complete attribute values will be written to ptr.
586  *
587  * If an attribute occurs multiple times in the request the attribute values
588  * will be concatenated into a single value array.
589  *
590  * JSON request format is:
591 @verbatim
592 {
593         "<attribute0>":{
594                 "type":"<type0>",
595                 "value":[<value0>,<value1>,<valueN>]
596         },
597         "<attribute1>":{
598                 "type":"<type1>",
599                 "value":[...]
600         },
601         "<attributeN>":{
602                 "type":"<typeN>",
603                 "value":[...]
604         },
605 }
606 @endverbatim
607  *
608  * @param[out] ptr Char buffer to write encoded data to.
609  * @param[in] size Multiply by nmemb to get the length of ptr.
610  * @param[in] nmemb Multiply by size to get the length of ptr.
611  * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
612  * @return length of data (including NULL) written to ptr, or 0 if no more
613  *      data to write.
614  */
615 static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
616                                void *userdata)
617 {
618         rlm_rest_read_t *ctx    = userdata;
619         REQUEST *request        = ctx->request; /* Used by RDEBUG */
620         VALUE_PAIR **current    = ctx->next;
621
622         char *p = ptr;  /* Position in buffer */
623         char *f = ptr;  /* Position in buffer of last fully encoded attribute or value */
624
625         const char *type;
626
627         ssize_t len = 0;
628         ssize_t s = (size * nmemb) - 1;
629
630         assert(s > 0);
631
632         /* Allow manual chunking */
633         if ((ctx->chunk) && (ctx->chunk <= s)) {
634                 s = (ctx->chunk - 1);
635         }
636         
637         if (ctx->state == READ_STATE_END) return FALSE;
638
639         if (ctx->state == READ_STATE_INIT) {
640                 ctx->state = READ_STATE_ATTR_BEGIN;
641
642                 if (!--s) goto no_space;
643                 *p++ = '{';
644         }
645
646         while (s > 0) {
647                 if (!*current) {
648                         ctx->state = READ_STATE_END;
649
650                         if (!--s) goto no_space;
651                         *p++ = '}';
652
653                         goto end_chunk;
654                 }
655
656                 /*
657                  *      New attribute, write name, type, and beginning of
658                  *      value array.
659                  */
660                 RDEBUG2("Encoding attribute \"%s\"", current[0]->name);
661                 if (ctx->state == READ_STATE_ATTR_BEGIN) {
662                         type = fr_int2str(dict_attr_types, current[0]->type,
663                                           "¿Unknown?");
664
665                         len  = strlen(type);
666                         len += strlen(current[0]->name);
667
668                         if (s < (23 + len)) goto no_space;
669
670                         len = sprintf(p, "\"%s\":{\"type\":\"%s\",\"value\":[" ,
671                                       current[0]->name, type);
672                         p += len;
673                         s -= len;
674
675                         RDEBUG2("\tType   : %s", type);
676
677                         /* 
678                          *      We wrote the attribute header, record progress
679                          */
680                         f = p;
681                         ctx->state = READ_STATE_ATTR_CONT;
682                 }
683
684                 /*
685                  *      Put all attribute values in an array for easier remote
686                  *      parsing whether they're multivalued or not.
687                  */
688                 while (TRUE) {
689                         len = vp_prints_value_json(p , s, current[0]);
690                         assert((s - len) >= 0);
691
692                         if (len < 0) goto no_space;
693
694                         /*
695                          *      Show actual value length minus quotes
696                          */
697                         RDEBUG2("\tLength : %i", (*p == '"') ? (len - 2) : len);
698                         RDEBUG2("\tValue  : %s", p);
699
700                         p += len;
701                         s -= len;
702
703                         /* 
704                          *      Multivalued attribute
705                          */
706                         if (current[1] && 
707                             ((current[0]->attribute == current[1]->attribute) &&
708                              (current[0]->vendor == current[1]->vendor))) {
709                                 *p++ = ',';
710                                 current++;
711
712                                 /* 
713                                  *      We wrote one attribute value, record
714                                  *      progress.
715                                  */
716                                 f = p;
717                                 ctx->next = current;
718                         } else {
719                                 break;
720                         }
721                 }
722
723                 if (!(s -= 2)) goto no_space;
724                 *p++ = ']';
725                 *p++ = '}';
726
727                 if (*++current) {
728                         if (!--s) goto no_space;
729                         *p++ = ',';
730                 }
731
732                 /* 
733                  *      We wrote one full attribute value pair, record progress.
734                  */
735                 f = p;
736                 ctx->next = current;
737                 ctx->state = READ_STATE_ATTR_BEGIN;
738         }
739
740         end_chunk:
741
742         *p = '\0';
743
744         len = p - (char*)ptr;
745
746         RDEBUG2("JSON Data: %s", (char*) ptr);
747         RDEBUG2("Returning %i bytes of JSON data", len);
748
749         return len;
750
751         /* 
752          * Were out of buffer space
753          */ 
754         no_space:
755
756         *f = '\0';
757
758         len = f - (char*)ptr;
759
760         RDEBUG2("JSON Data: %s", (char*) ptr);
761
762         /*
763          *      The buffer wasn't big enough to encode a single attribute chunk.
764          */
765         if (!len) {
766                 radlog(L_ERR, "rlm_rest (%s): AVP exceeds buffer length"
767                        " or chunk", ctx->instance->xlat_name);
768         } else {
769                 RDEBUG2("Returning %i bytes of JSON data"
770                         " (buffer full or chunk exceeded)", len);
771         }
772
773         return len;
774 }
775
776 /** Emulates successive libcurl calls to an encoding function
777  *
778  * This function is used when the request will be sent to the HTTP server as one
779  * contiguous entity. A buffer of REST_BODY_INCR bytes is allocated and passed
780  * to the stream encoding function.
781  * 
782  * If the stream function does not return 0, a new buffer is allocated which is
783  * the size of the previous buffer + REST_BODY_INCR bytes, the data from the
784  * previous buffer is copied, and freed, and another call is made to the stream
785  * function, passing a pointer into the new buffer at the end of the previously
786  * written data.
787  * 
788  * This process continues until the stream function signals (by returning 0)
789  * that it has no more data to write.
790  *
791  * @param[out] buffer where the pointer to the malloced buffer should
792  *      be written.
793  * @param[in] func Stream function.
794  * @param[in] limit Maximum buffer size to alloc.
795  * @param[in] userdata rlm_rest_read_t to keep encoding state between calls to
796  *      stream function.
797  * @return the length of the data written to the buffer (excluding NULL) or -1
798  *      if alloc >= limit.
799  */
800 static ssize_t rest_read_wrapper(char **buffer, rest_read_t func,
801                                  size_t limit, void *userdata)
802 {
803         char *previous = NULL;
804         char *current;
805
806         size_t alloc = REST_BODY_INCR;  /* Size of buffer to malloc */
807         size_t used  = 0;               /* Size of data written */
808         size_t len   = 0;
809
810         while (alloc < limit) {
811                 current = rad_malloc(alloc);
812
813                 if (previous) {
814                         strlcpy(current, previous, used + 1);
815                         free(previous);
816                 }
817
818                 len = func(current + used, REST_BODY_INCR, 1, userdata);
819                 used += len;
820                 if (!len) {
821                         *buffer = current;
822                         return used;
823                 }
824
825                 alloc += REST_BODY_INCR;
826                 previous = current;
827         };
828
829         free(current);
830
831         return -1;
832 }
833
834 /** (Re-)Initialises the data in a rlm_rest_read_t.
835  *
836  * Resets the values of a rlm_rest_read_t to their defaults.
837  * 
838  * Must be called between encoding sessions.
839  *
840  * As part of initialisation all VALUE_PAIR pointers in the REQUEST packet are
841  * written to an array.
842  *
843  * If sort is TRUE, this array of VALUE_PAIR pointers will be sorted by vendor
844  * and then by attribute. This is for stream encoders which may concatenate
845  * multiple attribute values together into an array.
846  *
847  * After the encoding session has completed this array must be freed by calling
848  * rest_read_ctx_free .
849  *
850  * @see rest_read_ctx_free
851  *
852  * @param[in] request Current request.
853  * @param[in] ctx to initialise.
854  * @param[in] sort If TRUE VALUE_PAIRs will be sorted within the VALUE_PAIR
855  *      pointer array.
856  */
857 static void rest_read_ctx_init(REQUEST *request,
858                                rlm_rest_read_t *ctx,
859                                int sort)
860 {
861         unsigned short count = 0, i;
862         unsigned short swap;
863
864         VALUE_PAIR **current, *tmp;
865
866         /*
867          * Setup stream read data
868          */
869         ctx->request = request;
870         ctx->state   = READ_STATE_INIT;
871
872         /*
873          * Create sorted array of VP pointers
874          */
875         tmp = request->packet->vps;
876         while (tmp != NULL) {
877                 tmp = tmp->next;
878                 count++;
879         }
880
881         ctx->first = current = rad_malloc((sizeof(tmp) * (count + 1)));
882         ctx->next = ctx->first;
883
884         tmp = request->packet->vps;
885         while (tmp != NULL) {
886                 *current++ = tmp;
887                 tmp = tmp->next;
888         }
889         current[0] = NULL;
890         current = ctx->first;
891
892         if (!sort || (count < 2)) return;
893
894         /* TODO: Quicksort would be faster... */
895         do {
896                 for(i = 1; i < count; i++) {
897                         assert(current[i-1]->attribute &&
898                                current[i]->attribute);
899
900                         swap = 0;
901                         if ((current[i-1]->vendor > current[i]->vendor) ||
902                             ((current[i-1]->vendor == current[i]->vendor) &&
903                              (current[i-1]->attribute > current[i]->attribute)
904                             )) {
905                                 tmp          = current[i];
906                                 current[i]   = current[i-1];
907                                 current[i-1] = tmp;
908                                 swap = 1;
909                         }
910                 }
911         } while (swap);
912 }
913
914 /** Frees the VALUE_PAIR array created by rest_read_ctx_init.
915  *
916  * Must be called between encoding sessions else module will leak VALUE_PAIR
917  * pointers.
918  *
919  * @see rest_read_ctx_init
920  *
921  * @param[in] ctx to free.
922  */
923 static void rest_read_ctx_free(rlm_rest_read_t *ctx)
924 {
925         if (ctx->first != NULL) {
926                 free(ctx->first);
927         }
928 }
929
930 /** Verify that value wasn't truncated when it was converted to a VALUE_PAIR
931  *
932  * Certain values may be truncated when they're converted into VALUE_PAIRs
933  * for example 64bit integers converted to 32bit integers. Warn the user
934  * when this happens.
935  * 
936  * @param[in] request Current request.
937  * @param[in] raw string from decoder.
938  * @param[in] vp containing parsed value.
939  */
940 static void rest_check_truncation(REQUEST *request, const char *raw,
941                                   VALUE_PAIR *vp)
942 {
943         char cooked[1024];
944
945         vp_prints_value(cooked, sizeof(cooked), vp, 0);
946         if (strcmp(raw, cooked) != 0) {
947                 RDEBUG("WARNING: Value-Pair does not match POST value, "
948                        "truncation may have occurred");
949                 RDEBUG("\tValue (pair) : \"%s\"", cooked);
950                 RDEBUG("\tValue (post) : \"%s\"", raw);
951         }
952 }
953
954 /** Converts POST response into VALUE_PAIRs and adds them to the request
955  *
956  * Accepts VALUE_PAIRS in the same format as rest_encode_post, but with the
957  * addition of optional attribute list qualifiers as part of the attribute name
958  * string.
959  * 
960  * If no qualifiers are specified, will default to the request list.
961  *
962  * POST response format is:
963  * @verbatim [outer.][<list>:]<attribute0>=<value0>&[outer.][<list>:]<attribute1>=<value1>&[outer.][<list>:]<attributeN>=<valueN> @endverbatim
964  *
965  * @see rest_encode_post
966  *
967  * @param[in] instance configuration data.
968  * @param[in] section configuration data.
969  * @param[in] handle rlm_rest_handle_t to use.
970  * @param[in] request Current request.
971  * @param[in] raw buffer containing POST data.
972  * @param[in] rawlen Length of data in raw buffer.
973  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
974  */
975 static int rest_decode_post(rlm_rest_t *instance,
976                             UNUSED rlm_rest_section_t *section,
977                             REQUEST *request, void *handle, char *raw,
978                             UNUSED size_t rawlen)
979 {
980         rlm_rest_handle_t *randle = handle;
981         CURL *candle              = randle->handle;
982
983         const char *p = raw, *q;
984
985         const char *attribute;
986         char *name  = NULL;
987         char *value = NULL;
988
989         const DICT_ATTR *da;
990         VALUE_PAIR *vp;
991
992         const DICT_ATTR **current, *processed[REST_BODY_MAX_ATTRS + 1];
993         VALUE_PAIR *tmp;
994
995         pair_lists_t list_name;
996         request_refs_t request_name;
997         REQUEST *reference = request;
998         VALUE_PAIR **vps;
999
1000         size_t len;
1001         int curl_len; /* Length from last curl_easy_unescape call */
1002
1003         int count = 0;
1004
1005         processed[0] = NULL;
1006
1007         /*
1008          * Empty response?
1009          */
1010         while (isspace(*p)) p++;
1011         if (*p == '\0') return FALSE;
1012
1013         while (((q = strchr(p, '=')) != NULL) &&
1014                (count < REST_BODY_MAX_ATTRS)) {
1015                 attribute = name;
1016                 reference = request;
1017
1018                 name = curl_easy_unescape(candle, p, (q - p), &curl_len);
1019                 p = (q + 1);
1020
1021                 RDEBUG("Decoding attribute \"%s\"", name);
1022                 
1023                 request_name = radius_request_name(&attribute, REQUEST_CURRENT);
1024                 if (request_name == REQUEST_UNKNOWN) {
1025                         RDEBUG("WARNING: Invalid request qualifier, skipping");
1026
1027                         curl_free(name);
1028
1029                         continue;
1030                 }
1031
1032                 if (!radius_request(&reference, request_name)) {
1033                         RDEBUG("WARNING: Attribute name refers to outer request"
1034                                " but not in a tunnel, skipping");
1035
1036                         curl_free(name);
1037
1038                         continue;
1039                 }
1040
1041                 list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
1042                 if (list_name == PAIR_LIST_UNKNOWN) {
1043                         RDEBUG("WARNING: Invalid list qualifier, skipping");
1044
1045                         curl_free(name);
1046
1047                         continue;
1048                 }
1049
1050                 da = dict_attrbyname(attribute);
1051                 if (!da) {
1052                         RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
1053                                attribute);
1054
1055                         curl_free(name);
1056
1057                         continue;
1058                 }
1059
1060                 vps = radius_list(reference, list_name);
1061
1062                 assert(vps);
1063
1064                 RDEBUG2("\tType  : %s", fr_int2str(dict_attr_types, da->type,
1065                         "¿Unknown?"));
1066
1067                 q = strchr(p, '&');
1068                 len = (q == NULL) ? (rawlen - (p - raw)) : (unsigned)(q - p);
1069
1070                 value = curl_easy_unescape(candle, p, len, &curl_len);
1071
1072                 /* 
1073                  *      If we found a delimiter we want to skip over it,
1074                  *      if we didn't we do *NOT* want to skip over the end
1075                  *      of the buffer...
1076                  */
1077                 p += (q == NULL) ? len : (len + 1);
1078
1079                 RDEBUG2("\tLength : %i", curl_len);
1080                 RDEBUG2("\tValue  : \"%s\"", value);
1081
1082                 vp = paircreate(da->attr, da->vendor, da->type);
1083                 if (!vp) {
1084                         radlog(L_ERR, "rlm_rest (%s): Failed creating"
1085                                " value-pair", instance->xlat_name);
1086
1087                         goto error;
1088                 }
1089
1090                 vp->op = T_OP_SET;
1091  
1092                 /*
1093                  *      Check to see if we've already processed an
1094                  *      attribute of the same type if we have, change the op
1095                  *      from T_OP_ADD to T_OP_SET.
1096                  */
1097                 current = processed;
1098                 while (*current++) {
1099                         if ((current[0]->attr == da->attr) &&
1100                             (current[0]->vendor == da->vendor)) {
1101                                 vp->op = T_OP_ADD;
1102                                 break;
1103                         }
1104                 }
1105                 
1106                 if (vp->op != T_OP_ADD) {
1107                         current[0] = da;
1108                         current[1] = NULL;
1109                 }
1110
1111                 tmp = pairparsevalue(vp, value);
1112                 if (tmp == NULL) {
1113                         RDEBUG("Incompatible value assignment, skipping");
1114                         pairbasicfree(vp);
1115                         goto skip;
1116                 }
1117                 vp = tmp;
1118
1119                 rest_check_truncation(request, value, vp);
1120
1121                 vp->flags.do_xlat = 1;
1122
1123                 RDEBUG("Performing xlat expansion of response value", value);
1124                 pairxlatmove(request, vps, &vp);
1125
1126                 if (++count == REST_BODY_MAX_ATTRS) {
1127                         radlog(L_ERR, "rlm_rest (%s): At maximum"
1128                                " attribute limit", instance->xlat_name);
1129                         return count;
1130                 }
1131
1132                 skip:
1133
1134                 curl_free(name);
1135                 curl_free(value);
1136
1137                 continue;
1138
1139                 error:
1140
1141                 curl_free(name);
1142                 curl_free(value);
1143
1144                 return count;
1145         }
1146
1147         if (!count) {
1148                 radlog(L_ERR, "rlm_rest (%s): Malformed POST data \"%s\"",
1149                        instance->xlat_name, raw);
1150         }
1151
1152         return count;
1153
1154 }
1155
1156 #ifdef HAVE_JSON
1157 /** Converts JSON "value" key into VALUE_PAIR.
1158  *
1159  * If leaf is not in fact a leaf node, but contains JSON data, the data will
1160  * written to the attribute in JSON string format.
1161  *
1162  * @param[in] instance configuration data.
1163  * @param[in] section configuration data.
1164  * @param[in] request Current request.
1165  * @param[in] da Attribute to create.
1166  * @param[in] flags containing the operator other flags controlling value
1167  *      expansion.
1168  * @param[in] leaf object containing the VALUE_PAIR value.
1169  * @return The VALUE_PAIR just created, or NULL on error.
1170  */
1171 static VALUE_PAIR *json_pairmake_leaf(rlm_rest_t *instance,
1172                                       UNUSED rlm_rest_section_t *section,
1173                                       REQUEST *request, const DICT_ATTR *da,
1174                                       json_flags_t *flags, json_object *leaf)
1175 {
1176         const char *value;
1177         VALUE_PAIR *vp, *tmp;
1178
1179         /*
1180          *      Should encode any nested JSON structures into JSON strings.
1181          *
1182          *      "I knew you liked JSON so I put JSON in your JSON!"
1183          */
1184         value = json_object_get_string(leaf);
1185
1186         RDEBUG2("\tType   : %s", fr_int2str(dict_attr_types, da->type,
1187                                             "¿Unknown?"));
1188         RDEBUG2("\tLength : %i", strlen(value));
1189         RDEBUG2("\tValue  : \"%s\"", value);
1190
1191         vp = paircreate(da->attr, da->vendor, da->type);
1192         if (!vp) {
1193                 radlog(L_ERR, "rlm_rest (%s): Failed creating value-pair",
1194                        instance->xlat_name);
1195                 return NULL;
1196         }
1197
1198         vp->op = flags->op;
1199
1200         tmp = pairparsevalue(vp, value);
1201         if (tmp == NULL) {
1202                 RDEBUG("Incompatible value assignment, skipping");
1203                 pairbasicfree(vp);
1204                 return NULL;
1205         }
1206         vp = tmp;
1207
1208         rest_check_truncation(request, value, vp);
1209
1210         if (flags->do_xlat) vp->flags.do_xlat = 1;
1211
1212         return vp;
1213 }
1214
1215 /** Processes JSON response and converts it into multiple VALUE_PAIRs
1216  * 
1217  * Processes JSON attribute declarations in the format below. Will recurse when
1218  * processing nested attributes. When processing nested attributes flags and
1219  * operators from previous attributes are not inherited.
1220  *
1221  * JSON response format is:
1222 @verbatim
1223 {
1224         "<attribute0>":{
1225                 do_xlat:<bool>,
1226                 is_json:<bool>,
1227                 "op":"<operator>",
1228                 "value":[<value0>,<value1>,<valueN>]
1229         },
1230         "<attribute1>":{
1231                 "value":{
1232                         "<nested-attribute0>":{
1233                                 "op":"<operator>",
1234                                 "value":<value0>
1235                         }
1236                 }
1237         },
1238         "<attribute2>":"<value0>",
1239         "<attributeN>":"[<value0>,<value1>,<valueN>]"
1240 }
1241 @endverbatim
1242  * 
1243  * JSON valuepair flags (bools):
1244  *  - do_xlat   (optional) Controls xlat expansion of values. Defaults to TRUE.
1245  *  - is_json   (optional) If TRUE, any nested JSON data will be copied to the
1246  *                         VALUE_PAIR in string form. Defaults to TRUE.
1247  *  - op        (optional) Controls how the attribute is inserted into
1248  *                         the target list. Defaults to ':=' (T_OP_SET).
1249  *
1250  * If "op" is ':=' or '=', it will be automagically changed to '+=' for the
1251  * second and subsequent values in multivalued attributes. This does not work
1252  * between multiple attribute declarations.
1253  *
1254  * @see fr_tokens
1255  *
1256  * @param[in] instance configuration data.
1257  * @param[in] section configuration data.
1258  * @param[in] request Current request.
1259  * @param[in] object containing root node, or parent node.
1260  * @param[in] level Current nesting level.
1261  * @param[in] max_attrs counter, decremented after each VALUE_PAIR is created,
1262  *            when 0 no more attributes will be processed.
1263  * @return VALUE_PAIR or NULL on error.
1264  */
1265 static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
1266                                  UNUSED rlm_rest_section_t *section,
1267                                  REQUEST *request, json_object *object,
1268                                  int level, int *max_attrs)
1269 {
1270         const char *p;
1271         char *q;
1272         
1273         const char *name, *attribute;
1274
1275         struct json_object *value, *idx, *tmp;
1276         struct lh_entry *entry;
1277         json_flags_t flags;
1278
1279         const DICT_ATTR *da;
1280         VALUE_PAIR *vp;
1281         
1282         request_refs_t request_name;
1283         pair_lists_t list_name;
1284         REQUEST *reference = request;
1285         VALUE_PAIR **vps;
1286
1287         int i, len;
1288
1289         if (!json_object_is_type(object, json_type_object)) {
1290                 RDEBUG("Can't process VP container, expected JSON object,"
1291                        " got \"%s\", skipping",
1292                        json_object_get_type(object));
1293                 return NULL;
1294         }
1295    
1296         /*
1297          *      Process VP container
1298          */
1299         entry = json_object_get_object(object)->head;
1300         while (entry) {
1301                 flags.op = T_OP_SET;
1302                 flags.do_xlat  = 1;
1303                 flags.is_json  = 0;
1304
1305                 name = (char*)entry->k;
1306
1307                 /* Fix the compiler warnings regarding const... */
1308                 memcpy(&value, &entry->v, sizeof(value)); 
1309
1310                 entry = entry->next;
1311    
1312                 /*
1313                  *      For people handcrafting JSON responses
1314                  */
1315                 p = name;
1316                 while ((p = q = strchr(p, '|'))) {
1317                         *q = ':';
1318                         p++;
1319                 }
1320
1321                 attribute = name;
1322                 reference = request;
1323          
1324                 /*
1325                  *      Resolve attribute name to a dictionary entry and
1326                  *      pairlist.
1327                  */
1328                 RDEBUG2("Decoding attribute \"%s\"", name);
1329                 
1330                 request_name = radius_request_name(&attribute, REQUEST_CURRENT);
1331                 if (request_name == REQUEST_UNKNOWN) {
1332                         RDEBUG("WARNING: Request qualifier, skipping");
1333
1334                         continue;
1335                 }
1336
1337                 if (!radius_request(&reference, request_name)) {
1338                         RDEBUG("WARNING: Attribute name refers to outer request"
1339                                " but not in a tunnel, skipping");
1340
1341                         continue;
1342                 }
1343
1344                 list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
1345                 if (list_name == PAIR_LIST_UNKNOWN) {
1346                         RDEBUG("WARNING: Invalid list qualifier, skipping");
1347
1348                         continue;
1349                 }
1350
1351                 da = dict_attrbyname(attribute);
1352                 if (!da) {
1353                         RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
1354                                attribute);
1355
1356                         continue;
1357                 }
1358
1359                 vps = radius_list(reference, list_name);
1360
1361                 assert(vps);
1362
1363                 /*
1364                  *      Alternate JSON structure that allows operator,
1365                  *      and other flags to be specified.
1366                  *
1367                  *      "<name>":{
1368                  *              "do_xlat":<bool>,
1369                  *              "is_json":<bool>,
1370                  *              "op":"<op>",
1371                  *              "value":<value>
1372                  *      }
1373                  *
1374                  *      Where value is a:
1375                  *        - []  Multivalued array
1376                  *        - {}  Nested Valuepair
1377                  *        - *   Integer or string value
1378                  */
1379                 if (json_object_is_type(value, json_type_object)) {
1380                         /*
1381                          *      Process operator if present.
1382                          */
1383                         tmp = json_object_object_get(value, "op");
1384                         if (tmp) {
1385                                 flags.op = fr_str2int(fr_tokens,
1386                                                       json_object_get_string(tmp), 0);
1387
1388                                 if (!flags.op) {
1389                                         RDEBUG("Invalid operator value \"%s\","
1390                                                " skipping", tmp);
1391                                         continue;
1392                                 }
1393                         }
1394
1395                         /*
1396                          *      Process optional do_xlat bool.
1397                          */
1398                         tmp = json_object_object_get(value, "do_xlat");
1399                         if (tmp) {
1400                                 flags.do_xlat = json_object_get_boolean(tmp);
1401                         }
1402
1403                         /*
1404                          *      Process optional is_json bool.
1405                          */
1406                         tmp = json_object_object_get(value, "is_json");
1407                         if (tmp) {
1408                                 flags.is_json = json_object_get_boolean(tmp);
1409                         }
1410
1411                         /*
1412                          *      Value key must be present if were using
1413                          *      the expanded syntax.
1414                          */
1415                         value = json_object_object_get(value, "value");
1416                         if (!value) {
1417                                 RDEBUG("Value key missing, skipping", value);
1418                                 continue;
1419                         }
1420                 }
1421
1422         /*
1423          *      Setup pairmake / recursion loop.
1424          */
1425         if (!flags.is_json &&
1426             json_object_is_type(value, json_type_array)) {
1427                 len = json_object_array_length(value);
1428                 if (!len) {
1429                         RDEBUG("Zero length value array, skipping", value);
1430                         continue;
1431                 }
1432                 idx = json_object_array_get_idx(value, 0);
1433         } else {
1434                 len = 1;
1435                 idx = value;
1436         }
1437
1438         i = 0;
1439         do {
1440                 if (!(*max_attrs)--) {
1441                                 radlog(L_ERR, "rlm_rest (%s): At maximum"
1442                                        " attribute limit", instance->xlat_name);
1443                                 return NULL;
1444                 }
1445
1446                 /*
1447                  *      Automagically switch the op for multivalued
1448                  *      attributes.
1449                  */
1450                 if (((flags.op == T_OP_SET) ||
1451                      (flags.op == T_OP_EQ)) && (len > 1)) {
1452                         flags.op = T_OP_ADD;
1453                 }
1454
1455                 if (!flags.is_json &&
1456                     json_object_is_type(value, json_type_object)) {
1457                         /* TODO: Insert nested VP into VP structure...*/
1458                         RDEBUG("Found nested VP", value);
1459                         vp = json_pairmake(instance, section,
1460                                            request, value,
1461                                            level + 1, max_attrs);
1462                 } else {
1463                         vp = json_pairmake_leaf(instance, section,
1464                                                 request, da, &flags,
1465                                                 idx);
1466
1467                         if (vp != NULL) {
1468                                 if (vp->flags.do_xlat) {
1469                                         RDEBUG("Performing xlat"
1470                                                " expansion of response"
1471                                                " value", value);
1472                                 }
1473
1474                                 pairxlatmove(request, vps, &vp);
1475                         }
1476                 }
1477         } while ((++i < len) && (idx = json_object_array_get_idx(value, i)));
1478    }
1479
1480    return vp;
1481 }
1482
1483 /** Converts JSON response into VALUE_PAIRs and adds them to the request.
1484  * 
1485  * Converts the raw JSON string into a json-c object tree and passes it to
1486  * json_pairmake. After the tree has been parsed json_object_put is called
1487  * which decrements the reference count of the root node by one, and frees
1488  * the entire tree.
1489  *
1490  * @see rest_encode_json
1491  * @see json_pairmake
1492  *
1493  * @param[in] instance configuration data.
1494  * @param[in] section configuration data.
1495  * @param[in,out] request Current request.
1496  * @param[in] handle REST handle.
1497  * @param[in] raw buffer containing JSON data.
1498  * @param[in] rawlen Length of data in raw buffer.
1499  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
1500  */
1501 static int rest_decode_json(rlm_rest_t *instance,
1502                             UNUSED rlm_rest_section_t *section,
1503                             REQUEST *request, UNUSED void *handle,
1504                             char *raw, UNUSED size_t rawlen)
1505 {
1506         const char *p = raw;
1507         
1508         struct json_object *json;
1509         
1510         int max = REST_BODY_MAX_ATTRS;
1511
1512         /*
1513          *      Empty response?
1514          */
1515         while (isspace(*p)) p++;
1516         if (*p == '\0') return FALSE;
1517
1518         json = json_tokener_parse(p);
1519         if (!json) {
1520                 radlog(L_ERR, "rlm_rest (%s): Malformed JSON data \"%s\"",
1521                         instance->xlat_name, raw);
1522                 return -1;
1523         }
1524
1525         json_pairmake(instance, section, request, json, 0, &max);
1526
1527         /*
1528          *      Decrement reference count for root object, should free entire
1529          *      JSON tree.
1530          */
1531         json_object_put(json);
1532
1533         return (REST_BODY_MAX_ATTRS - max);
1534 }
1535 #endif
1536
1537 /** Processes incoming HTTP header data from libcurl.
1538  *
1539  * Processes the status line, and Content-Type headers from the incoming HTTP
1540  * response.
1541  *
1542  * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
1543  * by libcurl.
1544  *
1545  * @param[in] ptr Char buffer where inbound header data is written.
1546  * @param[in] size Multiply by nmemb to get the length of ptr.
1547  * @param[in] nmemb Multiply by size to get the length of ptr.
1548  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1549  * @return Length of data processed, or 0 on error.
1550  */
1551 static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
1552                                 void *userdata)
1553 {
1554         rlm_rest_write_t *ctx  = userdata;
1555         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1556         
1557         const char *p = ptr, *q;
1558         char *tmp;
1559
1560         const size_t t = (size * nmemb);
1561         size_t s = t;
1562         size_t len;
1563
1564         http_body_type_t type;
1565         http_body_type_t supp;
1566
1567         switch (ctx->state)
1568         {
1569                 case WRITE_STATE_INIT:
1570                         RDEBUG("Processing header");
1571
1572                         /* 
1573                          * HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
1574                          *
1575                          * "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
1576                          */
1577                         if (s < 14) goto malformed;
1578
1579                         /* 
1580                          * Check start of header matches...
1581                          */
1582                         if (strncasecmp("HTTP/", p, 5) != 0) goto malformed;
1583
1584                         p += 5;
1585                         s -= 5;
1586
1587                         /*
1588                          * Skip the version field, next space should mark start
1589                          * of reason_code.
1590                          */
1591                         q = memchr(p, ' ', s);
1592                         if (q == NULL) goto malformed;
1593
1594                         s -= (q - p);
1595                         p  = q;
1596
1597                         /* 
1598                          * Process reason_code.
1599                          *
1600                          * " 100" (4) + "\r\n" (2) = 6
1601                          */
1602                         if (s < 6) goto malformed;
1603                         p++;
1604                         s--;
1605
1606                         /* Char after reason code must be a space, or \r */
1607                         if (!((p[3] == ' ') || (p[3] == '\r'))) goto malformed;
1608
1609                         ctx->code = atoi(p);
1610
1611                         /*
1612                          *      Process reason_phrase (if present).
1613                          */
1614                         if (p[3] == ' ') {
1615                                 p += 4;
1616                                 s -= 4;
1617
1618                                 q = memchr(p, '\r', s);
1619                                 if (q == NULL) goto malformed;
1620
1621                                 len = (q - p);
1622
1623                                 tmp = rad_malloc(len + 1);
1624                                 strlcpy(tmp, p, len + 1);
1625
1626                                 RDEBUG("\tStatus : %i (%s)", ctx->code, tmp);
1627
1628                                 free(tmp);
1629                         } else {
1630                                 RDEBUG("\tStatus : %i", ctx->code);
1631                         }
1632
1633                         ctx->state = WRITE_STATE_PARSE_HEADERS;
1634
1635                         break;
1636
1637                 case WRITE_STATE_PARSE_HEADERS:
1638                         if ((s >= 14) &&
1639                             (strncasecmp("Content-Type: ", p, 14) == 0)) {
1640                                 p += 14;
1641                                 s -= 14;
1642
1643                                 /* 
1644                                  *      Check to see if there's a parameter
1645                                  *      separator.
1646                                  */
1647                                 q = memchr(p, ';', s);
1648
1649                                 /*
1650                                  *      If there's not, find the end of this
1651                                  *      header.
1652                                  */
1653                                 if (q == NULL) q = memchr(p, '\r', s);
1654
1655                                 len = (q == NULL) ? s : (unsigned)(q - p);
1656
1657                                 type = fr_substr2int(http_content_type_table,
1658                                         p, HTTP_BODY_UNKNOWN,
1659                                         len);
1660
1661                                 supp = http_body_type_supported[type];
1662
1663                                 tmp = rad_malloc(len + 1);
1664                                 strlcpy(tmp, p, len + 1);
1665
1666                                 RDEBUG("\tType   : %s (%s)",
1667                                         fr_int2str(http_body_type_table, type,
1668                                                 "¿Unknown?"), tmp);
1669
1670                                 free(tmp);
1671
1672                                 if (type == HTTP_BODY_UNKNOWN) {
1673                                         RDEBUG("Couldn't determine type, using"
1674                                                " request type \"%s\".",
1675                                                fr_int2str(http_body_type_table,
1676                                                           ctx->type,
1677                                                           "¿Unknown?"));
1678
1679                                 } else if (supp == HTTP_BODY_UNSUPPORTED) {
1680                                         RDEBUG("Type \"%s\" is currently"
1681                                                " unsupported",
1682                                                fr_int2str(http_body_type_table,
1683                                                           type, "¿Unknown?"));
1684                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1685                                 } else if (supp == HTTP_BODY_UNAVAILABLE) {
1686                                         RDEBUG("Type \"%s\" is currently"
1687                                                " unavailable, please rebuild"
1688                                                " this module with the required"
1689                                                " headers",
1690                                                fr_int2str(http_body_type_table,
1691                                                           type, "¿Unknown?"));
1692                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1693
1694                                 } else if (supp == HTTP_BODY_INVALID) {
1695                                         RDEBUG("Type \"%s\" is not a valid web"
1696                                                " API data markup format",
1697                                                fr_int2str(http_body_type_table,
1698                                                           type, "¿Unknown?"));
1699
1700                                         ctx->type = HTTP_BODY_INVALID;
1701
1702                                 } else if (type != ctx->type) {
1703                                         ctx->type = type;
1704                                 }
1705                         }
1706                         break;
1707                         
1708                 default:
1709                         break;
1710         }
1711         return t;
1712
1713         malformed:
1714
1715         RDEBUG("Incoming header was malformed");
1716         ctx->code = -1;
1717
1718         return (t - s);
1719 }
1720
1721 /** Processes incoming HTTP body data from libcurl.
1722  *
1723  * Writes incoming body data to an intermediary buffer for later parsing by
1724  * one of the decode functions.
1725  *
1726  * @param[in] ptr Char buffer where inbound header data is written
1727  * @param[in] size Multiply by nmemb to get the length of ptr.
1728  * @param[in] nmemb Multiply by size to get the length of ptr.
1729  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1730  * @return length of data processed, or 0 on error.
1731  */
1732 static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
1733                               void *userdata)
1734 {
1735         rlm_rest_write_t *ctx  = userdata;
1736         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1737         
1738         const char *p = ptr;
1739         char *tmp;
1740
1741         const size_t t = (size * nmemb);
1742
1743         /*
1744          *      Any post processing of headers should go here...
1745          */
1746         if (ctx->state == WRITE_STATE_PARSE_HEADERS) {
1747                 ctx->state = WRITE_STATE_PARSE_CONTENT;
1748         }
1749
1750         switch (ctx->type)
1751         {
1752                 case HTTP_BODY_UNSUPPORTED:
1753                         return t;
1754
1755                 case HTTP_BODY_INVALID:
1756                         tmp = rad_malloc(t + 1);
1757                         strlcpy(tmp, p, t + 1);
1758
1759                         RDEBUG2("%s", tmp);
1760
1761                         free(tmp);
1762
1763                         return t;
1764
1765                 default:
1766                         if (t > (ctx->alloc - ctx->used)) {
1767                                 ctx->alloc += ((t + 1) > REST_BODY_INCR) ?
1768                                         t + 1 : REST_BODY_INCR;
1769
1770                                 tmp = ctx->buffer;
1771
1772                                 ctx->buffer = rad_malloc(ctx->alloc);
1773
1774                                 /* If data has been written previously */
1775                                 if (tmp) {
1776                                         strlcpy(ctx->buffer, tmp,
1777                                                (ctx->used + 1));
1778                                         free(tmp);
1779                                 }
1780                         }
1781                         strlcpy(ctx->buffer + ctx->used, p, t + 1);
1782                         ctx->used += t;
1783
1784                         break;
1785         }
1786
1787         return t;
1788 }
1789
1790 /** (Re-)Initialises the data in a rlm_rest_write_t.
1791  *
1792  * This resets the values of the a rlm_rest_write_t to their defaults.
1793  * Must be called between encoding sessions.
1794  *
1795  * @see rest_write_body
1796  * @see rest_write_header
1797  * 
1798  * @param[in] request Current request.
1799  * @param[in] ctx data to initialise.
1800  * @param[in] type Default http_body_type to use when decoding raw data, may be
1801  * overwritten by rest_write_header.
1802  */
1803 static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx,
1804                                 http_body_type_t type)
1805 {
1806         ctx->request    = request;
1807         ctx->type       = type;
1808         ctx->state      = WRITE_STATE_INIT;
1809         ctx->alloc      = 0;
1810         ctx->used       = 0;
1811         ctx->buffer     = NULL;
1812 }
1813
1814 /** Frees the intermediary buffer created by rest_write.
1815  *
1816  * @param[in] ctx data to be freed.
1817  */
1818 static void rest_write_free(rlm_rest_write_t *ctx)
1819 {
1820         if (ctx->buffer != NULL) {
1821                 free(ctx->buffer);
1822         }
1823 }
1824
1825 /** Configures body specific curlopts.
1826  * 
1827  * Configures libcurl handle to use either chunked mode, where the request
1828  * data will be sent using multiple HTTP requests, or contiguous mode where
1829  * the request data will be sent in a single HTTP request.
1830  *
1831  * @param[in] instance configuration data.
1832  * @param[in] section configuration data.
1833  * @param[in] handle rlm_rest_handle_t to configure.
1834  * @param[in] func to pass to libcurl for chunked.
1835  *            transfers (NULL if not using chunked mode).
1836  * @return TRUE on success FALSE on error.
1837  */
1838 static int rest_request_config_body(rlm_rest_t *instance,
1839                                     rlm_rest_section_t *section,
1840                                     rlm_rest_handle_t *handle,
1841                                     rest_read_t func)
1842 {
1843         rlm_rest_curl_context_t *ctx = handle->ctx;
1844         CURL *candle                 = handle->handle;
1845
1846         ssize_t len;
1847         CURLcode ret;
1848
1849         if (section->chunk > 0) {
1850                 ret = curl_easy_setopt(candle, CURLOPT_READDATA,
1851                                        &ctx->read);
1852                 if (ret != CURLE_OK) goto error;
1853
1854                 ret = curl_easy_setopt(candle, CURLOPT_READFUNCTION,
1855                                        rest_encode_json);
1856                 if (ret != CURLE_OK) goto error;
1857         } else {
1858                 len = rest_read_wrapper(&ctx->body, func,
1859                                         REST_BODY_MAX_LEN , &ctx->read);
1860                 if (len <= 0) {
1861                         radlog(L_ERR, "rlm_rest (%s): Failed creating HTTP"
1862                                " body content", instance->xlat_name);
1863                         return FALSE;
1864                 }
1865
1866                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDS,
1867                                        ctx->body);
1868                 if (ret != CURLE_OK) goto error;
1869
1870                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDSIZE,
1871                                        len);
1872                 if (ret != CURLE_OK) goto error;
1873         }
1874
1875         return TRUE;
1876
1877         error:
1878         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
1879                 instance->xlat_name, ret, curl_easy_strerror(ret));
1880
1881         return FALSE;
1882 }
1883
1884 /** Configures request curlopts.
1885  * 
1886  * Configures libcurl handle setting various curlopts for things like local
1887  * client time, Content-Type, and other FreeRADIUS custom headers.
1888  * 
1889  * Current FreeRADIUS custom headers are:
1890  *  - X-FreeRADIUS-Section      The module section being processed.
1891  *  - X-FreeRADIUS-Server       The current virtual server the REQUEST is
1892  *                              passing through.
1893  *
1894  * Sets up callbacks for all response processing (buffers and body data).
1895  *
1896  * @param[in] instance configuration data.
1897  * @param[in] section configuration data.
1898  * @param[in] handle to configure.
1899  * @param[in] request Current request.
1900  * @param[in] method to use (HTTP verbs PUT, POST, DELETE etc...).
1901  * @param[in] type Content-Type for request encoding, also sets the default
1902  *      for decoding.
1903  * @param[in] uri buffer containing the expanded URI to send the request to.
1904  * @return TRUE on success (all opts configured) FALSE on error.
1905  */
1906 int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
1907                         REQUEST *request, void *handle, http_method_t method,
1908                         http_body_type_t type, char *uri)
1909 {
1910         rlm_rest_handle_t *randle       = handle;
1911         rlm_rest_curl_context_t *ctx    = randle->ctx;
1912         CURL *candle                    = randle->handle;
1913         
1914         http_auth_type_t auth = section->auth;
1915
1916         CURLcode ret;
1917         long val = 1;
1918
1919         char buffer[512];
1920
1921         buffer[(sizeof(buffer) - 1)] = '\0';
1922
1923         /*
1924          *      Setup any header options and generic headers.
1925          */
1926         ret = curl_easy_setopt(candle, CURLOPT_URL, uri);
1927         if (ret != CURLE_OK) goto error;
1928
1929         ret = curl_easy_setopt(candle, CURLOPT_USERAGENT, "FreeRADIUS");
1930         if (ret != CURLE_OK) goto error;
1931   
1932         snprintf(buffer, (sizeof(buffer) - 1), "Content-Type: %s",
1933                  fr_int2str(http_content_type_table, type, "¿Unknown?"));
1934         ctx->headers = curl_slist_append(ctx->headers, buffer);
1935         if (!ctx->headers) goto error_header;
1936         
1937         if (section->timeout) {
1938                 ret = curl_easy_setopt(candle, CURLOPT_TIMEOUT,
1939                                        section->timeout);
1940                 if (ret != CURLE_OK) goto error;
1941         }
1942         
1943         ret = curl_easy_setopt(candle, CURLOPT_PROTOCOLS,
1944                                (CURLPROTO_HTTP | CURLPROTO_HTTPS));
1945         if (ret != CURLE_OK) goto error;
1946         
1947         /*
1948          *      FreeRADIUS custom headers
1949          */
1950         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Section: %s",
1951                  section->name);
1952         ctx->headers = curl_slist_append(ctx->headers, buffer);
1953         if (!ctx->headers) goto error_header;
1954
1955         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Server: %s",
1956                  request->server);
1957         ctx->headers = curl_slist_append(ctx->headers, buffer);
1958         if (!ctx->headers) goto error_header;
1959
1960         /*
1961          *      Configure HTTP verb (GET, POST, PUT, DELETE, other...)
1962          */
1963         switch (method)
1964         {
1965                 case HTTP_METHOD_GET :
1966                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1967                                                val);
1968                         if (ret != CURLE_OK) goto error;
1969
1970                         break;
1971
1972                 case HTTP_METHOD_POST :
1973                         ret = curl_easy_setopt(candle, CURLOPT_POST,
1974                                                val);
1975                         if (ret != CURLE_OK) goto error;
1976
1977                         break;
1978
1979                 case HTTP_METHOD_PUT :
1980                         ret = curl_easy_setopt(candle, CURLOPT_PUT,
1981                                                val);
1982                         if (ret != CURLE_OK) goto error;
1983
1984                         break;
1985
1986                 case HTTP_METHOD_DELETE :
1987                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1988                                                val);
1989                         if (ret != CURLE_OK) goto error;
1990
1991                         ret = curl_easy_setopt(candle,
1992                                                CURLOPT_CUSTOMREQUEST, "DELETE");
1993                         if (ret != CURLE_OK) goto error;
1994
1995                         break;
1996
1997                 case HTTP_METHOD_CUSTOM :
1998                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1999                                                val);
2000                         if (ret != CURLE_OK) goto error;
2001
2002                         ret = curl_easy_setopt(candle,
2003                                                CURLOPT_CUSTOMREQUEST,
2004                                                section->method);
2005                         if (ret != CURLE_OK) goto error;
2006
2007                 default:
2008                         assert(0);
2009                         break;
2010         };
2011         
2012         /*
2013          *      Set user based authentication parameters
2014          */
2015         if (auth) {
2016                 if ((auth >= HTTP_AUTH_BASIC) &&
2017                     (auth <= HTTP_AUTH_ANY_SAFE)) {
2018                         ret = curl_easy_setopt(candle, CURLOPT_HTTPAUTH,
2019                                                http_curl_auth[auth]);
2020                         if (ret != CURLE_OK) goto error;
2021                         
2022                         if (section->username) {
2023                                 radius_xlat(buffer, sizeof(buffer),
2024                                             section->username, request, NULL, NULL);
2025                                             
2026                                 ret = curl_easy_setopt(candle, CURLOPT_USERNAME,
2027                                                        buffer);
2028                                 if (ret != CURLE_OK) goto error;
2029                         }
2030                         if (section->password) {
2031                                 radius_xlat(buffer, sizeof(buffer),
2032                                             section->password, request, NULL, NULL);
2033                                             
2034                                 ret = curl_easy_setopt(candle, CURLOPT_PASSWORD,
2035                                                        buffer);
2036                                 if (ret != CURLE_OK) goto error;
2037                         }
2038
2039 #ifdef CURLOPT_TLSAUTH_USERNAME
2040                 } else if (type == HTTP_AUTH_TLS_SRP) {
2041                         ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_TYPE,
2042                                                http_curl_auth[auth]);
2043                 
2044                         if (section->username) {
2045                                 radius_xlat(buffer, sizeof(buffer),
2046                                             section->username, request, NULL, NULL);
2047                                             
2048                                 ret = curl_easy_setopt(candle,
2049                                                        CURLOPT_TLSAUTH_USERNAME,
2050                                                        buffer);
2051                                 if (ret != CURLE_OK) goto error;
2052                         }
2053                         if (section->password) {
2054                                 radius_xlat(buffer, sizeof(buffer),
2055                                             section->password, request, NULL, NULL);
2056                                             
2057                                 ret = curl_easy_setopt(candle,
2058                                                        CURLOPT_TLSAUTH_PASSWORD,
2059                                                        buffer);
2060                                 if (ret != CURLE_OK) goto error;
2061                         }
2062 #endif
2063                 }
2064         }
2065         
2066         /*
2067          *      Set SSL/TLS authentication parameters
2068          */
2069         if (section->tls_certfile) {
2070                 ret = curl_easy_setopt(candle,
2071                                        CURLOPT_SSLCERT,
2072                                        section->tls_certfile);
2073                 if (ret != CURLE_OK) goto error;
2074         }
2075         
2076         if (section->tls_keyfile) {
2077                 ret = curl_easy_setopt(candle,
2078                                        CURLOPT_SSLKEY,
2079                                        section->tls_keyfile);
2080                 if (ret != CURLE_OK) goto error;
2081         }
2082
2083         if (section->tls_keypassword) {
2084                 ret = curl_easy_setopt(candle,
2085                                        CURLOPT_KEYPASSWD,
2086                                        section->tls_keypassword);
2087                 if (ret != CURLE_OK) goto error;
2088         }
2089         
2090         if (section->tls_cacertfile) {
2091                 ret = curl_easy_setopt(candle,
2092                                        CURLOPT_ISSUERCERT,
2093                                        section->tls_cacertfile);
2094                 if (ret != CURLE_OK) goto error;
2095         }
2096         
2097         if (section->tls_cacertdir) {
2098                 ret = curl_easy_setopt(candle,
2099                                        CURLOPT_CAPATH,
2100                                        section->tls_cacertdir);
2101                 if (ret != CURLE_OK) goto error;
2102         }
2103         
2104         if (section->tls_randfile) {
2105                 ret = curl_easy_setopt(candle,
2106                                        CURLOPT_RANDOM_FILE,
2107                                        section->tls_randfile);
2108                 if (ret != CURLE_OK) goto error;
2109         }
2110         
2111         if (section->tls_verify_cert) {
2112                 ret = curl_easy_setopt(candle,
2113                                        CURLOPT_SSL_VERIFYHOST,
2114                                        (section->tls_verify_cert_cn == TRUE) ?
2115                                         2 : 0);
2116                 if (ret != CURLE_OK) goto error;
2117         } else {
2118                 ret = curl_easy_setopt(candle,
2119                        CURLOPT_SSL_VERIFYPEER,
2120                        0);
2121                 if (ret != CURLE_OK) goto error;
2122         }
2123                 
2124         /*
2125          *      Tell CURL how to get HTTP body content, and how to process
2126          *      incoming data.
2127          */
2128         rest_write_ctx_init(request, &ctx->write, type);
2129
2130         ret = curl_easy_setopt(candle, CURLOPT_HEADERFUNCTION,
2131                                rest_write_header);
2132         if (ret != CURLE_OK) goto error;
2133
2134         ret = curl_easy_setopt(candle, CURLOPT_HEADERDATA,
2135                                &ctx->write);
2136         if (ret != CURLE_OK) goto error;
2137
2138         ret = curl_easy_setopt(candle, CURLOPT_WRITEFUNCTION,
2139                                rest_write_body);
2140         if (ret != CURLE_OK) goto error;
2141
2142         ret = curl_easy_setopt(candle, CURLOPT_WRITEDATA,
2143                                &ctx->write);
2144         if (ret != CURLE_OK) goto error;
2145
2146         switch (method)
2147         {
2148                 case HTTP_METHOD_GET :
2149                 case HTTP_METHOD_DELETE :
2150                         return FALSE;
2151                         break;
2152
2153                 case HTTP_METHOD_POST :
2154                 case HTTP_METHOD_PUT :
2155                 case HTTP_METHOD_CUSTOM :
2156                         if (section->chunk > 0) {
2157                                 ctx->read.chunk = section->chunk;
2158
2159                                 ctx->headers = curl_slist_append(ctx->headers,
2160                                                                  "Expect:");
2161                                 if (!ctx->headers) goto error_header;
2162
2163                                 ctx->headers = curl_slist_append(ctx->headers,
2164                                                                  "Transfer-Encoding: chunked");
2165                                 if (!ctx->headers) goto error_header;
2166                         }
2167
2168                         switch (type)
2169                         {
2170 #ifdef HAVE_JSON
2171                                 case HTTP_BODY_JSON:
2172                                         rest_read_ctx_init(request,
2173                                                            &ctx->read, 1);
2174
2175                                         ret = rest_request_config_body(instance,
2176                                                                        section,
2177                                                                        handle,
2178                                                                        rest_encode_json);
2179                                         if (!ret) return -1;
2180
2181                                         break;
2182 #endif
2183
2184                                 case HTTP_BODY_POST:
2185                                         rest_read_ctx_init(request,
2186                                                            &ctx->read, 0);
2187
2188                                         ret = rest_request_config_body(instance,
2189                                                                        section,
2190                                                                        handle,
2191                                                                        rest_encode_post);
2192                                         if (!ret) return -1;
2193
2194                                         break;
2195
2196                                 default:
2197                                         assert(0);
2198                         }
2199
2200                         ret = curl_easy_setopt(candle, CURLOPT_HTTPHEADER,
2201                                                ctx->headers);
2202                         if (ret != CURLE_OK) goto error;
2203
2204                         break;
2205
2206                 default:
2207                         assert(0);
2208         };
2209
2210         return TRUE;
2211
2212         error:
2213         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
2214                instance->xlat_name, ret, curl_easy_strerror(ret));
2215         return FALSE;
2216
2217         error_header:
2218         radlog(L_ERR, "rlm_rest (%s): Failed creating header",
2219                instance->xlat_name);
2220         return FALSE;
2221 }
2222
2223 /** Sends a REST (HTTP) request.
2224  * 
2225  * Send the actual REST request to the server. The response will be handled by
2226  * the numerous callbacks configured in rest_request_config.
2227  *
2228  * @param[in] instance configuration data.
2229  * @param[in] section configuration data.
2230  * @param[in] handle to use.
2231  * @return TRUE on success or FALSE on error.
2232  */
2233 int rest_request_perform(rlm_rest_t *instance,
2234                          UNUSED rlm_rest_section_t *section, void *handle)
2235 {
2236         rlm_rest_handle_t *randle = handle;
2237         CURL *candle              = randle->handle;
2238         CURLcode ret;
2239
2240         ret = curl_easy_perform(candle);
2241         if (ret != CURLE_OK) {
2242                 radlog(L_ERR, "rlm_rest (%s): Request failed: %i - %s",
2243                        instance->xlat_name, ret, curl_easy_strerror(ret));
2244                 return FALSE;
2245         }
2246
2247         return TRUE;
2248 }
2249
2250 /** Sends the response to the correct decode function.
2251  * 
2252  * Uses the Content-Type information written in rest_write_header to
2253  * determine the correct decode function to use. The decode function will
2254  * then convert the raw received data into VALUE_PAIRs.
2255  *
2256  * @param[in] instance configuration data.
2257  * @param[in] section configuration data.
2258  * @param[in] request Current request.
2259  * @param[in] handle to use.
2260  * @return TRUE on success or FALSE on error.
2261  */
2262 int rest_request_decode(rlm_rest_t *instance, 
2263                         UNUSED rlm_rest_section_t *section,
2264                         REQUEST *request, void *handle)
2265 {
2266         rlm_rest_handle_t *randle       = handle;
2267         rlm_rest_curl_context_t *ctx    = randle->ctx;
2268
2269         int ret;
2270
2271         if (ctx->write.buffer == NULL) {
2272                 RDEBUG("Skipping attribute processing, no body data received");
2273                 return FALSE;
2274         }
2275
2276         RDEBUG("Processing body", ret);
2277
2278         switch (ctx->write.type)
2279         {
2280                 case HTTP_BODY_POST:
2281                         ret = rest_decode_post(instance, section, request,
2282                                                handle, ctx->write.buffer,
2283                                                ctx->write.used);
2284                         break;
2285 #ifdef HAVE_JSON
2286                 case HTTP_BODY_JSON:
2287                         ret = rest_decode_json(instance, section, request,
2288                                                handle, ctx->write.buffer,
2289                                                ctx->write.used);
2290                         break;
2291 #endif
2292                 case HTTP_BODY_UNSUPPORTED:
2293                 case HTTP_BODY_UNAVAILABLE:
2294                 case HTTP_BODY_INVALID:
2295                         return -1;
2296
2297                 default:
2298                         assert(0);
2299         }
2300
2301         return ret;
2302 }
2303
2304 /** Cleans up after a REST request.
2305  * 
2306  * Resets all options associated with a CURL handle, and frees any headers
2307  * associated with it.
2308  *
2309  * Calls rest_read_ctx_free and rest_write_free to free any memory used by
2310  * context data.
2311  *
2312  * @param[in] instance configuration data.
2313  * @param[in] section configuration data.
2314  * @param[in] handle to cleanup.
2315  * @return TRUE on success or FALSE on error.
2316  */
2317 void rest_request_cleanup(UNUSED rlm_rest_t *instance,
2318                           UNUSED rlm_rest_section_t *section, void *handle)
2319 {
2320         rlm_rest_handle_t *randle       = handle;
2321         rlm_rest_curl_context_t *ctx    = randle->ctx;
2322         CURL *candle                    = randle->handle;
2323
2324         /*
2325          * Clear any previously configured options
2326          */
2327         curl_easy_reset(candle);
2328
2329         /*
2330          * Free header list
2331          */
2332         if (ctx->headers != NULL) {
2333                 curl_slist_free_all(ctx->headers);
2334                 ctx->headers = NULL;
2335         }
2336
2337         /*
2338          * Free body data (only used if chunking is disabled)
2339          */
2340         if (ctx->body != NULL) free(ctx->body);
2341   
2342         /*
2343          * Free other context info
2344          */
2345         rest_read_ctx_free(&ctx->read);
2346         rest_write_free(&ctx->write);
2347 }
2348
2349 /** URL encodes a string.
2350  * 
2351  * Encode special chars as per RFC 3986 section 4.
2352  *
2353  * @param[in] request Current request.
2354  * @param[out] out Where to write escaped string.
2355  * @param[in] outlen Size of out buffer.
2356  * @param[in] raw string to be urlencoded.
2357  * @param[in] arg pointer, gives context for escaping.
2358  * @return length of data written to out (excluding NULL).
2359  */
2360 static size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen,
2361                               const char *raw, UNUSED void *arg)
2362 {
2363         char *escaped;
2364
2365         escaped = curl_escape(raw, strlen(raw));
2366         strlcpy(out, escaped, outlen);
2367         curl_free(escaped);
2368
2369         return strlen(out);
2370 }
2371
2372 /** Builds URI; performs XLAT expansions and encoding.
2373  * 
2374  * Splits the URI into "http://example.org" and "/%{xlat}/query/?bar=foo"
2375  * Both components are expanded, but values expanded for the second component
2376  * are also url encoded.
2377  *
2378  * @param[in] instance configuration data.
2379  * @param[in] section configuration data.
2380  * @param[in] request Current request
2381  * @param[out] buffer to write expanded URI to.
2382  * @param[in] bufsize Size of buffer.
2383  * @return length of data written to buffer (excluding NULL) or < 0 if an error
2384  *      occurred.
2385  */
2386 ssize_t rest_uri_build(rlm_rest_t *instance, rlm_rest_section_t *section,
2387                        REQUEST *request, char *buffer, size_t bufsize)
2388 {
2389         const char *p, *q;
2390
2391         char *out, *scheme;
2392         const char *path;
2393
2394         unsigned short count = 0;
2395
2396         size_t len;
2397
2398         p = section->uri;
2399
2400         /*
2401          *      All URLs must contain at least <scheme>://<server>/
2402          */
2403         while ((q = strchr(p, '/'))) {
2404                 p = q + 1;
2405                 
2406                 if (++count == 3) {
2407                         break;
2408                 }
2409         }
2410
2411         if (count != 3) {
2412                 radlog(L_ERR, "rlm_rest (%s): Error URI is malformed,"
2413                        " can't find start of path", instance->xlat_name);
2414                 return -1;
2415         }
2416
2417         len = (q - p);
2418
2419         scheme = rad_malloc(len + 1);
2420         strlcpy(scheme, section->uri, len + 1);
2421
2422         path = (q + 1);
2423
2424         out = buffer;
2425         out += radius_xlat(out, bufsize, scheme, request, NULL, NULL);
2426
2427         free(scheme);
2428
2429         out += radius_xlat(out, (bufsize - (buffer - out)), path, request,
2430                          rest_uri_escape, NULL);
2431
2432         return (buffer - out);
2433 }