source: protocols/rpc/rpc.c @ bc73e2ba

Last change on this file since bc73e2ba was bc73e2ba, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-04-04T00:33:30Z

Error parsing, support for optional args, added handle_cmp to avoid segfault.

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