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