source: protocols/rpc/rpc.c @ f8feb8a

Last change on this file since f8feb8a was f8feb8a, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-05-07T10:33:06Z

add_buddy and remove_buddy should no longer be mandatory.

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