source: protocols/msn/gw.c @ 5fbf815

Last change on this file since 5fbf815 was e59eec0, checked in by dequis <dx@…>, at 2015-04-10T17:10:41Z

msn: switch to long polling http

  • Property mode set to 100644
File size: 4.1 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 msn_data *md)
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->data = md;
25        gw->in = g_byte_array_new();
26        gw->out = g_byte_array_new();
27        return gw;
28}
29
30void msn_gw_free(struct msn_gw *gw)
31{
32        if (gw->poll_timeout != -1) {
33                b_event_remove(gw->poll_timeout);
34        }
35        g_byte_array_free(gw->in, TRUE);
36        g_byte_array_free(gw->out, TRUE);
37        g_free(gw->session_id);
38        g_free(gw->last_host);
39        g_free(gw);
40}
41
42static gboolean msn_gw_parse_session_header(struct msn_gw *gw, char *value)
43{
44        int i;
45        char **subvalues;
46        gboolean closed = FALSE;
47
48        subvalues = g_strsplit(value, "; ", 0);
49
50        for (i = 0; subvalues[i]; i++) {
51                if (strcmp(subvalues[i], "Session=close") == 0) {
52                        /* gateway closed, signal the death of the socket */
53                        closed = TRUE;
54                } else if (g_str_has_prefix(subvalues[i], "SessionID=")) {
55                        /* copy the part after the = to session_id*/
56                        g_free(gw->session_id);
57                        gw->session_id = g_strdup(subvalues[i] + 10);
58                }
59        }
60
61        g_strfreev(subvalues);
62
63        return !closed;
64}
65
66void msn_gw_callback(struct http_request *req)
67{
68        char *value;
69        struct msn_gw *gw = req->data;
70
71        gw->waiting = FALSE;
72        gw->polling = FALSE;
73
74        if (!gw->open) {
75                /* the user tried to logout while the request was pending
76                 * see msn_ns_close() */
77                msn_gw_free(gw);
78                return;
79        }
80
81        if (getenv("BITLBEE_DEBUG")) {
82                fprintf(stderr, "\n\x1b[90mHTTP:%s\n", req->reply_body);
83                fprintf(stderr, "\n\x1b[97m\n");
84        }
85
86        if (req->status_code != 200) {
87                gw->callback(gw->data, -1, B_EV_IO_READ);
88                return;
89        }
90
91        if ((value = get_rfc822_header(req->reply_headers, "X-MSN-Messenger", 0))) {
92                if (!msn_gw_parse_session_header(gw, value)) {
93                        gw->callback(gw->data, -1, B_EV_IO_READ);
94                        g_free(value);
95                        return;
96                }
97                g_free(value);
98        }
99       
100        if ((value = get_rfc822_header(req->reply_headers, "X-MSN-Host", 0))) {
101                g_free(gw->last_host);
102                gw->last_host = value; /* transfer */
103        }
104
105        if (req->body_size) {
106                g_byte_array_append(gw->in, (const guint8 *) req->reply_body, req->body_size);
107                gw->callback(gw->data, -1, B_EV_IO_READ);
108        }
109
110        if (gw->poll_timeout != -1) {
111                b_event_remove(gw->poll_timeout);
112        }
113        gw->poll_timeout = b_timeout_add(500, msn_gw_poll_timeout, gw);
114
115}
116
117void msn_gw_dorequest(struct msn_gw *gw, char *args)
118{
119        char *request = NULL;
120        char *body = NULL;
121        size_t bodylen = 0;
122
123        if (gw->out) {
124                bodylen = gw->out->len;
125                g_byte_array_append(gw->out, (guint8 *) "", 1); /* nullnullnull */
126                body = (char *) g_byte_array_free(gw->out, FALSE);
127                gw->out = g_byte_array_new();
128        }
129
130        if (!bodylen && !args) {
131                args = "Action=poll&Lifespan=60";
132                gw->polling = TRUE;
133        }
134
135        request = g_strdup_printf(REQUEST_TEMPLATE,
136                gw->session_id ? : "", args ? : "", gw->last_host, bodylen, body ? : "");
137
138        http_dorequest(gw->last_host, gw->port, gw->ssl, request, msn_gw_callback, gw);
139        gw->open = TRUE;
140        gw->waiting = TRUE;
141
142        g_free(body);
143        g_free(request);
144}
145
146void msn_gw_open(struct msn_gw *gw)
147{
148        msn_gw_dorequest(gw, "Action=open&Server=NS");
149}
150
151static gboolean msn_gw_poll_timeout(gpointer data, gint source, b_input_condition cond)
152{
153        struct msn_gw *gw = data;
154        gw->poll_timeout = -1;
155        if (!gw->waiting) {
156                msn_gw_dorequest(gw, NULL);
157        }
158        return FALSE;
159}
160
161ssize_t msn_gw_read(struct msn_gw *gw, char **buf)
162{
163        size_t bodylen;
164        if (!gw->open) {
165                return 0;
166        }
167
168        bodylen = gw->in->len;
169        g_byte_array_append(gw->in, (guint8 *) "", 1); /* nullnullnull */
170        *buf = (char *) g_byte_array_free(gw->in, FALSE);
171        gw->in = g_byte_array_new();
172        return bodylen;
173}
174
175void msn_gw_write(struct msn_gw *gw, char *buf, size_t len)
176{
177        g_byte_array_append(gw->out, (const guint8 *) buf, len);
178        if (!gw->open) {
179                msn_gw_open(gw);
180        } else if (gw->polling || !gw->waiting) {
181                msn_gw_dorequest(gw, NULL);
182        }
183}
Note: See TracBrowser for help on using the repository browser.