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
RevLine 
[66aefeb]1#include <sys/socket.h>
2#include <sys/un.h>
3
[6cdecc7]4#include "bitlbee.h"
5#include "bee.h"
6#include "nogaim.h"
7#include "parson.h"
[4f6dfbb]8
[16652e9]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
[4f6dfbb]13static int next_rpc_id = 1;
14
[6cdecc7]15struct rpc_plugin {
[c5a7b8d]16        /* Socket address of the RPC server. */
[4f6dfbb]17        struct sockaddr *addr;
18        socklen_t addrlen;
[c5a7b8d]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). */
[16652e9]22        JSON_Value *settings;
[c5a7b8d]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;
[6cdecc7]31};
[4f6dfbb]32
33struct rpc_connection {
34        int fd;
35        char *buf;
[6cdecc7]36        int buflen;
[66aefeb]37        GHashTable *groupchats;
[6cdecc7]38};
[4f6dfbb]39
40struct rpc_groupchat {
[66aefeb]41        int id;
42        struct groupchat *gc;
[6cdecc7]43};
[4f6dfbb]44
[578790e]45static JSON_Value *jsonrpc_error(int code, const char *msg) {
46        JSON_Value *ret = json_value_init_object();
[bc73e2ba]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        }
[578790e]58       
59        return ret;
60}
61
[16652e9]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) {
[bc73e2ba]71        if (string)
[16652e9]72                json_object_set_string(object, key, string);
[bc73e2ba]73        else
[16652e9]74                json_object_set_null(object, key);
[bc73e2ba]75}
76
[4f6dfbb]77static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
78        JSON_Value *rpc = json_value_init_object();
[6cdecc7]79        json_object_set_string(json_object(rpc), "method", method);
80        json_object_set_number(json_object(rpc), "id", next_rpc_id++);
[4f6dfbb]81
[6cdecc7]82        JSON_Value *params = json_value_init_array();
83        json_object_set_value(json_object(rpc), "params", params);
[4f6dfbb]84
85        if (params_)
86                *params_ = json_array(params);
[6cdecc7]87
88        return rpc;
[4f6dfbb]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). */
[66aefeb]96static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
97        struct rpc_connection *rd = ic->proto_data;
[4f6dfbb]98        char *buf = json_serialize_to_string(rpc);
[66aefeb]99        int len = strlen(buf);
100        int st;
[4f6dfbb]101
[66aefeb]102        buf = g_realloc(buf, len + 3);
103        strcpy(buf + len, "\r\n");
104        len += 2;
105
106        st = write(rd->fd, buf, len);
[4f6dfbb]107        g_free(buf);
108        json_value_free(rpc);
[66aefeb]109
110        if (st != len) {
[16652e9]111                if (!(ic->flags & OPT_LOGGING_OUT))
112                        imcb_log(ic, "Write error");
[66aefeb]113                imc_logout(ic, TRUE);
114                return FALSE;
115        }
116
117        return TRUE;
[4f6dfbb]118}
119
[16652e9]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) {
[4f6dfbb]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);
[66aefeb]136        if (acc->server)
137                json_object_set_string(o, "server", acc->server);
[16652e9]138        json_object_set_value(o, "settings", rpc_ser_settings(&acc->set));
[4f6dfbb]139        return v;
140}
141
[f3af614]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
[4f6dfbb]157static void rpc_init(account_t *acc) {
[16652e9]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);
[f3af614]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. */
[16652e9]183        }
[c5a7b8d]184
185        acc->flags |= pd->account_flags;
[4f6dfbb]186}
187
[6cdecc7]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);
[16652e9]190static JSON_Value *rpc_init_isup();
[6cdecc7]191
[4f6dfbb]192static void rpc_login(account_t *acc) {
193        struct im_connection *ic = imcb_new(acc);
[6cdecc7]194        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
195        struct rpc_plugin *pd = acc->prpl->data;
[578790e]196        imcb_log(ic, "Connecting to RPC server");
[6cdecc7]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);
[c5a7b8d]201                imcb_error(ic, "RPC server unreachable");
202                imc_logout(ic, TRUE);
[6cdecc7]203                return;
204        }
205        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
[66aefeb]206        rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
[6cdecc7]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;
[16652e9]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
[4f6dfbb]219        RPC_OUT_INIT("login");
[6cdecc7]220        json_array_append_value(params, rpc_ser_account(ic->acc));
[16652e9]221        if (!rpc_send(ic, rpc))
222                return FALSE;
[6cdecc7]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");
[9ae7332]236        if (!rpc_send(ic, rpc))
237                return;
[66aefeb]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);
[6cdecc7]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);
[9ae7332]252        return rpc_send(ic, rpc);
[6cdecc7]253}
254
255static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
256        RPC_OUT_INIT("set_away");
[bc73e2ba]257        json_array_append_string_or_null(params, state);
258        json_array_append_string_or_null(params, message);
[6cdecc7]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);
[9ae7332]266        return rpc_send(ic, rpc);
[6cdecc7]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);
[bc73e2ba]272        json_array_append_string_or_null(params, group);
[6cdecc7]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);
[bc73e2ba]279        json_array_append_string_or_null(params, group);
[6cdecc7]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");
[66aefeb]315        struct rpc_groupchat *rc = gc->data;
316        json_array_append_number(params, rc->id);
[6cdecc7]317        json_array_append_string(params, who);
[bc73e2ba]318        json_array_append_string_or_null(params, message);
[6cdecc7]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");
[66aefeb]324        struct rpc_groupchat *rc = gc->data;
325        json_array_append_number(params, rc->id);
[6cdecc7]326        json_array_append_string(params, who);
[bc73e2ba]327        json_array_append_string_or_null(params, message);
[6cdecc7]328        rpc_send(gc->ic, rpc);
329}
330
331static void rpc_chat_leave(struct groupchat *gc) {
332        RPC_OUT_INIT("chat_leave");
[66aefeb]333        struct rpc_groupchat *rc = gc->data;
334        json_array_append_number(params, rc->id);
[4f6dfbb]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");       
[66aefeb]340        struct rpc_groupchat *rc = gc->data;
341        json_array_append_number(params, rc->id);
[4f6dfbb]342        json_array_append_string(params, msg);
343        json_array_append_number(params, flags);
344        rpc_send(gc->ic, rpc);
345}
346
[578790e]347static struct rpc_groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
348        struct rpc_connection *rd = ic->proto_data;
[66aefeb]349        struct groupchat *gc = imcb_chat_new(ic, handle);
350        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
[578790e]351        rc->id = next_rpc_id++;
[66aefeb]352        rc->gc = gc;
[578790e]353        g_hash_table_insert(rd->groupchats, &rc->id, rc);
354        return rc;  // TODO: RETVAL HERE AND BELOW
[66aefeb]355}
356
[578790e]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
[6cdecc7]373static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
374        RPC_OUT_INIT("chat_with");
[578790e]375        struct rpc_groupchat *rc = rpc_groupchat_new(ic, who);
[66aefeb]376        json_array_append_number(params, rc->id);
[6cdecc7]377        json_array_append_string(params, who);
378        rpc_send(ic, rpc);
379
[578790e]380        return rc->gc; 
[6cdecc7]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");
[578790e]386        struct rpc_groupchat *rc = rpc_groupchat_new(ic, room);
[66aefeb]387        json_array_append_number(params, rc->id);
[6cdecc7]388        json_array_append_string(params, room);
[bc73e2ba]389        json_array_append_string_or_null(params, nick);
390        json_array_append_string_or_null(params, password);
[16652e9]391        json_array_append_value(params, rpc_ser_settings(sets));
[6cdecc7]392        rpc_send(ic, rpc);
393
[578790e]394        return rc->gc;
[6cdecc7]395}
396
397static void rpc_chat_topic(struct groupchat *gc, char *topic) {
398        RPC_OUT_INIT("chat_topic");
[66aefeb]399        struct rpc_groupchat *rc = gc->data;
400        json_array_append_number(params, rc->id);
[6cdecc7]401        json_array_append_string(params, topic);
402        rpc_send(gc->ic, rpc);
403}
404
[c5a7b8d]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
[578790e]410static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
[6cdecc7]411
[9ae7332]412static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
[4f6dfbb]413        const char *cmd = json_object_get_string(rpc, "method");
[6cdecc7]414        JSON_Value *id = json_object_get_value(rpc, "id");
[bc73e2ba]415        JSON_Value *error = json_object_get_value(rpc, "error");
[6cdecc7]416        JSON_Array *params = json_object_get_array(rpc, "params");
[4f6dfbb]417
[578790e]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) {
[6cdecc7]422                imcb_log(ic, "Received invalid JSON-RPC object.");
423                imc_logout(ic, TRUE);
[9ae7332]424                return FALSE;
[4f6dfbb]425        }
426
[578790e]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                }
[9ae7332]433                json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
434                return rpc_send(ic, resp);
[bc73e2ba]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);
[4f6dfbb]441        }
[578790e]442
443        return TRUE;
[4f6dfbb]444}
445
[6cdecc7]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);
[9ae7332]455                rd->buflen += st;
[6cdecc7]456        }
457
[9ae7332]458        if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
[578790e]459                imcb_log(ic, "Lost RPC connection");
[6cdecc7]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))) {
[9ae7332]468                st = rpc_in(ic, json_object(parsed));
[6cdecc7]469                json_value_free(parsed);
470
[9ae7332]471                if (!st)
472                        return FALSE;
473
[6cdecc7]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);
[9ae7332]483                        rd->buflen = newlen;
[6cdecc7]484                        rd->buf[rd->buflen] = '\0';
485                }
486        }
487
488        return TRUE;
489}
490
[578790e]491static JSON_Value *rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
492        void (*func)(struct im_connection*, const char*, ...) = func_;
[9ae7332]493        func(ic, "%s", json_array_get_string(params, 0));
[578790e]494        return NULL;
[9ae7332]495}
496
[578790e]497static JSON_Value *rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]498        void (*func)(struct im_connection*) = func_;
499        func(ic);
[578790e]500        return NULL;
[9ae7332]501}
502
[578790e]503static JSON_Value *rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]504        void (*func)(struct im_connection*, gboolean) = func_;
505        func(ic, json_array_get_boolean(params, 0));
[578790e]506        return NULL;
[9ae7332]507}
508
[578790e]509static JSON_Value *rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]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));
[578790e]512        return NULL;
[9ae7332]513}
514
[578790e]515static JSON_Value *rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]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));
[578790e]519        return NULL;
[9ae7332]520}
521
[578790e]522static JSON_Value *rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]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));
[578790e]526        return NULL;
[9ae7332]527}
528
[578790e]529static JSON_Value *rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]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));
[578790e]533        return NULL;
[9ae7332]534}
535
[578790e]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_;
[9ae7332]538        func(ic, (char*) json_array_get_string(params, 0), json_array_get_number(params, 1));
[578790e]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;
[4f6dfbb]598}
599
[f3af614]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
[4f6dfbb]633struct rpc_in_method {
634        char *name;
[9ae7332]635        void *func;
[578790e]636        JSON_Value* (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
[9ae7332]637        char args[8];
638};
639
640static const struct rpc_in_method methods[] = {
[578790e]641        /* All these RPCs are equivalent of BitlBee C functions but with the
[f3af614]642         * struct im_connection* removed as this is in the object context. */
[9ae7332]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" },
[578790e]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
[f3af614]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
[9ae7332]678        { NULL },
[6cdecc7]679};
[4f6dfbb]680
[578790e]681static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
[9ae7332]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));
[578790e]688                                return jsonrpc_error(E2BIG, "Invalid number of arguments");
[9ae7332]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':
[bc73e2ba]696                                        ok = type == JSONString || type == JSONNull;
[9ae7332]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]);
[578790e]714                                        return jsonrpc_error(EINVAL, "Invalid argument type");
[9ae7332]715                                }
716                        }
[578790e]717                        return methods[i].wfunc(ic, methods[i].func, params);
[9ae7332]718                }
719        }
[578790e]720        return jsonrpc_error(ENOSYS, "Function not implemented");
[9ae7332]721}
722
[4f6dfbb]723#define RPC_ADD_FUNC(func) \
724        if (g_hash_table_contains(methods, #func)) \
[16652e9]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}
[4f6dfbb]744
[6cdecc7]745void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
[4f6dfbb]746        int st, fd, i;
747
[6cdecc7]748        fd = socket(address->sa_family, SOCK_STREAM, 0);
[4f6dfbb]749        if (fd == -1 || connect(fd, address, addrlen) == -1)
750                return;
751
[16652e9]752        JSON_Value *rpc = rpc_init_isup();
[4f6dfbb]753        char *s = json_serialize_to_string(rpc);
[16652e9]754        json_value_free(rpc);
755
[66aefeb]756        int len = strlen(s);
757        s = g_realloc(s, len + 3);
758        strcpy(s + len, "\r\n");
759        len += 2;
[4f6dfbb]760
[66aefeb]761        if ((st = write(fd, s, len)) != len) {
[4f6dfbb]762                // LOG ERROR
763                return;
764        }
765        g_free(s);
766
767        char *resp = NULL;
[6cdecc7]768        int buflen = 4096, resplen = 0;
[4f6dfbb]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
[66aefeb]782                        closesocket(fd);
[4f6dfbb]783                        return;
784                }
785               
[6cdecc7]786                if (resplen >= buflen)
787                        buflen *= 2;
788                resp = g_realloc(resp, buflen + 1);
789                st = read(fd, resp + resplen, buflen - resplen);
[4f6dfbb]790                if (st == -1) {
791                        if (sockerr_again())
792                                continue;
793                        // LOG ERROR
[66aefeb]794                        closesocket(fd);
[4f6dfbb]795                        return;
796                }
797                resplen += st;
798                resp[resplen] = '\0';
799        }
800        while (!(parsed = json_parse_string(resp)));
[66aefeb]801        closesocket(fd);
[4f6dfbb]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);
[c5a7b8d]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
[f3af614]833        JSON_Array *methods_a = json_object_get_array(isup, "method_list");
[6cdecc7]834        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
[4f6dfbb]835        for (i = 0; i < json_array_get_count(methods_a); i++)
[6cdecc7]836                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
[4f6dfbb]837
[6cdecc7]838        ret->init = rpc_init;
[4f6dfbb]839        RPC_ADD_FUNC(login);
[6cdecc7]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);
[4f6dfbb]855        RPC_ADD_FUNC(chat_msg);
[6cdecc7]856        RPC_ADD_FUNC(chat_with);
857        RPC_ADD_FUNC(chat_join);
858        RPC_ADD_FUNC(chat_topic);
[c5a7b8d]859        if (proto_data->away_states)
860                ret->away_states = rpc_away_states;
[4f6dfbb]861       
[6cdecc7]862        g_hash_table_destroy(methods);
[4f6dfbb]863
864        // TODO: Property for a few standard nickcmp implementations.
[bc73e2ba]865        ret->handle_cmp = g_ascii_strcasecmp;
[4f6dfbb]866       
867        register_protocol(ret);
868}
869
[66aefeb]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
[4f6dfbb]878void rpc_initmodule() {
[66aefeb]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);
[c5a7b8d]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. */
[66aefeb]897        }
[4f6dfbb]898}
899
Note: See TracBrowser for help on using the repository browser.