source: protocols/rpc/rpc.c @ 2d88cac4

Last change on this file since 2d88cac4 was 2d88cac4, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-05-14T14:59:39Z

Allow non-string default values in plugin settings dict.

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