source: protocols/msn/gw.c @ 701ab812

Last change on this file since 701ab812 was 4e4616a, checked in by dequis <dx@…>, at 2015-11-28T15:59:34Z

msn: Buffer writes a bit to send several commands with a single request

Just a 1msec timeout, so that it will run in the next main loop
iteration.

The official clients send the first few commands in the same request,
which reduces roundtrips during login. This commit doesn't do that.

  • Property mode set to 100644
File size: 4.8 KB
Line 
1#include "bitlbee.h"
2#include "lib/http_client.h"
3#include "msn.h"
4
5#define GATEWAY_HOST "geo.gateway.messenger.live.com"
6#define GATEWAY_PORT 443
7
8#define REQUEST_TEMPLATE \
9        "POST /gateway/gateway.dll?SessionID=%s&%s HTTP/1.1\r\n" \
10        "Host: %s\r\n" \
11        "Content-Length: %zd\r\n" \
12        "\r\n" \
13        "%s"
14
15static gboolean msn_gw_poll_timeout(gpointer data, gint source, b_input_condition cond);
16
17struct msn_gw *msn_gw_new(struct im_connection *ic)
18{
19        struct msn_gw *gw = g_new0(struct msn_gw, 1);
20        gw->last_host = g_strdup(GATEWAY_HOST);
21        gw->port = GATEWAY_PORT;
22        gw->ssl = (GATEWAY_PORT == 443);
23        gw->poll_timeout = -1;
24        gw->write_timeout = -1;
25        gw->ic = ic;
26        gw->md = ic->proto_data;
27        gw->in = g_byte_array_new();
28        gw->out = g_byte_array_new();
29        return gw;
30}
31
32void msn_gw_free(struct msn_gw *gw)
33{
34        if (gw->poll_timeout != -1) {
35                b_event_remove(gw->poll_timeout);
36        }
37
38        if (gw->write_timeout != -1) {
39                b_event_remove(gw->write_timeout);
40        }
41
42        g_byte_array_free(gw->in, TRUE);
43        g_byte_array_free(gw->out, TRUE);
44        g_free(gw->session_id);
45        g_free(gw->last_host);
46        g_free(gw);
47}
48
49static struct msn_gw *msn_gw_from_ic(struct im_connection *ic)
50{
51        if (g_slist_find(msn_connections, ic) == NULL) {
52                return NULL;
53        } else {
54                struct msn_data *md = ic->proto_data;
55                return md->gw;
56        }
57}
58
59static gboolean msn_gw_parse_session_header(struct msn_gw *gw, char *value)
60{
61        int i;
62        char **subvalues;
63        gboolean closed = FALSE;
64
65        subvalues = g_strsplit(value, "; ", 0);
66
67        for (i = 0; subvalues[i]; i++) {
68                if (strcmp(subvalues[i], "Session=close") == 0) {
69                        /* gateway closed, signal the death of the socket */
70                        closed = TRUE;
71                } else if (g_str_has_prefix(subvalues[i], "SessionID=")) {
72                        /* copy the part after the = to session_id*/
73                        g_free(gw->session_id);
74                        gw->session_id = g_strdup(subvalues[i] + 10);
75                }
76        }
77
78        g_strfreev(subvalues);
79
80        return !closed;
81}
82
83void msn_gw_callback(struct http_request *req)
84{
85        struct msn_gw *gw;
86        char *value;
87
88        if (!(gw = msn_gw_from_ic(req->data))) {
89                return;
90        }
91
92        gw->waiting = FALSE;
93        gw->polling = FALSE;
94
95        if (req->status_code != 200 || !req->reply_body) {
96                gw->callback(gw->md, -1, B_EV_IO_READ);
97                return;
98        }
99
100        if (getenv("BITLBEE_DEBUG")) {
101                fprintf(stderr, "\n\x1b[90mHTTP:%s\n", req->reply_body);
102                fprintf(stderr, "\n\x1b[97m\n");
103        }
104
105        if ((value = get_rfc822_header(req->reply_headers, "X-MSN-Messenger", 0))) {
106                if (!msn_gw_parse_session_header(gw, value)) {
107                        gw->callback(gw->md, -1, B_EV_IO_READ);
108                        g_free(value);
109                        return;
110                }
111                g_free(value);
112        }
113       
114        if ((value = get_rfc822_header(req->reply_headers, "X-MSN-Host", 0))) {
115                g_free(gw->last_host);
116                gw->last_host = value; /* transfer */
117        }
118
119        if (req->body_size) {
120                g_byte_array_append(gw->in, (const guint8 *) req->reply_body, req->body_size);
121
122                if (!gw->callback(gw->md, -1, B_EV_IO_READ)) {
123                        return;
124                }
125        }
126
127        if (gw->poll_timeout != -1) {
128                b_event_remove(gw->poll_timeout);
129        }
130        gw->poll_timeout = b_timeout_add(500, msn_gw_poll_timeout, gw->ic);
131
132}
133
134void msn_gw_dorequest(struct msn_gw *gw, char *args)
135{
136        char *request = NULL;
137        char *body = NULL;
138        size_t bodylen = 0;
139
140        if (gw->out) {
141                bodylen = gw->out->len;
142                g_byte_array_append(gw->out, (guint8 *) "", 1); /* nullnullnull */
143                body = (char *) g_byte_array_free(gw->out, FALSE);
144                gw->out = g_byte_array_new();
145        }
146
147        if (!bodylen && !args) {
148                args = "Action=poll&Lifespan=60";
149                gw->polling = TRUE;
150        }
151
152        request = g_strdup_printf(REQUEST_TEMPLATE,
153                gw->session_id ? : "", args ? : "", gw->last_host, bodylen, body ? : "");
154
155        http_dorequest(gw->last_host, gw->port, gw->ssl, request, msn_gw_callback, gw->ic);
156        gw->open = TRUE;
157        gw->waiting = TRUE;
158
159        g_free(body);
160        g_free(request);
161}
162
163void msn_gw_open(struct msn_gw *gw)
164{
165        msn_gw_dorequest(gw, "Action=open&Server=NS");
166}
167
168static gboolean msn_gw_poll_timeout(gpointer data, gint source, b_input_condition cond)
169{
170        struct msn_gw *gw;
171
172        if (!(gw = msn_gw_from_ic(data))) {
173                return FALSE;
174        }
175
176        gw->poll_timeout = -1;
177        if (!gw->waiting) {
178                msn_gw_dorequest(gw, NULL);
179        }
180        return FALSE;
181}
182
183ssize_t msn_gw_read(struct msn_gw *gw, char **buf)
184{
185        size_t bodylen;
186        if (!gw->open) {
187                return 0;
188        }
189
190        bodylen = gw->in->len;
191        g_byte_array_append(gw->in, (guint8 *) "", 1); /* nullnullnull */
192        *buf = (char *) g_byte_array_free(gw->in, FALSE);
193        gw->in = g_byte_array_new();
194        return bodylen;
195}
196
197static gboolean msn_gw_write_cb(gpointer data, gint source, b_input_condition cond)
198{
199        struct msn_gw *gw;
200       
201        if (!(gw = msn_gw_from_ic(data))) {
202                return FALSE;
203        }
204
205        if (!gw->open) {
206                msn_gw_open(gw);
207        } else if (gw->polling || !gw->waiting) {
208                msn_gw_dorequest(gw, NULL);
209        }
210
211        gw->write_timeout = -1;
212        return FALSE;
213}
214
215void msn_gw_write(struct msn_gw *gw, char *buf, size_t len)
216{
217        g_byte_array_append(gw->out, (const guint8 *) buf, len);
218
219        /* do a bit of buffering here to send several commands with a single request */
220        if (gw->write_timeout == -1) {
221                gw->write_timeout = b_timeout_add(1, msn_gw_write_cb, gw->ic);
222        }
223}
Note: See TracBrowser for help on using the repository browser.