source: protocols/rpc/rpc.c @ 9ae7332

Last change on this file since 9ae7332 was 9ae7332, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-02-27T23:58:11Z

Some support for calls *from* the plugin.

This gets uglier, also very poor const hygiene in BitlBee is not helping.
:-(

  • Property mode set to 100644
File size: 17.1 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
9static int next_rpc_id = 1;
10
11struct rpc_plugin {
12        struct sockaddr *addr;
13        socklen_t addrlen;
14};
15
16struct rpc_connection {
17        int fd;
18        char *buf;
19        int buflen;
20        GHashTable *groupchats;
21};
22
23struct rpc_groupchat {
24        int id;
25        struct groupchat *gc;
26};
27
28static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
29        JSON_Value *rpc = json_value_init_object();
30        json_object_set_string(json_object(rpc), "method", method);
31        json_object_set_number(json_object(rpc), "id", next_rpc_id++);
32
33        JSON_Value *params = json_value_init_array();
34        json_object_set_value(json_object(rpc), "params", params);
35
36        if (params_)
37                *params_ = json_array(params);
38
39        return rpc;
40}
41
42#define RPC_OUT_INIT(method) \
43        JSON_Array *params; \
44        JSON_Value *rpc = rpc_out_new(method, &params);
45
46/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
47static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
48        struct rpc_connection *rd = ic->proto_data;
49        char *buf = json_serialize_to_string(rpc);
50        int len = strlen(buf);
51        int st;
52
53        buf = g_realloc(buf, len + 3);
54        strcpy(buf + len, "\r\n");
55        len += 2;
56
57        st = write(rd->fd, buf, len);
58        g_free(buf);
59        json_value_free(rpc);
60
61        if (st != len) {
62                imcb_log(ic, "Write error");
63                imc_logout(ic, TRUE);
64                return FALSE;
65        }
66
67        return TRUE;
68}
69
70static JSON_Value *rpc_ser_account(const account_t *acc) {
71        JSON_Value *v = json_value_init_object();
72        JSON_Object *o = json_object(v);
73        json_object_set_string(o, "user", acc->user);
74        json_object_set_string(o, "pass", acc->user);
75        if (acc->server)
76                json_object_set_string(o, "server", acc->server);
77        return v;
78}
79
80static void rpc_init(account_t *acc) {
81        // Add settings. Probably should not RPC at all.
82}
83
84static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
85static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
86
87static void rpc_login(account_t *acc) {
88        struct im_connection *ic = imcb_new(acc);
89        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
90        struct rpc_plugin *pd = acc->prpl->data;
91        rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
92        sock_make_nonblocking(rd->fd);
93        if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
94                closesocket(rd->fd);
95                return;
96        }
97        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
98        rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
99}
100
101static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
102        struct im_connection *ic = data;
103        struct rpc_connection *rd = ic->proto_data;
104        RPC_OUT_INIT("login");
105        json_array_append_value(params, rpc_ser_account(ic->acc));
106        rpc_send(ic, rpc);
107
108        ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
109
110        return FALSE;
111}
112
113static void rpc_keepalive(struct im_connection *ic) {
114        RPC_OUT_INIT("keepalive");
115        rpc_send(ic, rpc);
116}
117
118static void rpc_logout(struct im_connection *ic) {
119        RPC_OUT_INIT("logout");
120        if (!rpc_send(ic, rpc))
121                return;
122
123        struct rpc_connection *rd = ic->proto_data;
124        b_event_remove(ic->inpa);
125        closesocket(rd->fd);
126        g_free(rd->buf);
127        g_hash_table_destroy(rd->groupchats);
128        g_free(rd);
129}
130
131static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
132        RPC_OUT_INIT("buddy_msg");
133        json_array_append_string(params, to);
134        json_array_append_string(params, message);
135        json_array_append_number(params, flags);
136        return rpc_send(ic, rpc);
137}
138
139static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
140        RPC_OUT_INIT("set_away");
141        json_array_append_string(params, state);
142        json_array_append_string(params, message);
143        rpc_send(ic, rpc);
144}
145
146static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
147        RPC_OUT_INIT("send_typing");
148        json_array_append_string(params, who);
149        json_array_append_number(params, flags);
150        return rpc_send(ic, rpc);
151}
152
153static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
154        RPC_OUT_INIT("add_buddy");
155        json_array_append_string(params, name);
156        json_array_append_string(params, group);
157        rpc_send(ic, rpc);
158}
159
160static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
161        RPC_OUT_INIT("remove_buddy");
162        json_array_append_string(params, name);
163        json_array_append_string(params, group);
164        rpc_send(ic, rpc);
165}
166
167static void rpc_add_permit(struct im_connection *ic, char *who) {
168        RPC_OUT_INIT("add_permit");
169        json_array_append_string(params, who);
170        rpc_send(ic, rpc);
171}
172
173static void rpc_add_deny(struct im_connection *ic, char *who) {
174        RPC_OUT_INIT("add_deny");
175        json_array_append_string(params, who);
176        rpc_send(ic, rpc);
177}
178
179static void rpc_rem_permit(struct im_connection *ic, char *who) {
180        RPC_OUT_INIT("rem_permit");
181        json_array_append_string(params, who);
182        rpc_send(ic, rpc);
183}
184
185static void rpc_rem_deny(struct im_connection *ic, char *who) {
186        RPC_OUT_INIT("rem_deny");
187        json_array_append_string(params, who);
188        rpc_send(ic, rpc);
189}
190
191static void rpc_get_info(struct im_connection *ic, char *who) {
192        RPC_OUT_INIT("get_info");
193        json_array_append_string(params, who);
194        rpc_send(ic, rpc);
195}
196
197static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
198        RPC_OUT_INIT("chat_invite");
199        struct rpc_groupchat *rc = gc->data;
200        json_array_append_number(params, rc->id);
201        json_array_append_string(params, who);
202        json_array_append_string(params, message);
203        rpc_send(gc->ic, rpc);
204}
205
206static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
207        RPC_OUT_INIT("chat_kick");
208        struct rpc_groupchat *rc = gc->data;
209        json_array_append_number(params, rc->id);
210        json_array_append_string(params, who);
211        json_array_append_string(params, message);
212        rpc_send(gc->ic, rpc);
213}
214
215static void rpc_chat_leave(struct groupchat *gc) {
216        RPC_OUT_INIT("chat_leave");
217        struct rpc_groupchat *rc = gc->data;
218        json_array_append_number(params, rc->id);
219        rpc_send(gc->ic, rpc);
220}
221
222static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
223        RPC_OUT_INIT("chat_msg");       
224        struct rpc_groupchat *rc = gc->data;
225        json_array_append_number(params, rc->id);
226        json_array_append_string(params, msg);
227        json_array_append_number(params, flags);
228        rpc_send(gc->ic, rpc);
229}
230
231static struct groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
232        struct groupchat *gc = imcb_chat_new(ic, handle);
233        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
234        rc->id = next_rpc_id;
235        rc->gc = gc;
236        return gc;  // TODO: RETVAL HERE AND BELOW
237}
238
239static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
240        RPC_OUT_INIT("chat_with");
241        struct groupchat *gc = rpc_groupchat_new(ic, who);
242        struct rpc_groupchat *rc = gc->data;
243        json_array_append_number(params, rc->id);
244        json_array_append_string(params, who);
245        rpc_send(ic, rpc);
246
247        return gc; 
248}
249
250static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
251                                       const char *password, set_t **sets) {
252        RPC_OUT_INIT("chat_join");
253        struct groupchat *gc = rpc_groupchat_new(ic, room);
254        struct rpc_groupchat *rc = gc->data;
255        json_array_append_number(params, rc->id);
256        json_array_append_string(params, room);
257        json_array_append_string(params, nick);
258        json_array_append_string(params, password);
259        //json_array_append_value(params, rpc_ser_sets(sets));
260        rpc_send(ic, rpc);
261
262        return gc;
263}
264
265static void rpc_chat_topic(struct groupchat *gc, char *topic) {
266        RPC_OUT_INIT("chat_topic");
267        struct rpc_groupchat *rc = gc->data;
268        json_array_append_number(params, rc->id);
269        json_array_append_string(params, topic);
270        rpc_send(gc->ic, rpc);
271}
272
273static gboolean rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
274
275static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
276        JSON_Object *res = json_object_get_object(rpc, "result");
277        const char *cmd = json_object_get_string(rpc, "method");
278        JSON_Value *id = json_object_get_value(rpc, "id");
279        JSON_Array *params = json_object_get_array(rpc, "params");
280
281        if ((!cmd && !res) || !id || (cmd && !params)) {
282                imcb_log(ic, "Received invalid JSON-RPC object.");
283                imc_logout(ic, TRUE);
284                return FALSE;
285        }
286
287        if (res) {
288                // handle response. I think I mostly won't.
289                return TRUE;
290        } else {
291                gboolean st = rpc_cmd_in(ic, cmd, params);
292                JSON_Value *resp = json_value_init_object();
293                json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
294                if (st)
295                        json_object_set_value(json_object(resp), "result", json_value_init_object());
296                else
297                        json_object_set_value(json_object(resp), "error", json_value_init_object());
298                return rpc_send(ic, resp);
299        }
300}
301
302static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
303        struct im_connection *ic = data;
304        struct rpc_connection *rd = ic->proto_data;
305        char buf[2048];
306        int st;
307
308        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
309                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
310                memcpy(rd->buf + rd->buflen, buf, st);
311                rd->buflen += st;
312        }
313
314        if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
315                imcb_log(ic, "Read error");
316                imc_logout(ic, TRUE);
317                return FALSE;
318        }
319        rd->buf[rd->buflen] = '\0';
320
321        JSON_Value *parsed;
322        const char *end;
323        while ((parsed = json_parse_first(rd->buf, &end))) {
324                st = rpc_in(ic, json_object(parsed));
325                json_value_free(parsed);
326
327                if (!st)
328                        return FALSE;
329
330                if (end == rd->buf + rd->buflen) {
331                        g_free(rd->buf);
332                        rd->buf = NULL;
333                } else {
334                        int newlen = rd->buf + rd->buflen - end;
335                        char new[newlen];
336                        memcpy(new, end, newlen);
337                        rd->buf = g_realloc(rd->buf, newlen + 1);
338                        memcpy(rd->buf, new, newlen);
339                        rd->buflen = newlen;
340                        rd->buf[rd->buflen] = '\0';
341                }
342        }
343
344        return TRUE;
345}
346
347static void rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
348        void (*func)(struct im_connection*, char*, ...) = func_;
349        func(ic, "%s", json_array_get_string(params, 0));
350}
351
352static void rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
353        void (*func)(struct im_connection*) = func_;
354        func(ic);
355}
356
357static void rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
358        void (*func)(struct im_connection*, gboolean) = func_;
359        func(ic, json_array_get_boolean(params, 0));
360}
361
362static void rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
363        void (*func)(struct im_connection*, const char*, const char*) = func_;
364        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1));
365}
366
367static void rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
368        void (*func)(struct im_connection*, const char*, int, const char*, const char*) = func_;
369        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
370                 json_array_get_string(params, 2), json_array_get_string(params, 3));
371}
372
373static void rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
374        void (*func)(struct im_connection*, const char*, int, int) = func_;
375        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
376                 json_array_get_number(params, 2));
377}
378
379static void rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
380        void (*func)(struct im_connection*, const char*, const char*, int, int) = func_;
381        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1),
382                 json_array_get_number(params, 2), json_array_get_number(params, 3));
383}
384
385static void rpc_imcb_buddy_typing(struct im_connection *ic, void *func_, JSON_Array *params) {
386        void (*func)(struct im_connection*, char*, int) = func_;
387        func(ic, (char*) json_array_get_string(params, 0), json_array_get_number(params, 1));
388}
389
390struct rpc_in_method {
391        char *name;
392        void *func;
393        void (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
394        char args[8];
395};
396
397static const struct rpc_in_method methods[] = {
398        { "imcb_log", imcb_log, rpc_imcb_log, "s" },
399        { "imcb_error", imcb_error, rpc_imcb_log, "s" },
400        { "imcb_connected", imcb_connected, rpc_imcb_connected, "" },
401        { "imc_logout", imc_logout, rpc_imc_logout, "b" },
402        { "imcb_add_buddy", imcb_add_buddy, rpc_imcb_add_buddy, "ss" },
403        { "imcb_remove_buddy", imcb_remove_buddy, rpc_imcb_add_buddy, "ss" },
404        { "imcb_rename_buddy", imcb_rename_buddy, rpc_imcb_add_buddy, "ss" },
405        { "imcb_buddy_nick_hint", imcb_buddy_nick_hint, rpc_imcb_add_buddy, "ss" },
406        { "imcb_buddy_status", imcb_buddy_status, rpc_imcb_buddy_status, "snss" },
407        { "imcb_buddy_status_msg", imcb_buddy_status_msg, rpc_imcb_add_buddy, "ss" },
408        { "imcb_buddy_times", imcb_buddy_times, rpc_imcb_buddy_times, "snn" },
409        { "imcb_buddy_msg", imcb_buddy_msg, rpc_imcb_buddy_msg, "ssnn" },
410        { "imcb_buddy_typing", imcb_buddy_typing, rpc_imcb_buddy_typing, "sn" },
411        { NULL },
412};
413
414static gboolean rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
415        int i;
416
417        for (i = 0; methods[i].name; i++) {
418                if (strcmp(cmd, methods[i].name) == 0) {
419                        if (json_array_get_count(params) != strlen(methods[i].args)) {
420                                imcb_error(ic, "Invalid argument count to method %s: %d, wanted %zd", cmd, (int) json_array_get_count(params), strlen(methods[i].args));
421                                return FALSE;
422                        }
423                        int j;
424                        for (j = 0; methods[i].args[j]; j++) {
425                                JSON_Value_Type type = json_value_get_type(json_array_get_value(params, j));
426                                gboolean ok = FALSE;
427                                switch (methods[i].args[j]) {
428                                case 's':
429                                        ok = type == JSONString;
430                                        break;
431                                case 'n':
432                                        ok = type == JSONNumber;
433                                        break;
434                                case 'o':
435                                        ok = type == JSONObject;
436                                        break;
437                                case 'a':
438                                        ok = type == JSONArray;
439                                        break;
440                                case 'b':
441                                        ok = type == JSONBoolean;
442                                        break;
443                                }
444                                if (!ok) {
445                                        // This error sucks, but just get your types right!
446                                        imcb_error(ic, "Invalid argument type, %s parameter %d: %d not %c", cmd, j, type, methods[i].args[j]);
447                                        return FALSE;
448                                }
449                        }
450                        methods[i].wfunc(ic, methods[i].func, params);
451                        return TRUE;
452                }
453        }
454        return FALSE;
455}
456
457#define RPC_ADD_FUNC(func) \
458        if (g_hash_table_contains(methods, #func)) \
459                ret->func = rpc_ ## func
460
461void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
462        int st, fd, i;
463
464        fd = socket(address->sa_family, SOCK_STREAM, 0);
465        if (fd == -1 || connect(fd, address, addrlen) == -1)
466                return;
467
468        RPC_OUT_INIT("init");
469        JSON_Value *d = json_value_init_object();
470        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
471        json_object_set_number(json_object(d), "version", BITLBEE_VERSION_CODE);
472        json_array_append_value(params, d);
473        char *s = json_serialize_to_string(rpc);
474        int len = strlen(s);
475        s = g_realloc(s, len + 3);
476        strcpy(s + len, "\r\n");
477        len += 2;
478
479        if ((st = write(fd, s, len)) != len) {
480                // LOG ERROR
481                return;
482        }
483        g_free(s);
484
485        char *resp = NULL;
486        int buflen = 4096, resplen = 0;
487        JSON_Value *parsed;
488        do {
489                fd_set rfds;
490                struct timeval to;
491
492                FD_ZERO(&rfds);
493                FD_SET(fd, &rfds);
494                to.tv_sec = 1;
495                to.tv_usec = 0;
496                st = select(fd + 1, &rfds, NULL, NULL, &to);
497
498                if (st == 0) {
499                        // LOG ERROR
500                        closesocket(fd);
501                        return;
502                }
503               
504                if (resplen >= buflen)
505                        buflen *= 2;
506                resp = g_realloc(resp, buflen + 1);
507                st = read(fd, resp + resplen, buflen - resplen);
508                if (st == -1) {
509                        if (sockerr_again())
510                                continue;
511                        // LOG ERROR
512                        closesocket(fd);
513                        return;
514                }
515                resplen += st;
516                resp[resplen] = '\0';
517        }
518        while (!(parsed = json_parse_string(resp)));
519        closesocket(fd);
520
521        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
522        if (isup == NULL) {
523                // LOG ERROR
524                return;
525        }
526
527        struct prpl *ret = g_new0(struct prpl, 1);
528       
529        JSON_Array *methods_a = json_object_get_array(isup, "methods");
530        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
531        for (i = 0; i < json_array_get_count(methods_a); i++)
532                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
533
534        ret->init = rpc_init;
535        RPC_ADD_FUNC(login);
536        RPC_ADD_FUNC(keepalive);
537        RPC_ADD_FUNC(logout);
538        RPC_ADD_FUNC(buddy_msg);
539        RPC_ADD_FUNC(set_away);
540        RPC_ADD_FUNC(send_typing);
541        RPC_ADD_FUNC(add_buddy);
542        RPC_ADD_FUNC(remove_buddy);
543        RPC_ADD_FUNC(add_permit);
544        RPC_ADD_FUNC(add_deny);
545        RPC_ADD_FUNC(rem_permit);
546        RPC_ADD_FUNC(rem_deny);
547        RPC_ADD_FUNC(get_info);
548        RPC_ADD_FUNC(chat_invite);
549        RPC_ADD_FUNC(chat_kick);
550        RPC_ADD_FUNC(chat_leave);
551        RPC_ADD_FUNC(chat_msg);
552        RPC_ADD_FUNC(chat_with);
553        RPC_ADD_FUNC(chat_join);
554        RPC_ADD_FUNC(chat_topic);
555       
556        g_hash_table_destroy(methods);
557
558        // TODO: Property for a few standard nickcmp implementations.
559       
560        JSON_Array *settings = json_object_get_array(isup, "settings");
561        for (i = 0; i < json_array_get_count(settings); i++) {
562                //JSON_Object *set = json_array_get_object(settings, i);
563                // set..name, set..type, set..default, set..flags ?
564        }
565
566        ret->name = g_strdup(json_object_get_string(isup, "name"));
567
568        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
569        proto_data->addr = g_memdup(address, addrlen);
570        proto_data->addrlen = addrlen;
571        ret->data = proto_data;
572
573        register_protocol(ret);
574}
575
576#define PDIR "/tmp/rpcplugins"
577
578/* YA RLY :-/ */
579#ifndef UNIX_PATH_MAX
580struct sockaddr_un sizecheck;
581#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
582#endif
583
584void rpc_initmodule() {
585        DIR *pdir = opendir(PDIR);
586        struct dirent *de;
587
588        if (!pdir)
589                return;
590
591        while ((de = readdir(pdir))) {
592                char *fn = g_build_filename(PDIR, de->d_name, NULL);
593                struct sockaddr_un su;
594
595                strncpy(su.sun_path, fn, UNIX_PATH_MAX);
596                su.sun_path[UNIX_PATH_MAX-1] = '\0';
597                su.sun_family = AF_UNIX;
598                rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
599                g_free(fn);
600        }
601}
602
Note: See TracBrowser for help on using the repository browser.