source: protocols/rpc/rpc.c @ 66aefeb

Last change on this file since 66aefeb was 66aefeb, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-02-27T00:54:47Z

init works. Still very rough otherwise, and next to no callbacks into
BitlBee implemented yet.

  • Property mode set to 100644
File size: 12.8 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        rpc_send(ic, rpc);
121
122        struct rpc_connection *rd = ic->proto_data;
123        b_event_remove(ic->inpa);
124        closesocket(rd->fd);
125        g_free(rd->buf);
126        g_hash_table_destroy(rd->groupchats);
127        g_free(rd);
128}
129
130static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
131        RPC_OUT_INIT("buddy_msg");
132        json_array_append_string(params, to);
133        json_array_append_string(params, message);
134        json_array_append_number(params, flags);
135        rpc_send(ic, rpc);
136
137        return 1; // BOGUS
138}
139
140static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
141        RPC_OUT_INIT("set_away");
142        json_array_append_string(params, state);
143        json_array_append_string(params, message);
144        rpc_send(ic, rpc);
145}
146
147static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
148        RPC_OUT_INIT("send_typing");
149        json_array_append_string(params, who);
150        json_array_append_number(params, flags);
151        rpc_send(ic, rpc);
152
153        return 1; // BOGUS
154}
155
156static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
157        RPC_OUT_INIT("add_buddy");
158        json_array_append_string(params, name);
159        json_array_append_string(params, group);
160        rpc_send(ic, rpc);
161}
162
163static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
164        RPC_OUT_INIT("remove_buddy");
165        json_array_append_string(params, name);
166        json_array_append_string(params, group);
167        rpc_send(ic, rpc);
168}
169
170static void rpc_add_permit(struct im_connection *ic, char *who) {
171        RPC_OUT_INIT("add_permit");
172        json_array_append_string(params, who);
173        rpc_send(ic, rpc);
174}
175
176static void rpc_add_deny(struct im_connection *ic, char *who) {
177        RPC_OUT_INIT("add_deny");
178        json_array_append_string(params, who);
179        rpc_send(ic, rpc);
180}
181
182static void rpc_rem_permit(struct im_connection *ic, char *who) {
183        RPC_OUT_INIT("rem_permit");
184        json_array_append_string(params, who);
185        rpc_send(ic, rpc);
186}
187
188static void rpc_rem_deny(struct im_connection *ic, char *who) {
189        RPC_OUT_INIT("rem_deny");
190        json_array_append_string(params, who);
191        rpc_send(ic, rpc);
192}
193
194static void rpc_get_info(struct im_connection *ic, char *who) {
195        RPC_OUT_INIT("get_info");
196        json_array_append_string(params, who);
197        rpc_send(ic, rpc);
198}
199
200static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
201        RPC_OUT_INIT("chat_invite");
202        struct rpc_groupchat *rc = gc->data;
203        json_array_append_number(params, rc->id);
204        json_array_append_string(params, who);
205        json_array_append_string(params, message);
206        rpc_send(gc->ic, rpc);
207}
208
209static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
210        RPC_OUT_INIT("chat_kick");
211        struct rpc_groupchat *rc = gc->data;
212        json_array_append_number(params, rc->id);
213        json_array_append_string(params, who);
214        json_array_append_string(params, message);
215        rpc_send(gc->ic, rpc);
216}
217
218static void rpc_chat_leave(struct groupchat *gc) {
219        RPC_OUT_INIT("chat_leave");
220        struct rpc_groupchat *rc = gc->data;
221        json_array_append_number(params, rc->id);
222        rpc_send(gc->ic, rpc);
223}
224
225static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
226        RPC_OUT_INIT("chat_msg");       
227        struct rpc_groupchat *rc = gc->data;
228        json_array_append_number(params, rc->id);
229        json_array_append_string(params, msg);
230        json_array_append_number(params, flags);
231        rpc_send(gc->ic, rpc);
232}
233
234static struct groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
235        struct groupchat *gc = imcb_chat_new(ic, handle);
236        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
237        rc->id = next_rpc_id;
238        rc->gc = gc;
239        return gc;
240}
241
242static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
243        RPC_OUT_INIT("chat_with");
244        struct groupchat *gc = rpc_groupchat_new(ic, who);
245        struct rpc_groupchat *rc = gc->data;
246        json_array_append_number(params, rc->id);
247        json_array_append_string(params, who);
248        rpc_send(ic, rpc);
249
250        return NULL;
251}
252
253static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
254                                       const char *password, set_t **sets) {
255        RPC_OUT_INIT("chat_join");
256        struct groupchat *gc = rpc_groupchat_new(ic, room);
257        struct rpc_groupchat *rc = gc->data;
258        json_array_append_number(params, rc->id);
259        json_array_append_string(params, room);
260        json_array_append_string(params, nick);
261        json_array_append_string(params, password);
262        //json_array_append_value(params, rpc_ser_sets(sets));
263        rpc_send(ic, rpc);
264
265        return NULL;
266}
267
268static void rpc_chat_topic(struct groupchat *gc, char *topic) {
269        RPC_OUT_INIT("chat_topic");
270        struct rpc_groupchat *rc = gc->data;
271        json_array_append_number(params, rc->id);
272        json_array_append_string(params, topic);
273        rpc_send(gc->ic, rpc);
274}
275
276static void rpc_cmd_in(const char *cmd, JSON_Array *params) {
277
278}
279
280static void rpc_in(struct im_connection *ic, JSON_Object *rpc) {
281        JSON_Object *res = json_object_get_object(rpc, "result");
282        const char *cmd = json_object_get_string(rpc, "method");
283        JSON_Value *id = json_object_get_value(rpc, "id");
284        JSON_Array *params = json_object_get_array(rpc, "params");
285
286        if ((!cmd && !res) || !id || (cmd && !params)) {
287                imcb_log(ic, "Received invalid JSON-RPC object.");
288                imc_logout(ic, TRUE);
289                return;
290        }
291
292        if (res) {
293                // handle response. I think I mostly won't.
294        } else {
295                rpc_cmd_in(cmd, params);
296        }
297}
298
299static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
300        struct im_connection *ic = data;
301        struct rpc_connection *rd = ic->proto_data;
302        char buf[2048];
303        int st;
304
305        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
306                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
307                memcpy(rd->buf + rd->buflen, buf, st);
308        }
309
310        if (st == 0 || (st == -1 && !sockerr_again())) {
311                imcb_log(ic, "Read error");
312                imc_logout(ic, TRUE);
313                return FALSE;
314        }
315        rd->buf[rd->buflen] = '\0';
316
317        JSON_Value *parsed;
318        const char *end;
319        while ((parsed = json_parse_first(rd->buf, &end))) {
320                rpc_in(ic, json_object(parsed));
321                json_value_free(parsed);
322
323                if (end == rd->buf + rd->buflen) {
324                        g_free(rd->buf);
325                        rd->buf = NULL;
326                } else {
327                        int newlen = rd->buf + rd->buflen - end;
328                        char new[newlen];
329                        memcpy(new, end, newlen);
330                        rd->buf = g_realloc(rd->buf, newlen + 1);
331                        memcpy(rd->buf, new, newlen);
332                        rd->buf[rd->buflen] = '\0';
333                }
334        }
335
336        return TRUE;
337}
338
339static void rpc_imcb_buddy_typing(struct im_connection *ic, const char *cmd, JSON_Array *params) {
340        const char *handle = json_array_get_string(params, 0);
341        int flags = json_array_get_number(params, 1);
342        imcb_buddy_typing(ic, handle, flags);
343}
344
345struct rpc_in_method {
346        char *name;
347        void (* func) (struct im_connection *ic, const char *cmd, JSON_Array *params);
348        char *args;
349};
350
351#define RPC_ADD_FUNC(func) \
352        if (g_hash_table_contains(methods, #func)) \
353                ret->func = rpc_ ## func
354
355void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
356        int st, fd, i;
357
358        fd = socket(address->sa_family, SOCK_STREAM, 0);
359        if (fd == -1 || connect(fd, address, addrlen) == -1)
360                return;
361
362        RPC_OUT_INIT("init");
363        JSON_Value *d = json_value_init_object();
364        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
365        json_object_set_number(json_object(d), "version", BITLBEE_VERSION_CODE);
366        json_array_append_value(params, d);
367        char *s = json_serialize_to_string(rpc);
368        int len = strlen(s);
369        s = g_realloc(s, len + 3);
370        strcpy(s + len, "\r\n");
371        len += 2;
372
373        if ((st = write(fd, s, len)) != len) {
374                // LOG ERROR
375                return;
376        }
377        g_free(s);
378
379        char *resp = NULL;
380        int buflen = 4096, resplen = 0;
381        JSON_Value *parsed;
382        do {
383                fd_set rfds;
384                struct timeval to;
385
386                FD_ZERO(&rfds);
387                FD_SET(fd, &rfds);
388                to.tv_sec = 1;
389                to.tv_usec = 0;
390                st = select(fd + 1, &rfds, NULL, NULL, &to);
391
392                if (st == 0) {
393                        // LOG ERROR
394                        closesocket(fd);
395                        return;
396                }
397               
398                if (resplen >= buflen)
399                        buflen *= 2;
400                resp = g_realloc(resp, buflen + 1);
401                st = read(fd, resp + resplen, buflen - resplen);
402                if (st == -1) {
403                        if (sockerr_again())
404                                continue;
405                        // LOG ERROR
406                        closesocket(fd);
407                        return;
408                }
409                resplen += st;
410                resp[resplen] = '\0';
411        }
412        while (!(parsed = json_parse_string(resp)));
413        closesocket(fd);
414
415        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
416        if (isup == NULL) {
417                // LOG ERROR
418                return;
419        }
420
421        struct prpl *ret = g_new0(struct prpl, 1);
422       
423        JSON_Array *methods_a = json_object_get_array(isup, "methods");
424        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
425        for (i = 0; i < json_array_get_count(methods_a); i++)
426                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
427
428        ret->init = rpc_init;
429        RPC_ADD_FUNC(login);
430        RPC_ADD_FUNC(keepalive);
431        RPC_ADD_FUNC(logout);
432        RPC_ADD_FUNC(buddy_msg);
433        RPC_ADD_FUNC(set_away);
434        RPC_ADD_FUNC(send_typing);
435        RPC_ADD_FUNC(add_buddy);
436        RPC_ADD_FUNC(remove_buddy);
437        RPC_ADD_FUNC(add_permit);
438        RPC_ADD_FUNC(add_deny);
439        RPC_ADD_FUNC(rem_permit);
440        RPC_ADD_FUNC(rem_deny);
441        RPC_ADD_FUNC(get_info);
442        RPC_ADD_FUNC(chat_invite);
443        RPC_ADD_FUNC(chat_kick);
444        RPC_ADD_FUNC(chat_leave);
445        RPC_ADD_FUNC(chat_msg);
446        RPC_ADD_FUNC(chat_with);
447        RPC_ADD_FUNC(chat_join);
448        RPC_ADD_FUNC(chat_topic);
449       
450        g_hash_table_destroy(methods);
451
452        // TODO: Property for a few standard nickcmp implementations.
453       
454        JSON_Array *settings = json_object_get_array(isup, "settings");
455        for (i = 0; i < json_array_get_count(settings); i++) {
456                //JSON_Object *set = json_array_get_object(settings, i);
457                // set..name, set..type, set..default, set..flags ?
458        }
459
460        ret->name = g_strdup(json_object_get_string(isup, "name"));
461
462        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
463        proto_data->addr = g_memdup(address, addrlen);
464        proto_data->addrlen = addrlen;
465        ret->data = proto_data;
466
467        register_protocol(ret);
468}
469
470#define PDIR "/tmp/rpcplugins"
471
472/* YA RLY :-/ */
473#ifndef UNIX_PATH_MAX
474struct sockaddr_un sizecheck;
475#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
476#endif
477
478void rpc_initmodule() {
479        DIR *pdir = opendir(PDIR);
480        struct dirent *de;
481
482        if (!pdir)
483                return;
484
485        while ((de = readdir(pdir))) {
486                char *fn = g_build_filename(PDIR, de->d_name, NULL);
487                struct sockaddr_un su;
488
489                strncpy(su.sun_path, fn, UNIX_PATH_MAX);
490                su.sun_path[UNIX_PATH_MAX-1] = '\0';
491                su.sun_family = AF_UNIX;
492                rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
493                g_free(fn);
494        }
495}
496
Note: See TracBrowser for help on using the repository browser.