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