| 2 * |
2 * |
| 3 * \author Jeff Connelly |
3 * \author Jeff Connelly |
| 4 * |
4 * |
| 5 * Copyright (C) 2007, Jeff Connelly <myspaceim@xyzzy.cjb.net> |
5 * Copyright (C) 2007, Jeff Connelly <myspaceim@xyzzy.cjb.net> |
| 6 * |
6 * |
| 7 * Based on Gaim's "C Plugin HOWTO" hello world example. |
7 * Based on Purple's "C Plugin HOWTO" hello world example. |
| 8 * |
8 * |
| 9 * Code also drawn from myspace: |
9 * Code also drawn from myspace: |
| 10 * http://snarfed.org/space/gaim+mock+protocol+plugin |
10 * http://snarfed.org/space/purple+mock+protocol+plugin |
| 11 * Copyright (C) 2004-2007, Ryan Barrett <mockprpl@ryanb.org> |
11 * Copyright (C) 2004-2007, Ryan Barrett <mockprpl@ryanb.org> |
| 12 * |
12 * |
| 13 * and some constructs also based on existing Gaim plugins, which are: |
13 * and some constructs also based on existing Purple plugins, which are: |
| 14 * Copyright (C) 2003, Robbert Haarman <gaim@inglorion.net> |
14 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net> |
| 15 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> |
15 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> |
| 16 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com> |
16 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com> |
| 17 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
17 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
| 18 * |
18 * |
| 19 * This program is free software; you can redistribute it and/or modify |
19 * This program is free software; you can redistribute it and/or modify |
| 107 * user information is available, ready to send a message. */ |
111 * user information is available, ready to send a message. */ |
| 108 typedef struct _send_im_cb_struct |
112 typedef struct _send_im_cb_struct |
| 109 { |
113 { |
| 110 gchar *who; |
114 gchar *who; |
| 111 gchar *message; |
115 gchar *message; |
| 112 GaimMessageFlags flags; |
116 PurpleMessageFlags flags; |
| 113 } send_im_cb_struct; |
117 } send_im_cb_struct; |
| 114 |
118 |
| 115 |
119 |
| 116 /* TODO: .h file */ |
120 /* TODO: .h file */ |
| 117 static void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data); |
121 static void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data); |
| 118 static inline gboolean msim_is_userid(const gchar *user); |
122 static inline gboolean msim_is_userid(const gchar *user); |
| 119 static void msim_session_destroy(MsimSession *session); |
123 static void msim_session_destroy(MsimSession *session); |
| 120 |
124 |
| 121 static void init_plugin(GaimPlugin *plugin) |
125 static void init_plugin(PurplePlugin *plugin) |
| 122 { |
126 { |
| 123 gaim_notify_message(plugin, GAIM_NOTIFY_MSG_INFO, "Hello World!", |
127 purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!", |
| 124 "This is the Hello World! plugin :)", NULL, NULL, NULL); |
128 "This is the Hello World! plugin :)", NULL, NULL, NULL); |
| 125 } |
129 } |
| 126 |
130 |
| 127 /** |
131 /** |
| 128 * Get possible user status types. Based on mockprpl. |
132 * Get possible user status types. Based on mockprpl. |
| 129 * |
133 * |
| 130 * @return GList of status types. |
134 * @return GList of status types. |
| 131 */ |
135 */ |
| 132 static GList *msim_status_types(GaimAccount *acct) |
136 static GList *msim_status_types(PurpleAccount *acct) |
| 133 { |
137 { |
| 134 GList *types; |
138 GList *types; |
| 135 GaimStatusType *type; |
139 PurpleStatusType *type; |
| 136 |
140 |
| 137 gaim_debug_info("myspace", "returning status types for %s: %s, %s, %s\n", |
141 purple_debug_info("myspace", "returning status types for %s: %s, %s, %s\n", |
| 138 acct->username, |
142 acct->username, |
| 139 MSIM_STATUS_ONLINE, MSIM_STATUS_AWAY, MSIM_STATUS_OFFLINE, MSIM_STATUS_INVISIBLE); |
143 MSIM_STATUS_ONLINE, MSIM_STATUS_AWAY, MSIM_STATUS_OFFLINE, MSIM_STATUS_INVISIBLE); |
| 140 |
144 |
| 141 |
145 |
| 142 types = NULL; |
146 types = NULL; |
| 143 |
147 |
| 144 type = gaim_status_type_new(GAIM_STATUS_AVAILABLE, MSIM_STATUS_ONLINE, |
148 type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, MSIM_STATUS_ONLINE, |
| 145 MSIM_STATUS_ONLINE, TRUE); |
149 MSIM_STATUS_ONLINE, TRUE); |
| 146 gaim_status_type_add_attr(type, "message", "Online", |
150 purple_status_type_add_attr(type, "message", "Online", |
| 147 gaim_value_new(GAIM_TYPE_STRING)); |
151 purple_value_new(PURPLE_TYPE_STRING)); |
| 148 types = g_list_append(types, type); |
152 types = g_list_append(types, type); |
| 149 |
153 |
| 150 type = gaim_status_type_new(GAIM_STATUS_AWAY, MSIM_STATUS_AWAY, |
154 type = purple_status_type_new(PURPLE_STATUS_AWAY, MSIM_STATUS_AWAY, |
| 151 MSIM_STATUS_AWAY, TRUE); |
155 MSIM_STATUS_AWAY, TRUE); |
| 152 gaim_status_type_add_attr(type, "message", "Away", |
156 purple_status_type_add_attr(type, "message", "Away", |
| 153 gaim_value_new(GAIM_TYPE_STRING)); |
157 purple_value_new(PURPLE_TYPE_STRING)); |
| 154 types = g_list_append(types, type); |
158 types = g_list_append(types, type); |
| 155 |
159 |
| 156 type = gaim_status_type_new(GAIM_STATUS_OFFLINE, MSIM_STATUS_OFFLINE, |
160 type = purple_status_type_new(PURPLE_STATUS_OFFLINE, MSIM_STATUS_OFFLINE, |
| 157 MSIM_STATUS_OFFLINE, TRUE); |
161 MSIM_STATUS_OFFLINE, TRUE); |
| 158 gaim_status_type_add_attr(type, "message", "Offline", |
162 purple_status_type_add_attr(type, "message", "Offline", |
| 159 gaim_value_new(GAIM_TYPE_STRING)); |
163 purple_value_new(PURPLE_TYPE_STRING)); |
| 160 types = g_list_append(types, type); |
164 types = g_list_append(types, type); |
| 161 |
165 |
| 162 type = gaim_status_type_new(GAIM_STATUS_INVISIBLE, MSIM_STATUS_INVISIBLE, |
166 type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, MSIM_STATUS_INVISIBLE, |
| 163 MSIM_STATUS_INVISIBLE, TRUE); |
167 MSIM_STATUS_INVISIBLE, TRUE); |
| 164 gaim_status_type_add_attr(type, "message", "Invisible", |
168 purple_status_type_add_attr(type, "message", "Invisible", |
| 165 gaim_value_new(GAIM_TYPE_STRING)); |
169 purple_value_new(PURPLE_TYPE_STRING)); |
| 166 types = g_list_append(types, type); |
170 types = g_list_append(types, type); |
| 167 |
171 |
| 168 return types; |
172 return types; |
| 169 } |
173 } |
| 170 |
174 |
| 266 //memset(nonce, 0, NONCE_HALF_SIZE); |
268 //memset(nonce, 0, NONCE_HALF_SIZE); |
| 267 //memset(nonce + NONCE_HALF_SIZE, 1, NONCE_HALF_SIZE); |
269 //memset(nonce + NONCE_HALF_SIZE, 1, NONCE_HALF_SIZE); |
| 268 |
270 |
| 269 /* Convert ASCII password to UTF16 little endian */ |
271 /* Convert ASCII password to UTF16 little endian */ |
| 270 /* TODO: use the built-in facility to do this, like Nathan Peterson does. */ |
272 /* TODO: use the built-in facility to do this, like Nathan Peterson does. */ |
| 271 gaim_debug_info("msim", "converting password to utf16le\n"); |
273 purple_debug_info("msim", "converting password to utf16le\n"); |
| 272 //printf("pw=<%s>\n",password); |
274 //printf("pw=<%s>\n",password); |
| 273 password_utf16le = g_new0(gchar, strlen(password) * 2); |
275 password_utf16le = g_new0(gchar, strlen(password) * 2); |
| 274 for (i = 0; i < strlen(password) * 2; i += 2) |
276 for (i = 0; i < strlen(password) * 2; i += 2) |
| 275 { |
277 { |
| 276 password_utf16le[i] = password[i / 2]; |
278 password_utf16le[i] = password[i / 2]; |
| 277 password_utf16le[i + 1] = 0; |
279 password_utf16le[i + 1] = 0; |
| 278 } |
280 } |
| 279 |
281 |
| 280 /* Compute password hash */ |
282 /* Compute password hash */ |
| 281 gaim_cipher_digest_region("sha1", (guchar*)password_utf16le, strlen(password) * 2, |
283 purple_cipher_digest_region("sha1", (guchar*)password_utf16le, strlen(password) * 2, |
| 282 sizeof(hash_pw), hash_pw, NULL); |
284 sizeof(hash_pw), hash_pw, NULL); |
| 283 |
285 |
| 284 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE |
286 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE |
| 285 printf("pwhash = "); |
287 printf("pwhash = "); |
| 286 for (i = 0; i < sizeof(hash_pw); i++) |
288 for (i = 0; i < sizeof(hash_pw); i++) |
| 287 printf("%.2x ", hash_pw[i]); |
289 printf("%.2x ", hash_pw[i]); |
| 288 printf("\n"); |
290 printf("\n"); |
| 289 #endif |
291 #endif |
| 290 |
292 |
| 291 /* key = sha1(sha1(pw) + nonce2) */ |
293 /* key = sha1(sha1(pw) + nonce2) */ |
| 292 sha1 = gaim_ciphers_find_cipher("sha1"); |
294 sha1 = purple_ciphers_find_cipher("sha1"); |
| 293 key_context = gaim_cipher_context_new(sha1, NULL); |
295 key_context = purple_cipher_context_new(sha1, NULL); |
| 294 gaim_cipher_context_append(key_context, hash_pw, HASH_SIZE); |
296 purple_cipher_context_append(key_context, hash_pw, HASH_SIZE); |
| 295 gaim_cipher_context_append(key_context, nonce + NONCE_HALF_SIZE, NONCE_HALF_SIZE); |
297 purple_cipher_context_append(key_context, nonce + NONCE_HALF_SIZE, NONCE_HALF_SIZE); |
| 296 gaim_cipher_context_digest(key_context, sizeof(key), key, NULL); |
298 purple_cipher_context_digest(key_context, sizeof(key), key, NULL); |
| 297 |
299 |
| 298 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE |
300 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE |
| 299 printf("key = "); |
301 printf("key = "); |
| 300 for (i = 0; i < sizeof(key); i++) |
302 for (i = 0; i < sizeof(key); i++) |
| 301 { |
303 { |
| 352 int ret; |
354 int ret; |
| 353 |
355 |
| 354 g_return_if_fail(MSIM_SESSION_VALID(session)); |
356 g_return_if_fail(MSIM_SESSION_VALID(session)); |
| 355 g_return_if_fail(msg != NULL); |
357 g_return_if_fail(msg != NULL); |
| 356 |
358 |
| 357 gaim_debug_info("msim", "msim_send: writing <%s>\n", msg); |
359 purple_debug_info("msim", "msim_send: writing <%s>\n", msg); |
| 358 |
360 |
| 359 ret = send(session->fd, msg, strlen(msg), 0); |
361 ret = send(session->fd, msg, strlen(msg), 0); |
| 360 |
362 |
| 361 if (ret != strlen(msg)) |
363 if (ret != strlen(msg)) |
| 362 { |
364 { |
| 363 gaim_debug_info("msim", "msim_send(%s): strlen=%d, but only wrote %s\n", |
365 purple_debug_info("msim", "msim_send(%s): strlen=%d, but only wrote %s\n", |
| 364 msg, strlen(msg), ret); |
366 msg, strlen(msg), ret); |
| 365 /* TODO: better error */ |
367 /* TODO: better error */ |
| 366 } |
368 } |
| 367 } |
369 } |
| 368 |
370 |
| 389 nc_str = g_hash_table_lookup(table, "nc"); |
391 nc_str = g_hash_table_lookup(table, "nc"); |
| 390 |
392 |
| 391 account = session->account; |
393 account = session->account; |
| 392 //assert(account); |
394 //assert(account); |
| 393 |
395 |
| 394 gaim_connection_update_progress(session->gc, "Reading challenge", 1, 4); |
396 purple_connection_update_progress(session->gc, "Reading challenge", 1, 4); |
| 395 |
397 |
| 396 gaim_debug_info("msim", "nc=<%s>\n", nc_str); |
398 purple_debug_info("msim", "nc=<%s>\n", nc_str); |
| 397 |
399 |
| 398 nc = (guchar*)gaim_base64_decode(nc_str, &nc_len); |
400 nc = (guchar*)purple_base64_decode(nc_str, &nc_len); |
| 399 gaim_debug_info("msim", "base64 decoded to %d bytes\n", nc_len); |
401 purple_debug_info("msim", "base64 decoded to %d bytes\n", nc_len); |
| 400 if (nc_len != 0x40) |
402 if (nc_len != 0x40) |
| 401 { |
403 { |
| 402 gaim_debug_info("msim", "bad nc length: %x != 0x40\n", nc_len); |
404 purple_debug_info("msim", "bad nc length: %x != 0x40\n", nc_len); |
| 403 gaim_connection_error(session->gc, "Unexpected challenge length from server"); |
405 purple_connection_error(session->gc, "Unexpected challenge length from server"); |
| 404 return 0; |
406 return 0; |
| 405 } |
407 } |
| 406 |
408 |
| 407 gaim_connection_update_progress(session->gc, "Logging in", 2, 4); |
409 purple_connection_update_progress(session->gc, "Logging in", 2, 4); |
| 408 |
410 |
| 409 printf("going to compute login response\n"); |
411 printf("going to compute login response\n"); |
| 410 //response_str = msim_compute_login_response(nc_str, "testuser", "testpw"); //session->gc->account->username, session->gc->account->password); |
412 //response_str = msim_compute_login_response(nc_str, "testuser", "testpw"); //session->gc->account->username, session->gc->account->password); |
| 411 response_str = msim_compute_login_response(nc, account->username, account->password); |
413 response_str = msim_compute_login_response(nc, account->username, account->password); |
| 412 printf("got back login response\n"); |
414 printf("got back login response\n"); |
| 493 * Immediately send an IM to a user, by their numeric user ID. |
495 * Immediately send an IM to a user, by their numeric user ID. |
| 494 * |
496 * |
| 495 * @param session |
497 * @param session |
| 496 * @param userid ASCII numeric userid. |
498 * @param userid ASCII numeric userid. |
| 497 * @param message Text of message to send. |
499 * @param message Text of message to send. |
| 498 * @param flags Gaim instant message flags. |
500 * @param flags Purple instant message flags. |
| 499 * |
501 * |
| 500 * @return 0, since the 'table' parameter is no longer needed. |
502 * @return 0, since the 'table' parameter is no longer needed. |
| 501 * |
503 * |
| 502 */ |
504 */ |
| 503 static int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, GaimMessageFlags flags) |
505 static int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, PurpleMessageFlags flags) |
| 504 { |
506 { |
| 505 gchar *msg_string; |
507 gchar *msg_string; |
| 506 |
508 |
| 507 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); |
509 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); |
| 508 g_return_val_if_fail(userid != NULL, 0); |
510 g_return_val_if_fail(userid != NULL, 0); |
| 511 |
513 |
| 512 /* TODO: constants for bm types */ |
514 /* TODO: constants for bm types */ |
| 513 msg_string = g_strdup_printf("\\bm\\121\\sesskey\\%s\\t\\%s\\cv\\%d\\msg\\%s\\final\\", |
515 msg_string = g_strdup_printf("\\bm\\121\\sesskey\\%s\\t\\%s\\cv\\%d\\msg\\%s\\final\\", |
| 514 session->sesskey, userid, MSIM_CLIENT_VERSION, message); |
516 session->sesskey, userid, MSIM_CLIENT_VERSION, message); |
| 515 |
517 |
| 516 gaim_debug_info("msim", "going to write: %s\n", msg_string); |
518 purple_debug_info("msim", "going to write: %s\n", msg_string); |
| 517 |
519 |
| 518 msim_send(session, msg_string); |
520 msim_send(session, msg_string); |
| 519 |
521 |
| 520 /* TODO: notify Gaim that we sent the IM. */ |
522 /* TODO: notify Purple that we sent the IM. */ |
| 521 |
523 |
| 522 /* Not needed since sending messages to yourself is allowed by MSIM! */ |
524 /* Not needed since sending messages to yourself is allowed by MSIM! */ |
| 523 /*if (strcmp(from_username, who) == 0) |
525 /*if (strcmp(from_username, who) == 0) |
| 524 serv_got_im(gc, from_username, message, GAIM_MESSAGE_RECV, time(NULL)); |
526 serv_got_im(gc, from_username, message, PURPLE_MESSAGE_RECV, time(NULL)); |
| 525 */ |
527 */ |
| 526 |
528 |
| 527 return 0; |
529 return 0; |
| 528 } |
530 } |
| 529 |
531 |
| 599 username = g_hash_table_lookup(body, "UserName"); |
601 username = g_hash_table_lookup(body, "UserName"); |
| 600 if (username) |
602 if (username) |
| 601 { |
603 { |
| 602 g_hash_table_insert(session->user_lookup_cache, g_strdup(username), body); |
604 g_hash_table_insert(session->user_lookup_cache, g_strdup(username), body); |
| 603 } else { |
605 } else { |
| 604 gaim_debug_info("msim", "msim_process_reply: not caching <%s>, no UserName", |
606 purple_debug_info("msim", "msim_process_reply: not caching <%s>, no UserName", |
| 605 g_hash_table_lookup(table, "body")); |
607 g_hash_table_lookup(table, "body")); |
| 606 } |
608 } |
| 607 |
609 |
| 608 /* If a callback is registered for this userid lookup, call it. */ |
610 /* If a callback is registered for this userid lookup, call it. */ |
| 609 |
611 |
| 610 cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid)); |
612 cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid)); |
| 611 data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); |
613 data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); |
| 612 |
614 |
| 613 if (cb) |
615 if (cb) |
| 614 { |
616 { |
| 615 gaim_debug_info("msim", "msim_process_body: calling callback now\n"); |
617 purple_debug_info("msim", "msim_process_body: calling callback now\n"); |
| 616 cb(session, table, data); |
618 cb(session, table, data); |
| 617 g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid)); |
619 g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid)); |
| 618 g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); |
620 g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); |
| 619 |
621 |
| 620 /* Return 1 to tell caller of msim_process (msim_input_cb) to |
622 /* Return 1 to tell caller of msim_process (msim_input_cb) to |
| 621 * not destroy 'table'; allow 'cb' to hang on to it and destroy |
623 * not destroy 'table'; allow 'cb' to hang on to it and destroy |
| 622 * it when it wants. */ |
624 * it when it wants. */ |
| 623 return 1; |
625 return 1; |
| 624 } else { |
626 } else { |
| 625 gaim_debug_info("msim", "msim_process_body: no callback for rid %d\n", rid); |
627 purple_debug_info("msim", "msim_process_body: no callback for rid %d\n", rid); |
| 626 } |
628 } |
| 627 } |
629 } |
| 628 return 0; |
630 return 0; |
| 629 } |
631 } |
| 630 |
632 |
| 646 err = g_hash_table_lookup(table, "err"); |
648 err = g_hash_table_lookup(table, "err"); |
| 647 errmsg = g_hash_table_lookup(table, "errmsg"); |
649 errmsg = g_hash_table_lookup(table, "errmsg"); |
| 648 |
650 |
| 649 full_errmsg = g_strdup_printf("Protocol error, code %s: %s", err, errmsg); |
651 full_errmsg = g_strdup_printf("Protocol error, code %s: %s", err, errmsg); |
| 650 |
652 |
| 651 gaim_debug_info("msim", "msim_error: %s\n", full_errmsg); |
653 purple_debug_info("msim", "msim_error: %s\n", full_errmsg); |
| 652 |
654 |
| 653 /* TODO: check 'fatal' and die if asked to. |
655 /* TODO: check 'fatal' and die if asked to. |
| 654 * TODO: do something with the error # (localization of errmsg?) */ |
656 * TODO: do something with the error # (localization of errmsg?) */ |
| 655 gaim_notify_error(session->account, g_strdup("MySpaceIM Error"), |
657 purple_notify_error(session->account, g_strdup("MySpaceIM Error"), |
| 656 full_errmsg, NULL); |
658 full_errmsg, NULL); |
| 657 |
659 |
| 658 if (g_hash_table_lookup(table, "fatal")) |
660 if (g_hash_table_lookup(table, "fatal")) |
| 659 { |
661 { |
| 660 gaim_debug_info("msim", "fatal error, destroy session\n"); |
662 purple_debug_info("msim", "fatal error, destroy session\n"); |
| 661 gaim_connection_error(session->gc, full_errmsg); |
663 purple_connection_error(session->gc, full_errmsg); |
| 662 close(session->fd); |
664 close(session->fd); |
| 663 //msim_session_destroy(session); |
665 //msim_session_destroy(session); |
| 664 } |
666 } |
| 665 |
667 |
| 666 return 0; |
668 return 0; |
| 783 |
785 |
| 784 /* Example fields: |s|0|ss|Offline */ |
786 /* Example fields: |s|0|ss|Offline */ |
| 785 status_code = g_list_nth_data(list, 2); |
787 status_code = g_list_nth_data(list, 2); |
| 786 status_text = g_list_nth_data(list, 4); |
788 status_text = g_list_nth_data(list, 4); |
| 787 |
789 |
| 788 blist = gaim_get_blist(); |
790 blist = purple_get_blist(); |
| 789 |
791 |
| 790 /* Add buddy if not found */ |
792 /* Add buddy if not found */ |
| 791 buddy = gaim_find_buddy(session->account, username); |
793 buddy = purple_find_buddy(session->account, username); |
| 792 if (!buddy) |
794 if (!buddy) |
| 793 { |
795 { |
| 794 /* TODO: gaim aliases, userids and usernames */ |
796 /* TODO: purple aliases, userids and usernames */ |
| 795 gaim_debug_info("msim", "msim_status: making new buddy for %s\n", username); |
797 purple_debug_info("msim", "msim_status: making new buddy for %s\n", username); |
| 796 buddy = gaim_buddy_new(session->account, username, NULL); |
798 buddy = purple_buddy_new(session->account, username, NULL); |
| 797 |
799 |
| 798 /* TODO: sometimes (when click on it), buddy list disappears. Fix. */ |
800 /* TODO: sometimes (when click on it), buddy list disappears. Fix. */ |
| 799 gaim_blist_add_buddy(buddy, NULL, NULL, NULL); |
801 purple_blist_add_buddy(buddy, NULL, NULL, NULL); |
| 800 } else { |
802 } else { |
| 801 gaim_debug_info("msim", "msim_status: found buddy %s\n", username); |
803 purple_debug_info("msim", "msim_status: found buddy %s\n", username); |
| 802 } |
804 } |
| 803 |
805 |
| 804 /* For now, always set status to online. |
806 /* For now, always set status to online. |
| 805 * TODO: make status reflect reality |
807 * TODO: make status reflect reality |
| 806 * TODO: show headline */ |
808 * TODO: show headline */ |
| 807 presence = gaim_presence_new_for_buddy(buddy); |
809 presence = purple_presence_new_for_buddy(buddy); |
| 808 gaim_presence_set_status_active(presence, MSIM_STATUS_ONLINE, TRUE); |
810 purple_presence_set_status_active(presence, MSIM_STATUS_ONLINE, TRUE); |
| 809 |
811 |
| 810 g_strfreev(status_array); |
812 g_strfreev(status_array); |
| 811 g_list_free(list); |
813 g_list_free(list); |
| 812 g_hash_table_destroy(body); |
814 g_hash_table_destroy(body); |
| 813 g_hash_table_destroy(userinfo); |
815 g_hash_table_destroy(userinfo); |
| 831 g_return_val_if_fail(table != NULL, 0); |
833 g_return_val_if_fail(table != NULL, 0); |
| 832 |
834 |
| 833 status_str = g_hash_table_lookup(table, "msg"); |
835 status_str = g_hash_table_lookup(table, "msg"); |
| 834 if (!status_str) |
836 if (!status_str) |
| 835 { |
837 { |
| 836 gaim_debug_info("msim", "msim_status: bm=100 but no status msg\n"); |
838 purple_debug_info("msim", "msim_status: bm=100 but no status msg\n"); |
| 837 return 0; |
839 return 0; |
| 838 } |
840 } |
| 839 |
841 |
| 840 userid = g_hash_table_lookup(table, "f"); |
842 userid = g_hash_table_lookup(table, "f"); |
| 841 if (!userid) |
843 if (!userid) |
| 842 { |
844 { |
| 843 gaim_debug_info("msim", "msim_status: bm=100 but no f field\n"); |
845 purple_debug_info("msim", "msim_status: bm=100 but no f field\n"); |
| 844 return 0; |
846 return 0; |
| 845 } |
847 } |
| 846 |
848 |
| 847 /* TODO: if buddies were identified on buddy list by uid, wouldn't have to lookup |
849 /* TODO: if buddies were identified on buddy list by uid, wouldn't have to lookup |
| 848 * before updating the status! Much more efficient. */ |
850 * before updating the status! Much more efficient. */ |
| 849 gaim_debug_info("msim", "msim_status: got status msg <%s> for <%s>, scheduling lookup\n", |
851 purple_debug_info("msim", "msim_status: got status msg <%s> for <%s>, scheduling lookup\n", |
| 850 status_str, userid); |
852 status_str, userid); |
| 851 |
853 |
| 852 /* Actually update status once obtain username */ |
854 /* Actually update status once obtain username */ |
| 853 msim_lookup_user(session, userid, msim_status_cb, g_strdup(status_str)); |
855 msim_lookup_user(session, userid, msim_status_cb, g_strdup(status_str)); |
| 854 |
856 |
| 882 { |
884 { |
| 883 return msim_login_challenge(session, table); |
885 return msim_login_challenge(session, table); |
| 884 } else if (g_hash_table_lookup(table, "sesskey")) { |
886 } else if (g_hash_table_lookup(table, "sesskey")) { |
| 885 printf("SESSKEY=<%s>\n", (gchar*)g_hash_table_lookup(table, "sesskey")); |
887 printf("SESSKEY=<%s>\n", (gchar*)g_hash_table_lookup(table, "sesskey")); |
| 886 |
888 |
| 887 gaim_connection_update_progress(gc, "Connected", 3, 4); |
889 purple_connection_update_progress(gc, "Connected", 3, 4); |
| 888 |
890 |
| 889 session->sesskey = g_strdup(g_hash_table_lookup(table, "sesskey")); |
891 session->sesskey = g_strdup(g_hash_table_lookup(table, "sesskey")); |
| 890 |
892 |
| 891 /* Comes with: proof,profileid,userid,uniquenick -- all same values |
893 /* Comes with: proof,profileid,userid,uniquenick -- all same values |
| 892 * (at least for me). */ |
894 * (at least for me). */ |
| 893 session->userid = g_strdup(g_hash_table_lookup(table, "userid")); |
895 session->userid = g_strdup(g_hash_table_lookup(table, "userid")); |
| 894 |
896 |
| 895 gaim_connection_set_state(gc, GAIM_CONNECTED); |
897 purple_connection_set_state(gc, PURPLE_CONNECTED); |
| 896 |
898 |
| 897 return 0; |
899 return 0; |
| 898 } else if (g_hash_table_lookup(table, "bm")) { |
900 } else if (g_hash_table_lookup(table, "bm")) { |
| 899 guint bm; |
901 guint bm; |
| 900 |
902 |
| 920 } else if (g_hash_table_lookup(table, "rid")) { |
922 } else if (g_hash_table_lookup(table, "rid")) { |
| 921 return msim_process_reply(session, table); |
923 return msim_process_reply(session, table); |
| 922 } else if (g_hash_table_lookup(table, "error")) { |
924 } else if (g_hash_table_lookup(table, "error")) { |
| 923 return msim_error(session, table); |
925 return msim_error(session, table); |
| 924 } else if (g_hash_table_lookup(table, "ka")) { |
926 } else if (g_hash_table_lookup(table, "ka")) { |
| 925 gaim_debug_info("msim", "msim_process: got keep alive\n"); |
927 purple_debug_info("msim", "msim_process: got keep alive\n"); |
| 926 return 0; |
928 return 0; |
| 927 } else { |
929 } else { |
| 928 printf("<<unhandled>>\n"); |
930 printf("<<unhandled>>\n"); |
| 929 return 0; |
931 return 0; |
| 930 } |
932 } |
| 931 } |
933 } |
| 932 |
934 |
| 933 /** |
935 /** |
| 934 * Callback when input available. |
936 * Callback when input available. |
| 935 * |
937 * |
| 936 * @param gc_uncasted A GaimConnection pointer. |
938 * @param gc_uncasted A PurpleConnection pointer. |
| 937 * @param source File descriptor. |
939 * @param source File descriptor. |
| 938 * @param cond GAIM_INPUT_READ |
940 * @param cond PURPLE_INPUT_READ |
| 939 * |
941 * |
| 940 * Reads the input, and dispatches calls msim_process to handle it. |
942 * Reads the input, and dispatches calls msim_process to handle it. |
| 941 */ |
943 */ |
| 942 static void msim_input_cb(gpointer gc_uncasted, gint source, GaimInputCondition cond) |
944 static void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond) |
| 943 { |
945 { |
| 944 GaimConnection *gc; |
946 PurpleConnection *gc; |
| 945 GaimAccount *account; |
947 PurpleAccount *account; |
| 946 MsimSession *session; |
948 MsimSession *session; |
| 947 gchar *end; |
949 gchar *end; |
| 948 int n; |
950 int n; |
| 949 |
951 |
| 950 g_return_if_fail(gc_uncasted != NULL); |
952 g_return_if_fail(gc_uncasted != NULL); |
| 951 g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */ |
953 g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */ |
| 952 |
954 |
| 953 gc = (GaimConnection*)(gc_uncasted); |
955 gc = (PurpleConnection*)(gc_uncasted); |
| 954 account = gaim_connection_get_account(gc); |
956 account = purple_connection_get_account(gc); |
| 955 session = gc->proto_data; |
957 session = gc->proto_data; |
| 956 |
958 |
| 957 g_return_if_fail(MSIM_SESSION_VALID(session)); |
959 g_return_if_fail(MSIM_SESSION_VALID(session)); |
| 958 |
960 |
| 959 g_assert(cond == GAIM_INPUT_READ); |
961 g_assert(cond == PURPLE_INPUT_READ); |
| 960 |
962 |
| 961 /* Only can handle so much data at once... |
963 /* Only can handle so much data at once... |
| 962 * If this happens, try recompiling with a higher MSIM_READ_BUF_SIZE. |
964 * If this happens, try recompiling with a higher MSIM_READ_BUF_SIZE. |
| 963 * Should be large enough to hold the largest protocol message. |
965 * Should be large enough to hold the largest protocol message. |
| 964 */ |
966 */ |
| 965 if (session->rxoff == MSIM_READ_BUF_SIZE) |
967 if (session->rxoff == MSIM_READ_BUF_SIZE) |
| 966 { |
968 { |
| 967 gaim_debug_error("msim", "msim_input_cb: %d-byte read buffer full!\n", |
969 purple_debug_error("msim", "msim_input_cb: %d-byte read buffer full!\n", |
| 968 MSIM_READ_BUF_SIZE); |
970 MSIM_READ_BUF_SIZE); |
| 969 gaim_connection_error(gc, "Read buffer full"); |
971 purple_connection_error(gc, "Read buffer full"); |
| 970 /* TODO: fix 100% CPU after closing */ |
972 /* TODO: fix 100% CPU after closing */ |
| 971 close(source); |
973 close(source); |
| 972 return; |
974 return; |
| 973 } |
975 } |
| 974 |
976 |
| 975 gaim_debug_info("msim", "buffer at %d (max %d), reading up to %d\n", |
977 purple_debug_info("msim", "buffer at %d (max %d), reading up to %d\n", |
| 976 session->rxoff, MSIM_READ_BUF_SIZE, MSIM_READ_BUF_SIZE - session->rxoff); |
978 session->rxoff, MSIM_READ_BUF_SIZE, MSIM_READ_BUF_SIZE - session->rxoff); |
| 977 |
979 |
| 978 /* Read into buffer. On Win32, need recv() not read(). session->fd also holds |
980 /* Read into buffer. On Win32, need recv() not read(). session->fd also holds |
| 979 * the file descriptor, but it sometimes differs from the 'source' parameter. |
981 * the file descriptor, but it sometimes differs from the 'source' parameter. |
| 980 */ |
982 */ |
| 984 { |
986 { |
| 985 return; |
987 return; |
| 986 } |
988 } |
| 987 else if (n < 0) |
989 else if (n < 0) |
| 988 { |
990 { |
| 989 gaim_connection_error(gc, "Read error"); |
991 purple_connection_error(gc, "Read error"); |
| 990 gaim_debug_error("msim", "msim_input_cb: read error, ret=%d, " |
992 purple_debug_error("msim", "msim_input_cb: read error, ret=%d, " |
| 991 "error=%s, source=%d, fd=%d (%X))\n", |
993 "error=%s, source=%d, fd=%d (%X))\n", |
| 992 n, strerror(errno), source, session->fd, session->fd); |
994 n, strerror(errno), source, session->fd, session->fd); |
| 993 close(source); |
995 close(source); |
| 994 return; |
996 return; |
| 995 } |
997 } |
| 996 else if (n == 0) |
998 else if (n == 0) |
| 997 { |
999 { |
| 998 gaim_debug_info("msim", "msim_input_cb: server disconnected\n"); |
1000 purple_debug_info("msim", "msim_input_cb: server disconnected\n"); |
| 999 gaim_connection_error(gc, "Server has disconnected"); |
1001 purple_connection_error(gc, "Server has disconnected"); |
| 1000 return; |
1002 return; |
| 1001 } |
1003 } |
| 1002 |
1004 |
| 1003 /* Null terminate */ |
1005 /* Null terminate */ |
| 1004 session->rxbuf[session->rxoff + n] = 0; |
1006 session->rxbuf[session->rxoff + n] = 0; |
| 1005 |
1007 |
| 1006 /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */ |
1008 /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */ |
| 1007 if (strlen(session->rxbuf + session->rxoff) != n) |
1009 if (strlen(session->rxbuf + session->rxoff) != n) |
| 1008 { |
1010 { |
| 1009 /* Occurs after login, but it is not a null byte. */ |
1011 /* Occurs after login, but it is not a null byte. */ |
| 1010 gaim_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes" |
1012 purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes" |
| 1011 "--null byte encountered?\n", strlen(session->rxbuf + session->rxoff), n); |
1013 "--null byte encountered?\n", strlen(session->rxbuf + session->rxoff), n); |
| 1012 //gaim_connection_error(gc, "Invalid message - null byte on input"); |
1014 //purple_connection_error(gc, "Invalid message - null byte on input"); |
| 1013 return; |
1015 return; |
| 1014 } |
1016 } |
| 1015 |
1017 |
| 1016 session->rxoff += n; |
1018 session->rxoff += n; |
| 1017 gaim_debug_info("msim", "msim_input_cb: read=%d\n", n); |
1019 purple_debug_info("msim", "msim_input_cb: read=%d\n", n); |
| 1018 |
1020 |
| 1019 //printf("buf=<%s>\n", session->rxbuf); |
1021 //printf("buf=<%s>\n", session->rxbuf); |
| 1020 |
1022 |
| 1021 /* Look for \\final\\ end markers. If found, process message. */ |
1023 /* Look for \\final\\ end markers. If found, process message. */ |
| 1022 while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) |
1024 while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) |
| 1049 } |
1051 } |
| 1050 |
1052 |
| 1051 /** |
1053 /** |
| 1052 * Callback when connected. Sets up input handlers. |
1054 * Callback when connected. Sets up input handlers. |
| 1053 * |
1055 * |
| 1054 * @param data A GaimConnection pointer. |
1056 * @param data A PurpleConnection pointer. |
| 1055 * @param source File descriptor. |
1057 * @param source File descriptor. |
| 1056 * @param error_message |
1058 * @param error_message |
| 1057 */ |
1059 */ |
| 1058 static void msim_connect_cb(gpointer data, gint source, const gchar *error_message) |
1060 static void msim_connect_cb(gpointer data, gint source, const gchar *error_message) |
| 1059 { |
1061 { |
| 1060 GaimConnection *gc; |
1062 PurpleConnection *gc; |
| 1061 MsimSession *session; |
1063 MsimSession *session; |
| 1062 |
1064 |
| 1063 g_return_if_fail(data != NULL); |
1065 g_return_if_fail(data != NULL); |
| 1064 |
1066 |
| 1065 gc = (GaimConnection*)data; |
1067 gc = (PurpleConnection*)data; |
| 1066 session = gc->proto_data; |
1068 session = gc->proto_data; |
| 1067 |
1069 |
| 1068 if (source < 0) |
1070 if (source < 0) |
| 1069 { |
1071 { |
| 1070 gaim_connection_error(gc, "Couldn't connect to host"); |
1072 purple_connection_error(gc, "Couldn't connect to host"); |
| 1071 gaim_connection_error(gc, g_strdup_printf("Couldn't connect to host: %s (%d)", |
1073 purple_connection_error(gc, g_strdup_printf("Couldn't connect to host: %s (%d)", |
| 1072 error_message, source)); |
1074 error_message, source)); |
| 1073 return; |
1075 return; |
| 1074 } |
1076 } |
| 1075 |
1077 |
| 1076 session->fd = source; |
1078 session->fd = source; |
| 1077 |
1079 |
| 1078 gc->inpa = gaim_input_add(source, GAIM_INPUT_READ, msim_input_cb, gc); |
1080 gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc); |
| 1079 } |
1081 } |
| 1080 |
1082 |
| 1081 /* Session methods */ |
1083 /* Session methods */ |
| 1082 |
1084 |
| 1083 /** |
1085 /** |
| 1085 * |
1087 * |
| 1086 * @param acct The account to create the session from. |
1088 * @param acct The account to create the session from. |
| 1087 * |
1089 * |
| 1088 * @return Pointer to a new session. Free with msim_session_destroy. |
1090 * @return Pointer to a new session. Free with msim_session_destroy. |
| 1089 */ |
1091 */ |
| 1090 static MsimSession *msim_session_new(GaimAccount *acct) |
1092 static MsimSession *msim_session_new(PurpleAccount *acct) |
| 1091 { |
1093 { |
| 1092 MsimSession *session; |
1094 MsimSession *session; |
| 1093 |
1095 |
| 1094 g_return_val_if_fail(acct != NULL, NULL); |
1096 g_return_val_if_fail(acct != NULL, NULL); |
| 1095 |
1097 |
| 1096 session = g_new0(MsimSession, 1); |
1098 session = g_new0(MsimSession, 1); |
| 1097 |
1099 |
| 1098 session->magic = MSIM_SESSION_STRUCT_MAGIC; |
1100 session->magic = MSIM_SESSION_STRUCT_MAGIC; |
| 1099 session->account = acct; |
1101 session->account = acct; |
| 1100 session->gc = gaim_account_get_connection(acct); |
1102 session->gc = purple_account_get_connection(acct); |
| 1101 session->fd = -1; |
1103 session->fd = -1; |
| 1102 session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); /* do NOT free function pointers! */ |
1104 session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); /* do NOT free function pointers! */ |
| 1103 session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); |
1105 session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); |
| 1104 session->user_lookup_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy); |
1106 session->user_lookup_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy); |
| 1105 session->rxoff = 0; |
1107 session->rxoff = 0; |
| 1129 /** |
1131 /** |
| 1130 * Start logging in to the MSIM servers. |
1132 * Start logging in to the MSIM servers. |
| 1131 * |
1133 * |
| 1132 * @param acct Account information to use to login. |
1134 * @param acct Account information to use to login. |
| 1133 */ |
1135 */ |
| 1134 static void msim_login(GaimAccount *acct) |
1136 static void msim_login(PurpleAccount *acct) |
| 1135 { |
1137 { |
| 1136 GaimConnection *gc; |
1138 PurpleConnection *gc; |
| 1137 const char *host; |
1139 const char *host; |
| 1138 int port; |
1140 int port; |
| 1139 |
1141 |
| 1140 g_return_if_fail(acct != NULL); |
1142 g_return_if_fail(acct != NULL); |
| 1141 |
1143 |
| 1142 gaim_debug_info("myspace", "logging in %s\n", acct->username); |
1144 purple_debug_info("myspace", "logging in %s\n", acct->username); |
| 1143 |
1145 |
| 1144 gc = gaim_account_get_connection(acct); |
1146 gc = purple_account_get_connection(acct); |
| 1145 gc->proto_data = msim_session_new(acct); |
1147 gc->proto_data = msim_session_new(acct); |
| 1146 |
1148 |
| 1147 /* 1. connect to server */ |
1149 /* 1. connect to server */ |
| 1148 gaim_connection_update_progress(gc, "Connecting", |
1150 purple_connection_update_progress(gc, "Connecting", |
| 1149 0, /* which connection step this is */ |
1151 0, /* which connection step this is */ |
| 1150 4); /* total number of steps */ |
1152 4); /* total number of steps */ |
| 1151 |
1153 |
| 1152 /* TODO: GUI option to be user-modifiable. */ |
1154 /* TODO: GUI option to be user-modifiable. */ |
| 1153 host = gaim_account_get_string(acct, "server", MSIM_SERVER); |
1155 host = purple_account_get_string(acct, "server", MSIM_SERVER); |
| 1154 port = gaim_account_get_int(acct, "port", MSIM_PORT); |
1156 port = purple_account_get_int(acct, "port", MSIM_PORT); |
| 1155 /* TODO: connect */ |
1157 /* TODO: connect */ |
| 1156 /* From gaim.sf.net/api: |
1158 /* From purple.sf.net/api: |
| 1157 * """Note that this function name can be misleading--although it is called |
1159 * """Note that this function name can be misleading--although it is called |
| 1158 * "proxy connect," it is used for establishing any outgoing TCP connection, |
1160 * "proxy connect," it is used for establishing any outgoing TCP connection, |
| 1159 * whether through a proxy or not.""" */ |
1161 * whether through a proxy or not.""" */ |
| 1160 |
1162 |
| 1161 /* Calls msim_connect_cb when connected. */ |
1163 /* Calls msim_connect_cb when connected. */ |
| 1162 if (gaim_proxy_connect(gc, acct, host, port, msim_connect_cb, gc) == NULL) |
1164 if (purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc) == NULL) |
| 1163 { |
1165 { |
| 1164 /* TODO: try other ports if in auto mode, then save |
1166 /* TODO: try other ports if in auto mode, then save |
| 1165 * working port and try that first next time. */ |
1167 * working port and try that first next time. */ |
| 1166 gaim_connection_error(gc, "Couldn't create socket"); |
1168 purple_connection_error(gc, "Couldn't create socket"); |
| 1167 return; |
1169 return; |
| 1168 } |
1170 } |
| 1169 |
1171 |
| 1170 } |
1172 } |
| 1171 |
1173 |
| 1320 * |
1322 * |
| 1321 * The callback function calls msim_send_im_by_userid() to send the actual |
1323 * The callback function calls msim_send_im_by_userid() to send the actual |
| 1322 * instant message. If a userid is specified directly, this function is called |
1324 * instant message. If a userid is specified directly, this function is called |
| 1323 * immediately here. |
1325 * immediately here. |
| 1324 */ |
1326 */ |
| 1325 static int msim_send_im(GaimConnection *gc, const char *who, |
1327 static int msim_send_im(PurpleConnection *gc, const char *who, |
| 1326 const char *message, GaimMessageFlags flags) |
1328 const char *message, PurpleMessageFlags flags) |
| 1327 { |
1329 { |
| 1328 MsimSession *session; |
1330 MsimSession *session; |
| 1329 const char *from_username = gc->account->username; |
1331 const char *from_username = gc->account->username; |
| 1330 send_im_cb_struct *cbinfo; |
1332 send_im_cb_struct *cbinfo; |
| 1331 |
1333 |
| 1332 g_return_val_if_fail(gc != NULL, 0); |
1334 g_return_val_if_fail(gc != NULL, 0); |
| 1333 g_return_val_if_fail(who != NULL, 0); |
1335 g_return_val_if_fail(who != NULL, 0); |
| 1334 g_return_val_if_fail(message != NULL, 0); |
1336 g_return_val_if_fail(message != NULL, 0); |
| 1335 |
1337 |
| 1336 gaim_debug_info("msim", "sending message from %s to %s: %s\n", |
1338 purple_debug_info("msim", "sending message from %s to %s: %s\n", |
| 1337 from_username, who, message); |
1339 from_username, who, message); |
| 1338 |
1340 |
| 1339 session = gc->proto_data; |
1341 session = gc->proto_data; |
| 1340 |
1342 |
| 1341 /* If numeric ID, can send message immediately without userid lookup */ |
1343 /* If numeric ID, can send message immediately without userid lookup */ |
| 1342 if (msim_is_userid(who)) |
1344 if (msim_is_userid(who)) |
| 1343 { |
1345 { |
| 1344 gaim_debug_info("msim", "msim_send_im: numeric 'who' detected, sending asap\n"); |
1346 purple_debug_info("msim", "msim_send_im: numeric 'who' detected, sending asap\n"); |
| 1345 msim_send_im_by_userid(session, who, message, flags); |
1347 msim_send_im_by_userid(session, who, message, flags); |
| 1346 return 1; |
1348 return 1; |
| 1347 } |
1349 } |
| 1348 |
1350 |
| 1349 /* Otherwise, add callback to IM when userid of destination is available */ |
1351 /* Otherwise, add callback to IM when userid of destination is available */ |
| 1357 /* Send the request to lookup the userid */ |
1359 /* Send the request to lookup the userid */ |
| 1358 msim_lookup_user(session, who, msim_send_im_by_userid_cb, cbinfo); |
1360 msim_lookup_user(session, who, msim_send_im_by_userid_cb, cbinfo); |
| 1359 |
1361 |
| 1360 /* msim_send_im_by_userid_cb will now be called once userid is looked up */ |
1362 /* msim_send_im_by_userid_cb will now be called once userid is looked up */ |
| 1361 |
1363 |
| 1362 /* Return 1 to have Gaim show this IM as being sent, 0 to not. I always |
1364 /* Return 1 to have Purple show this IM as being sent, 0 to not. I always |
| 1363 * return 1 even if the message could not be sent, since I don't know if |
1365 * return 1 even if the message could not be sent, since I don't know if |
| 1364 * it has failed yet--because the IM is only sent after the userid is |
1366 * it has failed yet--because the IM is only sent after the userid is |
| 1365 * retrieved from the server (which happens after this function returns). |
1367 * retrieved from the server (which happens after this function returns). |
| 1366 * |
1368 * |
| 1367 * TODO: In MySpace, you login with your email address, but don't talk to other |
1369 * TODO: In MySpace, you login with your email address, but don't talk to other |
| 1437 userinfo = g_hash_table_lookup(session->user_lookup_cache, buddy->name); |
1439 userinfo = g_hash_table_lookup(session->user_lookup_cache, buddy->name); |
| 1438 |
1440 |
| 1439 g_assert(userinfo != NULL); |
1441 g_assert(userinfo != NULL); |
| 1440 |
1442 |
| 1441 // TODO: if (full), do something different |
1443 // TODO: if (full), do something different |
| 1442 gaim_notify_user_info_add_pair(user_info, "User ID", g_hash_table_lookup(userinfo, "UserID")); |
1444 purple_notify_user_info_add_pair(user_info, "User ID", g_hash_table_lookup(userinfo, "UserID")); |
| 1443 gaim_notify_user_info_add_pair(user_info, "Display Name", g_hash_table_lookup(userinfo, "DisplayName")); |
1445 purple_notify_user_info_add_pair(user_info, "Display Name", g_hash_table_lookup(userinfo, "DisplayName")); |
| 1444 gaim_notify_user_info_add_pair(user_info, "User Name", g_hash_table_lookup(userinfo, "UserName")); |
1446 purple_notify_user_info_add_pair(user_info, "User Name", g_hash_table_lookup(userinfo, "UserName")); |
| 1445 gaim_notify_user_info_add_pair(user_info, "Total Friends", g_hash_table_lookup(userinfo, "TotalFriends")); |
1447 purple_notify_user_info_add_pair(user_info, "Total Friends", g_hash_table_lookup(userinfo, "TotalFriends")); |
| 1446 gaim_notify_user_info_add_pair(user_info, "Song", |
1448 purple_notify_user_info_add_pair(user_info, "Song", |
| 1447 g_strdup_printf("%s - %s", |
1449 g_strdup_printf("%s - %s", |
| 1448 (gchar*)g_hash_table_lookup(userinfo, "BandName"), |
1450 (gchar*)g_hash_table_lookup(userinfo, "BandName"), |
| 1449 (gchar*)g_hash_table_lookup(userinfo, "SongName"))); |
1451 (gchar*)g_hash_table_lookup(userinfo, "SongName"))); |
| 1450 } |
1452 } |
| 1451 } |
1453 } |
| 1452 |
1454 |
| 1453 /** Callbacks called by Gaim, to access this plugin. */ |
1455 /** Callbacks called by Purple, to access this plugin. */ |
| 1454 static GaimPluginProtocolInfo prpl_info = |
1456 static PurplePluginProtocolInfo prpl_info = |
| 1455 { |
1457 { |
| 1456 OPT_PROTO_MAIL_CHECK,/* options - TODO: myspace will notify of mail */ |
1458 OPT_PROTO_MAIL_CHECK,/* options - TODO: myspace will notify of mail */ |
| 1457 NULL, /* user_splits */ |
1459 NULL, /* user_splits */ |
| 1458 NULL, /* protocol_options */ |
1460 NULL, /* protocol_options */ |
| 1459 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ |
1461 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ |
| 1518 }; |
1520 }; |
| 1519 |
1521 |
| 1520 |
1522 |
| 1521 |
1523 |
| 1522 /** Based on MSN's plugin info comments. */ |
1524 /** Based on MSN's plugin info comments. */ |
| 1523 static GaimPluginInfo info = |
1525 static PurplePluginInfo info = |
| 1524 { |
1526 { |
| 1525 GAIM_PLUGIN_MAGIC, |
1527 PURPLE_PLUGIN_MAGIC, |
| 1526 GAIM_MAJOR_VERSION, |
1528 PURPLE_MAJOR_VERSION, |
| 1527 GAIM_MINOR_VERSION, |
1529 PURPLE_MINOR_VERSION, |
| 1528 GAIM_PLUGIN_PROTOCOL, /**< type */ |
1530 PURPLE_PLUGIN_PROTOCOL, /**< type */ |
| 1529 NULL, /**< ui_requirement */ |
1531 NULL, /**< ui_requirement */ |
| 1530 0, /**< flags */ |
1532 0, /**< flags */ |
| 1531 NULL, /**< dependencies */ |
1533 NULL, /**< dependencies */ |
| 1532 GAIM_PRIORITY_DEFAULT, /**< priority */ |
1534 PURPLE_PRIORITY_DEFAULT, /**< priority */ |
| 1533 |
1535 |
| 1534 "prpl-myspace", /**< id */ |
1536 "prpl-myspace", /**< id */ |
| 1535 "MySpaceIM", /**< name */ |
1537 "MySpaceIM", /**< name */ |
| 1536 "0.4", /**< version */ |
1538 "0.4", /**< version */ |
| 1537 /** summary */ |
1539 /** summary */ |