source: protocols/rpc/rpc.c @ c5a7b8d

Last change on this file since c5a7b8d was c5a7b8d, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-04-12T13:46:45Z

Away states/messages, account flags, error reporting, some comments.

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