source: protocols/rpc/rpc.c @ 3ddbd23c

Last change on this file since 3ddbd23c was 9076a1c, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-07-27T23:48:04Z

Fix cleanup of (RPC) groupchats, fixes some crashes at logout time.

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