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