source: protocols/rpc/rpc.c @ 6cdecc7

Last change on this file since 6cdecc7 was 6cdecc7, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-02-26T09:57:31Z

Lots of changes. Implemented both to-plugin RPCs though not all complete yet.
Also, some connect and I/O code. It compiles but is very unlikely to actually
work still.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1#include "bitlbee.h"
2#include "bee.h"
3#include "nogaim.h"
4#include "parson.h"
5
6static int next_rpc_id = 1;
7
8struct rpc_plugin {
9        struct sockaddr *addr;
10        socklen_t addrlen;
11};
12
13struct rpc_connection {
14        int fd;
15        char *buf;
16        int buflen;
17};
18
19struct rpc_groupchat {
20        struct groupchat *bee_gc;
21        char *rpc_handle;
22};
23
24static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
25        JSON_Value *rpc = json_value_init_object();
26        json_object_set_string(json_object(rpc), "method", method);
27        json_object_set_number(json_object(rpc), "id", next_rpc_id++);
28
29        JSON_Value *params = json_value_init_array();
30        json_object_set_value(json_object(rpc), "params", params);
31
32        if (params_)
33                *params_ = json_array(params);
34
35        return rpc;
36}
37
38#define RPC_OUT_INIT(method) \
39        JSON_Array *params; \
40        JSON_Value *rpc = rpc_out_new(method, &params);
41
42/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
43static void rpc_send(struct im_connection *ic, JSON_Value *rpc) {
44        char *buf = json_serialize_to_string(rpc);
45
46        g_free(buf);
47        json_value_free(rpc);
48}
49
50static JSON_Value *rpc_ser_account(const account_t *acc) {
51        JSON_Value *v = json_value_init_object();
52        JSON_Object *o = json_object(v);
53        json_object_set_string(o, "user", acc->user);
54        json_object_set_string(o, "pass", acc->user);
55        json_object_set_string(o, "server", acc->server);
56        return v;
57}
58
59static void rpc_init(account_t *acc) {
60        // Add settings. Probably should not RPC at all.
61}
62
63static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
64static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
65
66static void rpc_login(account_t *acc) {
67        struct im_connection *ic = imcb_new(acc);
68        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
69        struct rpc_plugin *pd = acc->prpl->data;
70        rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
71        sock_make_nonblocking(rd->fd);
72        if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
73                closesocket(rd->fd);
74                return;
75        }
76        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
77}
78
79static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
80        struct im_connection *ic = data;
81        struct rpc_connection *rd = ic->proto_data;
82        RPC_OUT_INIT("login");
83        json_array_append_value(params, rpc_ser_account(ic->acc));
84        rpc_send(ic, rpc);
85
86        ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
87
88        return FALSE;
89}
90
91static void rpc_keepalive(struct im_connection *ic) {
92        RPC_OUT_INIT("keepalive");
93        rpc_send(ic, rpc);
94}
95
96static void rpc_logout(struct im_connection *ic) {
97        RPC_OUT_INIT("logout");
98        rpc_send(ic, rpc);
99}
100
101static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
102        RPC_OUT_INIT("buddy_msg");
103        json_array_append_string(params, to);
104        json_array_append_string(params, message);
105        json_array_append_number(params, flags);
106        rpc_send(ic, rpc);
107
108        return 1; // BOGUS
109}
110
111static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
112        RPC_OUT_INIT("set_away");
113        json_array_append_string(params, state);
114        json_array_append_string(params, message);
115        rpc_send(ic, rpc);
116}
117
118static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
119        RPC_OUT_INIT("send_typing");
120        json_array_append_string(params, who);
121        json_array_append_number(params, flags);
122        rpc_send(ic, rpc);
123
124        return 1; // BOGUS
125}
126
127static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
128        RPC_OUT_INIT("add_buddy");
129        json_array_append_string(params, name);
130        json_array_append_string(params, group);
131        rpc_send(ic, rpc);
132}
133
134static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
135        RPC_OUT_INIT("remove_buddy");
136        json_array_append_string(params, name);
137        json_array_append_string(params, group);
138        rpc_send(ic, rpc);
139}
140
141static void rpc_add_permit(struct im_connection *ic, char *who) {
142        RPC_OUT_INIT("add_permit");
143        json_array_append_string(params, who);
144        rpc_send(ic, rpc);
145}
146
147static void rpc_add_deny(struct im_connection *ic, char *who) {
148        RPC_OUT_INIT("add_deny");
149        json_array_append_string(params, who);
150        rpc_send(ic, rpc);
151}
152
153static void rpc_rem_permit(struct im_connection *ic, char *who) {
154        RPC_OUT_INIT("rem_permit");
155        json_array_append_string(params, who);
156        rpc_send(ic, rpc);
157}
158
159static void rpc_rem_deny(struct im_connection *ic, char *who) {
160        RPC_OUT_INIT("rem_deny");
161        json_array_append_string(params, who);
162        rpc_send(ic, rpc);
163}
164
165static void rpc_get_info(struct im_connection *ic, char *who) {
166        RPC_OUT_INIT("get_info");
167        json_array_append_string(params, who);
168        rpc_send(ic, rpc);
169}
170
171static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
172        RPC_OUT_INIT("chat_invite");
173        json_array_append_string(params, who);
174        json_array_append_string(params, message);
175        rpc_send(gc->ic, rpc);
176}
177
178static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
179        RPC_OUT_INIT("chat_kick");
180        json_array_append_string(params, who);
181        json_array_append_string(params, message);
182        rpc_send(gc->ic, rpc);
183}
184
185static void rpc_chat_leave(struct groupchat *gc) {
186        RPC_OUT_INIT("chat_leave");
187        rpc_send(gc->ic, rpc);
188}
189
190static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
191        RPC_OUT_INIT("chat_msg");       
192        struct rpc_groupchat *data = gc->data;
193        json_array_append_string(params, data->rpc_handle);
194        json_array_append_string(params, msg);
195        json_array_append_number(params, flags);
196        rpc_send(gc->ic, rpc);
197}
198
199static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
200        RPC_OUT_INIT("chat_with");
201        json_array_append_string(params, who);
202        rpc_send(ic, rpc);
203
204        return NULL;
205}
206
207static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
208                                       const char *password, set_t **sets) {
209        RPC_OUT_INIT("chat_join");
210        json_array_append_string(params, room);
211        json_array_append_string(params, nick);
212        json_array_append_string(params, password);
213        //json_array_append_value(params, rpc_ser_sets(sets));
214        rpc_send(ic, rpc);
215
216        return NULL;
217}
218
219static void rpc_chat_topic(struct groupchat *gc, char *topic) {
220        RPC_OUT_INIT("chat_topic");
221        json_array_append_string(params, topic);
222        rpc_send(gc->ic, rpc);
223}
224
225static void rpc_cmd_in(const char *cmd, JSON_Array *params) {
226
227}
228
229static void rpc_in(struct im_connection *ic, JSON_Object *rpc) {
230        JSON_Object *res = json_object_get_object(rpc, "result");
231        const char *cmd = json_object_get_string(rpc, "method");
232        JSON_Value *id = json_object_get_value(rpc, "id");
233        JSON_Array *params = json_object_get_array(rpc, "params");
234
235        if ((!cmd && !res) || !id || (cmd && !params)) {
236                imcb_log(ic, "Received invalid JSON-RPC object.");
237                imc_logout(ic, TRUE);
238                return;
239        }
240
241        if (res) {
242                // handle response. I think I mostly won't.
243        } else {
244                rpc_cmd_in(cmd, params);
245        }
246}
247
248static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
249        struct im_connection *ic = data;
250        struct rpc_connection *rd = ic->proto_data;
251        char buf[2048];
252        int st;
253
254        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
255                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
256                memcpy(rd->buf + rd->buflen, buf, st);
257        }
258
259        if (st == 0 || (st == -1 && !sockerr_again())) {
260                imcb_log(ic, "Read error");
261                imc_logout(ic, TRUE);
262                return FALSE;
263        }
264        rd->buf[rd->buflen] = '\0';
265
266        JSON_Value *parsed;
267        const char *end;
268        while ((parsed = json_parse_first(rd->buf, &end))) {
269                rpc_in(ic, json_object(parsed));
270                json_value_free(parsed);
271
272                if (end == rd->buf + rd->buflen) {
273                        g_free(rd->buf);
274                        rd->buf = NULL;
275                } else {
276                        int newlen = rd->buf + rd->buflen - end;
277                        char new[newlen];
278                        memcpy(new, end, newlen);
279                        rd->buf = g_realloc(rd->buf, newlen + 1);
280                        memcpy(rd->buf, new, newlen);
281                        rd->buf[rd->buflen] = '\0';
282                }
283        }
284
285        return TRUE;
286}
287
288static void rpc_imcb_buddy_typing(struct im_connection *ic, const char *cmd, JSON_Array *params) {
289        const char *handle = json_array_get_string(params, 0);
290        int flags = json_array_get_number(params, 1);
291        imcb_buddy_typing(ic, handle, flags);
292}
293
294struct rpc_in_method {
295        char *name;
296        void (* func) (struct im_connection *ic, const char *cmd, JSON_Array *params);
297        char *args;
298};
299
300#define RPC_ADD_FUNC(func) \
301        if (g_hash_table_contains(methods, #func)) \
302                ret->func = rpc_ ## func
303
304void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
305        int st, fd, i;
306
307        fd = socket(address->sa_family, SOCK_STREAM, 0);
308        if (fd == -1 || connect(fd, address, addrlen) == -1)
309                return;
310
311        RPC_OUT_INIT("init");
312        JSON_Value *d = json_value_init_object();
313        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
314        json_object_set_number(json_object(d), "version", BITLBEE_VERSION_CODE);
315        json_array_append_value(params, d);
316        char *s = json_serialize_to_string(rpc);
317
318        if ((st = write(fd, s, strlen(s))) != strlen(s)) {
319                // LOG ERROR
320                return;
321        }
322        g_free(s);
323
324        char *resp = NULL;
325        int buflen = 4096, resplen = 0;
326        JSON_Value *parsed;
327        do {
328                fd_set rfds;
329                struct timeval to;
330
331                FD_ZERO(&rfds);
332                FD_SET(fd, &rfds);
333                to.tv_sec = 1;
334                to.tv_usec = 0;
335                st = select(fd + 1, &rfds, NULL, NULL, &to);
336
337                if (st == 0) {
338                        // LOG ERROR
339                        return;
340                }
341               
342                if (resplen >= buflen)
343                        buflen *= 2;
344                resp = g_realloc(resp, buflen + 1);
345                st = read(fd, resp + resplen, buflen - resplen);
346                if (st == -1) {
347                        if (sockerr_again())
348                                continue;
349                        // LOG ERROR
350                        return;
351                }
352                resplen += st;
353                resp[resplen] = '\0';
354        }
355        while (!(parsed = json_parse_string(resp)));
356
357        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
358        if (isup == NULL) {
359                // LOG ERROR
360                return;
361        }
362
363        struct prpl *ret = g_new0(struct prpl, 1);
364       
365        JSON_Array *methods_a = json_object_get_array(isup, "methods");
366        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
367        for (i = 0; i < json_array_get_count(methods_a); i++)
368                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
369
370        ret->init = rpc_init;
371        RPC_ADD_FUNC(login);
372        RPC_ADD_FUNC(keepalive);
373        RPC_ADD_FUNC(logout);
374        RPC_ADD_FUNC(buddy_msg);
375        RPC_ADD_FUNC(set_away);
376        RPC_ADD_FUNC(send_typing);
377        RPC_ADD_FUNC(add_buddy);
378        RPC_ADD_FUNC(remove_buddy);
379        RPC_ADD_FUNC(add_permit);
380        RPC_ADD_FUNC(add_deny);
381        RPC_ADD_FUNC(rem_permit);
382        RPC_ADD_FUNC(rem_deny);
383        RPC_ADD_FUNC(get_info);
384        RPC_ADD_FUNC(chat_invite);
385        RPC_ADD_FUNC(chat_kick);
386        RPC_ADD_FUNC(chat_leave);
387        RPC_ADD_FUNC(chat_msg);
388        RPC_ADD_FUNC(chat_with);
389        RPC_ADD_FUNC(chat_join);
390        RPC_ADD_FUNC(chat_topic);
391       
392        g_hash_table_destroy(methods);
393
394        // TODO: Property for a few standard nickcmp implementations.
395       
396        JSON_Array *settings = json_object_get_array(isup, "settings");
397        for (i = 0; i < json_array_get_count(settings); i++) {
398                //JSON_Object *set = json_array_get_object(settings, i);
399                // set..name, set..type, set..default, set..flags ?
400        }
401
402        ret->name = g_strdup(json_object_get_string(isup, "name"));
403
404        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
405        proto_data->addr = g_memdup(address, addrlen);
406        proto_data->addrlen = addrlen;
407        ret->data = proto_data;
408
409        register_protocol(ret);
410}
411
412void rpc_initmodule() {
413}
414
Note: See TracBrowser for help on using the repository browser.