Add missing break
[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         ret = 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         char buffer[1024];
959
960         const DICT_ATTR *da;
961         VALUE_PAIR *vp;
962
963         const DICT_ATTR **current, *processed[REST_BODY_MAX_ATTRS + 1];
964
965         pair_lists_t list_name;
966         request_refs_t request_name;
967         REQUEST *reference = request;
968         VALUE_PAIR **vps;
969
970         size_t len;
971         int curl_len; /* Length from last curl_easy_unescape call */
972
973         int count = 0;
974
975         processed[0] = NULL;
976
977         /*
978          * Empty response?
979          */
980         while (isspace(*p)) p++;
981         if (*p == '\0') return FALSE;
982
983         while (((q = strchr(p, '=')) != NULL) &&
984                (count < REST_BODY_MAX_ATTRS)) {
985                 attribute = name;
986                 reference = request;
987
988                 name = curl_easy_unescape(candle, p, (q - p), &curl_len);
989                 p = (q + 1);
990
991                 RDEBUG("Decoding attribute \"%s\"", name);
992                 
993                 request_name = radius_request_name(&attribute, REQUEST_CURRENT);
994                 if (request_name == REQUEST_UNKNOWN) {
995                         RDEBUG("WARNING: Invalid request qualifier, skipping");
996
997                         curl_free(name);
998
999                         continue;
1000                 }
1001
1002                 if (!radius_request(&reference, request_name)) {
1003                         RDEBUG("WARNING: Attribute name refers to outer request"
1004                                " but not in a tunnel, skipping");
1005
1006                         curl_free(name);
1007
1008                         continue;
1009                 }
1010
1011                 list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
1012                 if (list_name == PAIR_LIST_UNKNOWN) {
1013                         RDEBUG("WARNING: Invalid list qualifier, skipping");
1014
1015                         curl_free(name);
1016
1017                         continue;
1018                 }
1019
1020                 da = dict_attrbyname(attribute);
1021                 if (!da) {
1022                         RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
1023                                attribute);
1024
1025                         curl_free(name);
1026
1027                         continue;
1028                 }
1029
1030                 vps = radius_list(reference, list_name);
1031
1032                 assert(vps);
1033
1034                 RDEBUG2("\tType  : %s", fr_int2str(dict_attr_types, da->type,
1035                         "¿Unknown?"));
1036
1037                 q = strchr(p, '&');
1038                 len = (q == NULL) ? (rawlen - (p - raw)) : (unsigned)(q - p);
1039
1040                 value = curl_easy_unescape(candle, p, len, &curl_len);
1041
1042                 /* 
1043                  *      If we found a delimiter we want to skip over it,
1044                  *      if we didn't we do *NOT* want to skip over the end
1045                  *      of the buffer...
1046                  */
1047                 p += (q == NULL) ? len : (len + 1);
1048
1049                 RDEBUG2("\tLength : %i", curl_len);
1050                 RDEBUG2("\tValue  : \"%s\"", value);
1051                 
1052                 RDEBUG("Performing xlat expansion of response value");
1053                 
1054                 if (!radius_xlat(buffer, sizeof(buffer),
1055                                  value, request, NULL, NULL)) {
1056                         goto skip;
1057                 }
1058
1059                 vp = pairalloc(da);
1060                 if (!vp) {
1061                         radlog(L_ERR, "rlm_rest (%s): Failed creating"
1062                                " valuepair", instance->xlat_name);
1063
1064                         goto error;
1065                 }
1066
1067                 vp->op = T_OP_SET;
1068  
1069                 /*
1070                  *      Check to see if we've already processed an
1071                  *      attribute of the same type if we have, change the op
1072                  *      from T_OP_ADD to T_OP_SET.
1073                  */
1074                 current = processed;
1075                 while (*current++) {
1076                         if (current[0] == da) {
1077                                 vp->op = T_OP_ADD;
1078                                 break;
1079                         }
1080                 }
1081                 
1082                 if (vp->op != T_OP_ADD) {
1083                         current[0] = da;
1084                         current[1] = NULL;
1085                 }
1086
1087                 if (!pairparsevalue(vp, buffer)) {
1088                         RDEBUG("Incompatible value assignment, skipping");
1089                         pairbasicfree(vp);
1090                         goto skip;
1091                 }
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, *to_parse;
1144         char buffer[1024];
1145         
1146         VALUE_PAIR *vp;
1147
1148         /*
1149          *      Should encode any nested JSON structures into JSON strings.
1150          *
1151          *      "I knew you liked JSON so I put JSON in your JSON!"
1152          */
1153         value = json_object_get_string(leaf);
1154
1155         RDEBUG2("\tType   : %s", fr_int2str(dict_attr_types, da->type,
1156                                             "¿Unknown?"));
1157         RDEBUG2("\tLength : %i", strlen(value));
1158         RDEBUG2("\tValue  : \"%s\"", value);
1159
1160         if (flags->do_xlat) {
1161                 if (!radius_xlat(buffer, sizeof(buffer), value,
1162                                  request, NULL, NULL)) {
1163                         return NULL;
1164                 }
1165                 
1166                 to_parse = buffer;
1167         } else {
1168                 to_parse = value;
1169         }
1170
1171         vp = paircreate(da->attr, da->vendor);
1172         if (!vp) {
1173                 radlog(L_ERR, "rlm_rest (%s): Failed creating valuepair",
1174                        instance->xlat_name);
1175                 return NULL;
1176         }
1177
1178         vp->op = flags->op;
1179         
1180         if (!pairparsevalue(vp, to_parse)) {
1181                 RDEBUG("Incompatible value assignment, skipping");
1182                 pairbasicfree(vp);
1183                 return NULL;
1184         }
1185
1186         return vp;
1187 }
1188
1189 /** Processes JSON response and converts it into multiple VALUE_PAIRs
1190  * 
1191  * Processes JSON attribute declarations in the format below. Will recurse when
1192  * processing nested attributes. When processing nested attributes flags and
1193  * operators from previous attributes are not inherited.
1194  *
1195  * JSON response format is:
1196 @verbatim
1197 {
1198         "<attribute0>":{
1199                 do_xlat:<bool>,
1200                 is_json:<bool>,
1201                 "op":"<operator>",
1202                 "value":[<value0>,<value1>,<valueN>]
1203         },
1204         "<attribute1>":{
1205                 "value":{
1206                         "<nested-attribute0>":{
1207                                 "op":"<operator>",
1208                                 "value":<value0>
1209                         }
1210                 }
1211         },
1212         "<attribute2>":"<value0>",
1213         "<attributeN>":"[<value0>,<value1>,<valueN>]"
1214 }
1215 @endverbatim
1216  * 
1217  * JSON valuepair flags (bools):
1218  *  - do_xlat   (optional) Controls xlat expansion of values. Defaults to TRUE.
1219  *  - is_json   (optional) If TRUE, any nested JSON data will be copied to the
1220  *                         VALUE_PAIR in string form. Defaults to TRUE.
1221  *  - op        (optional) Controls how the attribute is inserted into
1222  *                         the target list. Defaults to ':=' (T_OP_SET).
1223  *
1224  * If "op" is ':=' or '=', it will be automagically changed to '+=' for the
1225  * second and subsequent values in multivalued attributes. This does not work
1226  * between multiple attribute declarations.
1227  *
1228  * @see fr_tokens
1229  *
1230  * @param[in] instance configuration data.
1231  * @param[in] section configuration data.
1232  * @param[in] request Current request.
1233  * @param[in] object containing root node, or parent node.
1234  * @param[in] level Current nesting level.
1235  * @param[in] max_attrs counter, decremented after each VALUE_PAIR is created,
1236  *            when 0 no more attributes will be processed.
1237  * @return VALUE_PAIR or NULL on error.
1238  */
1239 static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
1240                                  UNUSED rlm_rest_section_t *section,
1241                                  REQUEST *request, json_object *object,
1242                                  int level, int *max_attrs)
1243 {
1244         const char *p;
1245         char *q;
1246         
1247         const char *name, *attribute;
1248
1249         struct json_object *value, *idx, *tmp;
1250         struct lh_entry *entry;
1251         json_flags_t flags;
1252
1253         const DICT_ATTR *da;
1254         VALUE_PAIR *vp;
1255         
1256         request_refs_t request_name;
1257         pair_lists_t list_name;
1258         REQUEST *reference = request;
1259         VALUE_PAIR **vps;
1260
1261         int i, len;
1262
1263         if (!json_object_is_type(object, json_type_object)) {
1264                 RDEBUG("Can't process VP container, expected JSON object,"
1265                        " got \"%s\", skipping",
1266                        json_object_get_type(object));
1267                 return NULL;
1268         }
1269    
1270         /*
1271          *      Process VP container
1272          */
1273         entry = json_object_get_object(object)->head;
1274         while (entry) {
1275                 flags.op = T_OP_SET;
1276                 flags.do_xlat  = 1;
1277                 flags.is_json  = 0;
1278
1279                 name = (char*)entry->k;
1280
1281                 /* Fix the compiler warnings regarding const... */
1282                 memcpy(&value, &entry->v, sizeof(value)); 
1283
1284                 entry = entry->next;
1285    
1286                 /*
1287                  *      For people handcrafting JSON responses
1288                  */
1289                 p = name;
1290                 while ((p = q = strchr(p, '|'))) {
1291                         *q = ':';
1292                         p++;
1293                 }
1294
1295                 attribute = name;
1296                 reference = request;
1297          
1298                 /*
1299                  *      Resolve attribute name to a dictionary entry and
1300                  *      pairlist.
1301                  */
1302                 RDEBUG2("Decoding attribute \"%s\"", name);
1303                 
1304                 request_name = radius_request_name(&attribute, REQUEST_CURRENT);
1305                 if (request_name == REQUEST_UNKNOWN) {
1306                         RDEBUG("WARNING: Request qualifier unknown, skipping");
1307
1308                         continue;
1309                 }
1310
1311                 if (!radius_request(&reference, request_name)) {
1312                         RDEBUG("WARNING: Attribute name refers to outer request"
1313                                " but not in a tunnel, skipping");
1314
1315                         continue;
1316                 }
1317
1318                 list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
1319                 if (list_name == PAIR_LIST_UNKNOWN) {
1320                         RDEBUG("WARNING: Invalid list qualifier, skipping");
1321
1322                         continue;
1323                 }
1324
1325                 da = dict_attrbyname(attribute);
1326                 if (!da) {
1327                         RDEBUG("WARNING: Attribute \"%s\" unknown, skipping",
1328                                attribute);
1329
1330                         continue;
1331                 }
1332
1333                 vps = radius_list(reference, list_name);
1334
1335                 assert(vps);
1336
1337                 /*
1338                  *      Alternate JSON structure that allows operator,
1339                  *      and other flags to be specified.
1340                  *
1341                  *      "<name>":{
1342                  *              "do_xlat":<bool>,
1343                  *              "is_json":<bool>,
1344                  *              "op":"<op>",
1345                  *              "value":<value>
1346                  *      }
1347                  *
1348                  *      Where value is a:
1349                  *        - []  Multivalued array
1350                  *        - {}  Nested Valuepair
1351                  *        - *   Integer or string value
1352                  */
1353                 if (json_object_is_type(value, json_type_object)) {
1354                         /*
1355                          *      Process operator if present.
1356                          */
1357                         tmp = json_object_object_get(value, "op");
1358                         if (tmp) {
1359                                 flags.op = fr_str2int(fr_tokens, json_object_get_string(tmp), 0);
1360                                 if (!flags.op) {
1361                                         RDEBUG("Invalid operator value \"%s\","
1362                                                " skipping", tmp);
1363                                         continue;
1364                                 }
1365                         }
1366
1367                         /*
1368                          *      Process optional do_xlat bool.
1369                          */
1370                         tmp = json_object_object_get(value, "do_xlat");
1371                         if (tmp) {
1372                                 flags.do_xlat = json_object_get_boolean(tmp);
1373                         }
1374
1375                         /*
1376                          *      Process optional is_json bool.
1377                          */
1378                         tmp = json_object_object_get(value, "is_json");
1379                         if (tmp) {
1380                                 flags.is_json = json_object_get_boolean(tmp);
1381                         }
1382
1383                         /*
1384                          *      Value key must be present if were using
1385                          *      the expanded syntax.
1386                          */
1387                         value = json_object_object_get(value, "value");
1388                         if (!value) {
1389                                 RDEBUG("Value key missing, skipping", value);
1390                                 continue;
1391                         }
1392                 }
1393
1394                 /*
1395                  *      Setup pairmake / recursion loop.
1396                  */
1397                 if (!flags.is_json &&
1398                     json_object_is_type(value, json_type_array)) {
1399                         len = json_object_array_length(value);
1400                         if (!len) {
1401                                 RDEBUG("Zero length value array, skipping",
1402                                        value);
1403                                 continue;
1404                         }
1405                         idx = json_object_array_get_idx(value, 0);
1406                 } else {
1407                         len = 1;
1408                         idx = value;
1409                 }
1410
1411                 i = 0;
1412                 do {
1413                         if (!(*max_attrs)--) {
1414                                         radlog(L_ERR, "rlm_rest (%s): At "
1415                                                "maximum attribute limit",
1416                                                instance->xlat_name);
1417                                         return NULL;
1418                         }
1419
1420                         /*
1421                          *      Automagically switch the op for multivalued
1422                          *      attributes.
1423                          */
1424                         if (((flags.op == T_OP_SET) ||
1425                              (flags.op == T_OP_EQ)) && (len > 1)) {
1426                                 flags.op = T_OP_ADD;
1427                         }
1428
1429                         if (!flags.is_json &&
1430                             json_object_is_type(value, json_type_object)) {
1431                                 /* TODO: Insert nested VP into VP structure...*/
1432                                 RDEBUG("Found nested VP", value);
1433                                 vp = json_pairmake(instance, section,
1434                                                    request, value,
1435                                                    level + 1, max_attrs);
1436                         } else {
1437                                 vp = json_pairmake_leaf(instance, section,
1438                                                         request, da, &flags,
1439                                                         idx);
1440                         }
1441                 } while ((++i < len) &&
1442                          (idx = json_object_array_get_idx(value, i)));
1443    }
1444
1445    return vp;
1446 }
1447
1448 /** Converts JSON response into VALUE_PAIRs and adds them to the request.
1449  * 
1450  * Converts the raw JSON string into a json-c object tree and passes it to
1451  * json_pairmake. After the tree has been parsed json_object_put is called
1452  * which decrements the reference count of the root node by one, and frees
1453  * the entire tree.
1454  *
1455  * @see rest_encode_json
1456  * @see json_pairmake
1457  *
1458  * @param[in] instance configuration data.
1459  * @param[in] section configuration data.
1460  * @param[in,out] request Current request.
1461  * @param[in] handle REST handle.
1462  * @param[in] raw buffer containing JSON data.
1463  * @param[in] rawlen Length of data in raw buffer.
1464  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
1465  */
1466 static int rest_decode_json(rlm_rest_t *instance,
1467                             UNUSED rlm_rest_section_t *section,
1468                             REQUEST *request, UNUSED void *handle,
1469                             char *raw, UNUSED size_t rawlen)
1470 {
1471         const char *p = raw;
1472         
1473         struct json_object *json;
1474         
1475         int max = REST_BODY_MAX_ATTRS;
1476
1477         /*
1478          *      Empty response?
1479          */
1480         while (isspace(*p)) p++;
1481         if (*p == '\0') return FALSE;
1482
1483         json = json_tokener_parse(p);
1484         if (!json) {
1485                 radlog(L_ERR, "rlm_rest (%s): Malformed JSON data \"%s\"",
1486                         instance->xlat_name, raw);
1487                 return -1;
1488         }
1489
1490         json_pairmake(instance, section, request, json, 0, &max);
1491
1492         /*
1493          *      Decrement reference count for root object, should free entire
1494          *      JSON tree.
1495          */
1496         json_object_put(json);
1497
1498         return (REST_BODY_MAX_ATTRS - max);
1499 }
1500 #endif
1501
1502 /** Processes incoming HTTP header data from libcurl.
1503  *
1504  * Processes the status line, and Content-Type headers from the incoming HTTP
1505  * response.
1506  *
1507  * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
1508  * by libcurl.
1509  *
1510  * @param[in] ptr Char buffer where inbound header data is written.
1511  * @param[in] size Multiply by nmemb to get the length of ptr.
1512  * @param[in] nmemb Multiply by size to get the length of ptr.
1513  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1514  * @return Length of data processed, or 0 on error.
1515  */
1516 static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
1517                                 void *userdata)
1518 {
1519         rlm_rest_write_t *ctx  = userdata;
1520         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1521         
1522         const char *p = ptr, *q;
1523         char *tmp;
1524
1525         const size_t t = (size * nmemb);
1526         size_t s = t;
1527         size_t len;
1528
1529         http_body_type_t type;
1530         http_body_type_t supp;
1531
1532         switch (ctx->state)
1533         {
1534                 case WRITE_STATE_INIT:
1535                         RDEBUG("Processing header");
1536
1537                         /* 
1538                          * HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
1539                          *
1540                          * "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
1541                          */
1542                         if (s < 14) goto malformed;
1543
1544                         /* 
1545                          * Check start of header matches...
1546                          */
1547                         if (strncasecmp("HTTP/", p, 5) != 0) goto malformed;
1548
1549                         p += 5;
1550                         s -= 5;
1551
1552                         /*
1553                          * Skip the version field, next space should mark start
1554                          * of reason_code.
1555                          */
1556                         q = memchr(p, ' ', s);
1557                         if (q == NULL) goto malformed;
1558
1559                         s -= (q - p);
1560                         p  = q;
1561
1562                         /* 
1563                          * Process reason_code.
1564                          *
1565                          * " 100" (4) + "\r\n" (2) = 6
1566                          */
1567                         if (s < 6) goto malformed;
1568                         p++;
1569                         s--;
1570
1571                         /* Char after reason code must be a space, or \r */
1572                         if (!((p[3] == ' ') || (p[3] == '\r'))) goto malformed;
1573
1574                         ctx->code = atoi(p);
1575
1576                         /*
1577                          *      Process reason_phrase (if present).
1578                          */
1579                         if (p[3] == ' ') {
1580                                 p += 4;
1581                                 s -= 4;
1582
1583                                 q = memchr(p, '\r', s);
1584                                 if (q == NULL) goto malformed;
1585
1586                                 len = (q - p);
1587
1588                                 tmp = rad_malloc(len + 1);
1589                                 strlcpy(tmp, p, len + 1);
1590
1591                                 RDEBUG("\tStatus : %i (%s)", ctx->code, tmp);
1592
1593                                 free(tmp);
1594                         } else {
1595                                 RDEBUG("\tStatus : %i", ctx->code);
1596                         }
1597
1598                         ctx->state = WRITE_STATE_PARSE_HEADERS;
1599
1600                         break;
1601
1602                 case WRITE_STATE_PARSE_HEADERS:
1603                         if ((s >= 14) &&
1604                             (strncasecmp("Content-Type: ", p, 14) == 0)) {
1605                                 p += 14;
1606                                 s -= 14;
1607
1608                                 /* 
1609                                  *      Check to see if there's a parameter
1610                                  *      separator.
1611                                  */
1612                                 q = memchr(p, ';', s);
1613
1614                                 /*
1615                                  *      If there's not, find the end of this
1616                                  *      header.
1617                                  */
1618                                 if (q == NULL) q = memchr(p, '\r', s);
1619
1620                                 len = (q == NULL) ? s : (unsigned)(q - p);
1621
1622                                 type = fr_substr2int(http_content_type_table,
1623                                         p, HTTP_BODY_UNKNOWN,
1624                                         len);
1625
1626                                 supp = http_body_type_supported[type];
1627
1628                                 tmp = rad_malloc(len + 1);
1629                                 strlcpy(tmp, p, len + 1);
1630
1631                                 RDEBUG("\tType   : %s (%s)",
1632                                         fr_int2str(http_body_type_table, type,
1633                                                 "¿Unknown?"), tmp);
1634
1635                                 free(tmp);
1636
1637                                 if (type == HTTP_BODY_UNKNOWN) {
1638                                         RDEBUG("Couldn't determine type, using"
1639                                                " request type \"%s\".",
1640                                                fr_int2str(http_body_type_table,
1641                                                           ctx->type,
1642                                                           "¿Unknown?"));
1643
1644                                 } else if (supp == HTTP_BODY_UNSUPPORTED) {
1645                                         RDEBUG("Type \"%s\" is currently"
1646                                                " unsupported",
1647                                                fr_int2str(http_body_type_table,
1648                                                           type, "¿Unknown?"));
1649                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1650                                 } else if (supp == HTTP_BODY_UNAVAILABLE) {
1651                                         RDEBUG("Type \"%s\" is currently"
1652                                                " unavailable, please rebuild"
1653                                                " this module with the required"
1654                                                " headers",
1655                                                fr_int2str(http_body_type_table,
1656                                                           type, "¿Unknown?"));
1657                                         ctx->type = HTTP_BODY_UNSUPPORTED;
1658
1659                                 } else if (supp == HTTP_BODY_INVALID) {
1660                                         RDEBUG("Type \"%s\" is not a valid web"
1661                                                " API data markup format",
1662                                                fr_int2str(http_body_type_table,
1663                                                           type, "¿Unknown?"));
1664
1665                                         ctx->type = HTTP_BODY_INVALID;
1666
1667                                 } else if (type != ctx->type) {
1668                                         ctx->type = type;
1669                                 }
1670                         }
1671                         break;
1672                         
1673                 default:
1674                         break;
1675         }
1676         return t;
1677
1678         malformed:
1679
1680         RDEBUG("Incoming header was malformed");
1681         ctx->code = -1;
1682
1683         return (t - s);
1684 }
1685
1686 /** Processes incoming HTTP body data from libcurl.
1687  *
1688  * Writes incoming body data to an intermediary buffer for later parsing by
1689  * one of the decode functions.
1690  *
1691  * @param[in] ptr Char buffer where inbound header data is written
1692  * @param[in] size Multiply by nmemb to get the length of ptr.
1693  * @param[in] nmemb Multiply by size to get the length of ptr.
1694  * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
1695  * @return length of data processed, or 0 on error.
1696  */
1697 static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
1698                               void *userdata)
1699 {
1700         rlm_rest_write_t *ctx  = userdata;
1701         REQUEST *request       = ctx->request; /* Used by RDEBUG */
1702         
1703         const char *p = ptr;
1704         char *tmp;
1705
1706         const size_t t = (size * nmemb);
1707
1708         /*
1709          *      Any post processing of headers should go here...
1710          */
1711         if (ctx->state == WRITE_STATE_PARSE_HEADERS) {
1712                 ctx->state = WRITE_STATE_PARSE_CONTENT;
1713         }
1714
1715         switch (ctx->type)
1716         {
1717                 case HTTP_BODY_UNSUPPORTED:
1718                         return t;
1719
1720                 case HTTP_BODY_INVALID:
1721                         tmp = rad_malloc(t + 1);
1722                         strlcpy(tmp, p, t + 1);
1723
1724                         RDEBUG2("%s", tmp);
1725
1726                         free(tmp);
1727
1728                         return t;
1729
1730                 default:
1731                         if (t > (ctx->alloc - ctx->used)) {
1732                                 ctx->alloc += ((t + 1) > REST_BODY_INCR) ?
1733                                         t + 1 : REST_BODY_INCR;
1734
1735                                 tmp = ctx->buffer;
1736
1737                                 ctx->buffer = rad_malloc(ctx->alloc);
1738
1739                                 /* If data has been written previously */
1740                                 if (tmp) {
1741                                         strlcpy(ctx->buffer, tmp,
1742                                                (ctx->used + 1));
1743                                         free(tmp);
1744                                 }
1745                         }
1746                         strlcpy(ctx->buffer + ctx->used, p, t + 1);
1747                         ctx->used += t;
1748
1749                         break;
1750         }
1751
1752         return t;
1753 }
1754
1755 /** (Re-)Initialises the data in a rlm_rest_write_t.
1756  *
1757  * This resets the values of the a rlm_rest_write_t to their defaults.
1758  * Must be called between encoding sessions.
1759  *
1760  * @see rest_write_body
1761  * @see rest_write_header
1762  * 
1763  * @param[in] request Current request.
1764  * @param[in] ctx data to initialise.
1765  * @param[in] type Default http_body_type to use when decoding raw data, may be
1766  * overwritten by rest_write_header.
1767  */
1768 static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx,
1769                                 http_body_type_t type)
1770 {
1771         ctx->request    = request;
1772         ctx->type       = type;
1773         ctx->state      = WRITE_STATE_INIT;
1774         ctx->alloc      = 0;
1775         ctx->used       = 0;
1776         ctx->buffer     = NULL;
1777 }
1778
1779 /** Frees the intermediary buffer created by rest_write.
1780  *
1781  * @param[in] ctx data to be freed.
1782  */
1783 static void rest_write_free(rlm_rest_write_t *ctx)
1784 {
1785         if (ctx->buffer != NULL) {
1786                 free(ctx->buffer);
1787         }
1788 }
1789
1790 /** Configures body specific curlopts.
1791  * 
1792  * Configures libcurl handle to use either chunked mode, where the request
1793  * data will be sent using multiple HTTP requests, or contiguous mode where
1794  * the request data will be sent in a single HTTP request.
1795  *
1796  * @param[in] instance configuration data.
1797  * @param[in] section configuration data.
1798  * @param[in] handle rlm_rest_handle_t to configure.
1799  * @param[in] func to pass to libcurl for chunked.
1800  *            transfers (NULL if not using chunked mode).
1801  * @return TRUE on success FALSE on error.
1802  */
1803 static int rest_request_config_body(rlm_rest_t *instance,
1804                                     rlm_rest_section_t *section,
1805                                     rlm_rest_handle_t *handle,
1806                                     rest_read_t func)
1807 {
1808         rlm_rest_curl_context_t *ctx = handle->ctx;
1809         CURL *candle                 = handle->handle;
1810
1811         ssize_t len;
1812         CURLcode ret;
1813
1814         if (section->chunk > 0) {
1815                 ret = curl_easy_setopt(candle, CURLOPT_READDATA,
1816                                        &ctx->read);
1817                 if (ret != CURLE_OK) goto error;
1818
1819                 ret = curl_easy_setopt(candle, CURLOPT_READFUNCTION,
1820                                        rest_encode_json);
1821                 if (ret != CURLE_OK) goto error;
1822         } else {
1823                 len = rest_read_wrapper(&ctx->body, func,
1824                                         REST_BODY_MAX_LEN , &ctx->read);
1825                 if (len <= 0) {
1826                         radlog(L_ERR, "rlm_rest (%s): Failed creating HTTP"
1827                                " body content", instance->xlat_name);
1828                         return FALSE;
1829                 }
1830
1831                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDS,
1832                                        ctx->body);
1833                 if (ret != CURLE_OK) goto error;
1834
1835                 ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDSIZE,
1836                                        len);
1837                 if (ret != CURLE_OK) goto error;
1838         }
1839
1840         return TRUE;
1841
1842         error:
1843         radlog(L_ERR, "rlm_rest (%s): Failed setting curl option: %i - %s",
1844                 instance->xlat_name, ret, curl_easy_strerror(ret));
1845
1846         return FALSE;
1847 }
1848
1849 /** Configures request curlopts.
1850  * 
1851  * Configures libcurl handle setting various curlopts for things like local
1852  * client time, Content-Type, and other FreeRADIUS custom headers.
1853  * 
1854  * Current FreeRADIUS custom headers are:
1855  *  - X-FreeRADIUS-Section      The module section being processed.
1856  *  - X-FreeRADIUS-Server       The current virtual server the REQUEST is
1857  *                              passing through.
1858  *
1859  * Sets up callbacks for all response processing (buffers and body data).
1860  *
1861  * @param[in] instance configuration data.
1862  * @param[in] section configuration data.
1863  * @param[in] handle to configure.
1864  * @param[in] request Current request.
1865  * @param[in] method to use (HTTP verbs PUT, POST, DELETE etc...).
1866  * @param[in] type Content-Type for request encoding, also sets the default
1867  *      for decoding.
1868  * @param[in] uri buffer containing the expanded URI to send the request to.
1869  * @return TRUE on success (all opts configured) FALSE on error.
1870  */
1871 int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
1872                         REQUEST *request, void *handle, http_method_t method,
1873                         http_body_type_t type, char *uri)
1874 {
1875         rlm_rest_handle_t *randle       = handle;
1876         rlm_rest_curl_context_t *ctx    = randle->ctx;
1877         CURL *candle                    = randle->handle;
1878         
1879         http_auth_type_t auth = section->auth;
1880
1881         CURLcode ret;
1882         long val = 1;
1883
1884         char buffer[512];
1885
1886         buffer[(sizeof(buffer) - 1)] = '\0';
1887
1888         /*
1889          *      Setup any header options and generic headers.
1890          */
1891         ret = curl_easy_setopt(candle, CURLOPT_URL, uri);
1892         if (ret != CURLE_OK) goto error;
1893
1894         ret = curl_easy_setopt(candle, CURLOPT_USERAGENT, "FreeRADIUS");
1895         if (ret != CURLE_OK) goto error;
1896   
1897         snprintf(buffer, (sizeof(buffer) - 1), "Content-Type: %s",
1898                  fr_int2str(http_content_type_table, type, "¿Unknown?"));
1899         ctx->headers = curl_slist_append(ctx->headers, buffer);
1900         if (!ctx->headers) goto error_header;
1901         
1902         if (section->timeout) {
1903                 ret = curl_easy_setopt(candle, CURLOPT_TIMEOUT,
1904                                        section->timeout);
1905                 if (ret != CURLE_OK) goto error;
1906         }
1907         
1908         ret = curl_easy_setopt(candle, CURLOPT_PROTOCOLS,
1909                                (CURLPROTO_HTTP | CURLPROTO_HTTPS));
1910         if (ret != CURLE_OK) goto error;
1911         
1912         /*
1913          *      FreeRADIUS custom headers
1914          */
1915         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Section: %s",
1916                  section->name);
1917         ctx->headers = curl_slist_append(ctx->headers, buffer);
1918         if (!ctx->headers) goto error_header;
1919
1920         snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Server: %s",
1921                  request->server);
1922         ctx->headers = curl_slist_append(ctx->headers, buffer);
1923         if (!ctx->headers) goto error_header;
1924
1925         /*
1926          *      Configure HTTP verb (GET, POST, PUT, DELETE, other...)
1927          */
1928         switch (method)
1929         {
1930                 case HTTP_METHOD_GET :
1931                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1932                                                val);
1933                         if (ret != CURLE_OK) goto error;
1934
1935                         break;
1936
1937                 case HTTP_METHOD_POST :
1938                         ret = curl_easy_setopt(candle, CURLOPT_POST,
1939                                                val);
1940                         if (ret != CURLE_OK) goto error;
1941
1942                         break;
1943
1944                 case HTTP_METHOD_PUT :
1945                         ret = curl_easy_setopt(candle, CURLOPT_PUT,
1946                                                val);
1947                         if (ret != CURLE_OK) goto error;
1948
1949                         break;
1950
1951                 case HTTP_METHOD_DELETE :
1952                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1953                                                val);
1954                         if (ret != CURLE_OK) goto error;
1955
1956                         ret = curl_easy_setopt(candle,
1957                                                CURLOPT_CUSTOMREQUEST, "DELETE");
1958                         if (ret != CURLE_OK) goto error;
1959
1960                         break;
1961
1962                 case HTTP_METHOD_CUSTOM :
1963                         ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
1964                                                val);
1965                         if (ret != CURLE_OK) goto error;
1966
1967                         ret = curl_easy_setopt(candle,
1968                                                CURLOPT_CUSTOMREQUEST,
1969                                                section->method);
1970                         if (ret != CURLE_OK) goto error;
1971                         break;
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");
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 }