Mercurial > pidgin
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 } |