843f8ad31ea6c73a3d89abfe34fc3e48dc8c4a13
[freeradius.git] / src / modules / rlm_couchbase / couchbase.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 Wrapper functions around the libcouchbase Couchbase client driver.
21  * @file couchbase.c
22  *
23  * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
24  */
25
26 RCSID("$Id$");
27
28 #include <freeradius-devel/radiusd.h>
29
30 #include <libcouchbase/couchbase.h>
31 #include <json/json.h>
32
33 #include "couchbase.h"
34 #include "jsonc_missing.h"
35
36 /* general couchbase error callback */
37 void couchbase_error_callback(lcb_t instance, lcb_error_t error, const char *errinfo) {
38         /* log error */
39         ERROR("rlm_couchbase: (error_callback) %s (0x%x), %s", lcb_strerror(instance, error), error, errinfo);
40 }
41
42 /* couchbase value store callback */
43 void couchbase_store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp) {
44         if (error != LCB_SUCCESS) {
45                 /* log error */
46                 ERROR("rlm_couchbase: (store_callback) %s (0x%x)", lcb_strerror(instance, error), error);
47         }
48         /* silent compiler */
49         (void)cookie;
50         (void)operation;
51         (void)resp;
52 }
53
54 /* couchbase value get callback */
55 void couchbase_get_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp) {
56         cookie_u cu;                            /* union of const and non const pointers */
57         cu.cdata = cookie;                      /* set const union member to cookie passed from couchbase */
58         cookie_t *c = (cookie_t *) cu.data;     /* set our cookie struct using non-const member */
59         const char *bytes = resp->v.v0.bytes;   /* the payload of this chunk */
60         lcb_size_t nbytes = resp->v.v0.nbytes;  /* length of this data chunk */
61
62         /* check error */
63         switch (error) {
64                 case LCB_SUCCESS:
65                         /* check for valid bytes */
66                         if (bytes && nbytes > 1) {
67                                 /* debug */
68                                 DEBUG("rlm_couchbase: (get_callback) got %zu bytes", nbytes);
69                                 /* build json object */
70                                 c->jobj = json_tokener_parse_verbose(bytes, &c->jerr);
71                                 /* switch on current error status */
72                                 switch (c->jerr) {
73                                         case json_tokener_success:
74                                                 /* do nothing */
75                                         break;
76                                         default:
77                                                 /* log error */
78                                                 ERROR("rlm_couchbase: (get_callback) JSON Tokener error: %s", json_tokener_error_desc(c->jerr));
79                                         break;
80                                 }
81                         }
82                 break;
83                 case LCB_KEY_ENOENT:
84                         /* ignored */
85                         DEBUG("rlm_couchbase: (get_callback) key does not exist");
86                 break;
87                 default:
88                         /* log error */
89                         ERROR("rlm_couchbase: (get_callback) %s (0x%x)", lcb_strerror(instance, error), error);
90                 break;
91         }
92 }
93
94 /* connect to couchbase */
95 lcb_t couchbase_init_connection(const char *host, const char *bucket, const char *pass) {
96         lcb_t instance;                         /* couchbase instance */
97         lcb_error_t error;                      /* couchbase command return */
98         struct lcb_create_st options;           /* init create struct */
99
100         /* init options */
101         memset(&options, 0, sizeof(options));
102
103         /* assign couchbase create options */
104         options.v.v0.host = host;
105         options.v.v0.bucket = bucket;
106
107         /* assign user and password if they were both passed */
108         if (bucket != NULL && pass != NULL) {
109                 options.v.v0.user = bucket;
110                 options.v.v0.passwd = pass;
111         }
112
113         /* create couchbase connection instance */
114         if ((error = lcb_create(&instance, &options)) != LCB_SUCCESS) {
115                 /* log error and return */
116                 ERROR("rlm_couchbase: failed to create couchbase instance: %s (0x%x)", lcb_strerror(NULL, error), error);
117                 /* return instance */
118                 return instance;
119         }
120
121         /* initiate connection */
122         if ((error = lcb_connect(instance)) == LCB_SUCCESS) {
123                 /* set general method callbacks */
124                 lcb_set_error_callback(instance, couchbase_error_callback);
125                 lcb_set_get_callback(instance, couchbase_get_callback);
126                 lcb_set_store_callback(instance, couchbase_store_callback);
127                 /* wait on connection */
128                 lcb_wait(instance);
129         } else {
130                 /* log error */
131                 ERROR("rlm_couchbase: Failed to initiate couchbase connection: %s (0x%x)", lcb_strerror(NULL, error), error);
132         }
133
134         /* return instance */
135         return instance;
136 }
137
138 /* store document/key in couchbase */
139 lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire) {
140         lcb_error_t error;                  /* couchbase command return */
141         lcb_store_cmd_t cmd;                /* store command stuct */
142         const lcb_store_cmd_t *commands[1]; /* store commands array */
143
144         /* init commands */
145         commands[0] = &cmd;
146         memset(&cmd, 0, sizeof(cmd));
147
148         /* populate command struct */
149         cmd.v.v0.key = key;
150         cmd.v.v0.nkey = strlen(cmd.v.v0.key);
151         cmd.v.v0.bytes = document;
152         cmd.v.v0.nbytes = strlen(cmd.v.v0.bytes);
153         cmd.v.v0.exptime = expire;
154         cmd.v.v0.operation = LCB_SET;
155
156         /* store key/document in couchbase */
157         if ((error = lcb_store(instance, NULL, 1, commands)) == LCB_SUCCESS) {
158                 /* enter event loop on success */
159                 lcb_wait(instance);
160         }
161
162         /* return error */
163         return error;
164 }
165
166 /* pull document from couchbase by key */
167 lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key) {
168         lcb_error_t error;                  /* couchbase command return */
169         lcb_get_cmd_t cmd;                  /* get command struct */
170         const lcb_get_cmd_t *commands[1];   /* get commands array */
171
172         /* init commands */
173         commands[0] = &cmd;
174         memset(&cmd, 0, sizeof(cmd));
175
176         /* populate command struct */
177         cmd.v.v0.key = key;
178         cmd.v.v0.nkey = strlen(cmd.v.v0.key);
179
180         /* get document */
181         if ((error = lcb_get(instance, cookie, 1, commands)) == LCB_SUCCESS) {
182                 /* enter event loop on success */
183                 lcb_wait(instance);
184         }
185
186         /* return error */
187         return error;
188 }