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