comparison libpurple/protocols/myspace/myspace.c @ 16747:496855295bd7

Use MsimMessage via msim_send(), instead of msim_send_raw(). This is a more object-oriented approach that simplifies sending of messages. Support for: - Several field types used in the protocol (integer, string, binary). - Transparently escape strings and base64 binary data as necessary. - Multiple values per key are supported (unlike GHashTable), required for WebChallenge message. - Order is preserved (unlike GHashTable). Also required for WebChallenge. Note that currently MsimMessage is only used for sending messages. -
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Wed, 23 May 2007 04:18:05 +0000
parents 52357f6392a4
children 0fa4a3e9b318
comparison
equal deleted inserted replaced
16746:52357f6392a4 16747:496855295bd7
4 * 4 *
5 * Copyright (C) 2007, Jeff Connelly <jeff2@homing.pidgin.im> 5 * Copyright (C) 2007, Jeff Connelly <jeff2@homing.pidgin.im>
6 * 6 *
7 * Based on Purple'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 mockprpl:
10 * http://snarfed.org/space/purple+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 Purple plugins, which are: 13 * and some constructs also based on existing Purple plugins, which are:
14 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net> 14 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 */ 32 */
33 33
34 #define PURPLE_PLUGIN 34 #define PURPLE_PLUGIN
35 35
36 #include <string.h>
37 #include <errno.h> /* for EAGAIN */
38 #include <stdarg.h>
39
40 #include <glib.h>
41
42 #ifdef _WIN32
43 #include "win32dep.h"
44 #else
45 /* For recv() and send(); needed to match Win32 */
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #endif
49
50 #include "internal.h"
51
52 #include "notify.h"
53 #include "plugin.h"
54 #include "accountopt.h"
55 #include "version.h"
56 #include "cipher.h" /* for SHA-1 */
57 #include "util.h" /* for base64 */
58 #include "debug.h" /* for purple_debug_info */
59
60 #include "myspace.h" 36 #include "myspace.h"
61 #include "message.h" 37 #include "message.h"
62 38
63 /** 39 /**
64 * Load the plugin. 40 * Load the plugin.
65 */ 41 */
66 static gboolean msim_load(PurplePlugin *plugin) 42 gboolean msim_load(PurplePlugin *plugin)
67 { 43 {
68 #ifdef MSIM_USE_PURPLE_RC4 44 #ifdef MSIM_USE_PURPLE_RC4
69 /* If compiled to use RC4 from libpurple, check if it is really there. */ 45 /* If compiled to use RC4 from libpurple, check if it is really there. */
70 if (!purple_ciphers_find_cipher("rc4")) 46 if (!purple_ciphers_find_cipher("rc4"))
71 { 47 {
72 purple_debug_error("msim", "compiled with MSIM_USE_PURPLE_RC4 but rc4 not in libpurple - not loading MySpaceIM plugin!\n"); 48 purple_debug_error("msim", "compiled with MSIM_USE_PURPLE_RC4 but rc4 not in libpurple - not loading MySpaceIM plugin!\n");
73 purple_notify_error(plugin, _("Missing Cipher"), 49 purple_notify_error(plugin, _("Missing Cipher"),
74 _("The RC4 cipher could not be found"), 50 _("The RC4 cipher could not be found"),
75 _("Recompile without MSIM_USE_PURPLE_RC4, or upgrade " 51 _("Recompile without MSIM_USE_PURPLE_RC4, or upgrade "
76 "to a libpurple with RC4 support (>= 2.0.1). MySpaceIM " 52 "to a libpurple with RC4 support (>= 2.0.1). MySpaceIM "
77 "plugin will not be loaded."); 53 "plugin will not be loaded."));
78 return FALSE; 54 return FALSE;
79 } 55 }
80 #endif 56 #endif
81 return TRUE; 57 return TRUE;
82 } 58 }
84 /** 60 /**
85 * Get possible user status types. Based on mockprpl. 61 * Get possible user status types. Based on mockprpl.
86 * 62 *
87 * @return GList of status types. 63 * @return GList of status types.
88 */ 64 */
89 static GList *msim_status_types(PurpleAccount *acct) 65 GList *msim_status_types(PurpleAccount *acct)
90 { 66 {
91 GList *types; 67 GList *types;
92 PurpleStatusType *type; 68 PurpleStatusType *type;
93 69
94 purple_debug_info("myspace", "returning status types for %s: %s, %s, %s\n", 70 purple_debug_info("myspace", "returning status types for %s: %s, %s, %s\n",
132 * @param acct The account to find the icon for, or NULL for protocol icon. 108 * @param acct The account to find the icon for, or NULL for protocol icon.
133 * @param buddy The buddy to find the icon for, or NULL for the account icon. 109 * @param buddy The buddy to find the icon for, or NULL for the account icon.
134 * 110 *
135 * @return The base icon name string. 111 * @return The base icon name string.
136 */ 112 */
137 static const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) 113 const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
138 { 114 {
139 /* Use a MySpace icon submitted by hbons submitted one at 115 /* Use a MySpace icon submitted by hbons submitted one at
140 * http://developer.pidgin.im/wiki/MySpaceIM. */ 116 * http://developer.pidgin.im/wiki/MySpaceIM. */
141 return "myspace"; 117 return "myspace";
142 } 118 }
144 /** 120 /**
145 * Unescape a protocol message. 121 * Unescape a protocol message.
146 * 122 *
147 * @return The unescaped message. Caller must g_free(). 123 * @return The unescaped message. Caller must g_free().
148 */ 124 */
149 static gchar *msim_unescape(const gchar *msg) 125 gchar *msim_unescape(const gchar *msg)
150 { 126 {
151 /* TODO: make more elegant, refactor with msim_escape */ 127 /* TODO: make more elegant, refactor with msim_escape */
152 gchar *tmp, *ret; 128 gchar *tmp, *ret;
153 129
154 tmp = str_replace(msg, "/1", "/"); 130 tmp = str_replace(msg, "/1", "/");
160 /** 136 /**
161 * Escape a protocol message. 137 * Escape a protocol message.
162 * 138 *
163 * @return The escaped message. Caller must g_free(). 139 * @return The escaped message. Caller must g_free().
164 */ 140 */
165 static gchar *msim_escape(const gchar *msg) 141 gchar *msim_escape(const gchar *msg)
166 { 142 {
167 /* TODO: make more elegant, refactor with msim_unescape */ 143 /* TODO: make more elegant, refactor with msim_unescape */
168 gchar *tmp, *ret; 144 gchar *tmp, *ret;
169 145
170 tmp = str_replace(msg, "/", "/1"); 146 tmp = str_replace(msg, "/", "/1");
186 * 162 *
187 * This string replace method is based on 163 * This string replace method is based on
188 * http://mail.gnome.org/archives/gtk-app-devel-list/2000-July/msg00201.html 164 * http://mail.gnome.org/archives/gtk-app-devel-list/2000-July/msg00201.html
189 * 165 *
190 */ 166 */
191 static gchar *str_replace(const gchar* str, const gchar *old, const gchar *new) 167 gchar *str_replace(const gchar* str, const gchar *old, const gchar *new)
192 { 168 {
193 char **items; 169 char **items;
194 char *ret; 170 char *ret;
195 171
196 items = g_strsplit(str, old, -1); 172 items = g_strsplit(str, old, -1);
207 * @param msg The message string to parse, will be g_free()'d. 183 * @param msg The message string to parse, will be g_free()'d.
208 * 184 *
209 * @return Hash table of message. Caller should destroy with 185 * @return Hash table of message. Caller should destroy with
210 * g_hash_table_destroy() when done. 186 * g_hash_table_destroy() when done.
211 */ 187 */
212 static GHashTable* msim_parse(gchar* msg) 188 GHashTable* msim_parse(gchar* msg)
213 { 189 {
214 GHashTable *table; 190 GHashTable *table;
215 gchar *token; 191 gchar *token;
216 gchar **tokens; 192 gchar **tokens;
217 gchar *key; 193 gchar *key;
290 * @param body_str The text of the dictionary to parse. Often the 266 * @param body_str The text of the dictionary to parse. Often the
291 * value for the 'body' field. 267 * value for the 'body' field.
292 * 268 *
293 * @return Hash table of the keys and values. Must g_hash_table_destroy() when done. 269 * @return Hash table of the keys and values. Must g_hash_table_destroy() when done.
294 */ 270 */
295 static GHashTable *msim_parse_body(const gchar *body_str) 271 GHashTable *msim_parse_body(const gchar *body_str)
296 { 272 {
297 GHashTable *table; 273 GHashTable *table;
298 gchar *item; 274 gchar *item;
299 gchar **items; 275 gchar **items;
300 gchar **elements; 276 gchar **elements;
349 } 325 }
350 326
351 327
352 328
353 #ifdef MSIM_DEBUG_MSG 329 #ifdef MSIM_DEBUG_MSG
354 static void print_hash_item(gpointer key, gpointer value, gpointer user_data) 330 void print_hash_item(gpointer key, gpointer value, gpointer user_data)
355 { 331 {
356 purple_debug_info("msim", "%s=%s\n", (char*)key, (char*)value); 332 purple_debug_info("msim", "%s=%s\n", (char*)key, (char*)value);
357 } 333 }
358 #endif 334 #endif
359 335
364 * @param msg The raw data to send. 340 * @param msg The raw data to send.
365 * 341 *
366 * @return TRUE if succeeded, FALSE if not. 342 * @return TRUE if succeeded, FALSE if not.
367 * 343 *
368 */ 344 */
369 static gboolean msim_send_raw(MsimSession *session, const gchar *msg) 345 gboolean msim_send_raw(MsimSession *session, const gchar *msg)
370 { 346 {
371 int total_bytes_sent, total_bytes; 347 int total_bytes_sent, total_bytes;
372 348
373 purple_debug_info("msim", "msim_send: writing <%s>\n", msg); 349 purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg);
374 350
375 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 351 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
376 g_return_val_if_fail(msg != NULL, FALSE); 352 g_return_val_if_fail(msg != NULL, FALSE);
377 353
378 354
396 372
397 } while(total_bytes_sent < total_bytes); 373 } while(total_bytes_sent < total_bytes);
398 return TRUE; 374 return TRUE;
399 } 375 }
400 376
401 /** Send an existing MsimMessage.
402 */
403
404 /* TODO: move to message.c */
405 static gboolean msim_msg_send(MsimSession *session, MsimMessage *msg)
406 {
407 gchar *raw;
408 gboolean success;
409
410 raw = msim_msg_pack(msg);
411 success = msim_send_raw(session, raw);
412 g_free(raw);
413
414 return success;
415 }
416
417 /**
418 *
419 * Send a message to the server, whose contents is specified using
420 * variable arguments.
421 *
422 * @param session
423 * @param ... A sequence of gchar* key/value pairs, terminated with NULL.
424 *
425 * IMPORTANT: The key/value pair strings are not copied. The 'key' strings
426 * are not freed, but the 'value' pair is. Use g_strdup() to pass a static
427 * string as a value. This function operates this way so that you can easily
428 * use a call to g_strdup_printf() for the 'value' strings and not have to
429 * worry about freeing the memory.
430 *
431 * It bears repeating: THE VALUE STRINGS WILL BE FREED. Copy if static.
432 *
433 * This function exists for coding convenience: a message can be created
434 * and sent in one line of code. Internally it calls msim_msg_send().
435 *
436 */
437
438 static gboolean msim_send(MsimSession *session, ...)
439 {
440 va_list argp;
441 gchar *key, *value;
442 MsimMessageType type;
443 gboolean success;
444 MsimMessage *msg;
445
446 msg = msim_msg_new();
447
448 /* Parse key, value pairs until NULL. */
449 va_start(argp, session);
450 do
451 {
452 key = va_arg(argp, gchar *);
453 if (!key)
454 {
455 break;
456 }
457
458 type = va_arg(argp, int);
459
460 switch (type)
461 {
462 case MSIM_TYPE_INTEGER:
463 msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int)));
464 break;
465
466 case MSIM_TYPE_STRING:
467 value = va_arg(argp, char *);
468
469 g_return_val_if_fail(value != NULL, FALSE);
470
471 msim_msg_append(msg, key, type, value);
472 break;
473
474 default:
475 purple_debug_info("msim", "msim_send: unknown type %d\n", type);
476 break;
477 }
478 } while(key && value);
479
480 /* Actually send the message. */
481 success = msim_msg_send(session, msg);
482
483 /* Cleanup. */
484 va_end(argp);
485 msim_msg_free(msg);
486
487 return success;
488 }
489
490 /** 377 /**
491 * Start logging in to the MSIM servers. 378 * Start logging in to the MSIM servers.
492 * 379 *
493 * @param acct Account information to use to login. 380 * @param acct Account information to use to login.
494 */ 381 */
495 static void msim_login(PurpleAccount *acct) 382 void msim_login(PurpleAccount *acct)
496 { 383 {
497 PurpleConnection *gc; 384 PurpleConnection *gc;
498 const char *host; 385 const char *host;
499 int port; 386 int port;
500 387
534 * @param session 421 * @param session
535 * @param table Hash table of login challenge message. 422 * @param table Hash table of login challenge message.
536 * 423 *
537 * @return 0, since the 'table' parameter is no longer needed. 424 * @return 0, since the 'table' parameter is no longer needed.
538 */ 425 */
539 static int msim_login_challenge(MsimSession *session, GHashTable *table) 426 int msim_login_challenge(MsimSession *session, GHashTable *table)
540 { 427 {
541 PurpleAccount *account; 428 PurpleAccount *account;
542 gchar *nc_str; 429 gchar *nc_str;
543 guchar *nc; 430 guchar *nc;
544 gchar *response_str; 431 gchar *response;
545 gsize nc_len; 432 gsize nc_len;
546 gchar *buf; 433 guint response_len;
547 434
548 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 435 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
549 g_return_val_if_fail(table != NULL, 0); 436 g_return_val_if_fail(table != NULL, 0);
550 437
551 nc_str = g_hash_table_lookup(table, "nc"); 438 nc_str = g_hash_table_lookup(table, "nc");
566 return 0; 453 return 0;
567 } 454 }
568 455
569 purple_connection_update_progress(session->gc, _("Logging in"), 2, 4); 456 purple_connection_update_progress(session->gc, _("Logging in"), 2, 4);
570 457
571 response_str = msim_compute_login_response(nc, account->username, account->password); 458 response = msim_compute_login_response(nc, account->username, account->password, &response_len);
572 459
573 g_free(nc); 460 g_free(nc);
574 461
575 /* Reply */
576 /* \status\100\id\1\login2\196610\username\msimprpl@xyzzy.cjb.net\clientver\673\reconn\0\response\+6M+DQhovMxwdt1ceIervus9O5K+X9BR02w6B4+4+Zrg66pWrSzX94ER8efSb8Ju9kye2MnDdpTDwybziACy2mQFWIB9Mf5/1Tdlr6kAtJA==\final\
577 this fails. what is it?
578 - escaping? no, removing msim_escape() still causes login failure. should verify.
579 - message order? should login2 come first?
580 - something else?
581 */
582 #if 0
583 msim_send(session, 462 msim_send(session,
584 "login2", g_strdup_printf("%d", MSIM_AUTH_ALGORITHM), 463 "login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM,
585 "username", g_strdup(account->username), 464 "username", MSIM_TYPE_STRING, g_strdup(account->username),
586 "response", g_strdup(response_str), 465 /* GString and gchar * response will be freed in msim_msg_free() in msim_send(). */
587 "clientver", g_strdup_printf("%d", MSIM_CLIENT_VERSION), 466 "response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len),
588 "reconn", g_strdup_printf("%d", 0), 467 "clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
589 "status", g_strdup_printf("%d", 100), 468 "reconn", MSIM_TYPE_INTEGER, 0,
590 "id", g_strdup_printf("%d", 1), 469 "status", MSIM_TYPE_INTEGER, 100,
470 "id", MSIM_TYPE_INTEGER, 1,
591 NULL); 471 NULL);
592 #else
593
594 /* TODO: use msim_send. But, response_str must NOT be escaped. */
595 /* \login2\196610\username\msimprpl@xyzzy.cjb.net\response\nseVXvvrwgQsv7FUAbHJLMP8YPEGKHftwN+Z0zCjmxOTOc0/nVPQWZ5Znv5i6kh26XfZlqNzvoPqaXNbXL6TsSZpU/guAAg0o6XBA1e/Sw==\clientver\673\reconn\0\status\100\id\1\final\ - works*/
596 buf = g_strdup_printf("\\login2\\%d\\username\\%s\\response\\%s\\clientver\\%d\\reconn\\%d\\status\\%d\\id\\1\\final\\",
597 MSIM_AUTH_ALGORITHM, account->username, response_str, MSIM_CLIENT_VERSION, 0, 100);
598
599
600 purple_debug_info("msim", "response=<%s>\n", buf);
601
602 msim_send_raw(session, buf);
603
604 g_free(buf);
605 #endif
606
607 g_free(response_str);
608 472
609 return 0; 473 return 0;
610 } 474 }
611 475
612 #ifndef MSIM_USE_PURPLE_RC4 476 #ifndef MSIM_USE_PURPLE_RC4
721 * Compute the base64'd login challenge response based on username, password, nonce, and IPs. 585 * Compute the base64'd login challenge response based on username, password, nonce, and IPs.
722 * 586 *
723 * @param nonce The base64 encoded nonce ('nc') field from the server. 587 * @param nonce The base64 encoded nonce ('nc') field from the server.
724 * @param email User's email address (used as login name). 588 * @param email User's email address (used as login name).
725 * @param password User's cleartext password. 589 * @param password User's cleartext password.
726 * 590 * @param response_len Will be written with response length.
727 * @return Encoded login challenge response, ready to send to the server. Must be g_free()'d 591 *
592 * @return Binary login challenge response, ready to send to the server. Must be g_free()'d
728 * when finished. 593 * when finished.
729 */ 594 */
730 static gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE], 595 gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE],
731 gchar* email, gchar* password) 596 gchar* email, gchar* password, guint* response_len)
732 { 597 {
733 PurpleCipherContext *key_context; 598 PurpleCipherContext *key_context;
734 PurpleCipher *sha1; 599 PurpleCipher *sha1;
735 #ifdef MSIM_USE_PURPLE_RC4 600 #ifdef MSIM_USE_PURPLE_RC4
736 PurpleCipherContext *rc4; 601 PurpleCipherContext *rc4;
741 guchar hash_pw[HASH_SIZE]; 606 guchar hash_pw[HASH_SIZE];
742 guchar key[HASH_SIZE]; 607 guchar key[HASH_SIZE];
743 gchar* password_utf16le; 608 gchar* password_utf16le;
744 guchar* data; 609 guchar* data;
745 guchar* data_out; 610 guchar* data_out;
746 gchar* response;
747 size_t data_len, data_out_len; 611 size_t data_len, data_out_len;
748 gsize conv_bytes_read, conv_bytes_written; 612 gsize conv_bytes_read, conv_bytes_written;
749 GError* conv_error; 613 GError* conv_error;
750 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 614 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
751 int i; 615 int i;
839 data_out = data; 703 data_out = data;
840 #endif 704 #endif
841 705
842 g_assert(data_out_len == data_len); 706 g_assert(data_out_len == data_len);
843 707
844 response = purple_base64_encode(data_out, data_out_len); 708 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
845 #ifdef MSIM_USE_PURPLE_RC4 709 purple_debug_info("msim", "response=<%s>\n", data_out);
846 g_free(data_out);
847 #endif 710 #endif
848 711
849 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 712 *response_len = data_out_len;
850 purple_debug_info("msim", "response=<%s>\n", response); 713
851 #endif 714 return (gchar*)data_out;
852
853 return response;
854 } 715 }
855 716
856 /** 717 /**
857 * Schedule an IM to be sent once the user ID is looked up. 718 * Schedule an IM to be sent once the user ID is looked up.
858 * 719 *
871 * 732 *
872 * The callback function calls msim_send_im_by_userid() to send the actual 733 * The callback function calls msim_send_im_by_userid() to send the actual
873 * instant message. If a userid is specified directly, this function is called 734 * instant message. If a userid is specified directly, this function is called
874 * immediately here. 735 * immediately here.
875 */ 736 */
876 static int msim_send_im(PurpleConnection *gc, const char *who, 737 int msim_send_im(PurpleConnection *gc, const char *who,
877 const char *message, PurpleMessageFlags flags) 738 const char *message, PurpleMessageFlags flags)
878 { 739 {
879 MsimSession *session; 740 MsimSession *session;
880 const char *from_username = gc->account->username; 741 const char *from_username = gc->account->username;
881 send_im_cb_struct *cbinfo; 742 send_im_cb_struct *cbinfo;
939 * @param flags Purple instant message flags. 800 * @param flags Purple instant message flags.
940 * 801 *
941 * @return 0, since the 'table' parameter is no longer needed. 802 * @return 0, since the 'table' parameter is no longer needed.
942 * 803 *
943 */ 804 */
944 static int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, PurpleMessageFlags flags) 805 int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, PurpleMessageFlags flags)
945 { 806 {
946 gchar *msg_string;
947 gchar *escaped_message;
948
949 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 807 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
950 g_return_val_if_fail(userid != NULL, 0); 808 g_return_val_if_fail(userid != NULL, 0);
951 g_return_val_if_fail(msim_is_userid(userid) == TRUE, 0); 809 g_return_val_if_fail(msim_is_userid(userid) == TRUE, 0);
952 g_return_val_if_fail(message != NULL, 0); 810 g_return_val_if_fail(message != NULL, 0);
953 811
954 /* TODO: Remove this code and use MsimMessage after it is implemented and escapes strings. */ 812 msim_send(session,
955 escaped_message = msim_escape(message); 813 "bm", MSIM_TYPE_INTEGER, MSIM_BM_INSTANT,
956 814 "sesskey", MSIM_TYPE_STRING, g_strdup(session->sesskey),
957 /* TODO: escape values */ 815 "t", MSIM_TYPE_STRING, g_strdup(userid),
958 msg_string = g_strdup_printf("\\bm\\%d\\sesskey\\%s\\t\\%s\\cv\\%d\\msg\\%s\\final\\", 816 "cv", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
959 MSIM_BM_INSTANT, session->sesskey, userid, MSIM_CLIENT_VERSION, message); 817 "msg", MSIM_TYPE_STRING, g_strdup(message),
960 818 NULL);
961 /* TODO: Remove this code and use MsimMessage after it is implemented and escapes strings. */
962 g_free(escaped_message);
963
964 purple_debug_info("msim", "going to write: %s\n", msg_string);
965
966 msim_send_raw(session, msg_string);
967
968 /* TODO: notify Purple that we sent the IM. */
969 819
970 /* Not needed since sending messages to yourself is allowed by MSIM! */ 820 /* Not needed since sending messages to yourself is allowed by MSIM! */
971 /*if (strcmp(from_username, who) == 0) 821 /*if (strcmp(from_username, who) == 0)
972 serv_got_im(gc, from_username, message, PURPLE_MESSAGE_RECV, time(NULL)); 822 serv_got_im(gc, from_username, message, PURPLE_MESSAGE_RECV, time(NULL));
973 */ 823 */
984 * @param userinfo User info message from server containing a 'body' field 834 * @param userinfo User info message from server containing a 'body' field
985 * with a 'UserID' key. This is where the user ID is taken from. 835 * with a 'UserID' key. This is where the user ID is taken from.
986 * @param data A send_im_cb_struct* of information on the IM to send. 836 * @param data A send_im_cb_struct* of information on the IM to send.
987 * 837 *
988 */ 838 */
989 static void msim_send_im_by_userid_cb(MsimSession *session, GHashTable *userinfo, gpointer data) 839 void msim_send_im_by_userid_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
990 { 840 {
991 send_im_cb_struct *s; 841 send_im_cb_struct *s;
992 gchar *userid; 842 gchar *userid;
993 GHashTable *body; 843 GHashTable *body;
994 844
1014 * 864 *
1015 * @param session 865 * @param session
1016 * @param userinfo Message from server on user's info, containing UserName. 866 * @param userinfo Message from server on user's info, containing UserName.
1017 * @param data A gchar* of the incoming instant message's text. 867 * @param data A gchar* of the incoming instant message's text.
1018 */ 868 */
1019 static void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, gpointer data) 869 void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
1020 { 870 {
1021 gchar *msg; 871 gchar *msg;
1022 gchar *username; 872 gchar *username;
1023 GHashTable *body; 873 GHashTable *body;
1024 874
1043 * @param session The session 893 * @param session The session
1044 * @param table Message from the server, containing 'f' (userid from) and 'msg'. 894 * @param table Message from the server, containing 'f' (userid from) and 'msg'.
1045 * 895 *
1046 * @return 0, since table can be freed. 896 * @return 0, since table can be freed.
1047 */ 897 */
1048 static int msim_incoming_im(MsimSession *session, GHashTable *table) 898 int msim_incoming_im(MsimSession *session, GHashTable *table)
1049 { 899 {
1050 gchar *userid; 900 gchar *userid;
1051 gchar *msg; 901 gchar *msg;
1052 902
1053 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 903 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
1074 * @param table Any message from the server. 924 * @param table Any message from the server.
1075 * 925 *
1076 * @return The return value of the function used to process the message, or -1 if 926 * @return The return value of the function used to process the message, or -1 if
1077 * called with invalid parameters. 927 * called with invalid parameters.
1078 */ 928 */
1079 static int msim_process(PurpleConnection *gc, GHashTable *table) 929 int msim_process(PurpleConnection *gc, GHashTable *table)
1080 { 930 {
1081 MsimSession *session; 931 MsimSession *session;
1082 932
1083 g_return_val_if_fail(gc != NULL, -1); 933 g_return_val_if_fail(gc != NULL, -1);
1084 g_return_val_if_fail(table != NULL, -1); 934 g_return_val_if_fail(table != NULL, -1);
1151 * @param session 1001 * @param session
1152 * @param table Message reply from server. 1002 * @param table Message reply from server.
1153 * 1003 *
1154 * @return 0, since the 'table' field is no longer needed. 1004 * @return 0, since the 'table' field is no longer needed.
1155 */ 1005 */
1156 static int msim_process_reply(MsimSession *session, GHashTable *table) 1006 int msim_process_reply(MsimSession *session, GHashTable *table)
1157 { 1007 {
1158 gchar *rid_str; 1008 gchar *rid_str;
1159 1009
1160 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 1010 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
1161 g_return_val_if_fail(table != NULL, 0); 1011 g_return_val_if_fail(table != NULL, 0);
1221 * @param session 1071 * @param session
1222 * @param table The message. 1072 * @param table The message.
1223 * 1073 *
1224 * @return 0, since 'table' can be freed. 1074 * @return 0, since 'table' can be freed.
1225 */ 1075 */
1226 static int msim_error(MsimSession *session, GHashTable *table) 1076 int msim_error(MsimSession *session, GHashTable *table)
1227 { 1077 {
1228 gchar *err, *errmsg, *full_errmsg; 1078 gchar *err, *errmsg, *full_errmsg;
1229 1079
1230 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 1080 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
1231 g_return_val_if_fail(table != NULL, 0); 1081 g_return_val_if_fail(table != NULL, 0);
1253 return 0; 1103 return 0;
1254 } 1104 }
1255 1105
1256 #if 0 1106 #if 0
1257 /* Not sure about this */ 1107 /* Not sure about this */
1258 static void msim_status_now(gchar *who, gpointer data) 1108 void msim_status_now(gchar *who, gpointer data)
1259 { 1109 {
1260 printf("msim_status_now: %s\n", who); 1110 printf("msim_status_now: %s\n", who);
1261 } 1111 }
1262 #endif 1112 #endif
1263 1113
1267 * @param session 1117 * @param session
1268 * @param userinfo Looked up user information from server. 1118 * @param userinfo Looked up user information from server.
1269 * @param data gchar* status string. 1119 * @param data gchar* status string.
1270 * 1120 *
1271 */ 1121 */
1272 static void msim_status_cb(MsimSession *session, GHashTable *userinfo, gpointer data) 1122 void msim_status_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
1273 { 1123 {
1274 PurpleBuddyList *blist; 1124 PurpleBuddyList *blist;
1275 PurpleBuddy *buddy; 1125 PurpleBuddy *buddy;
1276 PurplePresence *presence; 1126 PurplePresence *presence;
1277 GHashTable *body; 1127 GHashTable *body;
1354 * @param session 1204 * @param session
1355 * @param table Status update message. 1205 * @param table Status update message.
1356 * 1206 *
1357 * @return 0, since 'table' can be freed. 1207 * @return 0, since 'table' can be freed.
1358 */ 1208 */
1359 static int msim_status(MsimSession *session, GHashTable *table) 1209 int msim_status(MsimSession *session, GHashTable *table)
1360 { 1210 {
1361 gchar *status_str; 1211 gchar *status_str;
1362 gchar *userid; 1212 gchar *userid;
1363 1213
1364 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 1214 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
1399 * @param source File descriptor. 1249 * @param source File descriptor.
1400 * @param cond PURPLE_INPUT_READ 1250 * @param cond PURPLE_INPUT_READ
1401 * 1251 *
1402 * Reads the input, and dispatches calls msim_process to handle it. 1252 * Reads the input, and dispatches calls msim_process to handle it.
1403 */ 1253 */
1404 static void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond) 1254 void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond)
1405 { 1255 {
1406 PurpleConnection *gc; 1256 PurpleConnection *gc;
1407 PurpleAccount *account; 1257 PurpleAccount *account;
1408 MsimSession *session; 1258 MsimSession *session;
1409 gchar *end; 1259 gchar *end;
1521 * 1371 *
1522 * @param data A PurpleConnection pointer. 1372 * @param data A PurpleConnection pointer.
1523 * @param source File descriptor. 1373 * @param source File descriptor.
1524 * @param error_message 1374 * @param error_message
1525 */ 1375 */
1526 static void msim_connect_cb(gpointer data, gint source, const gchar *error_message) 1376 void msim_connect_cb(gpointer data, gint source, const gchar *error_message)
1527 { 1377 {
1528 PurpleConnection *gc; 1378 PurpleConnection *gc;
1529 MsimSession *session; 1379 MsimSession *session;
1530 1380
1531 g_return_if_fail(data != NULL); 1381 g_return_if_fail(data != NULL);
1553 * 1403 *
1554 * @param acct The account to create the session from. 1404 * @param acct The account to create the session from.
1555 * 1405 *
1556 * @return Pointer to a new session. Free with msim_session_destroy. 1406 * @return Pointer to a new session. Free with msim_session_destroy.
1557 */ 1407 */
1558 static MsimSession *msim_session_new(PurpleAccount *acct) 1408 MsimSession *msim_session_new(PurpleAccount *acct)
1559 { 1409 {
1560 MsimSession *session; 1410 MsimSession *session;
1561 1411
1562 g_return_val_if_fail(acct != NULL, NULL); 1412 g_return_val_if_fail(acct != NULL, NULL);
1563 1413
1583 /** 1433 /**
1584 * Free a session. 1434 * Free a session.
1585 * 1435 *
1586 * @param session The session to destroy. 1436 * @param session The session to destroy.
1587 */ 1437 */
1588 static void msim_session_destroy(MsimSession *session) 1438 void msim_session_destroy(MsimSession *session)
1589 { 1439 {
1590 g_return_if_fail(MSIM_SESSION_VALID(session)); 1440 g_return_if_fail(MSIM_SESSION_VALID(session));
1591 1441
1592 session->magic = -1; 1442 session->magic = -1;
1593 1443
1603 /** 1453 /**
1604 * Close the connection. 1454 * Close the connection.
1605 * 1455 *
1606 * @param gc The connection. 1456 * @param gc The connection.
1607 */ 1457 */
1608 static void msim_close(PurpleConnection *gc) 1458 void msim_close(PurpleConnection *gc)
1609 { 1459 {
1610 g_return_if_fail(gc != NULL); 1460 g_return_if_fail(gc != NULL);
1611 1461
1612 msim_session_destroy(gc->proto_data); 1462 msim_session_destroy(gc->proto_data);
1613 } 1463 }
1618 * 1468 *
1619 * @param user The user id, email, or name. 1469 * @param user The user id, email, or name.
1620 * 1470 *
1621 * @return TRUE if is userid, FALSE if not. 1471 * @return TRUE if is userid, FALSE if not.
1622 */ 1472 */
1623 static inline gboolean msim_is_userid(const gchar *user) 1473 gboolean msim_is_userid(const gchar *user)
1624 { 1474 {
1625 g_return_val_if_fail(user != NULL, FALSE); 1475 g_return_val_if_fail(user != NULL, FALSE);
1626 1476
1627 return strspn(user, "0123456789") == strlen(user); 1477 return strspn(user, "0123456789") == strlen(user);
1628 } 1478 }
1637 * This function is not intended to be used as a generic 1487 * This function is not intended to be used as a generic
1638 * means of validating email addresses, but to distinguish 1488 * means of validating email addresses, but to distinguish
1639 * between a user represented by an email address from 1489 * between a user represented by an email address from
1640 * other forms of identification. 1490 * other forms of identification.
1641 */ 1491 */
1642 static inline gboolean msim_is_email(const gchar *user) 1492 gboolean msim_is_email(const gchar *user)
1643 { 1493 {
1644 g_return_val_if_fail(user != NULL, FALSE); 1494 g_return_val_if_fail(user != NULL, FALSE);
1645 1495
1646 return strchr(user, '@') != NULL; 1496 return strchr(user, '@') != NULL;
1647 } 1497 }
1653 * @param session 1503 * @param session
1654 * @param user The user id, email address, or username. 1504 * @param user The user id, email address, or username.
1655 * @param cb Callback, called with user information when available. 1505 * @param cb Callback, called with user information when available.
1656 * @param data An arbitray data pointer passed to the callback. 1506 * @param data An arbitray data pointer passed to the callback.
1657 */ 1507 */
1658 static void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data) 1508 void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
1659 { 1509 {
1660 gchar *field_name; 1510 gchar *field_name;
1661 gchar *msg_string;
1662 guint rid, cmd, dsn, lid; 1511 guint rid, cmd, dsn, lid;
1663 1512
1664 g_return_if_fail(MSIM_SESSION_VALID(session)); 1513 g_return_if_fail(MSIM_SESSION_VALID(session));
1665 g_return_if_fail(user != NULL); 1514 g_return_if_fail(user != NULL);
1666 g_return_if_fail(cb != NULL); 1515 g_return_if_fail(cb != NULL);
1704 field_name = "UserName"; 1553 field_name = "UserName";
1705 dsn = 5; 1554 dsn = 5;
1706 lid = 7; 1555 lid = 7;
1707 } 1556 }
1708 1557
1709 /* TODO: escape values */ 1558
1710 msg_string = g_strdup_printf("\\persist\\1\\sesskey\\%s\\cmd\\1\\dsn\\%d\\uid\\%s\\lid\\%d\\rid\\%d\\body\\%s=%s\\final\\", 1559 msim_send(session,
1711 session->sesskey, dsn, session->userid, lid, rid, field_name, user); 1560 "persist", MSIM_TYPE_INTEGER, 1,
1712 1561 "sesskey", MSIM_TYPE_STRING, g_strdup(session->sesskey),
1713 msim_send_raw(session, msg_string); 1562 "cmd", MSIM_TYPE_INTEGER, 1,
1563 "dsn", MSIM_TYPE_INTEGER, dsn,
1564 "uid", MSIM_TYPE_STRING, g_strdup(session->userid),
1565 "lid", MSIM_TYPE_INTEGER, lid,
1566 "rid", MSIM_TYPE_INTEGER, rid,
1567 /* TODO: dictionary field type */
1568 "body", MSIM_TYPE_STRING, g_strdup_printf("%s=%s", field_name, user),
1569 NULL);
1714 } 1570 }
1715 1571
1716 1572
1717 /** 1573 /**
1718 * Obtain the status text for a buddy. 1574 * Obtain the status text for a buddy.
1720 * @param buddy The buddy to obtain status text for. 1576 * @param buddy The buddy to obtain status text for.
1721 * 1577 *
1722 * @return Status text, or NULL if error. 1578 * @return Status text, or NULL if error.
1723 * 1579 *
1724 */ 1580 */
1725 static char *msim_status_text(PurpleBuddy *buddy) 1581 char *msim_status_text(PurpleBuddy *buddy)
1726 { 1582 {
1727 MsimSession *session; 1583 MsimSession *session;
1728 GHashTable *userinfo; 1584 GHashTable *userinfo;
1729 gchar *display_name; 1585 gchar *display_name;
1730 1586
1752 * @param buddy Buddy to obtain tooltip text on. 1608 * @param buddy Buddy to obtain tooltip text on.
1753 * @param user_info Variable modified to have the tooltip text. 1609 * @param user_info Variable modified to have the tooltip text.
1754 * @param full TRUE if should obtain full tooltip text. 1610 * @param full TRUE if should obtain full tooltip text.
1755 * 1611 *
1756 */ 1612 */
1757 static void msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full) 1613 void msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
1758 { 1614 {
1759 g_return_if_fail(buddy != NULL); 1615 g_return_if_fail(buddy != NULL);
1760 g_return_if_fail(user_info != NULL); 1616 g_return_if_fail(user_info != NULL);
1761 1617
1762 if (PURPLE_BUDDY_IS_ONLINE(buddy)) 1618 if (PURPLE_BUDDY_IS_ONLINE(buddy))
1784 (gchar*)g_hash_table_lookup(userinfo, "SongName"))); 1640 (gchar*)g_hash_table_lookup(userinfo, "SongName")));
1785 } 1641 }
1786 } 1642 }
1787 1643
1788 /** Callbacks called by Purple, to access this plugin. */ 1644 /** Callbacks called by Purple, to access this plugin. */
1789 static PurplePluginProtocolInfo prpl_info = 1645 PurplePluginProtocolInfo prpl_info =
1790 { 1646 {
1791 OPT_PROTO_MAIL_CHECK,/* options - TODO: myspace will notify of mail */ 1647 OPT_PROTO_MAIL_CHECK,/* options - TODO: myspace will notify of mail */
1792 NULL, /* user_splits */ 1648 NULL, /* user_splits */
1793 NULL, /* protocol_options */ 1649 NULL, /* protocol_options */
1794 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ 1650 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */
1857 }; 1713 };
1858 1714
1859 1715
1860 1716
1861 /** Based on MSN's plugin info comments. */ 1717 /** Based on MSN's plugin info comments. */
1862 static PurplePluginInfo info = 1718 PurplePluginInfo info =
1863 { 1719 {
1864 PURPLE_PLUGIN_MAGIC, 1720 PURPLE_PLUGIN_MAGIC,
1865 PURPLE_MAJOR_VERSION, 1721 PURPLE_MAJOR_VERSION,
1866 PURPLE_MINOR_VERSION, 1722 PURPLE_MINOR_VERSION,
1867 PURPLE_PLUGIN_PROTOCOL, /**< type */ 1723 PURPLE_PLUGIN_PROTOCOL, /**< type */
1896 NULL /**< reserved4 */ 1752 NULL /**< reserved4 */
1897 }; 1753 };
1898 1754
1899 #include "message.h" 1755 #include "message.h"
1900 1756
1901 static void init_plugin(PurplePlugin *plugin) 1757 void init_plugin(PurplePlugin *plugin)
1902 { 1758 {
1903 PurpleAccountOption *option; 1759 PurpleAccountOption *option;
1904 #ifdef _TEST_MSIM_MSG 1760 #ifdef _TEST_MSIM_MSG
1905 MsimMessage *msg = msim_msg_new(); 1761 MsimMessage *msg = msim_msg_new();
1906 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v1"); 1762 msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len(g_strdup("XXX"), 3));
1763 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1"));
1907 msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, 42); 1764 msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, 42);
1908 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v43"); 1765 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v43"));
1909 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v5"); 1766 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v52/xxx\\yyy"));
1910 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v7"); 1767 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v7"));
1768 purple_debug_info("msim", "msg=%s\n", msim_msg_debug_string(msg));
1911 purple_debug_info("msim", "msg=%s\n", msim_msg_pack(msg)); 1769 purple_debug_info("msim", "msg=%s\n", msim_msg_pack(msg));
1912 purple_debug_info("msim", "msg=%s\n", msim_msg_debug_string(msg));
1913 msim_msg_free(msg); 1770 msim_msg_free(msg);
1914 exit(0); 1771 exit(0);
1915 #endif 1772 #endif
1916 1773
1917 /* TODO: default to automatically try different ports. Make the user be 1774 /* TODO: default to automatically try different ports. Make the user be