source: protocols/rpc/rpc.c @ 16652e9

Last change on this file since 16652e9 was 16652e9, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-04-06T01:12:29Z

More improvements: Settings, methods list in init.

  • Property mode set to 100644
File size: 23.7 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        struct sockaddr *addr;
17        socklen_t addrlen;
18        JSON_Value *settings;
19};
20
21struct rpc_connection {
22        int fd;
23        char *buf;
24        int buflen;
25        GHashTable *groupchats;
26};
27
28struct rpc_groupchat {
29        int id;
30        struct groupchat *gc;
31};
32
33static JSON_Value *jsonrpc_error(int code, const char *msg) {
34        JSON_Value *ret = json_value_init_object();
35        json_object_set_null(json_object(ret), "result");
36        if (TRUE) {
37                /* Format from http://jsonrpc.org/historical/json-rpc-1-1-alt.html.
38                 * Not sure whether to use it. */
39                JSON_Value *error = json_value_init_object();
40                json_object_set_number(json_object(error), "code", code);
41                json_object_set_string(json_object(error), "message", msg);
42                json_object_set_value(json_object(ret), "error", error);
43        } else {
44                json_object_set_string(json_object(ret), "error", msg);
45        }
46       
47        return ret;
48}
49
50// Might have liked to have this one in the library for optional values/etc.
51static void json_array_append_string_or_null(JSON_Array *array, const char *string) {
52        if (string)
53                json_array_append_string(array, string);
54        else
55                json_array_append_null(array);
56}
57
58static void json_object_set_string_or_null(JSON_Object *object, const char *key, const char *string) {
59        if (string)
60                json_object_set_string(object, key, string);
61        else
62                json_object_set_null(object, key);
63}
64
65static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
66        JSON_Value *rpc = json_value_init_object();
67        json_object_set_string(json_object(rpc), "method", method);
68        json_object_set_number(json_object(rpc), "id", next_rpc_id++);
69
70        JSON_Value *params = json_value_init_array();
71        json_object_set_value(json_object(rpc), "params", params);
72
73        if (params_)
74                *params_ = json_array(params);
75
76        return rpc;
77}
78
79#define RPC_OUT_INIT(method) \
80        JSON_Array *params; \
81        JSON_Value *rpc = rpc_out_new(method, &params);
82
83/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
84static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
85        struct rpc_connection *rd = ic->proto_data;
86        char *buf = json_serialize_to_string(rpc);
87        int len = strlen(buf);
88        int st;
89
90        buf = g_realloc(buf, len + 3);
91        strcpy(buf + len, "\r\n");
92        len += 2;
93
94        st = write(rd->fd, buf, len);
95        g_free(buf);
96        json_value_free(rpc);
97
98        if (st != len) {
99                if (!(ic->flags & OPT_LOGGING_OUT))
100                        imcb_log(ic, "Write error");
101                imc_logout(ic, TRUE);
102                return FALSE;
103        }
104
105        return TRUE;
106}
107
108static JSON_Value *rpc_ser_settings(set_t **set) {
109        const set_t *s;
110        JSON_Value *ret = json_value_init_object();
111       
112        for (s = *set; s; s = s->next) {
113                json_object_set_string_or_null(json_object(ret), s->key, set_getstr(set, s->key));
114        }
115
116        return ret;
117}
118
119static JSON_Value *rpc_ser_account(account_t *acc) {
120        JSON_Value *v = json_value_init_object();
121        JSON_Object *o = json_object(v);
122        json_object_set_string(o, "user", acc->user);
123        json_object_set_string(o, "pass", acc->user);
124        if (acc->server)
125                json_object_set_string(o, "server", acc->server);
126        json_object_set_value(o, "settings", rpc_ser_settings(&acc->set));
127        return v;
128}
129
130static void rpc_init(account_t *acc) {
131        struct rpc_plugin *pd = acc->prpl->data;
132
133        JSON_O_FOREACH(json_object(pd->settings), name, value) {
134                JSON_Object *o = json_object(value);
135                set_t *set = set_add(&acc->set, name, json_object_get_string(o, "default"), NULL, acc);
136        }
137}
138
139static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
140static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
141static JSON_Value *rpc_init_isup();
142
143static void rpc_login(account_t *acc) {
144        struct im_connection *ic = imcb_new(acc);
145        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
146        struct rpc_plugin *pd = acc->prpl->data;
147        imcb_log(ic, "Connecting to RPC server");
148        rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
149        sock_make_nonblocking(rd->fd);
150        if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
151                closesocket(rd->fd);
152                return;
153        }
154        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
155        rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
156}
157
158static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
159        struct im_connection *ic = data;
160        struct rpc_connection *rd = ic->proto_data;
161
162        /* Need to repeat this since each IM connection means an actual new
163         * RPC session. */
164        JSON_Value *init = rpc_init_isup();
165        if (!rpc_send(ic, init))
166                return FALSE;
167
168        RPC_OUT_INIT("login");
169        json_array_append_value(params, rpc_ser_account(ic->acc));
170        if (!rpc_send(ic, rpc))
171                return FALSE;
172
173        ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
174
175        return FALSE;
176}
177
178static void rpc_keepalive(struct im_connection *ic) {
179        RPC_OUT_INIT("keepalive");
180        rpc_send(ic, rpc);
181}
182
183static void rpc_logout(struct im_connection *ic) {
184        RPC_OUT_INIT("logout");
185        if (!rpc_send(ic, rpc))
186                return;
187
188        struct rpc_connection *rd = ic->proto_data;
189        b_event_remove(ic->inpa);
190        closesocket(rd->fd);
191        g_free(rd->buf);
192        g_hash_table_destroy(rd->groupchats);
193        g_free(rd);
194}
195
196static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
197        RPC_OUT_INIT("buddy_msg");
198        json_array_append_string(params, to);
199        json_array_append_string(params, message);
200        json_array_append_number(params, flags);
201        return rpc_send(ic, rpc);
202}
203
204static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
205        RPC_OUT_INIT("set_away");
206        json_array_append_string_or_null(params, state);
207        json_array_append_string_or_null(params, message);
208        rpc_send(ic, rpc);
209}
210
211static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
212        RPC_OUT_INIT("send_typing");
213        json_array_append_string(params, who);
214        json_array_append_number(params, flags);
215        return rpc_send(ic, rpc);
216}
217
218static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
219        RPC_OUT_INIT("add_buddy");
220        json_array_append_string(params, name);
221        json_array_append_string_or_null(params, group);
222        rpc_send(ic, rpc);
223}
224
225static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
226        RPC_OUT_INIT("remove_buddy");
227        json_array_append_string(params, name);
228        json_array_append_string_or_null(params, group);
229        rpc_send(ic, rpc);
230}
231
232static void rpc_add_permit(struct im_connection *ic, char *who) {
233        RPC_OUT_INIT("add_permit");
234        json_array_append_string(params, who);
235        rpc_send(ic, rpc);
236}
237
238static void rpc_add_deny(struct im_connection *ic, char *who) {
239        RPC_OUT_INIT("add_deny");
240        json_array_append_string(params, who);
241        rpc_send(ic, rpc);
242}
243
244static void rpc_rem_permit(struct im_connection *ic, char *who) {
245        RPC_OUT_INIT("rem_permit");
246        json_array_append_string(params, who);
247        rpc_send(ic, rpc);
248}
249
250static void rpc_rem_deny(struct im_connection *ic, char *who) {
251        RPC_OUT_INIT("rem_deny");
252        json_array_append_string(params, who);
253        rpc_send(ic, rpc);
254}
255
256static void rpc_get_info(struct im_connection *ic, char *who) {
257        RPC_OUT_INIT("get_info");
258        json_array_append_string(params, who);
259        rpc_send(ic, rpc);
260}
261
262static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
263        RPC_OUT_INIT("chat_invite");
264        struct rpc_groupchat *rc = gc->data;
265        json_array_append_number(params, rc->id);
266        json_array_append_string(params, who);
267        json_array_append_string_or_null(params, message);
268        rpc_send(gc->ic, rpc);
269}
270
271static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
272        RPC_OUT_INIT("chat_kick");
273        struct rpc_groupchat *rc = gc->data;
274        json_array_append_number(params, rc->id);
275        json_array_append_string(params, who);
276        json_array_append_string_or_null(params, message);
277        rpc_send(gc->ic, rpc);
278}
279
280static void rpc_chat_leave(struct groupchat *gc) {
281        RPC_OUT_INIT("chat_leave");
282        struct rpc_groupchat *rc = gc->data;
283        json_array_append_number(params, rc->id);
284        rpc_send(gc->ic, rpc);
285}
286
287static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
288        RPC_OUT_INIT("chat_msg");       
289        struct rpc_groupchat *rc = gc->data;
290        json_array_append_number(params, rc->id);
291        json_array_append_string(params, msg);
292        json_array_append_number(params, flags);
293        rpc_send(gc->ic, rpc);
294}
295
296static struct rpc_groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
297        struct rpc_connection *rd = ic->proto_data;
298        struct groupchat *gc = imcb_chat_new(ic, handle);
299        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
300        rc->id = next_rpc_id++;
301        rc->gc = gc;
302        g_hash_table_insert(rd->groupchats, &rc->id, rc);
303        return rc;  // TODO: RETVAL HERE AND BELOW
304}
305
306static struct rpc_groupchat *rpc_groupchat_by_id(struct im_connection *ic, int id) {
307        struct rpc_connection *rd = ic->proto_data;
308        struct rpc_groupchat *rc = g_hash_table_lookup(rd->groupchats, &id);
309
310        return rc;
311}
312
313/* Boilerplate for all incoming RPCs (where groupchat is identified using
314 * numeric ID). */
315#define SET_GROUPCHAT(rc) \
316        do { \
317                rc = rpc_groupchat_by_id(ic, json_array_get_number(params, 0)); \
318                if (rc == NULL) \
319                        return jsonrpc_error(ENOENT, "No groupchat with that id."); \
320        } while (0)
321
322static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
323        RPC_OUT_INIT("chat_with");
324        struct rpc_groupchat *rc = rpc_groupchat_new(ic, who);
325        json_array_append_number(params, rc->id);
326        json_array_append_string(params, who);
327        rpc_send(ic, rpc);
328
329        return rc->gc; 
330}
331
332static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
333                                       const char *password, set_t **sets) {
334        RPC_OUT_INIT("chat_join");
335        struct rpc_groupchat *rc = rpc_groupchat_new(ic, room);
336        json_array_append_number(params, rc->id);
337        json_array_append_string(params, room);
338        json_array_append_string_or_null(params, nick);
339        json_array_append_string_or_null(params, password);
340        json_array_append_value(params, rpc_ser_settings(sets));
341        rpc_send(ic, rpc);
342
343        return rc->gc;
344}
345
346static void rpc_chat_topic(struct groupchat *gc, char *topic) {
347        RPC_OUT_INIT("chat_topic");
348        struct rpc_groupchat *rc = gc->data;
349        json_array_append_number(params, rc->id);
350        json_array_append_string(params, topic);
351        rpc_send(gc->ic, rpc);
352}
353
354static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
355
356static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
357        const char *cmd = json_object_get_string(rpc, "method");
358        JSON_Value *id = json_object_get_value(rpc, "id");
359        JSON_Value *error = json_object_get_value(rpc, "error");
360        JSON_Array *params = json_object_get_array(rpc, "params");
361
362        /* Removed checks for result/error/etc. as it's all too free-form and
363         * at least for now this code is not going to care about retvals as
364         * they come in late anyway. */
365        if (!id) {
366                imcb_log(ic, "Received invalid JSON-RPC object.");
367                imc_logout(ic, TRUE);
368                return FALSE;
369        }
370
371        if (cmd) {
372                JSON_Value *resp = rpc_cmd_in(ic, cmd, params);
373                if (!resp) {
374                        resp = json_value_init_object();
375                        json_object_set_boolean(json_object(resp), "result", TRUE);
376                }
377                json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
378                return rpc_send(ic, resp);
379        } else if (error && json_type(error) != JSONNull) {
380                char *error_str = json_serialize_to_string(error);
381                /* Maybe sanitise/truncate? Though really that should be done at
382                 * a different layer. */
383                imcb_error(ic, "RPC Error: %s", error_str);
384                g_free(error_str);
385        }
386
387        return TRUE;
388}
389
390static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
391        struct im_connection *ic = data;
392        struct rpc_connection *rd = ic->proto_data;
393        char buf[2048];
394        int st;
395
396        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
397                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
398                memcpy(rd->buf + rd->buflen, buf, st);
399                rd->buflen += st;
400        }
401
402        if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
403                imcb_log(ic, "Lost RPC connection");
404                imc_logout(ic, TRUE);
405                return FALSE;
406        }
407        rd->buf[rd->buflen] = '\0';
408
409        JSON_Value *parsed;
410        const char *end;
411        while ((parsed = json_parse_first(rd->buf, &end))) {
412                st = rpc_in(ic, json_object(parsed));
413                json_value_free(parsed);
414
415                if (!st)
416                        return FALSE;
417
418                if (end == rd->buf + rd->buflen) {
419                        g_free(rd->buf);
420                        rd->buf = NULL;
421                } else {
422                        int newlen = rd->buf + rd->buflen - end;
423                        char new[newlen];
424                        memcpy(new, end, newlen);
425                        rd->buf = g_realloc(rd->buf, newlen + 1);
426                        memcpy(rd->buf, new, newlen);
427                        rd->buflen = newlen;
428                        rd->buf[rd->buflen] = '\0';
429                }
430        }
431
432        return TRUE;
433}
434
435static JSON_Value *rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
436        void (*func)(struct im_connection*, const char*, ...) = func_;
437        func(ic, "%s", json_array_get_string(params, 0));
438        return NULL;
439}
440
441static JSON_Value *rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
442        void (*func)(struct im_connection*) = func_;
443        func(ic);
444        return NULL;
445}
446
447static JSON_Value *rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
448        void (*func)(struct im_connection*, gboolean) = func_;
449        func(ic, json_array_get_boolean(params, 0));
450        return NULL;
451}
452
453static JSON_Value *rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
454        void (*func)(struct im_connection*, const char*, const char*) = func_;
455        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1));
456        return NULL;
457}
458
459static JSON_Value *rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
460        void (*func)(struct im_connection*, const char*, int, const char*, const char*) = func_;
461        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
462                 json_array_get_string(params, 2), json_array_get_string(params, 3));
463        return NULL;
464}
465
466static JSON_Value *rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
467        void (*func)(struct im_connection*, const char*, int, int) = func_;
468        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
469                 json_array_get_number(params, 2));
470        return NULL;
471}
472
473static JSON_Value *rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
474        void (*func)(struct im_connection*, const char*, const char*, int, int) = func_;
475        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1),
476                 json_array_get_number(params, 2), json_array_get_number(params, 3));
477        return NULL;
478}
479
480static JSON_Value *rpc_imcb_buddy_typing(struct im_connection *ic, void *func_, JSON_Array *params) {
481        void (*func)(struct im_connection*, const char*, int) = func_;
482        func(ic, (char*) json_array_get_string(params, 0), json_array_get_number(params, 1));
483        return NULL;
484}
485
486static JSON_Value *rpc_imcb_chat_new(struct im_connection *ic, void *func_, JSON_Array *params) {
487        struct rpc_groupchat *rc = rpc_groupchat_new(ic, json_array_get_string(params, 0));
488        JSON_Value *resp = json_value_init_object();
489        json_object_set_number(json_object(resp), "result", rc->id);
490        return resp;
491}
492
493static JSON_Value *rpc_imcb_chat_name_hint(struct im_connection *ic, void *func_, JSON_Array *params) {
494        void (*func)(struct groupchat*, const char*) = func_;
495        struct rpc_groupchat *rc;
496        SET_GROUPCHAT(rc);
497        func(rc->gc, json_array_get_string(params, 1));
498        return NULL;
499}
500
501static JSON_Value *rpc_imcb_chat_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
502        void (*func)(struct groupchat*, const char*, const char*, guint32, time_t) = func_;
503        struct rpc_groupchat *rc;
504        SET_GROUPCHAT(rc);
505        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
506             json_array_get_number(params, 3), json_array_get_number(params, 4));
507        return NULL;
508}
509
510static JSON_Value *rpc_imcb_chat_log(struct im_connection *ic, void *func_, JSON_Array *params) {
511        void (*func)(struct groupchat*, const char*, ...) = func_;
512        struct rpc_groupchat *rc;
513        SET_GROUPCHAT(rc);
514        func(rc->gc, "%s", json_array_get_string(params, 1));
515        return NULL;
516}
517
518static JSON_Value *rpc_imcb_chat_topic(struct im_connection *ic, void *func_, JSON_Array *params) {
519        void (*func)(struct groupchat*, const char*, const char*, time_t) = func_;
520        struct rpc_groupchat *rc;
521        SET_GROUPCHAT(rc);
522        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
523             json_array_get_number(params, 3));
524        return NULL;
525}
526
527static JSON_Value *rpc_imcb_chat_remove_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
528        void (*func)(struct groupchat*, const char*, const char*) = func_;
529        struct rpc_groupchat *rc;
530        SET_GROUPCHAT(rc);
531        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2));
532        return NULL;
533}
534
535static JSON_Value *rpc_imcb_chat_invite(struct im_connection *ic, void *func_, JSON_Array *params) {
536        void (*func)(struct groupchat*, const char*, const char*, const char*) = func_;
537        struct rpc_groupchat *rc;
538        SET_GROUPCHAT(rc);
539        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
540             json_array_get_string(params, 3));
541        return NULL;
542}
543
544struct rpc_in_method {
545        char *name;
546        void *func;
547        JSON_Value* (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
548        char args[8];
549};
550
551static const struct rpc_in_method methods[] = {
552        /* All these RPCs are equivalent of BitlBee C functions but with the
553         * struct im_connection* removed. */
554        { "imcb_log", imcb_log, rpc_imcb_log, "s" },
555        { "imcb_error", imcb_error, rpc_imcb_log, "s" },
556        { "imcb_connected", imcb_connected, rpc_imcb_connected, "" },
557        { "imc_logout", imc_logout, rpc_imc_logout, "b" },
558        { "imcb_add_buddy", imcb_add_buddy, rpc_imcb_add_buddy, "ss" },
559        { "imcb_remove_buddy", imcb_remove_buddy, rpc_imcb_add_buddy, "ss" },
560        { "imcb_rename_buddy", imcb_rename_buddy, rpc_imcb_add_buddy, "ss" },
561        { "imcb_buddy_nick_hint", imcb_buddy_nick_hint, rpc_imcb_add_buddy, "ss" },
562        { "imcb_buddy_status", imcb_buddy_status, rpc_imcb_buddy_status, "snss" },
563        { "imcb_buddy_status_msg", imcb_buddy_status_msg, rpc_imcb_add_buddy, "ss" },
564        { "imcb_buddy_times", imcb_buddy_times, rpc_imcb_buddy_times, "snn" },
565        { "imcb_buddy_msg", imcb_buddy_msg, rpc_imcb_buddy_msg, "ssnn" },
566        { "imcb_buddy_typing", imcb_buddy_typing, rpc_imcb_buddy_typing, "sn" },
567        { "imcb_chat_new", NULL, rpc_imcb_chat_new, "s" },
568       
569        /* RPCs below are equivalent, but with the struct groupchat* replaced
570         * with the numeric id of the chat. */
571        { "imcb_chat_name_hint", imcb_chat_name_hint, rpc_imcb_chat_name_hint, "ns" },
572        { "imcb_chat_msg", imcb_chat_msg, rpc_imcb_chat_msg, "nssnn" },
573        { "imcb_chat_log", imcb_chat_log, rpc_imcb_chat_log, "ns" },
574        { "imcb_chat_topic", imcb_chat_topic, rpc_imcb_chat_topic, "nssn" },
575        { "imcb_chat_add_buddy", imcb_chat_add_buddy, rpc_imcb_chat_name_hint, "ns" },
576        { "imcb_chat_remove_buddy", imcb_chat_remove_buddy, rpc_imcb_chat_remove_buddy, "nss" },
577        { "imcb_chat_invite", imcb_chat_invite, rpc_imcb_chat_invite, "nsss" },
578
579        { NULL },
580};
581
582static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
583        int i;
584
585        for (i = 0; methods[i].name; i++) {
586                if (strcmp(cmd, methods[i].name) == 0) {
587                        if (json_array_get_count(params) != strlen(methods[i].args)) {
588                                imcb_error(ic, "Invalid argument count to method %s: %d, wanted %zd", cmd, (int) json_array_get_count(params), strlen(methods[i].args));
589                                return jsonrpc_error(E2BIG, "Invalid number of arguments");
590                        }
591                        int j;
592                        for (j = 0; methods[i].args[j]; j++) {
593                                JSON_Value_Type type = json_value_get_type(json_array_get_value(params, j));
594                                gboolean ok = FALSE;
595                                switch (methods[i].args[j]) {
596                                case 's':
597                                        ok = type == JSONString || type == JSONNull;
598                                        break;
599                                case 'n':
600                                        ok = type == JSONNumber;
601                                        break;
602                                case 'o':
603                                        ok = type == JSONObject;
604                                        break;
605                                case 'a':
606                                        ok = type == JSONArray;
607                                        break;
608                                case 'b':
609                                        ok = type == JSONBoolean;
610                                        break;
611                                }
612                                if (!ok) {
613                                        // This error sucks, but just get your types right!
614                                        imcb_error(ic, "Invalid argument type, %s parameter %d: %d not %c", cmd, j, type, methods[i].args[j]);
615                                        return jsonrpc_error(EINVAL, "Invalid argument type");
616                                }
617                        }
618                        return methods[i].wfunc(ic, methods[i].func, params);
619                }
620        }
621        return jsonrpc_error(ENOSYS, "Function not implemented");
622}
623
624#define RPC_ADD_FUNC(func) \
625        if (g_hash_table_contains(methods, #func)) \
626                ret->func = rpc_ ## func
627
628static JSON_Value *rpc_init_isup() {
629        int i;
630
631        RPC_OUT_INIT("init");
632        JSON_Value *d = json_value_init_object();
633        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
634        json_object_set_number(json_object(d), "version", BITLBEE_VERSION_CODE);
635       
636        JSON_Value *ml = json_value_init_array();
637        for (i = 0; methods[i].name; i++) {
638                json_array_append_string(json_array(ml), methods[i].name);
639        }
640        json_object_set_value(json_object(d), "method_list", ml);
641        json_array_append_value(params, d);
642
643        return rpc;
644}
645
646void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
647        int st, fd, i;
648
649        fd = socket(address->sa_family, SOCK_STREAM, 0);
650        if (fd == -1 || connect(fd, address, addrlen) == -1)
651                return;
652
653        JSON_Value *rpc = rpc_init_isup();
654        char *s = json_serialize_to_string(rpc);
655        json_value_free(rpc);
656
657        int len = strlen(s);
658        s = g_realloc(s, len + 3);
659        strcpy(s + len, "\r\n");
660        len += 2;
661
662        if ((st = write(fd, s, len)) != len) {
663                // LOG ERROR
664                return;
665        }
666        g_free(s);
667
668        char *resp = NULL;
669        int buflen = 4096, resplen = 0;
670        JSON_Value *parsed;
671        do {
672                fd_set rfds;
673                struct timeval to;
674
675                FD_ZERO(&rfds);
676                FD_SET(fd, &rfds);
677                to.tv_sec = 1;
678                to.tv_usec = 0;
679                st = select(fd + 1, &rfds, NULL, NULL, &to);
680
681                if (st == 0) {
682                        // LOG ERROR
683                        closesocket(fd);
684                        return;
685                }
686               
687                if (resplen >= buflen)
688                        buflen *= 2;
689                resp = g_realloc(resp, buflen + 1);
690                st = read(fd, resp + resplen, buflen - resplen);
691                if (st == -1) {
692                        if (sockerr_again())
693                                continue;
694                        // LOG ERROR
695                        closesocket(fd);
696                        return;
697                }
698                resplen += st;
699                resp[resplen] = '\0';
700        }
701        while (!(parsed = json_parse_string(resp)));
702        closesocket(fd);
703
704        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
705        if (isup == NULL) {
706                // LOG ERROR
707                return;
708        }
709
710        struct prpl *ret = g_new0(struct prpl, 1);
711       
712        JSON_Array *methods_a = json_object_get_array(isup, "methods");
713        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
714        for (i = 0; i < json_array_get_count(methods_a); i++)
715                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
716
717        ret->init = rpc_init;
718        RPC_ADD_FUNC(login);
719        RPC_ADD_FUNC(keepalive);
720        RPC_ADD_FUNC(logout);
721        RPC_ADD_FUNC(buddy_msg);
722        RPC_ADD_FUNC(set_away);
723        RPC_ADD_FUNC(send_typing);
724        RPC_ADD_FUNC(add_buddy);
725        RPC_ADD_FUNC(remove_buddy);
726        RPC_ADD_FUNC(add_permit);
727        RPC_ADD_FUNC(add_deny);
728        RPC_ADD_FUNC(rem_permit);
729        RPC_ADD_FUNC(rem_deny);
730        RPC_ADD_FUNC(get_info);
731        RPC_ADD_FUNC(chat_invite);
732        RPC_ADD_FUNC(chat_kick);
733        RPC_ADD_FUNC(chat_leave);
734        RPC_ADD_FUNC(chat_msg);
735        RPC_ADD_FUNC(chat_with);
736        RPC_ADD_FUNC(chat_join);
737        RPC_ADD_FUNC(chat_topic);
738       
739        g_hash_table_destroy(methods);
740
741        // TODO: Property for a few standard nickcmp implementations.
742        ret->handle_cmp = g_ascii_strcasecmp;
743       
744        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
745        proto_data->addr = g_memdup(address, addrlen);
746        proto_data->addrlen = addrlen;
747        ret->name = g_strdup(json_object_get_string(isup, "name"));
748        ret->data = proto_data;
749
750        /* Keep a full copy of the settings list, we can only use it when we
751         * have an account to work on. */
752        JSON_Value *settings = json_object_get_value(isup, "settings");
753        if (json_type(settings) == JSONObject)
754                proto_data->settings = json_value_deep_copy(settings);
755
756        register_protocol(ret);
757}
758
759#define PDIR "/tmp/rpcplugins"
760
761/* YA RLY :-/ */
762#ifndef UNIX_PATH_MAX
763struct sockaddr_un sizecheck;
764#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
765#endif
766
767void rpc_initmodule() {
768        DIR *pdir = opendir(PDIR);
769        struct dirent *de;
770
771        if (!pdir)
772                return;
773
774        while ((de = readdir(pdir))) {
775                char *fn = g_build_filename(PDIR, de->d_name, NULL);
776                struct sockaddr_un su;
777
778                strncpy(su.sun_path, fn, UNIX_PATH_MAX);
779                su.sun_path[UNIX_PATH_MAX-1] = '\0';
780                su.sun_family = AF_UNIX;
781                rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
782                g_free(fn);
783        }
784}
785
Note: See TracBrowser for help on using the repository browser.