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