comparison libpurple/protocols/msnp9/nexus.c @ 21312:a07cfce78345

Add MSNP9 back as an alternative alongside the existing MSN prpl. Cowardly old fools like me who prefer the stability of our MSNP9 code over the features of MSNP14 can enable this using the --disable-msnp14 ./configure option. If we want to release from i.p.p and MSN stability is the only blocker, we can trivially flick the default to use MSNP9 in configure.ac
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 11 Nov 2007 12:57:52 +0000
parents
children f5874552b8d5
comparison
equal deleted inserted replaced
21311:7d031cec5ba2 21312:a07cfce78345
1 /**
2 * @file nexus.c MSN Nexus functions
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */
24 #include "msn.h"
25 #include "nexus.h"
26 #include "notification.h"
27
28 /**************************************************************************
29 * Main
30 **************************************************************************/
31
32 MsnNexus *
33 msn_nexus_new(MsnSession *session)
34 {
35 MsnNexus *nexus;
36
37 nexus = g_new0(MsnNexus, 1);
38 nexus->session = session;
39 nexus->challenge_data = g_hash_table_new_full(g_str_hash,
40 g_str_equal, g_free, g_free);
41
42 return nexus;
43 }
44
45 void
46 msn_nexus_destroy(MsnNexus *nexus)
47 {
48 if (nexus->gsc)
49 purple_ssl_close(nexus->gsc);
50
51 g_free(nexus->login_host);
52
53 g_free(nexus->login_path);
54
55 if (nexus->challenge_data != NULL)
56 g_hash_table_destroy(nexus->challenge_data);
57
58 if (nexus->input_handler > 0)
59 purple_input_remove(nexus->input_handler);
60 g_free(nexus->write_buf);
61 g_free(nexus->read_buf);
62
63 g_free(nexus);
64 }
65
66 /**************************************************************************
67 * Util
68 **************************************************************************/
69
70 static gssize
71 msn_ssl_read(MsnNexus *nexus)
72 {
73 gssize len;
74 char temp_buf[4096];
75
76 if ((len = purple_ssl_read(nexus->gsc, temp_buf,
77 sizeof(temp_buf))) > 0)
78 {
79 nexus->read_buf = g_realloc(nexus->read_buf,
80 nexus->read_len + len + 1);
81 strncpy(nexus->read_buf + nexus->read_len, temp_buf, len);
82 nexus->read_len += len;
83 nexus->read_buf[nexus->read_len] = '\0';
84 }
85
86 return len;
87 }
88
89 static void
90 nexus_write_cb(gpointer data, gint source, PurpleInputCondition cond)
91 {
92 MsnNexus *nexus = data;
93 int len, total_len;
94
95 total_len = strlen(nexus->write_buf);
96
97 len = purple_ssl_write(nexus->gsc,
98 nexus->write_buf + nexus->written_len,
99 total_len - nexus->written_len);
100
101 if (len < 0 && errno == EAGAIN)
102 return;
103 else if (len <= 0) {
104 purple_input_remove(nexus->input_handler);
105 nexus->input_handler = 0;
106 /* TODO: notify of the error */
107 return;
108 }
109 nexus->written_len += len;
110
111 if (nexus->written_len < total_len)
112 return;
113
114 purple_input_remove(nexus->input_handler);
115 nexus->input_handler = 0;
116
117 g_free(nexus->write_buf);
118 nexus->write_buf = NULL;
119 nexus->written_len = 0;
120
121 nexus->written_cb(nexus, source, 0);
122 }
123
124 /**************************************************************************
125 * Login
126 **************************************************************************/
127
128 static void
129 login_connect_cb(gpointer data, PurpleSslConnection *gsc,
130 PurpleInputCondition cond);
131
132 static void
133 login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
134 {
135 MsnNexus *nexus;
136 MsnSession *session;
137
138 nexus = data;
139 g_return_if_fail(nexus != NULL);
140
141 nexus->gsc = NULL;
142
143 session = nexus->session;
144 g_return_if_fail(session != NULL);
145
146 msn_session_set_error(session, MSN_ERROR_AUTH, _("Unable to connect"));
147 /* the above line will result in nexus being destroyed, so we don't want
148 * to destroy it here, or we'd crash */
149 }
150
151 static void
152 nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
153 {
154 MsnNexus *nexus = data;
155 MsnSession *session;
156 int len;
157
158 session = nexus->session;
159 g_return_if_fail(session != NULL);
160
161 if (nexus->input_handler == 0)
162 /* TODO: Use purple_ssl_input_add()? */
163 nexus->input_handler = purple_input_add(nexus->gsc->fd,
164 PURPLE_INPUT_READ, nexus_login_written_cb, nexus);
165
166
167 len = msn_ssl_read(nexus);
168
169 if (len < 0 && errno == EAGAIN)
170 return;
171 else if (len < 0) {
172 purple_input_remove(nexus->input_handler);
173 nexus->input_handler = 0;
174 g_free(nexus->read_buf);
175 nexus->read_buf = NULL;
176 nexus->read_len = 0;
177 /* TODO: error handling */
178 return;
179 }
180
181 if (g_strstr_len(nexus->read_buf, nexus->read_len,
182 "\r\n\r\n") == NULL)
183 return;
184
185 purple_input_remove(nexus->input_handler);
186 nexus->input_handler = 0;
187
188 purple_ssl_close(nexus->gsc);
189 nexus->gsc = NULL;
190
191 purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf);
192
193 if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL)
194 {
195 /* Redirect. */
196 char *location, *c;
197
198 location = strstr(nexus->read_buf, "Location: ");
199 if (location == NULL)
200 {
201 g_free(nexus->read_buf);
202 nexus->read_buf = NULL;
203 nexus->read_len = 0;
204
205 return;
206 }
207 location = strchr(location, ' ') + 1;
208
209 if ((c = strchr(location, '\r')) != NULL)
210 *c = '\0';
211
212 /* Skip the http:// */
213 if ((c = strchr(location, '/')) != NULL)
214 location = c + 2;
215
216 if ((c = strchr(location, '/')) != NULL)
217 {
218 g_free(nexus->login_path);
219 nexus->login_path = g_strdup(c);
220
221 *c = '\0';
222 }
223
224 g_free(nexus->login_host);
225 nexus->login_host = g_strdup(location);
226
227 nexus->gsc = purple_ssl_connect(session->account,
228 nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
229 login_connect_cb, login_error_cb, nexus);
230 }
231 else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
232 {
233 const char *error;
234
235 if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL)
236 {
237 if ((error = strstr(error, "cbtxt=")) != NULL)
238 {
239 const char *c;
240 char *temp;
241
242 error += strlen("cbtxt=");
243
244 if ((c = strchr(error, '\n')) == NULL)
245 c = error + strlen(error);
246
247 temp = g_strndup(error, c - error);
248 error = purple_url_decode(temp);
249 g_free(temp);
250 if ((temp = strstr(error, " Do one of the following or try again:")) != NULL)
251 *temp = '\0';
252 }
253 }
254
255 msn_session_set_error(session, MSN_ERROR_AUTH, error);
256 }
257 else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable"))
258 {
259 msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
260 }
261 else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK"))
262 {
263 char *base, *c;
264 char *login_params;
265
266 #if 0
267 /* All your base are belong to us. */
268 base = buffer;
269
270 /* For great cookie! */
271 while ((base = strstr(base, "Set-Cookie: ")) != NULL)
272 {
273 base += strlen("Set-Cookie: ");
274
275 c = strchr(base, ';');
276
277 session->login_cookies =
278 g_list_append(session->login_cookies,
279 g_strndup(base, c - base));
280 }
281 #endif
282
283 base = strstr(nexus->read_buf, "Authentication-Info: ");
284
285 g_return_if_fail(base != NULL);
286
287 base = strstr(base, "from-PP='");
288 base += strlen("from-PP='");
289 c = strchr(base, '\'');
290
291 login_params = g_strndup(base, c - base);
292
293 msn_got_login_params(session, login_params);
294
295 g_free(login_params);
296
297 msn_nexus_destroy(nexus);
298 session->nexus = NULL;
299 return;
300 }
301
302 g_free(nexus->read_buf);
303 nexus->read_buf = NULL;
304 nexus->read_len = 0;
305
306 }
307
308 /* this guards against missing hash entries */
309 static char *
310 nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key)
311 {
312 char *entry;
313
314 return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ?
315 entry : "(null)";
316 }
317
318 void
319 login_connect_cb(gpointer data, PurpleSslConnection *gsc,
320 PurpleInputCondition cond)
321 {
322 MsnNexus *nexus;
323 MsnSession *session;
324 char *username, *password;
325 char *request_str, *head, *tail;
326 char *buffer = NULL;
327 guint32 ctint;
328
329 nexus = data;
330 g_return_if_fail(nexus != NULL);
331
332 session = nexus->session;
333 g_return_if_fail(session != NULL);
334
335 msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
336
337 username =
338 g_strdup(purple_url_encode(purple_account_get_username(session->account)));
339
340 password =
341 g_strndup(purple_url_encode(purple_connection_get_password(session->account->gc)), 16);
342
343 ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
344
345 head = g_strdup_printf(
346 "GET %s HTTP/1.1\r\n"
347 "Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s",
348 nexus->login_path,
349 (char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
350 username);
351
352 tail = g_strdup_printf(
353 "lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
354 "User-Agent: MSMSGS\r\n"
355 "Host: %s\r\n"
356 "Connection: Keep-Alive\r\n"
357 "Cache-Control: no-cache\r\n",
358 nexus_challenge_data_lookup(nexus->challenge_data, "lc"),
359 nexus_challenge_data_lookup(nexus->challenge_data, "id"),
360 nexus_challenge_data_lookup(nexus->challenge_data, "tw"),
361 nexus_challenge_data_lookup(nexus->challenge_data, "fs"),
362 nexus_challenge_data_lookup(nexus->challenge_data, "ru"),
363 ctint,
364 nexus_challenge_data_lookup(nexus->challenge_data, "kpp"),
365 nexus_challenge_data_lookup(nexus->challenge_data, "kv"),
366 nexus_challenge_data_lookup(nexus->challenge_data, "ver"),
367 nexus_challenge_data_lookup(nexus->challenge_data, "tpf"),
368 nexus->login_host);
369
370 buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail);
371 request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, password, tail);
372
373 purple_debug_misc("msn", "Sending: {%s}\n", buffer);
374
375 g_free(buffer);
376 g_free(head);
377 g_free(tail);
378 g_free(username);
379 g_free(password);
380
381 nexus->write_buf = request_str;
382 nexus->written_len = 0;
383
384 nexus->read_len = 0;
385
386 nexus->written_cb = nexus_login_written_cb;
387
388 nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
389 nexus_write_cb, nexus);
390
391 nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
392
393 return;
394
395
396 }
397
398 static void
399 nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
400 {
401 MsnNexus *nexus = data;
402 int len;
403 char *da_login;
404 char *base, *c;
405
406 if (nexus->input_handler == 0)
407 /* TODO: Use purple_ssl_input_add()? */
408 nexus->input_handler = purple_input_add(nexus->gsc->fd,
409 PURPLE_INPUT_READ, nexus_connect_written_cb, nexus);
410
411 /* Get the PassportURLs line. */
412 len = msn_ssl_read(nexus);
413
414 if (len < 0 && errno == EAGAIN)
415 return;
416 else if (len < 0) {
417 purple_input_remove(nexus->input_handler);
418 nexus->input_handler = 0;
419 g_free(nexus->read_buf);
420 nexus->read_buf = NULL;
421 nexus->read_len = 0;
422 /* TODO: error handling */
423 return;
424 }
425
426 if (g_strstr_len(nexus->read_buf, nexus->read_len,
427 "\r\n\r\n") == NULL)
428 return;
429
430 purple_input_remove(nexus->input_handler);
431 nexus->input_handler = 0;
432
433 base = strstr(nexus->read_buf, "PassportURLs");
434
435 if (base == NULL)
436 {
437 g_free(nexus->read_buf);
438 nexus->read_buf = NULL;
439 nexus->read_len = 0;
440 return;
441 }
442
443 if ((da_login = strstr(base, "DALogin=")) != NULL)
444 {
445 /* skip over "DALogin=" */
446 da_login += 8;
447
448 if ((c = strchr(da_login, ',')) != NULL)
449 *c = '\0';
450
451 if ((c = strchr(da_login, '/')) != NULL)
452 {
453 nexus->login_path = g_strdup(c);
454 *c = '\0';
455 }
456
457 nexus->login_host = g_strdup(da_login);
458 }
459
460 g_free(nexus->read_buf);
461 nexus->read_buf = NULL;
462 nexus->read_len = 0;
463
464 purple_ssl_close(nexus->gsc);
465
466 /* Now begin the connection to the login server. */
467 nexus->gsc = purple_ssl_connect(nexus->session->account,
468 nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
469 login_connect_cb, login_error_cb, nexus);
470 }
471
472
473 /**************************************************************************
474 * Connect
475 **************************************************************************/
476
477 static void
478 nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
479 PurpleInputCondition cond)
480 {
481 MsnNexus *nexus;
482 MsnSession *session;
483
484 nexus = data;
485 g_return_if_fail(nexus != NULL);
486
487 session = nexus->session;
488 g_return_if_fail(session != NULL);
489
490 msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH);
491
492 nexus->write_buf = g_strdup("GET /rdr/pprdr.asp\r\n\r\n");
493 nexus->written_len = 0;
494
495 nexus->read_len = 0;
496
497 nexus->written_cb = nexus_connect_written_cb;
498
499 nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
500 nexus_write_cb, nexus);
501
502 nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
503 }
504
505 void
506 msn_nexus_connect(MsnNexus *nexus)
507 {
508 nexus->gsc = purple_ssl_connect(nexus->session->account,
509 "nexus.passport.com", PURPLE_SSL_DEFAULT_PORT,
510 nexus_connect_cb, login_error_cb, nexus);
511 }