Check for JSON-C library as well
[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 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
187  *
188  * These fields are set when parsing the expanded format for value pairs in
189  * JSON, and control how json_pairmake_leaf and json_pairmake convert the JSON
190  * value, and move the new VALUE_PAIR into an attribute list.
191  *
192  * @see json_pairmake
193  * @see json_pairmake_leaf
194  */
195 #ifdef HAVE_JSON
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":['</pre> @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] read 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] read 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] raw string from decoder.
935  * @param[in] vp containing parsed value.
936  */
937 static void rest_check_truncation(REQUEST *request, const char *raw,
938                                   VALUE_PAIR *vp)
939 {
940         char cooked[1024];
941
942         vp_prints_value(cooked, sizeof(cooked), vp, 0);
943         if (strcmp(raw, cooked) != 0) {
944                 RDEBUG("WARNING: Value-Pair does not match POST value, "
945                        "truncation may have occurred");
946                 RDEBUG("\tValue (pair) : \"%s\"", cooked);
947                 RDEBUG("\tValue (post) : \"%s\"", raw);
948         }
949 }
950
951 /** Converts POST response into VALUE_PAIRs and adds them to the request
952  *
953  * Accepts VALUE_PAIRS in the same format as rest_encode_post, but with the
954  * addition of optional attribute list qualifiers as part of the attribute name
955  * string.
956  * 
957  * If no qualifiers are specified, will default to the request list.
958  *
959  * POST response format is:
960  * @verbatim [outer.][<list>:]<attribute0>=<value0>&[outer.][<list>:]<attribute1>=<value1>&[outer.][<list>:]<attributeN>=<valueN> @endverbatim
961  *
962  * @see rest_encode_post
963  *
964  * @param[in] instance configuration data.
965  * @param[in] section configuration data.
966  * @param[in] handle rlm_rest_handle_t to use.
967  * @param[in] request Current request.
968  * @param[in] raw buffer containing POST data.
969  * @param[in] rawlen Length of data in raw buffer.
970  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
971  */
972 static int rest_decode_post(rlm_rest_t *instance,
973                             UNUSED rlm_rest_section_t *section,
974                             REQUEST *request, void *handle, char *raw,
975                             UNUSED size_t rawlen)
976 {
977         rlm_rest_handle_t *randle = handle;
978         CURL *candle              = randle->handle;
979
980         const char *p = raw, *q;
981
982         const char *attribute;
983         char *name  = NULL;
984         char *value = NULL;
985
986         const DICT_ATTR *da;
987         VALUE_PAIR *vp;
988
989         const DICT_ATTR **current, *processed[REST_BODY_MAX_ATTRS + 1];
990         VALUE_PAIR *tmp;
991
992         pair_lists_t list_name;
993         request_refs_t request_name;
994         REQUEST *reference = request;
995         VALUE_PAIR **vps;
996
997         size_t len;
998         int curl_len; /* Length from last curl_easy_unescape call */
999
1000         int count = 0;
1001
1002         processed[0] = NULL;
1003
1004         /*
1005          * Empty response?
1006          */
1007         while (isspace(*p)) p++;
1008
1009         if (p == NULL) 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 /** Converts JSON "value" key into VALUE_PAIR.
1155  *
1156  * If leaf is not in fact a leaf node, but contains JSON data, the data will
1157  * written to the attribute in JSON string format.
1158  *
1159  * @param[in] instance configuration data.
1160  * @param[in] section configuration data.
1161  * @param[in] request Current request.
1162  * @param[in] attribute name without qualifiers.
1163  * @param[in] flags containing the operator other flags controlling value
1164  *      expansion.
1165  * @param[in] leaf object containing the VALUE_PAIR value.
1166  * @return The VALUE_PAIR just created, or NULL on error.
1167  */
1168 #ifdef HAVE_JSON
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 to 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] g to use.
1494  * @param[in] request Current request.
1495  * @param[in] raw buffer containing JSON data.
1496  * @param[in] rawlen Length of data in raw buffer.
1497  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
1498  */
1499 static int rest_decode_json(rlm_rest_t *instance,
1500                             UNUSED rlm_rest_section_t *section,
1501                             UNUSED REQUEST *request, UNUSED void *handle,
1502                             char *raw, UNUSED size_t rawlen)
1503 {
1504         const char *p = raw;
1505         
1506         struct json_object *json;
1507         
1508         int max = REST_BODY_MAX_ATTRS;
1509
1510         /*
1511          *      Empty response?
1512          */
1513         while (isspace(*p)) p++;
1514         if (p == NULL) return FALSE;
1515
1516         json = json_tokener_parse(p);
1517         if (!json) {
1518                 radlog(L_ERR, "rlm_rest (%s): Malformed JSON data \"%s\"",
1519                         instance->xlat_name, raw);
1520                 return -1;
1521         }
1522
1523         json_pairmake(instance, section, request, json, 0, &max);
1524
1525         /*
1526          *      Decrement reference count for root object, should free entire
1527          *      JSON tree.
1528          */
1529         json_object_put(json);
1530
1531         return (REST_BODY_MAX_ATTRS - max);
1532 }
1533 #endif
1534
1535 /** Processes incoming HTTP header data from libcurl.
1536  *
1537  * Processes the status line, and Content-Type headers from the incoming HTTP
1538  * response.
1539  *
1540  * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
1541  * by libcurl.
1542  *
1543  * @param[in] ptr Char buffer where inbound header data is written.
1544  * @param[in] size Multiply by nmemb to get the length of ptr.
1545  * @param[in] nmemb Multiply by size to get the length of ptr.
1546  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1547  * @return Length of data processed, or 0 on error.
1548  */
1549 static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
1550                                 void *userdata)
1551 {
1552         rlm_rest_write_t *ctx  = userdata;
1553         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1554         
1555         const char *p = ptr, *q;
1556         char *tmp;
1557
1558         const size_t t = (size * nmemb);
1559         size_t s = t;
1560         size_t len;
1561
1562         http_body_type_t type;
1563         http_body_type_t supp;
1564
1565         switch (ctx->state)
1566         {
1567                 case WRITE_STATE_INIT:
1568                         RDEBUG("Processing header");
1569
1570                         /* 
1571                          * HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
1572                          *
1573                          * "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
1574                          */
1575                         if (s < 14) goto malformed;
1576
1577                         /* 
1578                          * Check start of header matches...
1579                          */
1580                         if (strncasecmp("HTTP/", p, 5) != 0) goto malformed;
1581
1582                         p += 5;
1583                         s -= 5;
1584
1585                         /*
1586                          * Skip the version field, next space should mark start
1587                          * of reason_code.
1588                          */
1589                         q = memchr(p, ' ', s);
1590                         if (q == NULL) goto malformed;
1591
1592                         s -= (q - p);
1593                         p  = q;
1594
1595                         /* 
1596                          * Process reason_code.
1597                          *
1598                          * " 100" (4) + "\r\n" (2) = 6
1599                          */
1600                         if (s < 6) goto malformed;
1601                         p++;
1602                         s--;
1603
1604                         /* Char after reason code must be a space, or \r */
1605                         if (!((p[3] == ' ') || (p[3] == '\r'))) goto malformed;
1606
1607                         ctx->code = atoi(p);
1608
1609                         /*
1610                          * Process reason_phrase (if present).
1611                          */
1612                         if (p[3] == ' ') {
1613                                 p += 4;
1614                                 s -= 4;
1615
1616                                 q = memchr(p, '\r', s);
1617                                 if (q == NULL) goto malformed;
1618
1619                                 len = (q - p);
1620
1621                                 tmp = rad_malloc(len + 1);
1622                                 strlcpy(tmp, p, len + 1);
1623
1624                                 RDEBUG("\tStatus : %i (%s)", ctx->code, tmp);
1625
1626                                 free(tmp);
1627                         } else {
1628                                 RDEBUG("\tStatus : %i", ctx->code);
1629                         }
1630
1631                         ctx->state = WRITE_STATE_PARSE_HEADERS;
1632
1633                         break;
1634
1635                 case WRITE_STATE_PARSE_HEADERS:
1636                         if ((s >= 14) &&
1637                             (strncasecmp("Content-Type: ", p, 14) == 0)) {
1638                                 p += 14;
1639                                 s -= 14;
1640
1641                                 /* 
1642                                  *      Check to see if there's a parameter
1643                                  *      separator.
1644                                  */
1645                                 q = memchr(p, ';', s);
1646
1647                                 /*
1648                                  *      If there's not, find the end of this
1649                                  *      header.
1650                                  */
1651                                 if (q == NULL) q = memchr(p, '\r', s);
1652
1653                                 len = (q == NULL) ? s : (unsigned)(q - p);
1654
1655                                 type = fr_substr2int(http_content_type_table,
1656                                         p, HTTP_BODY_UNKNOWN,
1657                                         len);
1658
1659                                 supp = http_body_type_supported[type];
1660
1661                                 tmp = rad_malloc(len + 1);
1662                                 strlcpy(tmp, p, len + 1);
1663
1664                                 RDEBUG("\tType   : %s (%s)",
1665                                         fr_int2str(http_body_type_table, type,
1666                                                 "¿Unknown?"), tmp);
1667
1668                                 free(tmp);
1669
1670                                 if (type == HTTP_BODY_UNKNOWN) {
1671                                         RDEBUG("Couldn't determine type, using"
1672                                                " request type \"%s\".",
1673                                                fr_int2str(http_body_type_table,
1674                                                           ctx->type,
1675                                                           "¿Unknown?"));
1676
1677                                 } else if (supp == HTTP_BODY_UNSUPPORTED) {
1678                                         RDEBUG("Type \"%s\" is currently"
1679                                                " unsupported",
1680                                                fr_int2str(http_body_type_table,
1681                                                           type, "¿Unknown?"));
1682                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1683                                 } else if (supp == HTTP_BODY_UNAVAILABLE) {
1684                                         RDEBUG("Type \"%s\" is currently"
1685                                                " unavailable, please rebuild"
1686                                                " this module with the required"
1687                                                " headers",
1688                                                fr_int2str(http_body_type_table,
1689                                                           type, "¿Unknown?"));
1690                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1691
1692                                 } else if (supp == HTTP_BODY_INVALID) {
1693                                         RDEBUG("Type \"%s\" is not a valid web"
1694                                                " API data markup format",
1695                                                fr_int2str(http_body_type_table,
1696                                                           type, "¿Unknown?"));
1697
1698                                         ctx->type = HTTP_BODY_INVALID;
1699
1700                                 } else if (type != ctx->type) {
1701                                         ctx->type = type;
1702                                 }
1703                         }
1704                         break;
1705                         
1706                 default:
1707                         break;
1708         }
1709         return t;
1710
1711         malformed:
1712
1713         RDEBUG("Incoming header was malformed");
1714         ctx->code = -1;
1715
1716         return (t - s);
1717 }
1718
1719 /** Processes incoming HTTP body data from libcurl.
1720  *
1721  * Writes incoming body data to an intermediary buffer for later parsing by
1722  * one of the decode functions.
1723  *
1724  * @param[in] ptr Char buffer where inbound header data is written
1725  * @param[in] size Multiply by nmemb to get the length of ptr.
1726  * @param[in] nmemb Multiply by size to get the length of ptr.
1727  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1728  * @return length of data processed, or 0 on error.
1729  */
1730 static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
1731                               void *userdata)
1732 {
1733         rlm_rest_write_t *ctx  = userdata;
1734         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1735         
1736         const char *p = ptr;
1737         char *tmp;
1738
1739         const size_t t = (size * nmemb);
1740
1741         /*
1742          *      Any post processing of headers should go here...
1743          */
1744         if (ctx->state == WRITE_STATE_PARSE_HEADERS) {
1745                 ctx->state = WRITE_STATE_PARSE_CONTENT;
1746         }
1747
1748         switch (ctx->type)
1749         {
1750                 case HTTP_BODY_UNSUPPORTED:
1751                         return t;
1752
1753                 case HTTP_BODY_INVALID:
1754                         tmp = rad_malloc(t + 1);
1755                         strlcpy(tmp, p, t + 1);
1756
1757                         RDEBUG2("%s", tmp);
1758
1759                         free(tmp);
1760
1761                         return t;
1762
1763                 default:
1764                         if (t > (ctx->alloc - ctx->used)) {
1765                                 ctx->alloc += ((t + 1) > REST_BODY_INCR) ?
1766                                         t + 1 : REST_BODY_INCR;
1767
1768                                 tmp = ctx->buffer;
1769
1770                                 ctx->buffer = rad_malloc(ctx->alloc);
1771
1772                                 /* If data has been written previously */
1773                                 if (tmp) {
1774                                         strlcpy(ctx->buffer, tmp,
1775                                                (ctx->used + 1));
1776                                         free(tmp);
1777                                 }
1778                         }
1779                         strlcpy(ctx->buffer + ctx->used, p, t + 1);
1780                         ctx->used += t;
1781
1782                         break;
1783         }
1784
1785         return t;
1786 }
1787
1788 /** (Re-)Initialises the data in a rlm_rest_write_t.
1789  *
1790  * This resets the values of the a rlm_rest_write_t to their defaults.
1791  * Must be called between encoding sessions.
1792  *
1793  * @see rest_write_body
1794  * @see rest_write_header
1795  * 
1796  * @param[in] request Current request.
1797  * @param[in] data to initialise.
1798  * @param[in] type Default http_body_type to use when decoding raw data, may be
1799  * overwritten by rest_write_header.
1800  */
1801 static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx,
1802                                 http_body_type_t type)
1803 {
1804         ctx->request    = request;
1805         ctx->type       = type;
1806         ctx->state      = WRITE_STATE_INIT;
1807         ctx->alloc      = 0;
1808         ctx->used       = 0;
1809         ctx->buffer     = NULL;
1810 }
1811
1812 /** Frees the intermediary buffer created by rest_write.
1813  *
1814  * @param[in] data to be freed.
1815  */
1816 static void rest_write_free(rlm_rest_write_t *ctx)
1817 {
1818         if (ctx->buffer != NULL) {
1819                 free(ctx->buffer);
1820         }
1821 }
1822
1823 /** Configures body specific curlopts.
1824  * 
1825  * Configures libcurl handle to use either chunked mode, where the request
1826  * data will be sent using multiple HTTP requests, or contiguous mode where
1827  * the request data will be sent in a single HTTP request.
1828  *
1829  * @param[in] instance configuration data.
1830  * @param[in] section configuration data.
1831  * @param[in] handle rlm_rest_handle_t to configure.
1832  * @param[in] func to pass to libcurl for chunked.
1833  *      transfers (NULL if not using chunked mode).
1834  * @return TRUE on success FALSE on error.
1835  */
1836 static int rest_request_config_body(rlm_rest_t *instance,
1837                                     rlm_rest_section_t *section,
1838                                     rlm_rest_handle_t *handle,
1839                                     rest_read_t func)
1840 {
1841         rlm_rest_curl_context_t *ctx = handle->ctx;
1842         CURL *candle                 = handle->handle;
1843
1844         ssize_t len;
1845         CURLcode ret;
1846
1847         if (section->chunk > 0) {
1848                 ret = curl_easy_setopt(candle, CURLOPT_READDATA,
1849                                        &ctx->read);
1850                 if (ret != CURLE_OK) goto error;
1851
1852                 ret = curl_easy_setopt(candle, CURLOPT_READFUNCTION,
1853                                        rest_encode_json);
1854                 if (ret != CURLE_OK) goto error;
1855         } else {
1856                 len = rest_read_wrapper(&ctx->body, func,
1857                                         REST_BODY_MAX_LEN , &ctx->read);
1858                 if (len <= 0) {
1859                         radlog(L_ERR, "rlm_rest (%s): Failed creating HTTP"
1860                                " body content", instance->xlat_name);
1861                         return FALSE;
1862                 }
1863
1864                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDS,
1865                                        ctx->body);
1866                 if (ret != CURLE_OK) goto error;
1867
1868                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDSIZE,
1869                                        len);
1870                 if (ret != CURLE_OK) goto error;
1871         }
1872
1873         return TRUE;
1874
1875         error:
1876         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
1877                 instance->xlat_name, ret, curl_easy_strerror(ret));
1878
1879         return FALSE;
1880 }
1881
1882 /** Configures request curlopts.
1883  * 
1884  * Configures libcurl handle setting various curlopts for things like local
1885  * client time, Content-Type, and other FreeRADIUS custom headers.
1886  * 
1887  * Current FreeRADIUS custom headers are:
1888  *  - X-FreeRADIUS-Section      The module section being processed.
1889  *  - X-FreeRADIUS-Server       The current virtual server the REQUEST is
1890  *                              passing through.
1891  *
1892  * Sets up callbacks for all response processing (buffers and body data).
1893  *
1894  * @param[in] instance configuration data.
1895  * @param[in] section configuration data.
1896  * @param[in] handle to configure.
1897  * @param[in] request Current request.
1898  * @param[in] method to use (HTTP verbs PUT, POST, DELETE etc...).
1899  * @param[in] type Content-Type for request encoding, also sets the default
1900  *      for decoding.
1901  * @param[in] uri buffer containing the expanded URI to send the request to.
1902  * @return TRUE on success (all opts configured) FALSE on error.
1903  */
1904 int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
1905                         REQUEST *request, void *handle, http_method_t method,
1906                         http_body_type_t type, char *uri)
1907 {
1908         rlm_rest_handle_t *randle       = handle;
1909         rlm_rest_curl_context_t *ctx    = randle->ctx;
1910         CURL *candle                    = randle->handle;
1911         
1912         http_auth_type_t auth = section->auth;
1913
1914         CURLcode ret;
1915         long val = 1;
1916
1917         char buffer[512];
1918
1919         buffer[(sizeof(buffer) - 1)] = '\0';
1920
1921         /*
1922          *      Setup any header options and generic headers.
1923          */
1924         ret = curl_easy_setopt(candle, CURLOPT_URL, uri);
1925         if (ret != CURLE_OK) goto error;
1926
1927         ret = curl_easy_setopt(candle, CURLOPT_USERAGENT, "FreeRADIUS");
1928         if (ret != CURLE_OK) goto error;
1929   
1930         snprintf(buffer, (sizeof(buffer) - 1), "Content-Type: %s",
1931                  fr_int2str(http_content_type_table, type, "¿Unknown?"));
1932         ctx->headers = curl_slist_append(ctx->headers, buffer);
1933         if (!ctx->headers) goto error_header;
1934         
1935         if (section->timeout) {
1936                 ret = curl_easy_setopt(candle, CURLOPT_TIMEOUT,
1937                                        section->timeout);
1938                 if (ret != CURLE_OK) goto error;
1939         }
1940         
1941         ret = curl_easy_setopt(candle, CURLOPT_PROTOCOLS,
1942                                (CURLPROTO_HTTP | CURLPROTO_HTTPS));
1943         if (ret != CURLE_OK) goto error;
1944         
1945         /*
1946          *      FreeRADIUS custom headers
1947          */
1948         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Section: %s",
1949                  section->name);
1950         ctx->headers = curl_slist_append(ctx->headers, buffer);
1951         if (!ctx->headers) goto error_header;
1952
1953         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Server: %s",
1954                  request->server);
1955         ctx->headers = curl_slist_append(ctx->headers, buffer);
1956         if (!ctx->headers) goto error_header;
1957
1958         /*
1959          *      Configure HTTP verb (GET, POST, PUT, DELETE, other...)
1960          */
1961         switch (method)
1962         {
1963                 case HTTP_METHOD_GET :
1964                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1965                                                val);
1966                         if (ret != CURLE_OK) goto error;
1967
1968                         break;
1969
1970                 case HTTP_METHOD_POST :
1971                         ret = curl_easy_setopt(candle, CURLOPT_POST,
1972                                                val);
1973                         if (ret != CURLE_OK) goto error;
1974
1975                         break;
1976
1977                 case HTTP_METHOD_PUT :
1978                         ret = curl_easy_setopt(candle, CURLOPT_PUT,
1979                                                val);
1980                         if (ret != CURLE_OK) goto error;
1981
1982                         break;
1983
1984                 case HTTP_METHOD_DELETE :
1985                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1986                                                val);
1987                         if (ret != CURLE_OK) goto error;
1988
1989                         ret = curl_easy_setopt(candle,
1990                                                CURLOPT_CUSTOMREQUEST, "DELETE");
1991                         if (ret != CURLE_OK) goto error;
1992
1993                         break;
1994
1995                 case HTTP_METHOD_CUSTOM :
1996                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1997                                                val);
1998                         if (ret != CURLE_OK) goto error;
1999
2000                         ret = curl_easy_setopt(candle,
2001                                                CURLOPT_CUSTOMREQUEST,
2002                                                section->method);
2003                         if (ret != CURLE_OK) goto error;
2004
2005                 default:
2006                         assert(0);
2007                         break;
2008         };
2009         
2010         /*
2011          *      Set user based authentication parameters
2012          */
2013         if (auth) {
2014                 if ((auth >= HTTP_AUTH_BASIC) &&
2015                     (auth <= HTTP_AUTH_ANY_SAFE)) {
2016                         ret = curl_easy_setopt(candle, CURLOPT_HTTPAUTH,
2017                                                http_curl_auth[auth]);
2018                         if (ret != CURLE_OK) goto error;
2019                         
2020                         if (section->username) {
2021                                 radius_xlat(buffer, sizeof(buffer),
2022                                             section->username, request, NULL, NULL);
2023                                             
2024                                 ret = curl_easy_setopt(candle, CURLOPT_USERNAME,
2025                                                        buffer);
2026                                 if (ret != CURLE_OK) goto error;
2027                         }
2028                         if (section->password) {
2029                                 radius_xlat(buffer, sizeof(buffer),
2030                                             section->password, request, NULL, NULL);
2031                                             
2032                                 ret = curl_easy_setopt(candle, CURLOPT_PASSWORD,
2033                                                        buffer);
2034                                 if (ret != CURLE_OK) goto error;
2035                         }
2036
2037 #ifdef CURLOPT_TLSAUTH_USERNAME
2038                 } else if (type == HTTP_AUTH_TLS_SRP) {
2039                         ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_TYPE,
2040                                                http_curl_auth[auth]);
2041                 
2042                         if (section->username) {
2043                                 radius_xlat(buffer, sizeof(buffer),
2044                                             section->username, request, NULL, NULL);
2045                                             
2046                                 ret = curl_easy_setopt(candle,
2047                                                        CURLOPT_TLSAUTH_USERNAME,
2048                                                        buffer);
2049                                 if (ret != CURLE_OK) goto error;
2050                         }
2051                         if (section->password) {
2052                                 radius_xlat(buffer, sizeof(buffer),
2053                                             section->password, request, NULL, NULL);
2054                                             
2055                                 ret = curl_easy_setopt(candle,
2056                                                        CURLOPT_TLSAUTH_PASSWORD,
2057                                                        buffer);
2058                                 if (ret != CURLE_OK) goto error;
2059                         }
2060 #endif
2061                 }
2062         }
2063         
2064         /*
2065          *      Set SSL/TLS authentication parameters
2066          */
2067         if (section->tls_certfile) {
2068                 ret = curl_easy_setopt(candle,
2069                                        CURLOPT_SSLCERT,
2070                                        section->tls_certfile);
2071                 if (ret != CURLE_OK) goto error;
2072         }
2073         
2074         if (section->tls_keyfile) {
2075                 ret = curl_easy_setopt(candle,
2076                                        CURLOPT_SSLKEY,
2077                                        section->tls_keyfile);
2078                 if (ret != CURLE_OK) goto error;
2079         }
2080
2081         if (section->tls_keypassword) {
2082                 ret = curl_easy_setopt(candle,
2083                                        CURLOPT_KEYPASSWD,
2084                                        section->tls_keypassword);
2085                 if (ret != CURLE_OK) goto error;
2086         }
2087         
2088         if (section->tls_cacertfile) {
2089                 ret = curl_easy_setopt(candle,
2090                                        CURLOPT_ISSUERCERT,
2091                                        section->tls_cacertfile);
2092                 if (ret != CURLE_OK) goto error;
2093         }
2094         
2095         if (section->tls_cacertdir) {
2096                 ret = curl_easy_setopt(candle,
2097                                        CURLOPT_CAPATH,
2098                                        section->tls_cacertdir);
2099                 if (ret != CURLE_OK) goto error;
2100         }
2101         
2102         if (section->tls_randfile) {
2103                 ret = curl_easy_setopt(candle,
2104                                        CURLOPT_RANDOM_FILE,
2105                                        section->tls_randfile);
2106                 if (ret != CURLE_OK) goto error;
2107         }
2108         
2109         if (section->tls_verify_cert) {
2110                 ret = curl_easy_setopt(candle,
2111                                        CURLOPT_SSL_VERIFYHOST,
2112                                        (section->tls_verify_cert_cn == TRUE) ?
2113                                         2 : 0);
2114                 if (ret != CURLE_OK) goto error;
2115         } else {
2116                 ret = curl_easy_setopt(candle,
2117                        CURLOPT_SSL_VERIFYPEER,
2118                        0);
2119                 if (ret != CURLE_OK) goto error;
2120         }
2121                 
2122         /*
2123          *      Tell CURL how to get HTTP body content, and how to process
2124          *      incoming data.
2125          */
2126         rest_write_ctx_init(request, &ctx->write, type);
2127
2128         ret = curl_easy_setopt(candle, CURLOPT_HEADERFUNCTION,
2129                                rest_write_header);
2130         if (ret != CURLE_OK) goto error;
2131
2132         ret = curl_easy_setopt(candle, CURLOPT_HEADERDATA,
2133                                &ctx->write);
2134         if (ret != CURLE_OK) goto error;
2135
2136         ret = curl_easy_setopt(candle, CURLOPT_WRITEFUNCTION,
2137                                rest_write_body);
2138         if (ret != CURLE_OK) goto error;
2139
2140         ret = curl_easy_setopt(candle, CURLOPT_WRITEDATA,
2141                                &ctx->write);
2142         if (ret != CURLE_OK) goto error;
2143
2144         switch (method)
2145         {
2146                 case HTTP_METHOD_GET :
2147                 case HTTP_METHOD_DELETE :
2148                         return FALSE;
2149                         break;
2150
2151                 case HTTP_METHOD_POST :
2152                 case HTTP_METHOD_PUT :
2153                 case HTTP_METHOD_CUSTOM :
2154                         if (section->chunk > 0) {
2155                                 ctx->read.chunk = section->chunk;
2156
2157                                 ctx->headers = curl_slist_append(ctx->headers,
2158                                                                  "Expect:");
2159                                 if (!ctx->headers) goto error_header;
2160
2161                                 ctx->headers = curl_slist_append(ctx->headers,
2162                                                                  "Transfer-Encoding: chunked");
2163                                 if (!ctx->headers) goto error_header;
2164                         }
2165
2166                         switch (type)
2167                         {
2168 #ifdef HAVE_JSON
2169                                 case HTTP_BODY_JSON:
2170                                         rest_read_ctx_init(request,
2171                                                            &ctx->read, 1);
2172
2173                                         ret = rest_request_config_body(instance,
2174                                                                        section,
2175                                                                        handle,
2176                                                                        rest_encode_json);
2177                                         if (!ret) return -1;
2178
2179                                         break;
2180 #endif
2181
2182                                 case HTTP_BODY_POST:
2183                                         rest_read_ctx_init(request,
2184                                                            &ctx->read, 0);
2185
2186                                         ret = rest_request_config_body(instance,
2187                                                                        section,
2188                                                                        handle,
2189                                                                        rest_encode_post);
2190                                         if (!ret) return -1;
2191
2192                                         break;
2193
2194                                 default:
2195                                         assert(0);
2196                         }
2197
2198                         ret = curl_easy_setopt(candle, CURLOPT_HTTPHEADER,
2199                                                ctx->headers);
2200                         if (ret != CURLE_OK) goto error;
2201
2202                         break;
2203
2204                 default:
2205                         assert(0);
2206         };
2207
2208         return TRUE;
2209
2210         error:
2211         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
2212                instance->xlat_name, ret, curl_easy_strerror(ret));
2213         return FALSE;
2214
2215         error_header:
2216         radlog(L_ERR, "rlm_rest (%s): Failed creating header",
2217                instance->xlat_name);
2218         return FALSE;
2219 }
2220
2221 /** Sends a REST (HTTP) request.
2222  * 
2223  * Send the actual REST request to the server. The response will be handled by
2224  * the numerous callbacks configured in rest_request_config.
2225  *
2226  * @param[in] instance configuration data.
2227  * @param[in] section configuration data.
2228  * @param[in] handle to use.
2229  * @return TRUE on success or FALSE on error.
2230  */
2231 int rest_request_perform(rlm_rest_t *instance,
2232                          UNUSED rlm_rest_section_t *section, void *handle)
2233 {
2234         rlm_rest_handle_t *randle = handle;
2235         CURL *candle              = randle->handle;
2236         CURLcode ret;
2237
2238         ret = curl_easy_perform(candle);
2239         if (ret != CURLE_OK) {
2240                 radlog(L_ERR, "rlm_rest (%s): Request failed: %i - %s",
2241                        instance->xlat_name, ret, curl_easy_strerror(ret));
2242                 return FALSE;
2243         }
2244
2245         return TRUE;
2246 }
2247
2248 /** Sends the response to the correct decode function.
2249  * 
2250  * Uses the Content-Type information written in rest_write_header to
2251  * determine the correct decode function to use. The decode function will
2252  * then convert the raw received data into VALUE_PAIRs.
2253  *
2254  * @param[in] instance configuration data.
2255  * @param[in] section configuration data.
2256  * @param[in] request Current request.
2257  * @param[in] handle to use.
2258  * @return TRUE on success or FALSE on error.
2259  */
2260 int rest_request_decode(rlm_rest_t *instance, 
2261                         UNUSED rlm_rest_section_t *section,
2262                         REQUEST *request, void *handle)
2263 {
2264         rlm_rest_handle_t *randle       = handle;
2265         rlm_rest_curl_context_t *ctx    = randle->ctx;
2266
2267         int ret;
2268
2269         if (ctx->write.buffer == NULL) {
2270                 RDEBUG("Skipping attribute processing, no body data received");
2271                 return FALSE;
2272         }
2273
2274         RDEBUG("Processing body", ret);
2275
2276         switch (ctx->write.type)
2277         {
2278                 case HTTP_BODY_POST:
2279                         ret = rest_decode_post(instance, section, request,
2280                                                handle, ctx->write.buffer,
2281                                                ctx->write.used);
2282                         break;
2283 #ifdef HAVE_JSON
2284                 case HTTP_BODY_JSON:
2285                         ret = rest_decode_json(instance, section, request,
2286                                                handle, ctx->write.buffer,
2287                                                ctx->write.used);
2288                         break;
2289 #endif
2290                 case HTTP_BODY_UNSUPPORTED:
2291                 case HTTP_BODY_UNAVAILABLE:
2292                 case HTTP_BODY_INVALID:
2293                         return -1;
2294
2295                 default:
2296                         assert(0);
2297         }
2298
2299         return ret;
2300 }
2301
2302 /** Cleans up after a REST request.
2303  * 
2304  * Resets all options associated with a CURL handle, and frees any headers
2305  * associated with it.
2306  *
2307  * Calls rest_read_ctx_free and rest_write_free to free any memory used by
2308  * context data.
2309  *
2310  * @param[in] instance configuration data.
2311  * @param[in] section configuration data.
2312  * @param[in] handle to cleanup.
2313  * @return TRUE on success or FALSE on error.
2314  */
2315 void rest_request_cleanup(UNUSED rlm_rest_t *instance,
2316                           UNUSED rlm_rest_section_t *section, void *handle)
2317 {
2318         rlm_rest_handle_t *randle       = handle;
2319         rlm_rest_curl_context_t *ctx    = randle->ctx;
2320         CURL *candle                    = randle->handle;
2321
2322         /*
2323          * Clear any previously configured options
2324          */
2325         curl_easy_reset(candle);
2326
2327         /*
2328          * Free header list
2329          */
2330         if (ctx->headers != NULL) {
2331                 curl_slist_free_all(ctx->headers);
2332                 ctx->headers = NULL;
2333         }
2334
2335         /*
2336          * Free body data (only used if chunking is disabled)
2337          */
2338         if (ctx->body != NULL) free(ctx->body);
2339   
2340         /*
2341          * Free other context info
2342          */
2343         rest_read_ctx_free(&ctx->read);
2344         rest_write_free(&ctx->write);
2345 }
2346
2347 /** URL encodes a string.
2348  * 
2349  * Encode special chars as per RFC 3986 section 4.
2350  *
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  * @return length of data written to out (excluding NULL).
2355  */
2356 static size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen,
2357                               const char *raw, UNUSED void *arg)
2358 {
2359         char *escaped;
2360
2361         escaped = curl_escape(raw, strlen(raw));
2362         strlcpy(out, escaped, outlen);
2363         curl_free(escaped);
2364
2365         return strlen(out);
2366 }
2367
2368 /** Builds URI; performs XLAT expansions and encoding.
2369  * 
2370  * Splits the URI into "http://example.org" and "/%{xlat}/query/?bar=foo"
2371  * Both components are expanded, but values expanded for the second component
2372  * are also url encoded.
2373  *
2374  * @param[in] instance configuration data.
2375  * @param[in] section configuration data.
2376  * @param[in] request Current request
2377  * @param[out] buffer to write expanded URI to.
2378  * @param[in] bufsize Size of buffer.
2379  * @return length of data written to buffer (excluding NULL) or < 0 if an error
2380  *      occurred.
2381  */
2382 ssize_t rest_uri_build(rlm_rest_t *instance, rlm_rest_section_t *section,
2383                        REQUEST *request, char *buffer, size_t bufsize)
2384 {
2385         const char *p, *q;
2386
2387         char *out, *scheme;
2388         const char *path;
2389
2390         unsigned short count = 0;
2391
2392         size_t len;
2393
2394         p = section->uri;
2395
2396         while ((q = strchr(p, '/')) && (count++ < 3)) p = (q + 1);
2397
2398         if (count != 3) {
2399                 radlog(L_ERR, "rlm_rest (%s): Error URI is malformed,"
2400                        " can't find start of path", instance->xlat_name);
2401                 return -1;
2402         }
2403
2404         len = (q - p);
2405
2406         scheme = rad_malloc(len + 1);
2407         strlcpy(scheme, section->uri, len + 1);
2408
2409         path = (q + 1);
2410
2411         out = buffer;
2412         out += radius_xlat(out, bufsize, scheme, request, NULL, NULL);
2413
2414         free(scheme);
2415
2416         out += radius_xlat(out, (bufsize - (buffer - out)), path, request,
2417                          rest_uri_escape, NULL);
2418
2419         return (buffer - out);
2420 }