source: protocols/rpc/rpc.c @ 578790e

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

Bunch of rpc.c improvements.
It can do requests, responses, but it's all still pretty ugly. Should start
with at least splitting up this pretty big file.

  • Property mode set to 100644
File size: 21.3 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
9static int next_rpc_id = 1;
10
[6cdecc7]11struct rpc_plugin {
[4f6dfbb]12        struct sockaddr *addr;
13        socklen_t addrlen;
[6cdecc7]14};
[4f6dfbb]15
16struct rpc_connection {
17        int fd;
18        char *buf;
[6cdecc7]19        int buflen;
[66aefeb]20        GHashTable *groupchats;
[6cdecc7]21};
[4f6dfbb]22
23struct rpc_groupchat {
[66aefeb]24        int id;
25        struct groupchat *gc;
[6cdecc7]26};
[4f6dfbb]27
[578790e]28static JSON_Value *jsonrpc_error(int code, const char *msg) {
29        JSON_Value *error = json_value_init_object();
30        json_object_set_number(json_object(error), "code", code);
31        json_object_set_string(json_object(error), "message", msg);
32        JSON_Value *ret = json_value_init_object();
33        json_object_set_value(json_object(ret), "error", error);
34       
35        return ret;
36}
37
[4f6dfbb]38static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
39        JSON_Value *rpc = json_value_init_object();
[6cdecc7]40        json_object_set_string(json_object(rpc), "method", method);
41        json_object_set_number(json_object(rpc), "id", next_rpc_id++);
[4f6dfbb]42
[6cdecc7]43        JSON_Value *params = json_value_init_array();
44        json_object_set_value(json_object(rpc), "params", params);
[4f6dfbb]45
46        if (params_)
47                *params_ = json_array(params);
[6cdecc7]48
49        return rpc;
[4f6dfbb]50}
51
52#define RPC_OUT_INIT(method) \
53        JSON_Array *params; \
54        JSON_Value *rpc = rpc_out_new(method, &params);
55
56/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
[66aefeb]57static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
58        struct rpc_connection *rd = ic->proto_data;
[4f6dfbb]59        char *buf = json_serialize_to_string(rpc);
[66aefeb]60        int len = strlen(buf);
61        int st;
[4f6dfbb]62
[66aefeb]63        buf = g_realloc(buf, len + 3);
64        strcpy(buf + len, "\r\n");
65        len += 2;
66
67        st = write(rd->fd, buf, len);
[4f6dfbb]68        g_free(buf);
69        json_value_free(rpc);
[66aefeb]70
71        if (st != len) {
72                imcb_log(ic, "Write error");
73                imc_logout(ic, TRUE);
74                return FALSE;
75        }
76
77        return TRUE;
[4f6dfbb]78}
79
[6cdecc7]80static JSON_Value *rpc_ser_account(const account_t *acc) {
[4f6dfbb]81        JSON_Value *v = json_value_init_object();
82        JSON_Object *o = json_object(v);
83        json_object_set_string(o, "user", acc->user);
84        json_object_set_string(o, "pass", acc->user);
[66aefeb]85        if (acc->server)
86                json_object_set_string(o, "server", acc->server);
[4f6dfbb]87        return v;
88}
89
90static void rpc_init(account_t *acc) {
91        // Add settings. Probably should not RPC at all.
92}
93
[6cdecc7]94static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
95static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
96
[4f6dfbb]97static void rpc_login(account_t *acc) {
98        struct im_connection *ic = imcb_new(acc);
[6cdecc7]99        struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
100        struct rpc_plugin *pd = acc->prpl->data;
[578790e]101        imcb_log(ic, "Connecting to RPC server");
[6cdecc7]102        rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
103        sock_make_nonblocking(rd->fd);
104        if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
105                closesocket(rd->fd);
106                return;
107        }
108        ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
[66aefeb]109        rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
[6cdecc7]110}
111
112static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
113        struct im_connection *ic = data;
114        struct rpc_connection *rd = ic->proto_data;
[4f6dfbb]115        RPC_OUT_INIT("login");
[6cdecc7]116        json_array_append_value(params, rpc_ser_account(ic->acc));
117        rpc_send(ic, rpc);
118
119        ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
120
121        return FALSE;
122}
123
124static void rpc_keepalive(struct im_connection *ic) {
125        RPC_OUT_INIT("keepalive");
126        rpc_send(ic, rpc);
127}
128
129static void rpc_logout(struct im_connection *ic) {
130        RPC_OUT_INIT("logout");
[9ae7332]131        if (!rpc_send(ic, rpc))
132                return;
[66aefeb]133
134        struct rpc_connection *rd = ic->proto_data;
135        b_event_remove(ic->inpa);
136        closesocket(rd->fd);
137        g_free(rd->buf);
138        g_hash_table_destroy(rd->groupchats);
139        g_free(rd);
[6cdecc7]140}
141
142static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
143        RPC_OUT_INIT("buddy_msg");
144        json_array_append_string(params, to);
145        json_array_append_string(params, message);
146        json_array_append_number(params, flags);
[9ae7332]147        return rpc_send(ic, rpc);
[6cdecc7]148}
149
150static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
151        RPC_OUT_INIT("set_away");
152        json_array_append_string(params, state);
153        json_array_append_string(params, message);
154        rpc_send(ic, rpc);
155}
156
157static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
158        RPC_OUT_INIT("send_typing");
159        json_array_append_string(params, who);
160        json_array_append_number(params, flags);
[9ae7332]161        return rpc_send(ic, rpc);
[6cdecc7]162}
163
164static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
165        RPC_OUT_INIT("add_buddy");
166        json_array_append_string(params, name);
167        json_array_append_string(params, group);
168        rpc_send(ic, rpc);
169}
170
171static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
172        RPC_OUT_INIT("remove_buddy");
173        json_array_append_string(params, name);
174        json_array_append_string(params, group);
175        rpc_send(ic, rpc);
176}
177
178static void rpc_add_permit(struct im_connection *ic, char *who) {
179        RPC_OUT_INIT("add_permit");
180        json_array_append_string(params, who);
181        rpc_send(ic, rpc);
182}
183
184static void rpc_add_deny(struct im_connection *ic, char *who) {
185        RPC_OUT_INIT("add_deny");
186        json_array_append_string(params, who);
187        rpc_send(ic, rpc);
188}
189
190static void rpc_rem_permit(struct im_connection *ic, char *who) {
191        RPC_OUT_INIT("rem_permit");
192        json_array_append_string(params, who);
193        rpc_send(ic, rpc);
194}
195
196static void rpc_rem_deny(struct im_connection *ic, char *who) {
197        RPC_OUT_INIT("rem_deny");
198        json_array_append_string(params, who);
199        rpc_send(ic, rpc);
200}
201
202static void rpc_get_info(struct im_connection *ic, char *who) {
203        RPC_OUT_INIT("get_info");
204        json_array_append_string(params, who);
205        rpc_send(ic, rpc);
206}
207
208static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
209        RPC_OUT_INIT("chat_invite");
[66aefeb]210        struct rpc_groupchat *rc = gc->data;
211        json_array_append_number(params, rc->id);
[6cdecc7]212        json_array_append_string(params, who);
213        json_array_append_string(params, message);
214        rpc_send(gc->ic, rpc);
215}
216
217static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
218        RPC_OUT_INIT("chat_kick");
[66aefeb]219        struct rpc_groupchat *rc = gc->data;
220        json_array_append_number(params, rc->id);
[6cdecc7]221        json_array_append_string(params, who);
222        json_array_append_string(params, message);
223        rpc_send(gc->ic, rpc);
224}
225
226static void rpc_chat_leave(struct groupchat *gc) {
227        RPC_OUT_INIT("chat_leave");
[66aefeb]228        struct rpc_groupchat *rc = gc->data;
229        json_array_append_number(params, rc->id);
[4f6dfbb]230        rpc_send(gc->ic, rpc);
231}
232
233static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
234        RPC_OUT_INIT("chat_msg");       
[66aefeb]235        struct rpc_groupchat *rc = gc->data;
236        json_array_append_number(params, rc->id);
[4f6dfbb]237        json_array_append_string(params, msg);
238        json_array_append_number(params, flags);
239        rpc_send(gc->ic, rpc);
240}
241
[578790e]242static struct rpc_groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
243        struct rpc_connection *rd = ic->proto_data;
[66aefeb]244        struct groupchat *gc = imcb_chat_new(ic, handle);
245        struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
[578790e]246        rc->id = next_rpc_id++;
[66aefeb]247        rc->gc = gc;
[578790e]248        g_hash_table_insert(rd->groupchats, &rc->id, rc);
249        return rc;  // TODO: RETVAL HERE AND BELOW
[66aefeb]250}
251
[578790e]252static struct rpc_groupchat *rpc_groupchat_by_id(struct im_connection *ic, int id) {
253        struct rpc_connection *rd = ic->proto_data;
254        struct rpc_groupchat *rc = g_hash_table_lookup(rd->groupchats, &id);
255
256        return rc;
257}
258
259/* Boilerplate for all incoming RPCs (where groupchat is identified using
260 * numeric ID). */
261#define SET_GROUPCHAT(rc) \
262        do { \
263                rc = rpc_groupchat_by_id(ic, json_array_get_number(params, 0)); \
264                if (rc == NULL) \
265                        return jsonrpc_error(ENOENT, "No groupchat with that id."); \
266        } while (0)
267
[6cdecc7]268static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
269        RPC_OUT_INIT("chat_with");
[578790e]270        struct rpc_groupchat *rc = rpc_groupchat_new(ic, who);
[66aefeb]271        json_array_append_number(params, rc->id);
[6cdecc7]272        json_array_append_string(params, who);
273        rpc_send(ic, rpc);
274
[578790e]275        return rc->gc; 
[6cdecc7]276}
277
278static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
279                                       const char *password, set_t **sets) {
280        RPC_OUT_INIT("chat_join");
[578790e]281        struct rpc_groupchat *rc = rpc_groupchat_new(ic, room);
[66aefeb]282        json_array_append_number(params, rc->id);
[6cdecc7]283        json_array_append_string(params, room);
284        json_array_append_string(params, nick);
285        json_array_append_string(params, password);
286        //json_array_append_value(params, rpc_ser_sets(sets));
287        rpc_send(ic, rpc);
288
[578790e]289        return rc->gc;
[6cdecc7]290}
291
292static void rpc_chat_topic(struct groupchat *gc, char *topic) {
293        RPC_OUT_INIT("chat_topic");
[66aefeb]294        struct rpc_groupchat *rc = gc->data;
295        json_array_append_number(params, rc->id);
[6cdecc7]296        json_array_append_string(params, topic);
297        rpc_send(gc->ic, rpc);
298}
299
[578790e]300static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
[6cdecc7]301
[9ae7332]302static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
[4f6dfbb]303        const char *cmd = json_object_get_string(rpc, "method");
[6cdecc7]304        JSON_Value *id = json_object_get_value(rpc, "id");
305        JSON_Array *params = json_object_get_array(rpc, "params");
[4f6dfbb]306
[578790e]307        /* Removed checks for result/error/etc. as it's all too free-form and
308         * at least for now this code is not going to care about retvals as
309         * they come in late anyway. */
310        if (!id) {
[6cdecc7]311                imcb_log(ic, "Received invalid JSON-RPC object.");
312                imc_logout(ic, TRUE);
[9ae7332]313                return FALSE;
[4f6dfbb]314        }
315
[578790e]316        if (cmd) {
317                JSON_Value *resp = rpc_cmd_in(ic, cmd, params);
318                if (!resp) {
319                        resp = json_value_init_object();
320                        json_object_set_boolean(json_object(resp), "result", TRUE);
321                }
[9ae7332]322                json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
323                return rpc_send(ic, resp);
[4f6dfbb]324        }
[578790e]325
326        return TRUE;
[4f6dfbb]327}
328
[6cdecc7]329static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
330        struct im_connection *ic = data;
331        struct rpc_connection *rd = ic->proto_data;
332        char buf[2048];
333        int st;
334
335        while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
336                rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
337                memcpy(rd->buf + rd->buflen, buf, st);
[9ae7332]338                rd->buflen += st;
[6cdecc7]339        }
340
[9ae7332]341        if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
[578790e]342                imcb_log(ic, "Lost RPC connection");
[6cdecc7]343                imc_logout(ic, TRUE);
344                return FALSE;
345        }
346        rd->buf[rd->buflen] = '\0';
347
348        JSON_Value *parsed;
349        const char *end;
350        while ((parsed = json_parse_first(rd->buf, &end))) {
[9ae7332]351                st = rpc_in(ic, json_object(parsed));
[6cdecc7]352                json_value_free(parsed);
353
[9ae7332]354                if (!st)
355                        return FALSE;
356
[6cdecc7]357                if (end == rd->buf + rd->buflen) {
358                        g_free(rd->buf);
359                        rd->buf = NULL;
360                } else {
361                        int newlen = rd->buf + rd->buflen - end;
362                        char new[newlen];
363                        memcpy(new, end, newlen);
364                        rd->buf = g_realloc(rd->buf, newlen + 1);
365                        memcpy(rd->buf, new, newlen);
[9ae7332]366                        rd->buflen = newlen;
[6cdecc7]367                        rd->buf[rd->buflen] = '\0';
368                }
369        }
370
371        return TRUE;
372}
373
[578790e]374static JSON_Value *rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
375        void (*func)(struct im_connection*, const char*, ...) = func_;
[9ae7332]376        func(ic, "%s", json_array_get_string(params, 0));
[578790e]377        return NULL;
[9ae7332]378}
379
[578790e]380static JSON_Value *rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]381        void (*func)(struct im_connection*) = func_;
382        func(ic);
[578790e]383        return NULL;
[9ae7332]384}
385
[578790e]386static JSON_Value *rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]387        void (*func)(struct im_connection*, gboolean) = func_;
388        func(ic, json_array_get_boolean(params, 0));
[578790e]389        return NULL;
[9ae7332]390}
391
[578790e]392static JSON_Value *rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]393        void (*func)(struct im_connection*, const char*, const char*) = func_;
394        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1));
[578790e]395        return NULL;
[9ae7332]396}
397
[578790e]398static JSON_Value *rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]399        void (*func)(struct im_connection*, const char*, int, const char*, const char*) = func_;
400        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
401                 json_array_get_string(params, 2), json_array_get_string(params, 3));
[578790e]402        return NULL;
[9ae7332]403}
404
[578790e]405static JSON_Value *rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]406        void (*func)(struct im_connection*, const char*, int, int) = func_;
407        func(ic, json_array_get_string(params, 0), json_array_get_number(params, 1),
408                 json_array_get_number(params, 2));
[578790e]409        return NULL;
[9ae7332]410}
411
[578790e]412static JSON_Value *rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
[9ae7332]413        void (*func)(struct im_connection*, const char*, const char*, int, int) = func_;
414        func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1),
415                 json_array_get_number(params, 2), json_array_get_number(params, 3));
[578790e]416        return NULL;
[9ae7332]417}
418
[578790e]419static JSON_Value *rpc_imcb_buddy_typing(struct im_connection *ic, void *func_, JSON_Array *params) {
420        void (*func)(struct im_connection*, const char*, int) = func_;
[9ae7332]421        func(ic, (char*) json_array_get_string(params, 0), json_array_get_number(params, 1));
[578790e]422        return NULL;
423}
424
425static JSON_Value *rpc_imcb_chat_new(struct im_connection *ic, void *func_, JSON_Array *params) {
426        struct rpc_groupchat *rc = rpc_groupchat_new(ic, json_array_get_string(params, 0));
427        JSON_Value *resp = json_value_init_object();
428        json_object_set_number(json_object(resp), "result", rc->id);
429        return resp;
430}
431
432static JSON_Value *rpc_imcb_chat_name_hint(struct im_connection *ic, void *func_, JSON_Array *params) {
433        void (*func)(struct groupchat*, const char*) = func_;
434        struct rpc_groupchat *rc;
435        SET_GROUPCHAT(rc);
436        func(rc->gc, json_array_get_string(params, 1));
437        return NULL;
438}
439
440static JSON_Value *rpc_imcb_chat_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
441        void (*func)(struct groupchat*, const char*, const char*, guint32, time_t) = func_;
442        struct rpc_groupchat *rc;
443        SET_GROUPCHAT(rc);
444        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
445             json_array_get_number(params, 3), json_array_get_number(params, 4));
446        return NULL;
447}
448
449static JSON_Value *rpc_imcb_chat_log(struct im_connection *ic, void *func_, JSON_Array *params) {
450        void (*func)(struct groupchat*, const char*, ...) = func_;
451        struct rpc_groupchat *rc;
452        SET_GROUPCHAT(rc);
453        func(rc->gc, "%s", json_array_get_string(params, 1));
454        return NULL;
455}
456
457static JSON_Value *rpc_imcb_chat_topic(struct im_connection *ic, void *func_, JSON_Array *params) {
458        void (*func)(struct groupchat*, const char*, const char*, time_t) = func_;
459        struct rpc_groupchat *rc;
460        SET_GROUPCHAT(rc);
461        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
462             json_array_get_number(params, 3));
463        return NULL;
464}
465
466static JSON_Value *rpc_imcb_chat_remove_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
467        void (*func)(struct groupchat*, const char*, const char*) = func_;
468        struct rpc_groupchat *rc;
469        SET_GROUPCHAT(rc);
470        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2));
471        return NULL;
472}
473
474static JSON_Value *rpc_imcb_chat_invite(struct im_connection *ic, void *func_, JSON_Array *params) {
475        void (*func)(struct groupchat*, const char*, const char*, const char*) = func_;
476        struct rpc_groupchat *rc;
477        SET_GROUPCHAT(rc);
478        func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
479             json_array_get_string(params, 3));
480        return NULL;
[4f6dfbb]481}
482
483struct rpc_in_method {
484        char *name;
[9ae7332]485        void *func;
[578790e]486        JSON_Value* (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
[9ae7332]487        char args[8];
488};
489
490static const struct rpc_in_method methods[] = {
[578790e]491        /* All these RPCs are equivalent of BitlBee C functions but with the
492         * struct im_connection* removed. */
[9ae7332]493        { "imcb_log", imcb_log, rpc_imcb_log, "s" },
494        { "imcb_error", imcb_error, rpc_imcb_log, "s" },
495        { "imcb_connected", imcb_connected, rpc_imcb_connected, "" },
496        { "imc_logout", imc_logout, rpc_imc_logout, "b" },
497        { "imcb_add_buddy", imcb_add_buddy, rpc_imcb_add_buddy, "ss" },
498        { "imcb_remove_buddy", imcb_remove_buddy, rpc_imcb_add_buddy, "ss" },
499        { "imcb_rename_buddy", imcb_rename_buddy, rpc_imcb_add_buddy, "ss" },
500        { "imcb_buddy_nick_hint", imcb_buddy_nick_hint, rpc_imcb_add_buddy, "ss" },
501        { "imcb_buddy_status", imcb_buddy_status, rpc_imcb_buddy_status, "snss" },
502        { "imcb_buddy_status_msg", imcb_buddy_status_msg, rpc_imcb_add_buddy, "ss" },
503        { "imcb_buddy_times", imcb_buddy_times, rpc_imcb_buddy_times, "snn" },
504        { "imcb_buddy_msg", imcb_buddy_msg, rpc_imcb_buddy_msg, "ssnn" },
505        { "imcb_buddy_typing", imcb_buddy_typing, rpc_imcb_buddy_typing, "sn" },
[578790e]506        { "imcb_chat_new", NULL, rpc_imcb_chat_new, "s" },
507       
508        /* RPCs below are equivalent, but with the struct groupchat* replaced
509         * with the numeric id of the chat. */
510        { "imcb_chat_name_hint", imcb_chat_name_hint, rpc_imcb_chat_name_hint, "ns" },
511        { "imcb_chat_msg", imcb_chat_msg, rpc_imcb_chat_msg, "nssnn" },
512        { "imcb_chat_log", imcb_chat_log, rpc_imcb_chat_log, "ns" },
513        { "imcb_chat_topic", imcb_chat_topic, rpc_imcb_chat_topic, "nssn" },
514        { "imcb_chat_add_buddy", imcb_chat_add_buddy, rpc_imcb_chat_name_hint, "ns" },
515        { "imcb_chat_remove_buddy", imcb_chat_remove_buddy, rpc_imcb_chat_remove_buddy, "nss" },
516        { "imcb_chat_invite", imcb_chat_invite, rpc_imcb_chat_invite, "nsss" },
517
[9ae7332]518        { NULL },
[6cdecc7]519};
[4f6dfbb]520
[578790e]521static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
[9ae7332]522        int i;
523
524        for (i = 0; methods[i].name; i++) {
525                if (strcmp(cmd, methods[i].name) == 0) {
526                        if (json_array_get_count(params) != strlen(methods[i].args)) {
527                                imcb_error(ic, "Invalid argument count to method %s: %d, wanted %zd", cmd, (int) json_array_get_count(params), strlen(methods[i].args));
[578790e]528                                return jsonrpc_error(E2BIG, "Invalid number of arguments");
[9ae7332]529                        }
530                        int j;
531                        for (j = 0; methods[i].args[j]; j++) {
532                                JSON_Value_Type type = json_value_get_type(json_array_get_value(params, j));
533                                gboolean ok = FALSE;
534                                switch (methods[i].args[j]) {
535                                case 's':
536                                        ok = type == JSONString;
537                                        break;
538                                case 'n':
539                                        ok = type == JSONNumber;
540                                        break;
541                                case 'o':
542                                        ok = type == JSONObject;
543                                        break;
544                                case 'a':
545                                        ok = type == JSONArray;
546                                        break;
547                                case 'b':
548                                        ok = type == JSONBoolean;
549                                        break;
550                                }
551                                if (!ok) {
552                                        // This error sucks, but just get your types right!
553                                        imcb_error(ic, "Invalid argument type, %s parameter %d: %d not %c", cmd, j, type, methods[i].args[j]);
[578790e]554                                        return jsonrpc_error(EINVAL, "Invalid argument type");
[9ae7332]555                                }
556                        }
[578790e]557                        return methods[i].wfunc(ic, methods[i].func, params);
[9ae7332]558                }
559        }
[578790e]560        return jsonrpc_error(ENOSYS, "Function not implemented");
[9ae7332]561}
562
[4f6dfbb]563#define RPC_ADD_FUNC(func) \
564        if (g_hash_table_contains(methods, #func)) \
[6cdecc7]565                ret->func = rpc_ ## func
[4f6dfbb]566
[6cdecc7]567void rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
[4f6dfbb]568        int st, fd, i;
569
[6cdecc7]570        fd = socket(address->sa_family, SOCK_STREAM, 0);
[4f6dfbb]571        if (fd == -1 || connect(fd, address, addrlen) == -1)
572                return;
573
574        RPC_OUT_INIT("init");
575        JSON_Value *d = json_value_init_object();
576        json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
577        json_object_set_number(json_object(d), "version", BITLBEE_VERSION_CODE);
[6cdecc7]578        json_array_append_value(params, d);
[4f6dfbb]579        char *s = json_serialize_to_string(rpc);
[66aefeb]580        int len = strlen(s);
581        s = g_realloc(s, len + 3);
582        strcpy(s + len, "\r\n");
583        len += 2;
[4f6dfbb]584
[66aefeb]585        if ((st = write(fd, s, len)) != len) {
[4f6dfbb]586                // LOG ERROR
587                return;
588        }
589        g_free(s);
590
591        char *resp = NULL;
[6cdecc7]592        int buflen = 4096, resplen = 0;
[4f6dfbb]593        JSON_Value *parsed;
594        do {
595                fd_set rfds;
596                struct timeval to;
597
598                FD_ZERO(&rfds);
599                FD_SET(fd, &rfds);
600                to.tv_sec = 1;
601                to.tv_usec = 0;
602                st = select(fd + 1, &rfds, NULL, NULL, &to);
603
604                if (st == 0) {
605                        // LOG ERROR
[66aefeb]606                        closesocket(fd);
[4f6dfbb]607                        return;
608                }
609               
[6cdecc7]610                if (resplen >= buflen)
611                        buflen *= 2;
612                resp = g_realloc(resp, buflen + 1);
613                st = read(fd, resp + resplen, buflen - resplen);
[4f6dfbb]614                if (st == -1) {
615                        if (sockerr_again())
616                                continue;
617                        // LOG ERROR
[66aefeb]618                        closesocket(fd);
[4f6dfbb]619                        return;
620                }
621                resplen += st;
622                resp[resplen] = '\0';
623        }
624        while (!(parsed = json_parse_string(resp)));
[66aefeb]625        closesocket(fd);
[4f6dfbb]626
627        JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
628        if (isup == NULL) {
629                // LOG ERROR
630                return;
631        }
632
633        struct prpl *ret = g_new0(struct prpl, 1);
634       
635        JSON_Array *methods_a = json_object_get_array(isup, "methods");
[6cdecc7]636        GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
[4f6dfbb]637        for (i = 0; i < json_array_get_count(methods_a); i++)
[6cdecc7]638                g_hash_table_add(methods, (void*) json_array_get_string(methods_a, i));
[4f6dfbb]639
[6cdecc7]640        ret->init = rpc_init;
[4f6dfbb]641        RPC_ADD_FUNC(login);
[6cdecc7]642        RPC_ADD_FUNC(keepalive);
643        RPC_ADD_FUNC(logout);
644        RPC_ADD_FUNC(buddy_msg);
645        RPC_ADD_FUNC(set_away);
646        RPC_ADD_FUNC(send_typing);
647        RPC_ADD_FUNC(add_buddy);
648        RPC_ADD_FUNC(remove_buddy);
649        RPC_ADD_FUNC(add_permit);
650        RPC_ADD_FUNC(add_deny);
651        RPC_ADD_FUNC(rem_permit);
652        RPC_ADD_FUNC(rem_deny);
653        RPC_ADD_FUNC(get_info);
654        RPC_ADD_FUNC(chat_invite);
655        RPC_ADD_FUNC(chat_kick);
656        RPC_ADD_FUNC(chat_leave);
[4f6dfbb]657        RPC_ADD_FUNC(chat_msg);
[6cdecc7]658        RPC_ADD_FUNC(chat_with);
659        RPC_ADD_FUNC(chat_join);
660        RPC_ADD_FUNC(chat_topic);
[4f6dfbb]661       
[6cdecc7]662        g_hash_table_destroy(methods);
[4f6dfbb]663
664        // TODO: Property for a few standard nickcmp implementations.
665       
[578790e]666        struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
667        proto_data->addr = g_memdup(address, addrlen);
668        proto_data->addrlen = addrlen;
669        ret->name = g_strdup(json_object_get_string(isup, "name"));
670        ret->data = proto_data;
671
[4f6dfbb]672        JSON_Array *settings = json_object_get_array(isup, "settings");
673        for (i = 0; i < json_array_get_count(settings); i++) {
[6cdecc7]674                //JSON_Object *set = json_array_get_object(settings, i);
[4f6dfbb]675                // set..name, set..type, set..default, set..flags ?
676        }
677
678        register_protocol(ret);
679}
680
[66aefeb]681#define PDIR "/tmp/rpcplugins"
682
683/* YA RLY :-/ */
684#ifndef UNIX_PATH_MAX
685struct sockaddr_un sizecheck;
686#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
687#endif
688
[4f6dfbb]689void rpc_initmodule() {
[66aefeb]690        DIR *pdir = opendir(PDIR);
691        struct dirent *de;
692
693        if (!pdir)
694                return;
695
696        while ((de = readdir(pdir))) {
697                char *fn = g_build_filename(PDIR, de->d_name, NULL);
698                struct sockaddr_un su;
699
700                strncpy(su.sun_path, fn, UNIX_PATH_MAX);
701                su.sun_path[UNIX_PATH_MAX-1] = '\0';
702                su.sun_family = AF_UNIX;
703                rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
704                g_free(fn);
705        }
[4f6dfbb]706}
707
Note: See TracBrowser for help on using the repository browser.