source: protocols/rpc/rpc.c @ f15553d

Last change on this file since f15553d was f15553d, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-04-12T15:04:55Z

Common set evaluator that sends updates to the RPC server if online.

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