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