source: protocols/rpc/rpc.c @ 578e5b0

Last change on this file since 578e5b0 was 578e5b0, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-05-14T16:08:06Z

Try to send settings as native types instead of always strings.

This is still ugly for most settings not owned by the plugin but that's
simply how this is implemented in BitlBee. Let's see how annoying and
painful this is really going to be.

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