source: protocols/rpc/rpc.c @ 0ace810

Last change on this file since 0ace810 was 0ace810, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-05-09T07:47:42Z

Add two missing includes to rpc.c for opendir(3).

On my system (Debian testing) these were getting included via-via by
accident apparently, not on wheezy and likely many others.

  • Property mode set to 100644
File size: 30.4 KB
RevLine 
[66aefeb]1#include <sys/socket.h>
2#include <sys/un.h>
[0ace810]3#include <sys/types.h>
4#include <dirent.h>
[66aefeb]5
[6cdecc7]6#include "bitlbee.h"
7#include "bee.h"
8#include "nogaim.h"
9#include "parson.h"
[4f6dfbb]10
[16652e9]11#define JSON_O_FOREACH(o, k, v) \
12    const char *k; const JSON_Value *v; int __i; \
13    for (__i = 0; json_object_get_tuple(o, __i, &k, &v); __i++)
14
[4f6dfbb]15static int next_rpc_id = 1;
16
[6cdecc7]17struct rpc_plugin {
[c5a7b8d]18        /* Socket address of the RPC server. */
[4f6dfbb]19        struct sockaddr *addr;
20        socklen_t addrlen;
[c5a7b8d]21        /* Full copy of the "settings" section of the init message. This info
22         * can only be applied later on, when an account is created (but well
23         * before logging in). */
[16652e9]24        JSON_Value *settings;
[c5a7b8d]25        /* Supported away states returned by the away_states() function. Since
26         * RPC servers can't do return values, just get this info at init time.
27         * This means the list of possible away states is static from init time
28         * which hopefully won't be a problem. If NULL, the away_states function
29         * will not be set on this protocol. */
30        GList *away_states;
31        /* Account flags. See account.h. Plugin lib should provide constants. */
32        int account_flags;
[6cdecc7]33};
[4f6dfbb]34
35struct rpc_connection {
36        int fd;
37        char *buf;
[6cdecc7]38        int buflen;
[66aefeb]39        GHashTable *groupchats;
[6cdecc7]40};
[4f6dfbb]41
42struct rpc_groupchat {
[66aefeb]43        int id;
44        struct groupchat *gc;
[6cdecc7]45};
[4f6dfbb]46
[578790e]47static JSON_Value *jsonrpc_error(int code, const char *msg) {
48        JSON_Value *ret = json_value_init_object();
[bc73e2ba]49        json_object_set_null(json_object(ret), "result");
50        if (TRUE) {
51                /* Format from http://jsonrpc.org/historical/json-rpc-1-1-alt.html.
52                 * Not sure whether to use it. */
53                JSON_Value *error = json_value_init_object();
[1a81c83]54                json_object_set_integer(json_object(error), "code", code);
[bc73e2ba]55                json_object_set_string(json_object(error), "message", msg);
56                json_object_set_value(json_object(ret), "error", error);
57        } else {
58                json_object_set_string(json_object(ret), "error", msg);
59        }
[578790e]60       
61        return ret;
62}
63
[16652e9]64// Might have liked to have this one in the library for optional values/etc.
65static void json_array_append_string_or_null(JSON_Array *array, const char *string) {
66        if (string)
67                json_array_append_string(array, string);
68        else
69                json_array_append_null(array);
70}
71
72static void json_object_set_string_or_null(JSON_Object *object, const char *key, const char *string) {
[bc73e2ba]73        if (string)
[16652e9]74                json_object_set_string(object, key, string);
[bc73e2ba]75        else
[16652e9]76                json_object_set_null(object, key);
[bc73e2ba]77}
78
[4f6dfbb]79static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
80        JSON_Value *rpc = json_value_init_object();
[6cdecc7]81        json_object_set_string(json_object(rpc), "method", method);
[1a81c83]82        json_object_set_integer(json_object(rpc), "id", next_rpc_id++);
[4f6dfbb]83
[6cdecc7]84        JSON_Value *params = json_value_init_array();
85        json_object_set_value(json_object(rpc), "params", params);
[4f6dfbb]86
87        if (params_)
88                *params_ = json_array(params);
[6cdecc7]89
90        return rpc;
[4f6dfbb]91}
92
93#define RPC_OUT_INIT(method) \
94        JSON_Array *params; \
95        JSON_Value *rpc = rpc_out_new(method, &params);
96
97/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
[66aefeb]98static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
99        struct rpc_connection *rd = ic->proto_data;
[4f6dfbb]100        char *buf = json_serialize_to_string(rpc);
[66aefeb]101        int len = strlen(buf);
102        int st;
[4f6dfbb]103
[66aefeb]104        buf = g_realloc(buf, len + 3);
105        strcpy(buf + len, "\r\n");
106        len += 2;
107
108        st = write(rd->fd, buf, len);
[4f6dfbb]109        g_free(buf);
110        json_value_free(rpc);
[66aefeb]111
112        if (st != len) {
[16652e9]113                if (!(ic->flags & OPT_LOGGING_OUT))
114                        imcb_log(ic, "Write error");
[66aefeb]115                imc_logout(ic, TRUE);
116                return FALSE;
117        }
118
119        return TRUE;
[4f6dfbb]120}
121
[16652e9]122static JSON_Value *rpc_ser_settings(set_t **set) {
123        const set_t *s;
124        JSON_Value *ret = json_value_init_object();
125       
126        for (s = *set; s; s = s->next) {
127                json_object_set_string_or_null(json_object(ret), s->key, set_getstr(set, s->key));
128        }
129
130        return ret;
131}
132
133static JSON_Value *rpc_ser_account(account_t *acc) {
[4f6dfbb]134        JSON_Value *v = json_value_init_object();
135        JSON_Object *o = json_object(v);
136        json_object_set_string(o, "user", acc->user);
[e9face7]137        json_object_set_string(o, "pass", acc->pass);
[66aefeb]138        if (acc->server)
139                json_object_set_string(o, "server", acc->server);
[16652e9]140        json_object_set_value(o, "settings", rpc_ser_settings(&acc->set));
[4f6dfbb]141        return v;
142}
143
[f3af614]144static JSON_Value *rpc_ser_bee_user(bee_user_t *bu) {
145        JSON_Value *v = json_value_init_object();
146        JSON_Object *o = json_object(v);
147        json_object_set_string_or_null(o, "handle", bu->handle);
148        json_object_set_string_or_null(o, "fullname", bu->fullname);
149        json_object_set_string_or_null(o, "nick", bu->nick);
150        json_object_set_string_or_null(o, "group", bu->group ? bu->group->name : NULL);
[1a81c83]151        json_object_set_integer(o, "flags", bu->flags);
[f3af614]152        json_object_set_string_or_null(o, "status", bu->status);
153        json_object_set_string_or_null(o, "status_msg", bu->status_msg);
[1a81c83]154        json_object_set_integer(o, "login_time", bu->login_time);
155        json_object_set_integer(o, "idle_time", bu->idle_time);
[f3af614]156        return v;
157}
158
[f15553d]159static char *rpc_set_evaluator(set_t *set, char *value);
160
[4f6dfbb]161static void rpc_init(account_t *acc) {
[16652e9]162        struct rpc_plugin *pd = acc->prpl->data;
163
164        JSON_O_FOREACH(json_object(pd->settings), name, value) {
165                JSON_Object *o = json_object(value);
166                set_t *set = set_add(&acc->set, name, json_object_get_string(o, "default"), NULL, acc);
[1a81c83]167                set->flags |= json_object_get_integer(o, "flags");
[f15553d]168                set->eval = rpc_set_evaluator;
169                set->eval_data = o;
[f3af614]170                /* eval_list turns out to be a memory leak so don't implement it
171                 * for now.
172                 * Allowing a plugin to define its own evaluator is not really
173                 * possible without having BitlBee block on it responding which
174                 * I don't want to do.
175                 * Should a module want to override a user's setting, it can
[531eabd]176                 * use set_setstr(). */
[16652e9]177        }
[c5a7b8d]178
179        acc->flags |= pd->account_flags;
[4f6dfbb]180}
181
[f15553d]182static char *rpc_set_evaluator(set_t *set, char *value) {
183        JSON_Object *o = set->eval_data;
184        const char *type = json_object_get_string(o, "type");
185
186        /* Just allow two simple int/bool evaluators with no protocol awareness. */
187        set_eval type_eval = NULL;
188        if (type == NULL) {
189        } else if (strncmp(type, "int", 3) == 0) {
190                type_eval = set_eval_int;
191        } else if (strncmp(type, "bool", 4) == 0) {
192                type_eval = set_eval_bool;
193        }
194
195        if (type_eval) {
196                char *new = type_eval(set, value);
197                if (new == SET_INVALID) {
198                        return SET_INVALID;
199                }
200        }
201
202        account_t *acc = set->data;
203        if (acc->ic) {
204                /* But do send RPCs to the plugin for each changed setting so
205                 * it always has up-to-date values. */
206                RPC_OUT_INIT("set_set");
207                json_array_append_string(params, set->key);
208                if (type_eval == set_eval_int) {
209                        int num = 0;
210                        /* Evaluator already did validation so ignore retval. */
211                        sscanf(value, "%d", &num);
[1a81c83]212                        json_array_append_integer(params, num);
[f15553d]213                } else if (type_eval == set_eval_bool) {
214                        json_array_append_boolean(params, bool2int(value));
215                } else {
216                        json_array_append_string(params, value);
217                }
218                rpc_send(acc->ic, rpc);
219        }
220
221        return value;
222}
223
[6cdecc7]224static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
225static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
[16652e9]226static JSON_Value *rpc_init_isup();
[6cdecc7]227
[4f6dfbb]228static void rpc_login(account_t *acc) {
229        struct im_connection *ic = imcb_new(acc);
[6cdecc7]230        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
231        struct rpc_plugin *pd = acc->prpl->data;
[578790e]232        imcb_log(ic, "Connecting to RPC server");
[6cdecc7]233        rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
234        sock_make_nonblocking(rd->fd);
235        if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
236                closesocket(rd->fd);
[c5a7b8d]237                imcb_error(ic, "RPC server unreachable");
238                imc_logout(ic, TRUE);
[6cdecc7]239                return;
240        }
241        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
[66aefeb]242        rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
[6cdecc7]243}
244
245static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
246        struct im_connection *ic = data;
247        struct rpc_connection *rd = ic->proto_data;
[16652e9]248
249        /* Need to repeat this since each IM connection means an actual new
250         * RPC session. */
251        JSON_Value *init = rpc_init_isup();
252        if (!rpc_send(ic, init))
253                return FALSE;
254
[4f6dfbb]255        RPC_OUT_INIT("login");
[6cdecc7]256        json_array_append_value(params, rpc_ser_account(ic->acc));
[16652e9]257        if (!rpc_send(ic, rpc))
258                return FALSE;
[6cdecc7]259
260        ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
261
262        return FALSE;
263}
264
265static void rpc_keepalive(struct im_connection *ic) {
266        RPC_OUT_INIT("keepalive");
267        rpc_send(ic, rpc);
268}
269
270static void rpc_logout(struct im_connection *ic) {
271        RPC_OUT_INIT("logout");
[9ae7332]272        if (!rpc_send(ic, rpc))
273                return;
[66aefeb]274
275        struct rpc_connection *rd = ic->proto_data;
276        b_event_remove(ic->inpa);
277        closesocket(rd->fd);
278        g_free(rd->buf);
279        g_hash_table_destroy(rd->groupchats);
280        g_free(rd);
[6cdecc7]281}
282
283static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
284        RPC_OUT_INIT("buddy_msg");
285        json_array_append_string(params, to);
286        json_array_append_string(params, message);
[1a81c83]287        json_array_append_integer(params, flags);
[9ae7332]288        return rpc_send(ic, rpc);
[6cdecc7]289}
290
291static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
292        RPC_OUT_INIT("set_away");
[bc73e2ba]293        json_array_append_string_or_null(params, state);
294        json_array_append_string_or_null(params, message);
[6cdecc7]295        rpc_send(ic, rpc);
296}
297
298static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
299        RPC_OUT_INIT("send_typing");
300        json_array_append_string(params, who);
[1a81c83]301        json_array_append_integer(params, flags);
[9ae7332]302        return rpc_send(ic, rpc);
[6cdecc7]303}
304
305static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
306        RPC_OUT_INIT("add_buddy");
307        json_array_append_string(params, name);
[bc73e2ba]308        json_array_append_string_or_null(params, group);
[6cdecc7]309        rpc_send(ic, rpc);
310}
311
312static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
313        RPC_OUT_INIT("remove_buddy");
314        json_array_append_string(params, name);
[bc73e2ba]315        json_array_append_string_or_null(params, group);
[6cdecc7]316        rpc_send(ic, rpc);
317}
318
319static void rpc_add_permit(struct im_connection *ic, char *who) {
320        RPC_OUT_INIT("add_permit");
321        json_array_append_string(params, who);
322        rpc_send(ic, rpc);
323}
324
325static void rpc_add_deny(struct im_connection *ic, char *who) {
326        RPC_OUT_INIT("add_deny");
327        json_array_append_string(params, who);
328        rpc_send(ic, rpc);
329}
330
331static void rpc_rem_permit(struct im_connection *ic, char *who) {
332        RPC_OUT_INIT("rem_permit");
333        json_array_append_string(params, who);
334        rpc_send(ic, rpc);
335}
336
337static void rpc_rem_deny(struct im_connection *ic, char *who) {
338        RPC_OUT_INIT("rem_deny");
339        json_array_append_string(params, who);
340        rpc_send(ic, rpc);
341}
342
343static void rpc_get_info(struct im_connection *ic, char *who) {
344        RPC_OUT_INIT("get_info");
345        json_array_append_string(params, who);
346        rpc_send(ic, rpc);
347}
348
349static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
350        RPC_OUT_INIT("chat_invite");
[66aefeb]351        struct rpc_groupchat *rc = gc->data;
[1a81c83]352        json_array_append_integer(params, rc->id);
[6cdecc7]353        json_array_append_string(params, who);
[bc73e2ba]354        json_array_append_string_or_null(params, message);
[6cdecc7]355        rpc_send(gc->ic, rpc);
356}
357
358static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
359        RPC_OUT_INIT("chat_kick");
[66aefeb]360        struct rpc_groupchat *rc = gc->data;
[1a81c83]361        json_array_append_integer(params, rc->id);
[6cdecc7]362        json_array_append_string(params, who);
[bc73e2ba]363        json_array_append_string_or_null(params, message);
[6cdecc7]364        rpc_send(gc->ic, rpc);
365}
366
367static void rpc_chat_leave(struct groupchat *gc) {
368        RPC_OUT_INIT("chat_leave");
[66aefeb]369        struct rpc_groupchat *rc = gc->data;
[1a81c83]370        json_array_append_integer(params, rc->id);
[4f6dfbb]371        rpc_send(gc->ic, rpc);
372}
373
374static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
375        RPC_OUT_INIT("chat_msg");       
[66aefeb]376        struct rpc_groupchat *rc = gc->data;
[1a81c83]377        json_array_append_integer(params, rc->id);
[4f6dfbb]378        json_array_append_string(params, msg);
[1a81c83]379        json_array_append_integer(params, flags);
[4f6dfbb]380        rpc_send(gc->ic, rpc);
381}
382
[578790e]383static struct rpc_groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
384        struct rpc_connection *rd = ic->proto_data;
[66aefeb]385        struct groupchat *gc = imcb_chat_new(ic, handle);
386        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
[578790e]387        rc->id = next_rpc_id++;
[66aefeb]388        rc->gc = gc;
[578790e]389        g_hash_table_insert(rd->groupchats, &rc->id, rc);
390        return rc;  // TODO: RETVAL HERE AND BELOW
[66aefeb]391}
392
[578790e]393static struct rpc_groupchat *rpc_groupchat_by_id(struct im_connection *ic, int id) {
394        struct rpc_connection *rd = ic->proto_data;
395        struct rpc_groupchat *rc = g_hash_table_lookup(rd->groupchats, &id);
396
397        return rc;
398}
399
400/* Boilerplate for all incoming RPCs (where groupchat is identified using
401 * numeric ID). */
402#define SET_GROUPCHAT(rc) \
403        do { \
[1a81c83]404                rc = rpc_groupchat_by_id(ic, json_array_get_integer(params, 0)); \
[578790e]405                if (rc == NULL) \
406                        return jsonrpc_error(ENOENT, "No groupchat with that id."); \
407        } while (0)
408
[6cdecc7]409static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
410        RPC_OUT_INIT("chat_with");
[578790e]411        struct rpc_groupchat *rc = rpc_groupchat_new(ic, who);
[1a81c83]412        json_array_append_integer(params, rc->id);
[6cdecc7]413        json_array_append_string(params, who);
414        rpc_send(ic, rpc);
415
[578790e]416        return rc->gc; 
[6cdecc7]417}
418
419static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
420                                       const char *password, set_t **sets) {
421        RPC_OUT_INIT("chat_join");
[578790e]422        struct rpc_groupchat *rc = rpc_groupchat_new(ic, room);
[1a81c83]423        json_array_append_integer(params, rc->id);
[6cdecc7]424        json_array_append_string(params, room);
[bc73e2ba]425        json_array_append_string_or_null(params, nick);
426        json_array_append_string_or_null(params, password);
[16652e9]427        json_array_append_value(params, rpc_ser_settings(sets));
[6cdecc7]428        rpc_send(ic, rpc);
429
[578790e]430        return rc->gc;
[6cdecc7]431}
432
433static void rpc_chat_topic(struct groupchat *gc, char *topic) {
434        RPC_OUT_INIT("chat_topic");
[66aefeb]435        struct rpc_groupchat *rc = gc->data;
[1a81c83]436        json_array_append_integer(params, rc->id);
[6cdecc7]437        json_array_append_string(params, topic);
438        rpc_send(gc->ic, rpc);
439}
440
[c5a7b8d]441static GList *rpc_away_states(struct im_connection *ic) {
442        struct rpc_plugin *pd = ic->acc->prpl->data;
443        return pd->away_states;
444}
445
[578790e]446static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
[6cdecc7]447
[9ae7332]448static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
[4f6dfbb]449        const char *cmd = json_object_get_string(rpc, "method");
[6cdecc7]450        JSON_Value *id = json_object_get_value(rpc, "id");
[bc73e2ba]451        JSON_Value *error = json_object_get_value(rpc, "error");
[6cdecc7]452        JSON_Array *params = json_object_get_array(rpc, "params");
[4f6dfbb]453
[578790e]454        /* Removed checks for result/error/etc. as it's all too free-form and
455         * at least for now this code is not going to care about retvals as
456         * they come in late anyway. */
457        if (!id) {
[6cdecc7]458                imcb_log(ic, "Received invalid JSON-RPC object.");
459                imc_logout(ic, TRUE);
[9ae7332]460                return FALSE;
[4f6dfbb]461        }
462
[578790e]463        if (cmd) {
464                JSON_Value *resp = rpc_cmd_in(ic, cmd, params);
465                if (!resp) {
466                        resp = json_value_init_object();
467                        json_object_set_boolean(json_object(resp), "result", TRUE);
468                }
[9ae7332]469                json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
470                return rpc_send(ic, resp);
[bc73e2ba]471        } else if (error && json_type(error) != JSONNull) {
472                char *error_str = json_serialize_to_string(error);
473                /* Maybe sanitise/truncate? Though really that should be done at
474                 * a different layer. */
475                imcb_error(ic, "RPC Error: %s", error_str);
476                g_free(error_str);
[4f6dfbb]477        }
[578790e]478
479        return TRUE;
[4f6dfbb]480}
481
[6cdecc7]482static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
483        struct im_connection *ic = data;
484        struct rpc_connection *rd = ic->proto_data;
485        char buf[2048];
486        int st;
487
488        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
489                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
490                memcpy(rd->buf + rd->buflen, buf, st);
[9ae7332]491                rd->buflen += st;
[6cdecc7]492        }
493
[9ae7332]494        if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
[578790e]495                imcb_log(ic, "Lost RPC connection");
[6cdecc7]496                imc_logout(ic, TRUE);
497                return FALSE;
498        }
499        rd->buf[rd->buflen] = '\0';
500
501        JSON_Value *parsed;
502        const char *end;
503        while ((parsed = json_parse_first(rd->buf, &end))) {
[9ae7332]504                st = rpc_in(ic, json_object(parsed));
[6cdecc7]505                json_value_free(parsed);
506
[9ae7332]507                if (!st)
508                        return FALSE;
509
[6cdecc7]510                if (end == rd->buf + rd->buflen) {
511                        g_free(rd->buf);
512                        rd->buf = NULL;
513                } else {
514                        int newlen = rd->buf + rd->buflen - end;
515                        char new[newlen];
516                        memcpy(new, end, newlen);
517                        rd->buf = g_realloc(rd->buf, newlen + 1);
518                        memcpy(rd->buf, new, newlen);
[9ae7332]519                        rd->buflen = newlen;
[6cdecc7]520                        rd->buf[rd->buflen] = '\0';
521                }
522        }
523
524        return TRUE;
525}
526
[578790e]527static JSON_Value *rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
528        void (*func)(struct im_connection*, const char*, ...) = func_;
[9ae7332]529        func(ic, "%s", json_array_get_string(params, 0));
[578790e]530        return NULL;
[9ae7332]531}
532
[578790e]533static JSON_Value *rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]534        void (*func)(struct im_connection*) = func_;
535        func(ic);
[578790e]536        return NULL;
[9ae7332]537}
538
[578790e]539static JSON_Value *rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]540        void (*func)(struct im_connection*, gboolean) = func_;
541        func(ic, json_array_get_boolean(params, 0));
[578790e]542        return NULL;
[9ae7332]543}
544
[578790e]545static JSON_Value *rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]546        void (*func)(struct im_connection*, const char*, const char*) = func_;
547        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1));
[578790e]548        return NULL;
[9ae7332]549}
550
[578790e]551static JSON_Value *rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]552        void (*func)(struct im_connection*, const char*, int, const char*, const char*) = func_;
[1a81c83]553        func(ic, json_array_get_string(params, 0), json_array_get_integer(params, 1),
[9ae7332]554                 json_array_get_string(params, 2), json_array_get_string(params, 3));
[578790e]555        return NULL;
[9ae7332]556}
557
[578790e]558static JSON_Value *rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]559        void (*func)(struct im_connection*, const char*, int, int) = func_;
[1a81c83]560        func(ic, json_array_get_string(params, 0), json_array_get_integer(params, 1),
561                 json_array_get_integer(params, 2));
[578790e]562        return NULL;
[9ae7332]563}
564
[578790e]565static JSON_Value *rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]566        void (*func)(struct im_connection*, const char*, const char*, int, int) = func_;
567        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1),
[1a81c83]568                 json_array_get_integer(params, 2), json_array_get_integer(params, 3));
[578790e]569        return NULL;
[9ae7332]570}
571
[578790e]572static JSON_Value *rpc_imcb_buddy_typing(struct im_connection *ic, void *func_, JSON_Array *params) {
573        void (*func)(struct im_connection*, const char*, int) = func_;
[1a81c83]574        func(ic, (char*) json_array_get_string(params, 0), json_array_get_integer(params, 1));
[578790e]575        return NULL;
576}
577
578static JSON_Value *rpc_imcb_chat_new(struct im_connection *ic, void *func_, JSON_Array *params) {
579        struct rpc_groupchat *rc = rpc_groupchat_new(ic, json_array_get_string(params, 0));
580        JSON_Value *resp = json_value_init_object();
[1a81c83]581        json_object_set_integer(json_object(resp), "result", rc->id);
[578790e]582        return resp;
583}
584
585static JSON_Value *rpc_imcb_chat_name_hint(struct im_connection *ic, void *func_, JSON_Array *params) {
586        void (*func)(struct groupchat*, const char*) = func_;
587        struct rpc_groupchat *rc;
588        SET_GROUPCHAT(rc);
589        func(rc->gc, json_array_get_string(params, 1));
590        return NULL;
591}
592
593static JSON_Value *rpc_imcb_chat_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
594        void (*func)(struct groupchat*, const char*, const char*, guint32, time_t) = func_;
595        struct rpc_groupchat *rc;
596        SET_GROUPCHAT(rc);
597        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
[1a81c83]598             json_array_get_integer(params, 3), json_array_get_integer(params, 4));
[578790e]599        return NULL;
600}
601
602static JSON_Value *rpc_imcb_chat_log(struct im_connection *ic, void *func_, JSON_Array *params) {
603        void (*func)(struct groupchat*, const char*, ...) = func_;
604        struct rpc_groupchat *rc;
605        SET_GROUPCHAT(rc);
606        func(rc->gc, "%s", json_array_get_string(params, 1));
607        return NULL;
608}
609
610static JSON_Value *rpc_imcb_chat_topic(struct im_connection *ic, void *func_, JSON_Array *params) {
611        void (*func)(struct groupchat*, const char*, const char*, time_t) = func_;
612        struct rpc_groupchat *rc;
613        SET_GROUPCHAT(rc);
614        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
[1a81c83]615             json_array_get_integer(params, 3));
[578790e]616        return NULL;
617}
618
619static JSON_Value *rpc_imcb_chat_remove_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
620        void (*func)(struct groupchat*, const char*, const char*) = func_;
621        struct rpc_groupchat *rc;
622        SET_GROUPCHAT(rc);
623        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2));
624        return NULL;
625}
626
627static JSON_Value *rpc_imcb_chat_invite(struct im_connection *ic, void *func_, JSON_Array *params) {
628        void (*func)(struct groupchat*, const char*, const char*, const char*) = func_;
629        struct rpc_groupchat *rc;
630        SET_GROUPCHAT(rc);
631        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
632             json_array_get_string(params, 3));
633        return NULL;
[4f6dfbb]634}
635
[f3af614]636static JSON_Value *rpc_set_getstr(struct im_connection *ic, void *func_, JSON_Array *params) {
637        char *rets = set_getstr(&ic->acc->set, json_array_get_string(params, 0));
638        JSON_Value *ret = json_value_init_object();
639        if (rets)
640                json_object_set_string(json_object(ret), "result", rets);
641        else
642                json_object_set_null(json_object(ret), "result");
643        return ret;
644}
645
646static JSON_Value *rpc_set_setstr(struct im_connection *ic, void *func_, JSON_Array *params) {
647        /* Sadly use of const is very poor in BitlBee. :-( */
648        char *newval = g_strdup(json_array_get_string(params, 1));
649        set_setstr(&ic->acc->set, json_array_get_string(params, 0), newval);
650        g_free(newval);
651        return rpc_set_getstr(ic, func_, params);
652}
653
654static JSON_Value *rpc_set_reset(struct im_connection *ic, void *func_, JSON_Array *params) {
655        set_reset(&ic->acc->set, json_array_get_string(params, 0));
656        return rpc_set_getstr(ic, func_, params);
657}
658
659static JSON_Value *rpc_bee_user_by_handle(struct im_connection *ic, void *func_, JSON_Array *params) {
660        bee_user_t *bu = bee_user_by_handle(ic->bee, ic, json_array_get_string(params, 0));
661        JSON_Value *ret = json_value_init_object();
662        if (bu)
663                json_object_set_value(json_object(ret), "result", rpc_ser_bee_user(bu));
664        else
665                json_object_set_value(json_object(ret), "error", jsonrpc_error(ENOENT, "Contact not found"));
666        return ret;
667}
668
[4f6dfbb]669struct rpc_in_method {
670        char *name;
[9ae7332]671        void *func;
[578790e]672        JSON_Value* (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
[9ae7332]673        char args[8];
674};
675
676static const struct rpc_in_method methods[] = {
[578790e]677        /* All these RPCs are equivalent of BitlBee C functions but with the
[f3af614]678         * struct im_connection* removed as this is in the object context. */
[9ae7332]679        { "imcb_log", imcb_log, rpc_imcb_log, "s" },
680        { "imcb_error", imcb_error, rpc_imcb_log, "s" },
681        { "imcb_connected", imcb_connected, rpc_imcb_connected, "" },
682        { "imc_logout", imc_logout, rpc_imc_logout, "b" },
683        { "imcb_add_buddy", imcb_add_buddy, rpc_imcb_add_buddy, "ss" },
684        { "imcb_remove_buddy", imcb_remove_buddy, rpc_imcb_add_buddy, "ss" },
685        { "imcb_rename_buddy", imcb_rename_buddy, rpc_imcb_add_buddy, "ss" },
686        { "imcb_buddy_nick_hint", imcb_buddy_nick_hint, rpc_imcb_add_buddy, "ss" },
687        { "imcb_buddy_status", imcb_buddy_status, rpc_imcb_buddy_status, "snss" },
688        { "imcb_buddy_status_msg", imcb_buddy_status_msg, rpc_imcb_add_buddy, "ss" },
[1a81c83]689        { "imcb_buddy_times", imcb_buddy_times, rpc_imcb_buddy_times, "sii" },
690        { "imcb_buddy_msg", imcb_buddy_msg, rpc_imcb_buddy_msg, "ssii" },
691        { "imcb_buddy_typing", imcb_buddy_typing, rpc_imcb_buddy_typing, "si" },
[578790e]692        { "imcb_chat_new", NULL, rpc_imcb_chat_new, "s" },
693       
694        /* RPCs below are equivalent, but with the struct groupchat* replaced
695         * with the numeric id of the chat. */
[1a81c83]696        { "imcb_chat_name_hint", imcb_chat_name_hint, rpc_imcb_chat_name_hint, "is" },
697        { "imcb_chat_msg", imcb_chat_msg, rpc_imcb_chat_msg, "issii" },
698        { "imcb_chat_log", imcb_chat_log, rpc_imcb_chat_log, "is" },
699        { "imcb_chat_topic", imcb_chat_topic, rpc_imcb_chat_topic, "issi" },
700        { "imcb_chat_add_buddy", imcb_chat_add_buddy, rpc_imcb_chat_name_hint, "is" },
701        { "imcb_chat_remove_buddy", imcb_chat_remove_buddy, rpc_imcb_chat_remove_buddy, "iss" },
702        { "imcb_chat_invite", imcb_chat_invite, rpc_imcb_chat_invite, "isss" },
[578790e]703
[f3af614]704        /* These are not imcb* functions but should still be exported. */
705        /* Setting functions. Starting with just providing access to account
706         * settings. See later whether access to chat/chan settings is necessary.
707         * All of these will return the (new) value of given setting. */
708        { "set_getstr", NULL, rpc_set_getstr, "s" },
709        { "set_setstr", NULL, rpc_set_setstr, "ss" },
710        { "set_reset", NULL, rpc_set_reset, "s" },
711       
712        { "bee_user_by_handle", NULL, rpc_bee_user_by_handle, "s" },
713
[9ae7332]714        { NULL },
[6cdecc7]715};
[4f6dfbb]716
[578790e]717static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
[9ae7332]718        int i;
719
720        for (i = 0; methods[i].name; i++) {
721                if (strcmp(cmd, methods[i].name) == 0) {
722                        if (json_array_get_count(params) != strlen(methods[i].args)) {
723                                imcb_error(ic, "Invalid argument count to method %s: %d, wanted %zd", cmd, (int) json_array_get_count(params), strlen(methods[i].args));
[1a81c83]724                                return jsonrpc_error(E2BIG, "Invalid integer of arguments");
[9ae7332]725                        }
726                        int j;
727                        for (j = 0; methods[i].args[j]; j++) {
728                                JSON_Value_Type type = json_value_get_type(json_array_get_value(params, j));
729                                gboolean ok = FALSE;
730                                switch (methods[i].args[j]) {
731                                case 's':
[bc73e2ba]732                                        ok = type == JSONString || type == JSONNull;
[9ae7332]733                                        break;
[1a81c83]734                                case 'i':
735                                        ok = type == JSONInteger;
[9ae7332]736                                        break;
737                                case 'o':
738                                        ok = type == JSONObject;
739                                        break;
740                                case 'a':
741                                        ok = type == JSONArray;
742                                        break;
743                                case 'b':
744                                        ok = type == JSONBoolean;
745                                        break;
746                                }
747                                if (!ok) {
748                                        // This error sucks, but just get your types right!
749                                        imcb_error(ic, "Invalid argument type, %s parameter %d: %d not %c", cmd, j, type, methods[i].args[j]);
[578790e]750                                        return jsonrpc_error(EINVAL, "Invalid argument type");
[9ae7332]751                                }
752                        }
[578790e]753                        return methods[i].wfunc(ic, methods[i].func, params);
[9ae7332]754                }
755        }
[578790e]756        return jsonrpc_error(ENOSYS, "Function not implemented");
[9ae7332]757}
758
[4f6dfbb]759#define RPC_ADD_FUNC(func) \
[1a81c83]760        ret->func = rpc_ ## func
761#define RPC_ADD_OPT_FUNC(func) \
[4f6dfbb]762        if (g_hash_table_contains(methods, #func)) \
[1a81c83]763                RPC_ADD_FUNC(func)
[16652e9]764
765static JSON_Value *rpc_init_isup() {
766        int i;
767
768        RPC_OUT_INIT("init");
769        JSON_Value *d = json_value_init_object();
770        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
[1a81c83]771        json_object_set_integer(json_object(d), "version", BITLBEE_VERSION_CODE);
[16652e9]772       
773        JSON_Value *ml = json_value_init_array();
774        for (i = 0; methods[i].name; i++) {
775                json_array_append_string(json_array(ml), methods[i].name);
776        }
777        json_object_set_value(json_object(d), "method_list", ml);
778        json_array_append_value(params, d);
779
780        return rpc;
781}
[4f6dfbb]782
[531eabd]783gboolean rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
[4f6dfbb]784        int st, fd, i;
785
[6cdecc7]786        fd = socket(address->sa_family, SOCK_STREAM, 0);
[531eabd]787        if (fd == -1 || connect(fd, address, addrlen) == -1) {
788                log_message(LOGLVL_WARNING, "Failed to connect to RPC server: %s", strerror(errno));
789                if (fd != -1)
790                        closesocket(fd);
791                return FALSE;
792        }
[4f6dfbb]793
[16652e9]794        JSON_Value *rpc = rpc_init_isup();
[4f6dfbb]795        char *s = json_serialize_to_string(rpc);
[16652e9]796        json_value_free(rpc);
797
[66aefeb]798        int len = strlen(s);
799        s = g_realloc(s, len + 3);
800        strcpy(s + len, "\r\n");
801        len += 2;
[4f6dfbb]802
[66aefeb]803        if ((st = write(fd, s, len)) != len) {
[531eabd]804                log_message(LOGLVL_WARNING, "Error while writing to RPC server: %s", strerror(errno));
805                return FALSE;
[4f6dfbb]806        }
807        g_free(s);
808
809        char *resp = NULL;
[6cdecc7]810        int buflen = 4096, resplen = 0;
[4f6dfbb]811        JSON_Value *parsed;
812        do {
813                fd_set rfds;
814                struct timeval to;
815
816                FD_ZERO(&rfds);
817                FD_SET(fd, &rfds);
818                to.tv_sec = 1;
819                to.tv_usec = 0;
820                st = select(fd + 1, &rfds, NULL, NULL, &to);
821
822                if (st == 0) {
[531eabd]823                        log_message(LOGLVL_WARNING, "Error while reading from RPC server: %s", strerror(errno));
[66aefeb]824                        closesocket(fd);
[531eabd]825                        return FALSE;
[4f6dfbb]826                }
827               
[6cdecc7]828                if (resplen >= buflen)
829                        buflen *= 2;
830                resp = g_realloc(resp, buflen + 1);
831                st = read(fd, resp + resplen, buflen - resplen);
[4f6dfbb]832                if (st == -1) {
833                        if (sockerr_again())
834                                continue;
[531eabd]835                        log_message(LOGLVL_WARNING, "Error while reading from RPC server: %s", strerror(errno));
[66aefeb]836                        closesocket(fd);
[531eabd]837                        return FALSE;
[4f6dfbb]838                }
839                resplen += st;
840                resp[resplen] = '\0';
841        }
842        while (!(parsed = json_parse_string(resp)));
[66aefeb]843        closesocket(fd);
[4f6dfbb]844
845        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
846        if (isup == NULL) {
[531eabd]847                log_message(LOGLVL_WARNING, "Error while parsing RPC server response");
848                return FALSE;
[4f6dfbb]849        }
850
851        struct prpl *ret = g_new0(struct prpl, 1);
[c5a7b8d]852        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
853        proto_data->addr = g_memdup(address, addrlen);
854        proto_data->addrlen = addrlen;
855        ret->name = g_strdup(json_object_get_string(isup, "name"));
856        ret->data = proto_data;
857
[1a81c83]858        proto_data->account_flags = json_object_get_integer(isup, "account_flags");
[c5a7b8d]859
860        /* Keep a full copy of the settings list, we can only use it when we
861         * have an account to work on. */
862        JSON_Value *settings = json_object_get_value(isup, "settings");
863        if (json_type(settings) == JSONObject)
864                proto_data->settings = json_value_deep_copy(settings);
865
866        JSON_Array *aways_a = json_object_get_array(isup, "away_state_list");
867        for (i = 0; i < json_array_get_count(aways_a); ++i) {
868                JSON_Value *state = json_array_get_value(aways_a, i);
869                if (json_type(state) == JSONString)
870                        proto_data->away_states =
871                                g_list_append(proto_data->away_states,
872                                              g_strdup(json_string(state)));
873        }
874
[f3af614]875        JSON_Array *methods_a = json_object_get_array(isup, "method_list");
[6cdecc7]876        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
[4f6dfbb]877        for (i = 0; i < json_array_get_count(methods_a); i++)
[6cdecc7]878                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
[4f6dfbb]879
[6cdecc7]880        ret->init = rpc_init;
[4f6dfbb]881        RPC_ADD_FUNC(login);
[1a81c83]882        RPC_ADD_OPT_FUNC(keepalive);
[6cdecc7]883        RPC_ADD_FUNC(logout);
884        RPC_ADD_FUNC(buddy_msg);
[1a81c83]885        RPC_ADD_OPT_FUNC(set_away);
886        RPC_ADD_OPT_FUNC(send_typing);
[f8feb8a]887        RPC_ADD_OPT_FUNC(add_buddy);
888        RPC_ADD_OPT_FUNC(remove_buddy);
[1a81c83]889        RPC_ADD_OPT_FUNC(add_permit);
890        RPC_ADD_OPT_FUNC(add_deny);
891        RPC_ADD_OPT_FUNC(rem_permit);
892        RPC_ADD_OPT_FUNC(rem_deny);
893        RPC_ADD_OPT_FUNC(get_info);
894        RPC_ADD_OPT_FUNC(chat_invite);
895        RPC_ADD_OPT_FUNC(chat_kick);
896        RPC_ADD_OPT_FUNC(chat_leave);
897        RPC_ADD_OPT_FUNC(chat_msg);
898        RPC_ADD_OPT_FUNC(chat_with);
899        RPC_ADD_OPT_FUNC(chat_join);
900        RPC_ADD_OPT_FUNC(chat_topic);
[c5a7b8d]901        if (proto_data->away_states)
902                ret->away_states = rpc_away_states;
[4f6dfbb]903       
[6cdecc7]904        g_hash_table_destroy(methods);
[4f6dfbb]905
906        // TODO: Property for a few standard nickcmp implementations.
[bc73e2ba]907        ret->handle_cmp = g_ascii_strcasecmp;
[4f6dfbb]908       
909        register_protocol(ret);
[531eabd]910
911        return TRUE;
[4f6dfbb]912}
913
[66aefeb]914#define PDIR "/tmp/rpcplugins"
915
916/* YA RLY :-/ */
917#ifndef UNIX_PATH_MAX
918struct sockaddr_un sizecheck;
919#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
920#endif
921
[4f6dfbb]922void rpc_initmodule() {
[66aefeb]923        DIR *pdir = opendir(PDIR);
924        struct dirent *de;
925
926        if (!pdir)
927                return;
928
929        while ((de = readdir(pdir))) {
[531eabd]930                if (de->d_type != DT_SOCK && de->d_type != DT_UNKNOWN)
931                        continue;
932
[66aefeb]933                char *fn = g_build_filename(PDIR, de->d_name, NULL);
934                struct sockaddr_un su;
935
936                strncpy(su.sun_path, fn, UNIX_PATH_MAX);
[531eabd]937
938#if 0
939                struct stat fdata;
940                if (stat(fn, &fdata) == -1) {
941                        log_message(LOGLVL_WARNING, "Could not stat %s: %s", fn, strerror(errno));
942                        g_free(fn);
943                        continue;
944                }
945                /* For now just skip anything that is not a Unix domain socket. */
946                if (!S_ISSOCK(fdata.st_mode))
947                        continue;
948#endif
949
[66aefeb]950                su.sun_path[UNIX_PATH_MAX-1] = '\0';
951                su.sun_family = AF_UNIX;
[531eabd]952                gboolean st = rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
[66aefeb]953                g_free(fn);
[531eabd]954                if (!st)
955                        log_message(LOGLVL_WARNING, "Failed to register protocol %s", fn);
[c5a7b8d]956                /* Idea: Also support textfiles containing a host:port tuple to
957                 * connect to. Not that remote RPC'ing would be a great idea,
958                 * but maybe some jsonrpc libs don't support Unix domain sockets. */
[66aefeb]959        }
[531eabd]960        closedir(pdir);
[4f6dfbb]961}
962
Note: See TracBrowser for help on using the repository browser.