source: protocols/rpc/rpc.c @ f3af614

Last change on this file since f3af614 was f3af614, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-04-11T23:27:57Z

Whole bunch of changes: Added some more settings-related stuff and some calls
from plugin into BitlBee (where we *can* actually do return values). Also,
adding what I have so far for the Python counterpart, I suppose it makes sense
to have it in the source tree as well - though the naming/layout/etc is
completely made up.

Also, writing the commit message as if Git were a normal version control
system because screw arbitrary poorly documented "rules" on how commit
messages should be written in the world's most "special" VCS.

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