comparison libpurple/protocols/myspace/myspace.c @ 18895:f732d072b118

Change all code to better match the style of libpurple - use 8-space tabs for indentation everywhere. Spaces are still used for alignment when needed, as described on http://derkarl.org/why_to_tabs.html (ack for [^\t]\t to ensure tabs are only being used for indentation). Lots of vim regexes made this transformation possible. Also cuddled a few braces that I missed before. This is a big commit, but there are no actual code changes.
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Sat, 11 Aug 2007 05:53:11 +0000
parents daedc9647341
children d0be4366e876
comparison
equal deleted inserted replaced
18894:daedc9647341 18895:f732d072b118
53 53
54 /* TODO: Use extra smileys not in default theme! Hylke is also working on 54 /* TODO: Use extra smileys not in default theme! Hylke is also working on
55 * some new smileys specific to MySpaceIM, use them too! */ 55 * some new smileys specific to MySpaceIM, use them too! */
56 static struct MSIM_EMOTICON 56 static struct MSIM_EMOTICON
57 { 57 {
58 gchar *name; 58 gchar *name;
59 gchar *symbol; 59 gchar *symbol;
60 } msim_emoticons[] = { 60 } msim_emoticons[] = {
61 { "bigsmile", ":D" }, 61 { "bigsmile", ":D" },
62 { "growl", ">:o" }, 62 { "growl", ">:o" },
63 { "growl", ">:O" }, 63 { "growl", ">:O" },
64 { "mad", ":-[" }, 64 { "mad", ":-[" },
65 { "scared", "=-O" }, 65 { "scared", "=-O" },
66 { "scared", "=-o" }, 66 { "scared", "=-o" },
67 { "tongue", ":P" }, 67 { "tongue", ":P" },
68 { "tongue", ":p" }, 68 { "tongue", ":p" },
69 { "devil", "O:-)" }, 69 { "devil", "O:-)" },
70 { "devil", "o:-)" }, 70 { "devil", "o:-)" },
71 { "happy", ":)" }, 71 { "happy", ":)" },
72 { "happy", ":-)" }, 72 { "happy", ":-)" },
73 { "happi", ":-)" }, 73 { "happi", ":-)" },
74 { "messed", "8-)" }, 74 { "messed", "8-)" },
75 { "sidefrown", ":-$" } , 75 { "sidefrown", ":-$" } ,
76 { "upset", ":-$" }, 76 { "upset", ":-$" },
77 { "frazzled", ":-/" } , 77 { "frazzled", ":-/" } ,
78 { "heart", ";-)" }, 78 { "heart", ";-)" },
79 { "heart", ";)" }, 79 { "heart", ";)" },
80 { "nerd", "8-)"}, 80 { "nerd", "8-)"},
81 { "sinister", ":-,D" } , 81 { "sinister", ":-,D" } ,
82 { "wink", ";-)" }, 82 { "wink", ";-)" },
83 { "winc", ";-)" }, 83 { "winc", ";-)" },
84 { "geek", ":-X" }, 84 { "geek", ":-X" },
85 { "laugh", ":-D" }, 85 { "laugh", ":-D" },
86 { "laugh", ":-d" }, 86 { "laugh", ":-d" },
87 { "oops", ":'(" }, 87 { "oops", ":'(" },
88 { "smirk", "8-)" }, 88 { "smirk", "8-)" },
89 { "worried", ":-(" } , 89 { "worried", ":-(" } ,
90 { "worried", ":(" }, 90 { "worried", ":(" },
91 { "googles", "8-)" }, 91 { "googles", "8-)" },
92 { "mohawk", ":-X" }, 92 { "mohawk", ":-X" },
93 { "pirate", ":-)" }, 93 { "pirate", ":-)" },
94 { "straight", ":-!" }, 94 { "straight", ":-!" },
95 { "kiss", ":-*" }, 95 { "kiss", ":-*" },
96 { NULL, NULL } 96 { NULL, NULL }
97 }; 97 };
98 98
99 /* Internal functions */ 99 /* Internal functions */
100 static void msim_send_zap(PurpleBlistNode *node, gpointer zap_num_ptr); 100 static void msim_send_zap(PurpleBlistNode *node, gpointer zap_num_ptr);
101 101
102 #ifdef MSIM_DEBUG_MSG 102 #ifdef MSIM_DEBUG_MSG
103 static void print_hash_item(gpointer key, gpointer value, gpointer user_data); 103 static void print_hash_item(gpointer key, gpointer value, gpointer user_data);
104 #endif 104 #endif
105 105
106 static int msim_send_really_raw(PurpleConnection *gc, const char *buf, 106 static int msim_send_really_raw(PurpleConnection *gc, const char *buf,
107 int total_bytes); 107 int total_bytes);
108 static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg); 108 static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg);
109 static const gchar *msim_compute_login_response( 109 static const gchar *msim_compute_login_response(
110 const gchar nonce[2 * NONCE_SIZE], const gchar *email, 110 const gchar nonce[2 * NONCE_SIZE], const gchar *email,
111 const gchar *password, guint *response_len); 111 const gchar *password, guint *response_len);
112 static gboolean msim_send_bm(MsimSession *session, const gchar *who, 112 static gboolean msim_send_bm(MsimSession *session, const gchar *who,
113 const gchar *text, int type); 113 const gchar *text, int type);
114 114
115 static guint msim_point_to_purple_size(MsimSession *session, guint point); 115 static guint msim_point_to_purple_size(MsimSession *session, guint point);
116 static guint msim_purple_size_to_point(MsimSession *session, guint size); 116 static guint msim_purple_size_to_point(MsimSession *session, guint size);
117 static guint msim_height_to_point(MsimSession *session, guint height); 117 static guint msim_height_to_point(MsimSession *session, guint height);
118 static guint msim_point_to_height(MsimSession *session, guint point); 118 static guint msim_point_to_height(MsimSession *session, guint point);
119 119
120 static void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); 120 static void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note);
121 121
122 static void msim_markup_tag_to_html(MsimSession *, xmlnode *root, 122 static void msim_markup_tag_to_html(MsimSession *, xmlnode *root,
123 gchar **begin, gchar **end); 123 gchar **begin, gchar **end);
124 static void html_tag_to_msim_markup(MsimSession *, xmlnode *root, 124 static void html_tag_to_msim_markup(MsimSession *, xmlnode *root,
125 gchar **begin, gchar **end); 125 gchar **begin, gchar **end);
126 static gchar *msim_convert_xml(MsimSession *, const gchar *raw, 126 static gchar *msim_convert_xml(MsimSession *, const gchar *raw,
127 MSIM_XMLNODE_CONVERT f); 127 MSIM_XMLNODE_CONVERT f);
128 static gchar *msim_convert_smileys_to_markup(gchar *before); 128 static gchar *msim_convert_smileys_to_markup(gchar *before);
129 129
130 /* High-level msim markup <=> html conversion functions. */ 130 /* High-level msim markup <=> html conversion functions. */
131 static gchar *msim_markup_to_html(MsimSession *, const gchar *raw); 131 static gchar *msim_markup_to_html(MsimSession *, const gchar *raw);
132 static gchar *html_to_msim_markup(MsimSession *, const gchar *raw); 132 static gchar *html_to_msim_markup(MsimSession *, const gchar *raw);
133 133
134 static gboolean msim_incoming_bm_record_cv(MsimSession *session, 134 static gboolean msim_incoming_bm_record_cv(MsimSession *session,
135 MsimMessage *msg); 135 MsimMessage *msg);
136 static gboolean msim_incoming_bm(MsimSession *session, MsimMessage *msg); 136 static gboolean msim_incoming_bm(MsimSession *session, MsimMessage *msg);
137 static gboolean msim_incoming_status(MsimSession *session, MsimMessage *msg); 137 static gboolean msim_incoming_status(MsimSession *session, MsimMessage *msg);
138 static gboolean msim_incoming_im(MsimSession *session, MsimMessage *msg); 138 static gboolean msim_incoming_im(MsimSession *session, MsimMessage *msg);
139 static gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg); 139 static gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg);
140 static gboolean msim_incoming_action(MsimSession *session, MsimMessage *msg); 140 static gboolean msim_incoming_action(MsimSession *session, MsimMessage *msg);
141 static gboolean msim_incoming_media(MsimSession *session, MsimMessage *msg); 141 static gboolean msim_incoming_media(MsimSession *session, MsimMessage *msg);
142 static gboolean msim_incoming_unofficial_client(MsimSession *session, 142 static gboolean msim_incoming_unofficial_client(MsimSession *session,
143 MsimMessage *msg); 143 MsimMessage *msg);
144 144
145 #ifdef MSIM_SEND_CLIENT_VERSION 145 #ifdef MSIM_SEND_CLIENT_VERSION
146 static gboolean msim_send_unofficial_client(MsimSession *session, 146 static gboolean msim_send_unofficial_client(MsimSession *session,
147 gchar *username); 147 gchar *username);
148 #endif 148 #endif
149 149
150 static void msim_get_info_cb(MsimSession *session, MsimMessage *userinfo, gpointer data); 150 static void msim_get_info_cb(MsimSession *session, MsimMessage *userinfo, gpointer data);
151 151
152 static void msim_set_status_code(MsimSession *session, guint code, 152 static void msim_set_status_code(MsimSession *session, guint code,
153 gchar *statstring); 153 gchar *statstring);
154 154
155 static void msim_store_buddy_info_each(gpointer key, gpointer value, 155 static void msim_store_buddy_info_each(gpointer key, gpointer value,
156 gpointer user_data); 156 gpointer user_data);
157 static gboolean msim_store_buddy_info(MsimSession *session, MsimMessage *msg); 157 static gboolean msim_store_buddy_info(MsimSession *session, MsimMessage *msg);
158 static gboolean msim_process_server_info(MsimSession *session, 158 static gboolean msim_process_server_info(MsimSession *session,
159 MsimMessage *msg); 159 MsimMessage *msg);
160 static gboolean msim_web_challenge(MsimSession *session, MsimMessage *msg); 160 static gboolean msim_web_challenge(MsimSession *session, MsimMessage *msg);
161 static gboolean msim_process_reply(MsimSession *session, MsimMessage *msg); 161 static gboolean msim_process_reply(MsimSession *session, MsimMessage *msg);
162 162
163 static gboolean msim_preprocess_incoming(MsimSession *session,MsimMessage *msg); 163 static gboolean msim_preprocess_incoming(MsimSession *session,MsimMessage *msg);
164 164
169 static gboolean msim_we_are_logged_on(MsimSession *session, MsimMessage *msg); 169 static gboolean msim_we_are_logged_on(MsimSession *session, MsimMessage *msg);
170 170
171 static gboolean msim_process(MsimSession *session, MsimMessage *msg); 171 static gboolean msim_process(MsimSession *session, MsimMessage *msg);
172 172
173 static MsimMessage *msim_do_postprocessing(MsimMessage *msg, 173 static MsimMessage *msim_do_postprocessing(MsimMessage *msg,
174 const gchar *uid_field_name, const gchar *uid_before, guint uid); 174 const gchar *uid_field_name, const gchar *uid_before, guint uid);
175 static void msim_postprocess_outgoing_cb(MsimSession *session, 175 static void msim_postprocess_outgoing_cb(MsimSession *session,
176 MsimMessage *userinfo, gpointer data); 176 MsimMessage *userinfo, gpointer data);
177 static gboolean msim_postprocess_outgoing(MsimSession *session, 177 static gboolean msim_postprocess_outgoing(MsimSession *session,
178 MsimMessage *msg, const gchar *username, const gchar *uid_field_name, 178 MsimMessage *msg, const gchar *username, const gchar *uid_field_name,
179 const gchar *uid_before); 179 const gchar *uid_before);
180 180
181 static gboolean msim_error(MsimSession *session, MsimMessage *msg); 181 static gboolean msim_error(MsimSession *session, MsimMessage *msg);
182 182
183 static void msim_check_inbox_cb(MsimSession *session, MsimMessage *userinfo, 183 static void msim_check_inbox_cb(MsimSession *session, MsimMessage *userinfo,
184 gpointer data); 184 gpointer data);
185 static gboolean msim_check_inbox(gpointer data); 185 static gboolean msim_check_inbox(gpointer data);
186 186
187 static void msim_input_cb(gpointer gc_uncasted, gint source, 187 static void msim_input_cb(gpointer gc_uncasted, gint source,
188 PurpleInputCondition cond); 188 PurpleInputCondition cond);
189 189
190 static guint msim_new_reply_callback(MsimSession *session, 190 static guint msim_new_reply_callback(MsimSession *session,
191 MSIM_USER_LOOKUP_CB cb, gpointer data); 191 MSIM_USER_LOOKUP_CB cb, gpointer data);
192 192
193 static void msim_connect_cb(gpointer data, gint source, 193 static void msim_connect_cb(gpointer data, gint source,
194 const gchar *error_message); 194 const gchar *error_message);
195 195
196 static gboolean msim_is_userid(const gchar *user); 196 static gboolean msim_is_userid(const gchar *user);
204 /* round is part of C99, but sometimes is unavailable before then. 204 /* round is part of C99, but sometimes is unavailable before then.
205 * Based on http://forums.belution.com/en/cpp/000/050/13.shtml 205 * Based on http://forums.belution.com/en/cpp/000/050/13.shtml
206 */ 206 */
207 double msim_round(double value) 207 double msim_round(double value)
208 { 208 {
209 if (value < 0) { 209 if (value < 0) {
210 return -(floor(-value + 0.5)); 210 return -(floor(-value + 0.5));
211 } else { 211 } else {
212 return floor( value + 0.5); 212 return floor( value + 0.5);
213 } 213 }
214 } 214 }
215 215
216 /** 216 /**
217 * Load the plugin. 217 * Load the plugin.
218 */ 218 */
238 * @return GList of status types. 238 * @return GList of status types.
239 */ 239 */
240 GList * 240 GList *
241 msim_status_types(PurpleAccount *acct) 241 msim_status_types(PurpleAccount *acct)
242 { 242 {
243 GList *types; 243 GList *types;
244 PurpleStatusType *status; 244 PurpleStatusType *status;
245 245
246 purple_debug_info("myspace", "returning status types\n"); 246 purple_debug_info("myspace", "returning status types\n");
247 247
248 types = NULL; 248 types = NULL;
249 249
250 /* Statuses are almost all the same. Define a macro to reduce code repetition. */ 250 /* Statuses are almost all the same. Define a macro to reduce code repetition. */
251 #define _MSIM_ADD_NEW_STATUS(prim) status = \ 251 #define _MSIM_ADD_NEW_STATUS(prim) status = \
252 purple_status_type_new_with_attrs( \ 252 purple_status_type_new_with_attrs( \
253 prim, /* PurpleStatusPrimitive */ \ 253 prim, /* PurpleStatusPrimitive */ \
254 NULL, /* id - use default */ \ 254 NULL, /* id - use default */ \
255 NULL, /* name - use default */ \ 255 NULL, /* name - use default */ \
256 TRUE, /* savable */ \ 256 TRUE, /* savable */ \
257 TRUE, /* user_settable */ \ 257 TRUE, /* user_settable */ \
258 FALSE, /* not independent */ \ 258 FALSE, /* not independent */ \
259 \ 259 \
260 /* Attributes - each status can have a message. */ \ 260 /* Attributes - each status can have a message. */ \
261 "message", \ 261 "message", \
262 _("Message"), \ 262 _("Message"), \
263 purple_value_new(PURPLE_TYPE_STRING), \ 263 purple_value_new(PURPLE_TYPE_STRING), \
264 NULL); \ 264 NULL); \
265 \ 265 \
266 \ 266 \
267 types = g_list_append(types, status) 267 types = g_list_append(types, status)
268 268
269 269
270 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE); 270 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE);
271 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AWAY); 271 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AWAY);
272 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE); 272 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE);
273 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE); 273 _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE);
274 274
275 275
276 return types; 276 return types;
277 } 277 }
278 278
279 /** Zap someone. Callback from msim_blist_node_menu zap menu. */ 279 /** Zap someone. Callback from msim_blist_node_menu zap menu. */
280 static void 280 static void
281 msim_send_zap(PurpleBlistNode *node, gpointer zap_num_ptr) 281 msim_send_zap(PurpleBlistNode *node, gpointer zap_num_ptr)
282 { 282 {
283 PurpleBuddy *buddy; 283 PurpleBuddy *buddy;
284 PurpleConnection *gc; 284 PurpleConnection *gc;
285 MsimSession *session; 285 MsimSession *session;
286 gchar *username, *zap_string, *zap_text; 286 gchar *username, *zap_string, *zap_text;
287 guint zap; 287 guint zap;
288 const gchar *zap_gerund[10]; 288 const gchar *zap_gerund[10];
289 289
290 if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { 290 if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
291 /* Only know about buddies for now. */ 291 /* Only know about buddies for now. */
292 return; 292 return;
293 } 293 }
294 294
295 zap_gerund[0] = _("Zapping"); 295 zap_gerund[0] = _("Zapping");
296 zap_gerund[1] = _("Whacking"); 296 zap_gerund[1] = _("Whacking");
297 zap_gerund[2] = _("Torching"); 297 zap_gerund[2] = _("Torching");
298 zap_gerund[3] = _("Smooching"); 298 zap_gerund[3] = _("Smooching");
299 zap_gerund[4] = _("Hugging"); 299 zap_gerund[4] = _("Hugging");
300 zap_gerund[5] = _("Bslapping"); 300 zap_gerund[5] = _("Bslapping");
301 zap_gerund[6] = _("Goosing"); 301 zap_gerund[6] = _("Goosing");
302 zap_gerund[7] = _("Hi-fiving"); 302 zap_gerund[7] = _("Hi-fiving");
303 zap_gerund[8] = _("Punking"); 303 zap_gerund[8] = _("Punking");
304 zap_gerund[9] = _("Raspberry'ing"); 304 zap_gerund[9] = _("Raspberry'ing");
305 305
306 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); 306 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
307 307
308 buddy = (PurpleBuddy *)node; 308 buddy = (PurpleBuddy *)node;
309 gc = purple_account_get_connection(buddy->account); 309 gc = purple_account_get_connection(buddy->account);
310 g_return_if_fail(gc != NULL); 310 g_return_if_fail(gc != NULL);
311 311
312 session = (MsimSession *)(gc->proto_data); 312 session = (MsimSession *)(gc->proto_data);
313 g_return_if_fail(session != NULL); 313 g_return_if_fail(session != NULL);
314 314
315 username = buddy->name; 315 username = buddy->name;
316 g_return_if_fail(username != NULL); 316 g_return_if_fail(username != NULL);
317 317
318 zap = GPOINTER_TO_INT(zap_num_ptr); 318 zap = GPOINTER_TO_INT(zap_num_ptr);
319 zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", zap); 319 zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", zap);
320 zap_text = g_strdup_printf("*** %s! ***", zap_gerund[zap]); 320 zap_text = g_strdup_printf("*** %s! ***", zap_gerund[zap]);
321 321
322 serv_got_im(session->gc, username, zap_text, 322 serv_got_im(session->gc, username, zap_text,
323 PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM, time(NULL)); 323 PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM, time(NULL));
324 324
325 if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) { 325 if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) {
326 purple_debug_info("msim_send_zap", "msim_send_bm failed: zapping %s with %s", 326 purple_debug_info("msim_send_zap", "msim_send_bm failed: zapping %s with %s",
327 username, zap_string); 327 username, zap_string);
328 } 328 }
329 329
330 g_free(zap_string); 330 g_free(zap_string);
331 g_free(zap_text); 331 g_free(zap_text);
332 return; 332 return;
333 } 333 }
334 334
335 335
336 /** Return menu, if any, for a buddy list node. */ 336 /** Return menu, if any, for a buddy list node. */
337 GList * 337 GList *
338 msim_blist_node_menu(PurpleBlistNode *node) 338 msim_blist_node_menu(PurpleBlistNode *node)
339 { 339 {
340 GList *menu, *zap_menu; 340 GList *menu, *zap_menu;
341 PurpleMenuAction *act; 341 PurpleMenuAction *act;
342 const gchar *zap_names[10]; 342 const gchar *zap_names[10];
343 guint i; 343 guint i;
344 344
345 if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { 345 if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
346 /* Only know about buddies for now. */ 346 /* Only know about buddies for now. */
347 return NULL; 347 return NULL;
348 } 348 }
349 349
350 /* Names from official client. */ 350 /* Names from official client. */
351 zap_names[0] = _("zap"); 351 zap_names[0] = _("zap");
352 zap_names[1] = _("whack"); 352 zap_names[1] = _("whack");
353 zap_names[2] = _("torch"); 353 zap_names[2] = _("torch");
354 zap_names[3] = _("smooch"); 354 zap_names[3] = _("smooch");
355 zap_names[4] = _("hug"); 355 zap_names[4] = _("hug");
356 zap_names[5] = _("bslap"); 356 zap_names[5] = _("bslap");
357 zap_names[6] = _("goose"); 357 zap_names[6] = _("goose");
358 zap_names[7] = _("hi-five"); 358 zap_names[7] = _("hi-five");
359 zap_names[8] = _("punk'd"); 359 zap_names[8] = _("punk'd");
360 zap_names[9] = _("raspberry"); 360 zap_names[9] = _("raspberry");
361 361
362 menu = zap_menu = NULL; 362 menu = zap_menu = NULL;
363 363
364 /* TODO: move to / command, or better yet new API */ 364 /* TODO: move to / command, or better yet new API */
365 for (i = 0; i < sizeof(zap_names) / sizeof(zap_names[0]); ++i) { 365 for (i = 0; i < sizeof(zap_names) / sizeof(zap_names[0]); ++i) {
366 act = purple_menu_action_new(zap_names[i], PURPLE_CALLBACK(msim_send_zap), 366 act = purple_menu_action_new(zap_names[i], PURPLE_CALLBACK(msim_send_zap),
367 GUINT_TO_POINTER(i), NULL); 367 GUINT_TO_POINTER(i), NULL);
368 zap_menu = g_list_append(zap_menu, act); 368 zap_menu = g_list_append(zap_menu, act);
369 } 369 }
370 370
371 act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu); 371 act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu);
372 menu = g_list_append(menu, act); 372 menu = g_list_append(menu, act);
373 373
374 return menu; 374 return menu;
375 } 375 }
376 376
377 /** 377 /**
378 * Return the icon name for a buddy and account. 378 * Return the icon name for a buddy and account.
379 * 379 *
383 * @return The base icon name string. 383 * @return The base icon name string.
384 */ 384 */
385 const gchar * 385 const gchar *
386 msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) 386 msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
387 { 387 {
388 /* Use a MySpace icon submitted by hbons at 388 /* Use a MySpace icon submitted by hbons at
389 * http://developer.pidgin.im/wiki/MySpaceIM. */ 389 * http://developer.pidgin.im/wiki/MySpaceIM. */
390 return "myspace"; 390 return "myspace";
391 } 391 }
392 392
393 /** 393 /**
394 * Replace 'old' with 'new' in 'str'. 394 * Replace 'old' with 'new' in 'str'.
395 * 395 *
396 * @param str The original string. 396 * @param str The original string.
397 * @param old The substring of 'str' to replace. 397 * @param old The substring of 'str' to replace.
398 * @param new The replacement for 'old' within 'str'. 398 * @param new The replacement for 'old' within 'str'.
399 * 399 *
400 * @return A _new_ string, based on 'str', with 'old' replaced 400 * @return A _new_ string, based on 'str', with 'old' replaced
401 * by 'new'. Must be g_free()'d by caller. 401 * by 'new'. Must be g_free()'d by caller.
402 * 402 *
403 * This string replace method is based on 403 * This string replace method is based on
404 * http://mail.gnome.org/archives/gtk-app-devel-list/2000-July/msg00201.html 404 * http://mail.gnome.org/archives/gtk-app-devel-list/2000-July/msg00201.html
405 * 405 *
406 */ 406 */
418 418
419 #ifdef MSIM_DEBUG_MSG 419 #ifdef MSIM_DEBUG_MSG
420 static void 420 static void
421 print_hash_item(gpointer key, gpointer value, gpointer user_data) 421 print_hash_item(gpointer key, gpointer value, gpointer user_data)
422 { 422 {
423 purple_debug_info("msim", "%s=%s\n", 423 purple_debug_info("msim", "%s=%s\n",
424 key ? (gchar *)key : "(NULL)", 424 key ? (gchar *)key : "(NULL)",
425 value ? (gchar *)value : "(NULL)"); 425 value ? (gchar *)value : "(NULL)");
426 } 426 }
427 #endif 427 #endif
428 428
429 /** 429 /**
430 * Send raw data (given as a NUL-terminated string) to the server. 430 * Send raw data (given as a NUL-terminated string) to the server.
436 * 436 *
437 */ 437 */
438 gboolean 438 gboolean
439 msim_send_raw(MsimSession *session, const gchar *msg) 439 msim_send_raw(MsimSession *session, const gchar *msg)
440 { 440 {
441 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 441 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
442 g_return_val_if_fail(msg != NULL, FALSE); 442 g_return_val_if_fail(msg != NULL, FALSE);
443 443
444 purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg); 444 purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg);
445 445
446 return msim_send_really_raw(session->gc, msg, strlen(msg)) == 446 return msim_send_really_raw(session->gc, msg, strlen(msg)) ==
447 strlen(msg); 447 strlen(msg);
448 } 448 }
449 449
461 */ 461 */
462 static int 462 static int
463 msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes) 463 msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes)
464 { 464 {
465 int total_bytes_sent; 465 int total_bytes_sent;
466 MsimSession *session; 466 MsimSession *session;
467 467
468 g_return_val_if_fail(gc != NULL, -1); 468 g_return_val_if_fail(gc != NULL, -1);
469 g_return_val_if_fail(buf != NULL, -1); 469 g_return_val_if_fail(buf != NULL, -1);
470 g_return_val_if_fail(total_bytes >= 0, -1); 470 g_return_val_if_fail(total_bytes >= 0, -1);
471 471
472 session = (MsimSession *)(gc->proto_data); 472 session = (MsimSession *)(gc->proto_data);
473 473
474 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1); 474 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
475 475
476 /* Loop until all data is sent, or a failure occurs. */ 476 /* Loop until all data is sent, or a failure occurs. */
477 total_bytes_sent = 0; 477 total_bytes_sent = 0;
478 do { 478 do {
479 int bytes_sent; 479 int bytes_sent;
480 480
481 bytes_sent = send(session->fd, buf + total_bytes_sent, 481 bytes_sent = send(session->fd, buf + total_bytes_sent,
482 total_bytes - total_bytes_sent, 0); 482 total_bytes - total_bytes_sent, 0);
483 483
484 if (bytes_sent < 0) { 484 if (bytes_sent < 0) {
485 purple_debug_info("msim", "msim_send_raw(%s): send() failed: %s\n", 485 purple_debug_info("msim", "msim_send_raw(%s): send() failed: %s\n",
486 buf, g_strerror(errno)); 486 buf, g_strerror(errno));
487 return total_bytes_sent; 487 return total_bytes_sent;
500 * @param acct Account information to use to login. 500 * @param acct Account information to use to login.
501 */ 501 */
502 void 502 void
503 msim_login(PurpleAccount *acct) 503 msim_login(PurpleAccount *acct)
504 { 504 {
505 PurpleConnection *gc; 505 PurpleConnection *gc;
506 const gchar *host; 506 const gchar *host;
507 int port; 507 int port;
508 508
509 g_return_if_fail(acct != NULL); 509 g_return_if_fail(acct != NULL);
510 g_return_if_fail(acct->username != NULL); 510 g_return_if_fail(acct->username != NULL);
511 511
512 purple_debug_info("msim", "logging in %s\n", acct->username); 512 purple_debug_info("msim", "logging in %s\n", acct->username);
513 513
514 gc = purple_account_get_connection(acct); 514 gc = purple_account_get_connection(acct);
515 gc->proto_data = msim_session_new(acct); 515 gc->proto_data = msim_session_new(acct);
516 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC; 516 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC;
517 517
518 /* Passwords are limited in length. */ 518 /* Passwords are limited in length. */
519 if (strlen(acct->password) > MSIM_MAX_PASSWORD_LENGTH) { 519 if (strlen(acct->password) > MSIM_MAX_PASSWORD_LENGTH) {
520 gchar *str; 520 gchar *str;
521 521
522 str = g_strdup_printf( 522 str = g_strdup_printf(
523 _("Sorry, passwords over %d characters in length (yours is " 523 _("Sorry, passwords over %d characters in length (yours is "
526 (int)strlen(acct->password)); 526 (int)strlen(acct->password));
527 527
528 /* Notify an error message also, because this is important! */ 528 /* Notify an error message also, because this is important! */
529 purple_notify_error(acct, g_strdup(_("MySpaceIM Error")), str, NULL); 529 purple_notify_error(acct, g_strdup(_("MySpaceIM Error")), str, NULL);
530 530
531 purple_connection_error(gc, str); 531 purple_connection_error(gc, str);
532 532
533 g_free(str); 533 g_free(str);
534 } 534 }
535 535
536 /* 1. connect to server */ 536 /* 1. connect to server */
537 purple_connection_update_progress(gc, _("Connecting"), 537 purple_connection_update_progress(gc, _("Connecting"),
538 0, /* which connection step this is */ 538 0, /* which connection step this is */
539 4); /* total number of steps */ 539 4); /* total number of steps */
540 540
541 host = purple_account_get_string(acct, "server", MSIM_SERVER); 541 host = purple_account_get_string(acct, "server", MSIM_SERVER);
542 port = purple_account_get_int(acct, "port", MSIM_PORT); 542 port = purple_account_get_int(acct, "port", MSIM_PORT);
543 543
544 /* From purple.sf.net/api: 544 /* From purple.sf.net/api:
545 * """Note that this function name can be misleading--although it is called 545 * """Note that this function name can be misleading--although it is called
546 * "proxy connect," it is used for establishing any outgoing TCP connection, 546 * "proxy connect," it is used for establishing any outgoing TCP connection,
547 * whether through a proxy or not.""" */ 547 * whether through a proxy or not.""" */
548 548
549 /* Calls msim_connect_cb when connected. */ 549 /* Calls msim_connect_cb when connected. */
550 if (!purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc)) { 550 if (!purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc)) {
551 /* TODO: try other ports if in auto mode, then save 551 /* TODO: try other ports if in auto mode, then save
552 * working port and try that first next time. */ 552 * working port and try that first next time. */
553 purple_connection_error(gc, _("Couldn't create socket")); 553 purple_connection_error(gc, _("Couldn't create socket"));
554 return; 554 return;
555 } 555 }
556 } 556 }
557 557
558 /** 558 /**
559 * Process a login challenge, sending a response. 559 * Process a login challenge, sending a response.
560 * 560 *
564 * @return TRUE if successful, FALSE if not 564 * @return TRUE if successful, FALSE if not
565 */ 565 */
566 static gboolean 566 static gboolean
567 msim_login_challenge(MsimSession *session, MsimMessage *msg) 567 msim_login_challenge(MsimSession *session, MsimMessage *msg)
568 { 568 {
569 PurpleAccount *account; 569 PurpleAccount *account;
570 const gchar *response; 570 const gchar *response;
571 guint response_len; 571 guint response_len;
572 gchar *nc; 572 gchar *nc;
573 gsize nc_len; 573 gsize nc_len;
574 574
575 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 575 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
576 g_return_val_if_fail(msg != NULL, FALSE); 576 g_return_val_if_fail(msg != NULL, FALSE);
577 577
578 g_return_val_if_fail(msim_msg_get_binary(msg, "nc", &nc, &nc_len), FALSE); 578 g_return_val_if_fail(msim_msg_get_binary(msg, "nc", &nc, &nc_len), FALSE);
579 579
580 account = session->account; 580 account = session->account;
581 581
582 g_return_val_if_fail(account != NULL, FALSE); 582 g_return_val_if_fail(account != NULL, FALSE);
583 583
584 purple_connection_update_progress(session->gc, _("Reading challenge"), 1, 4); 584 purple_connection_update_progress(session->gc, _("Reading challenge"), 1, 4);
585 585
586 purple_debug_info("msim", "nc is %d bytes, decoded\n", nc_len); 586 purple_debug_info("msim", "nc is %d bytes, decoded\n", nc_len);
587 587
588 if (nc_len != MSIM_AUTH_CHALLENGE_LENGTH) { 588 if (nc_len != MSIM_AUTH_CHALLENGE_LENGTH) {
589 purple_debug_info("msim", "bad nc length: %x != 0x%x\n", nc_len, MSIM_AUTH_CHALLENGE_LENGTH); 589 purple_debug_info("msim", "bad nc length: %x != 0x%x\n", nc_len, MSIM_AUTH_CHALLENGE_LENGTH);
590 purple_connection_error(session->gc, _("Unexpected challenge length from server")); 590 purple_connection_error(session->gc, _("Unexpected challenge length from server"));
591 return FALSE; 591 return FALSE;
592 } 592 }
593 593
594 purple_connection_update_progress(session->gc, _("Logging in"), 2, 4); 594 purple_connection_update_progress(session->gc, _("Logging in"), 2, 4);
595 595
596 response_len = 0; 596 response_len = 0;
597 response = msim_compute_login_response(nc, account->username, account->password, &response_len); 597 response = msim_compute_login_response(nc, account->username, account->password, &response_len);
598 598
599 g_free(nc); 599 g_free(nc);
600 600
601 return msim_send(session, 601 return msim_send(session,
602 "login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM, 602 "login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM,
603 /* This is actually user's email address. */ 603 /* This is actually user's email address. */
604 "username", MSIM_TYPE_STRING, g_strdup(account->username), 604 "username", MSIM_TYPE_STRING, g_strdup(account->username),
605 /* GString and gchar * response will be freed in msim_msg_free() in msim_send(). */ 605 /* GString and gchar * response will be freed in msim_msg_free() in msim_send(). */
606 "response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len), 606 "response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len),
607 "clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION, 607 "clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
608 "langid", MSIM_TYPE_INTEGER, MSIM_LANGUAGE_ID_ENGLISH, 608 "langid", MSIM_TYPE_INTEGER, MSIM_LANGUAGE_ID_ENGLISH,
609 "imlang", MSIM_TYPE_STRING, g_strdup(MSIM_LANGUAGE_NAME_ENGLISH), 609 "imlang", MSIM_TYPE_STRING, g_strdup(MSIM_LANGUAGE_NAME_ENGLISH),
610 "reconn", MSIM_TYPE_INTEGER, 0, 610 "reconn", MSIM_TYPE_INTEGER, 0,
611 "status", MSIM_TYPE_INTEGER, 100, 611 "status", MSIM_TYPE_INTEGER, 100,
612 "id", MSIM_TYPE_INTEGER, 1, 612 "id", MSIM_TYPE_INTEGER, 1,
613 NULL); 613 NULL);
614 } 614 }
626 */ 626 */
627 static const gchar * 627 static const gchar *
628 msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], 628 msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE],
629 const gchar *email, const gchar *password, guint *response_len) 629 const gchar *email, const gchar *password, guint *response_len)
630 { 630 {
631 PurpleCipherContext *key_context; 631 PurpleCipherContext *key_context;
632 PurpleCipher *sha1; 632 PurpleCipher *sha1;
633 PurpleCipherContext *rc4; 633 PurpleCipherContext *rc4;
634 634
635 guchar hash_pw[HASH_SIZE]; 635 guchar hash_pw[HASH_SIZE];
636 guchar key[HASH_SIZE]; 636 guchar key[HASH_SIZE];
637 gchar *password_utf16le, *password_utf8_lc; 637 gchar *password_utf16le, *password_utf8_lc;
638 guchar *data; 638 guchar *data;
639 guchar *data_out; 639 guchar *data_out;
640 size_t data_len, data_out_len; 640 size_t data_len, data_out_len;
641 gsize conv_bytes_read, conv_bytes_written; 641 gsize conv_bytes_read, conv_bytes_written;
642 GError *conv_error; 642 GError *conv_error;
643 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 643 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
644 int i; 644 int i;
645 #endif 645 #endif
646 646
647 g_return_val_if_fail(nonce != NULL, NULL); 647 g_return_val_if_fail(nonce != NULL, NULL);
648 g_return_val_if_fail(email != NULL, NULL); 648 g_return_val_if_fail(email != NULL, NULL);
649 g_return_val_if_fail(password != NULL, NULL); 649 g_return_val_if_fail(password != NULL, NULL);
650 g_return_val_if_fail(response_len != NULL, NULL); 650 g_return_val_if_fail(response_len != NULL, NULL);
651 651
652 /* Convert password to lowercase (required for passwords containing 652 /* Convert password to lowercase (required for passwords containing
653 * uppercase characters). MySpace passwords are lowercase, 653 * uppercase characters). MySpace passwords are lowercase,
654 * see ticket #2066. */ 654 * see ticket #2066. */
655 password_utf8_lc = g_utf8_strdown(password, -1); 655 password_utf8_lc = g_utf8_strdown(password, -1);
656 656
657 /* Convert ASCII password to UTF16 little endian */ 657 /* Convert ASCII password to UTF16 little endian */
658 purple_debug_info("msim", "converting password to UTF-16LE\n"); 658 purple_debug_info("msim", "converting password to UTF-16LE\n");
659 conv_error = NULL; 659 conv_error = NULL;
660 password_utf16le = g_convert(password_utf8_lc, -1, "UTF-16LE", "UTF-8", 660 password_utf16le = g_convert(password_utf8_lc, -1, "UTF-16LE", "UTF-8",
661 &conv_bytes_read, &conv_bytes_written, &conv_error); 661 &conv_bytes_read, &conv_bytes_written, &conv_error);
662 g_free(password_utf8_lc); 662 g_free(password_utf8_lc);
663 663
664 g_return_val_if_fail(conv_bytes_read == strlen(password), NULL); 664 g_return_val_if_fail(conv_bytes_read == strlen(password), NULL);
665 665
666 if (conv_error != NULL) { 666 if (conv_error != NULL) {
667 purple_debug_error("msim", 667 purple_debug_error("msim",
668 "g_convert password UTF8->UTF16LE failed: %s", 668 "g_convert password UTF8->UTF16LE failed: %s",
669 conv_error->message); 669 conv_error->message);
670 g_error_free(conv_error); 670 g_error_free(conv_error);
671 return NULL; 671 return NULL;
672 } 672 }
673 673
674 /* Compute password hash */ 674 /* Compute password hash */
675 purple_cipher_digest_region("sha1", (guchar *)password_utf16le, 675 purple_cipher_digest_region("sha1", (guchar *)password_utf16le,
676 conv_bytes_written, sizeof(hash_pw), hash_pw, NULL); 676 conv_bytes_written, sizeof(hash_pw), hash_pw, NULL);
677 g_free(password_utf16le); 677 g_free(password_utf16le);
678 678
679 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 679 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
680 purple_debug_info("msim", "pwhash = "); 680 purple_debug_info("msim", "pwhash = ");
681 for (i = 0; i < sizeof(hash_pw); i++) 681 for (i = 0; i < sizeof(hash_pw); i++)
682 purple_debug_info("msim", "%.2x ", hash_pw[i]); 682 purple_debug_info("msim", "%.2x ", hash_pw[i]);
683 purple_debug_info("msim", "\n"); 683 purple_debug_info("msim", "\n");
684 #endif 684 #endif
685 685
686 /* key = sha1(sha1(pw) + nonce2) */ 686 /* key = sha1(sha1(pw) + nonce2) */
687 sha1 = purple_ciphers_find_cipher("sha1"); 687 sha1 = purple_ciphers_find_cipher("sha1");
688 key_context = purple_cipher_context_new(sha1, NULL); 688 key_context = purple_cipher_context_new(sha1, NULL);
689 purple_cipher_context_append(key_context, hash_pw, HASH_SIZE); 689 purple_cipher_context_append(key_context, hash_pw, HASH_SIZE);
690 purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE); 690 purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE);
691 purple_cipher_context_digest(key_context, sizeof(key), key, NULL); 691 purple_cipher_context_digest(key_context, sizeof(key), key, NULL);
692 692
693 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 693 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
694 purple_debug_info("msim", "key = "); 694 purple_debug_info("msim", "key = ");
695 for (i = 0; i < sizeof(key); i++) { 695 for (i = 0; i < sizeof(key); i++) {
696 purple_debug_info("msim", "%.2x ", key[i]); 696 purple_debug_info("msim", "%.2x ", key[i]);
697 } 697 }
698 purple_debug_info("msim", "\n"); 698 purple_debug_info("msim", "\n");
699 #endif 699 #endif
700 700
701 rc4 = purple_cipher_context_new_by_name("rc4", NULL); 701 rc4 = purple_cipher_context_new_by_name("rc4", NULL);
702 702
703 /* Note: 'key' variable is 0x14 bytes (from SHA-1 hash), 703 /* Note: 'key' variable is 0x14 bytes (from SHA-1 hash),
704 * but only first 0x10 used for the RC4 key. */ 704 * but only first 0x10 used for the RC4 key. */
705 purple_cipher_context_set_option(rc4, "key_len", (gpointer)0x10); 705 purple_cipher_context_set_option(rc4, "key_len", (gpointer)0x10);
706 purple_cipher_context_set_key(rc4, key); 706 purple_cipher_context_set_key(rc4, key);
707 707
708 /* TODO: obtain IPs of network interfaces */ 708 /* TODO: obtain IPs of network interfaces */
709 709
710 /* rc4 encrypt: 710 /* rc4 encrypt:
711 * nonce1+email+IP list */ 711 * nonce1+email+IP list */
712 712
713 data_len = NONCE_SIZE + strlen(email) + MSIM_LOGIN_IP_LIST_LEN; 713 data_len = NONCE_SIZE + strlen(email) + MSIM_LOGIN_IP_LIST_LEN;
714 data = g_new0(guchar, data_len); 714 data = g_new0(guchar, data_len);
715 memcpy(data, nonce, NONCE_SIZE); 715 memcpy(data, nonce, NONCE_SIZE);
716 memcpy(data + NONCE_SIZE, email, strlen(email)); 716 memcpy(data + NONCE_SIZE, email, strlen(email));
717 memcpy(data + NONCE_SIZE + strlen(email), MSIM_LOGIN_IP_LIST, MSIM_LOGIN_IP_LIST_LEN); 717 memcpy(data + NONCE_SIZE + strlen(email), MSIM_LOGIN_IP_LIST, MSIM_LOGIN_IP_LIST_LEN);
718 718
719 data_out = g_new0(guchar, data_len); 719 data_out = g_new0(guchar, data_len);
720 720
721 purple_cipher_context_encrypt(rc4, (const guchar *)data, 721 purple_cipher_context_encrypt(rc4, (const guchar *)data,
722 data_len, data_out, &data_out_len); 722 data_len, data_out, &data_out_len);
723 purple_cipher_context_destroy(rc4); 723 purple_cipher_context_destroy(rc4);
724 724
725 g_assert(data_out_len == data_len); 725 g_assert(data_out_len == data_len);
726 726
727 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE 727 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
728 purple_debug_info("msim", "response=<%s>\n", data_out); 728 purple_debug_info("msim", "response=<%s>\n", data_out);
729 #endif 729 #endif
730 730
731 *response_len = data_out_len; 731 *response_len = data_out_len;
732 732
733 return (const gchar *)data_out; 733 return (const gchar *)data_out;
734 } 734 }
735 735
736 /** 736 /**
737 * Schedule an IM to be sent once the user ID is looked up. 737 * Schedule an IM to be sent once the user ID is looked up.
738 * 738 *
749 */ 749 */
750 int 750 int
751 msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, 751 msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message,
752 PurpleMessageFlags flags) 752 PurpleMessageFlags flags)
753 { 753 {
754 MsimSession *session; 754 MsimSession *session;
755 gchar *message_msim; 755 gchar *message_msim;
756 int rc; 756 int rc;
757 757
758 g_return_val_if_fail(gc != NULL, -1); 758 g_return_val_if_fail(gc != NULL, -1);
759 g_return_val_if_fail(who != NULL, -1); 759 g_return_val_if_fail(who != NULL, -1);
760 g_return_val_if_fail(message != NULL, -1); 760 g_return_val_if_fail(message != NULL, -1);
761 761
762 /* 'flags' has many options, not used here. */ 762 /* 'flags' has many options, not used here. */
763 763
764 session = (MsimSession *)gc->proto_data; 764 session = (MsimSession *)gc->proto_data;
765 765
766 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1); 766 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
767 767
768 message_msim = html_to_msim_markup(session, message); 768 message_msim = html_to_msim_markup(session, message);
769 769
770 if (msim_send_bm(session, who, message_msim, MSIM_BM_INSTANT)) { 770 if (msim_send_bm(session, who, message_msim, MSIM_BM_INSTANT)) {
771 /* Return 1 to have Purple show this IM as being sent, 0 to not. I always 771 /* Return 1 to have Purple show this IM as being sent, 0 to not. I always
772 * return 1 even if the message could not be sent, since I don't know if 772 * return 1 even if the message could not be sent, since I don't know if
773 * it has failed yet--because the IM is only sent after the userid is 773 * it has failed yet--because the IM is only sent after the userid is
774 * retrieved from the server (which happens after this function returns). 774 * retrieved from the server (which happens after this function returns).
775 */ 775 */
776 /* TODO: maybe if message is delayed, don't echo to conv window, 776 /* TODO: maybe if message is delayed, don't echo to conv window,
777 * but do echo it to conv window manually once it is actually 777 * but do echo it to conv window manually once it is actually
778 * sent? Would be complicated. */ 778 * sent? Would be complicated. */
779 rc = 1; 779 rc = 1;
780 } else { 780 } else {
781 rc = -1; 781 rc = -1;
782 } 782 }
783 783
784 g_free(message_msim); 784 g_free(message_msim);
785 785
786 /* 786 /*
787 * In MySpace, you login with your email address, but don't talk to other 787 * In MySpace, you login with your email address, but don't talk to other
788 * users using their email address. So there is currently an asymmetry in the 788 * users using their email address. So there is currently an asymmetry in the
789 * IM windows when using this plugin: 789 * IM windows when using this plugin:
790 * 790 *
791 * you@example.com: hello 791 * you@example.com: hello
792 * some_other_user: what's going on? 792 * some_other_user: what's going on?
793 * you@example.com: just coding a prpl 793 * you@example.com: just coding a prpl
794 * 794 *
795 * TODO: Make the sent IM's appear as from the user's username, instead of 795 * TODO: Make the sent IM's appear as from the user's username, instead of
796 * their email address. Purple uses the login (in MSIM, the email)--change this. 796 * their email address. Purple uses the login (in MSIM, the email)--change this.
797 */ 797 */
798 798
799 return rc; 799 return rc;
800 } 800 }
801 801
802 /** Send a buddy message of a given type. 802 /** Send a buddy message of a given type.
803 * 803 *
804 * @param session 804 * @param session
815 msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, 815 msim_send_bm(MsimSession *session, const gchar *who, const gchar *text,
816 int type) 816 int type)
817 { 817 {
818 gboolean rc; 818 gboolean rc;
819 MsimMessage *msg; 819 MsimMessage *msg;
820 const gchar *from_username; 820 const gchar *from_username;
821 821
822 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 822 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
823 g_return_val_if_fail(who != NULL, FALSE); 823 g_return_val_if_fail(who != NULL, FALSE);
824 g_return_val_if_fail(text != NULL, FALSE); 824 g_return_val_if_fail(text != NULL, FALSE);
825 825
826 from_username = session->account->username; 826 from_username = session->account->username;
827 827
828 g_return_val_if_fail(from_username != NULL, FALSE); 828 g_return_val_if_fail(from_username != NULL, FALSE);
829 829
830 purple_debug_info("msim", "sending %d message from %s to %s: %s\n", 830 purple_debug_info("msim", "sending %d message from %s to %s: %s\n",
831 type, from_username, who, text); 831 type, from_username, who, text);
832 832
833 msg = msim_msg_new(TRUE, 833 msg = msim_msg_new(TRUE,
834 "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type), 834 "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type),
835 "sesskey", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(session->sesskey), 835 "sesskey", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(session->sesskey),
836 /* 't' will be inserted here */ 836 /* 't' will be inserted here */
856 /** Convert typographical font point size to HTML font size. 856 /** Convert typographical font point size to HTML font size.
857 * Based on libpurple/gtkimhtml.c */ 857 * Based on libpurple/gtkimhtml.c */
858 static guint 858 static guint
859 msim_point_to_purple_size(MsimSession *session, guint point) 859 msim_point_to_purple_size(MsimSession *session, guint point)
860 { 860 {
861 guint size, this_point, base; 861 guint size, this_point, base;
862 gdouble scale; 862 gdouble scale;
863 863
864 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); 864 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
865 865
866 for (size = 0; 866 for (size = 0;
867 size < sizeof(_font_scale) / sizeof(_font_scale[0]); 867 size < sizeof(_font_scale) / sizeof(_font_scale[0]);
868 ++size) { 868 ++size) {
869 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; 869 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1];
870 this_point = (guint)msim_round(scale * base); 870 this_point = (guint)msim_round(scale * base);
871 871
872 if (this_point >= point) { 872 if (this_point >= point) {
873 purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", 873 purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n",
874 point, size); 874 point, size);
875 return size; 875 return size;
876 } 876 }
877 } 877 }
878 878
879 /* No HTML font size was this big; return largest possible. */ 879 /* No HTML font size was this big; return largest possible. */
880 return this_point; 880 return this_point;
881 } 881 }
882 882
883 /** Convert HTML font size to point size. */ 883 /** Convert HTML font size to point size. */
884 static guint 884 static guint
885 msim_purple_size_to_point(MsimSession *session, guint size) 885 msim_purple_size_to_point(MsimSession *session, guint size)
886 { 886 {
887 gdouble scale; 887 gdouble scale;
888 guint point; 888 guint point;
889 guint base; 889 guint base;
890 890
891 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; 891 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1];
892 892
893 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); 893 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
894 894
895 point = (guint)msim_round(scale * base); 895 point = (guint)msim_round(scale * base);
896 896
897 purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n", 897 purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n",
898 size, point); 898 size, point);
899 899
900 return point; 900 return point;
901 } 901 }
902 902
903 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ 903 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */
904 static guint 904 static guint
905 msim_height_to_point(MsimSession *session, guint height) 905 msim_height_to_point(MsimSession *session, guint height)
906 { 906 {
907 guint dpi; 907 guint dpi;
908 908
909 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); 909 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI);
910 910
911 return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height); 911 return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height);
912 912
913 /* See also: libpurple/protocols/bonjour/jabber.c 913 /* See also: libpurple/protocols/bonjour/jabber.c
914 * _font_size_ichat_to_purple */ 914 * _font_size_ichat_to_purple */
915 } 915 }
916 916
917 /** Convert point size to msim pixel height font size specification, for outgoing messages. */ 917 /** Convert point size to msim pixel height font size specification, for outgoing messages. */
918 static guint 918 static guint
919 msim_point_to_height(MsimSession *session, guint point) 919 msim_point_to_height(MsimSession *session, guint point)
920 { 920 {
921 guint dpi; 921 guint dpi;
922 922
923 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); 923 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI);
924 924
925 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); 925 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point);
926 } 926 }
927 927
928 /** Convert the msim markup <f> (font) tag into HTML. */ 928 /** Convert the msim markup <f> (font) tag into HTML. */
929 static void 929 static void
930 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 930 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
931 { 931 {
932 const gchar *face, *height_str, *decor_str; 932 const gchar *face, *height_str, *decor_str;
933 GString *gs_end, *gs_begin; 933 GString *gs_end, *gs_begin;
934 guint decor, height; 934 guint decor, height;
935 935
936 face = xmlnode_get_attrib(root, "f"); 936 face = xmlnode_get_attrib(root, "f");
937 height_str = xmlnode_get_attrib(root, "h"); 937 height_str = xmlnode_get_attrib(root, "h");
938 decor_str = xmlnode_get_attrib(root, "s"); 938 decor_str = xmlnode_get_attrib(root, "s");
939 939
940 if (height_str) { 940 if (height_str) {
941 height = atol(height_str); 941 height = atol(height_str);
942 } else { 942 } else {
943 height = 12; 943 height = 12;
944 } 944 }
945 945
946 if (decor_str) { 946 if (decor_str) {
947 decor = atol(decor_str); 947 decor = atol(decor_str);
948 } else { 948 } else {
949 decor = 0; 949 decor = 0;
950 } 950 }
951 951
952 gs_begin = g_string_new(""); 952 gs_begin = g_string_new("");
953 /* TODO: get font size working */ 953 /* TODO: get font size working */
954 if (height && !face) { 954 if (height && !face) {
955 g_string_printf(gs_begin, "<font size='%d'>", 955 g_string_printf(gs_begin, "<font size='%d'>",
956 msim_point_to_purple_size(session, msim_height_to_point(session, height))); 956 msim_point_to_purple_size(session, msim_height_to_point(session, height)));
957 } else if (height && face) { 957 } else if (height && face) {
958 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, 958 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face,
959 msim_point_to_purple_size(session, msim_height_to_point(session, height))); 959 msim_point_to_purple_size(session, msim_height_to_point(session, height)));
960 } else { 960 } else {
961 g_string_printf(gs_begin, "<font>"); 961 g_string_printf(gs_begin, "<font>");
962 } 962 }
963 963
964 /* No support for font-size CSS? */ 964 /* No support for font-size CSS? */
965 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, 965 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face,
966 msim_height_to_point(height)); */ 966 msim_height_to_point(height)); */
967 967
972 g_string_prepend(gs_end, "</b>"); 972 g_string_prepend(gs_end, "</b>");
973 } 973 }
974 974
975 if (decor & MSIM_TEXT_ITALIC) { 975 if (decor & MSIM_TEXT_ITALIC) {
976 g_string_append(gs_begin, "<i>"); 976 g_string_append(gs_begin, "<i>");
977 g_string_append(gs_end, "</i>"); 977 g_string_append(gs_end, "</i>");
978 } 978 }
979 979
980 if (decor & MSIM_TEXT_UNDERLINE) { 980 if (decor & MSIM_TEXT_UNDERLINE) {
981 g_string_append(gs_begin, "<u>"); 981 g_string_append(gs_begin, "<u>");
982 g_string_append(gs_end, "</u>"); 982 g_string_append(gs_end, "</u>");
983 } 983 }
984 984
985 985
986 *begin = gs_begin->str; 986 *begin = gs_begin->str;
987 *end = gs_end->str; 987 *end = gs_end->str;
998 { 998 {
999 guint red, green, blue; 999 guint red, green, blue;
1000 1000
1001 if (!msim) { 1001 if (!msim) {
1002 return g_strdup("black"); 1002 return g_strdup("black");
1003 } 1003 }
1004 1004
1005 if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) { 1005 if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) {
1006 /* Color name. */ 1006 /* Color name. */
1007 return g_strdup(msim); 1007 return g_strdup(msim);
1008 } 1008 }
1009 /* TODO: rgba (alpha). */ 1009 /* TODO: rgba (alpha). */
1010 1010
1011 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); 1011 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue);
1012 } 1012 }
1013 1013
1014 /** Convert the msim markup <p> (paragraph) tag into HTML. */ 1014 /** Convert the msim markup <p> (paragraph) tag into HTML. */
1015 static void 1015 static void
1016 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 1016 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
1017 { 1017 {
1018 /* Just pass through unchanged. 1018 /* Just pass through unchanged.
1019 * 1019 *
1020 * Note: attributes currently aren't passed, if there are any. */ 1020 * Note: attributes currently aren't passed, if there are any. */
1021 *begin = g_strdup("<p>"); 1021 *begin = g_strdup("<p>");
1022 *end = g_strdup("</p>"); 1022 *end = g_strdup("</p>");
1023 } 1023 }
1077 /** Convert the msim markup <i> tag (emoticon image) into HTML. */ 1077 /** Convert the msim markup <i> tag (emoticon image) into HTML. */
1078 static void 1078 static void
1079 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) 1079 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
1080 { 1080 {
1081 const gchar *name; 1081 const gchar *name;
1082 guint i; 1082 guint i;
1083 struct MSIM_EMOTICON *emote; 1083 struct MSIM_EMOTICON *emote;
1084 1084
1085 name = xmlnode_get_attrib(root, "n"); 1085 name = xmlnode_get_attrib(root, "n");
1086 if (!name) { 1086 if (!name) {
1087 purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n"); 1087 purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n");
1088 *begin = g_strdup(""); 1088 *begin = g_strdup("");
1089 *end = g_strdup(""); 1089 *end = g_strdup("");
1090 /* TODO: log as unrecognized */ 1090 /* TODO: log as unrecognized */
1091 return; 1091 return;
1092 } 1092 }
1093 1093
1094 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { 1094 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
1095 gchar *name; 1095 gchar *name;
1096 gchar *symbol; 1096 gchar *symbol;
1097 1097
1098 name = emote->name; 1098 name = emote->name;
1099 symbol = emote->symbol; 1099 symbol = emote->symbol;
1100 1100
1101 if (!strcmp(name, name)) { 1101 if (!strcmp(name, name)) {
1102 *begin = g_strdup(symbol); 1102 *begin = g_strdup(symbol);
1103 *end = g_strdup(""); 1103 *end = g_strdup("");
1104 return; 1104 return;
1105 } 1105 }
1106 } 1106 }
1107 1107
1108 *begin = g_strdup(name); 1108 *begin = g_strdup(name);
1109 *end = g_strdup(""); 1109 *end = g_strdup("");
1110 } 1110 }
1111 1111
1112 /** Convert an individual msim markup tag to HTML. */ 1112 /** Convert an individual msim markup tag to HTML. */
1113 static void 1113 static void
1114 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, 1114 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin,
1115 gchar **end) 1115 gchar **end)
1116 { 1116 {
1117 if (!strcmp(root->name, "f")) { 1117 if (!strcmp(root->name, "f")) {
1118 msim_markup_f_to_html(session, root, begin, end); 1118 msim_markup_f_to_html(session, root, begin, end);
1119 } else if (!strcmp(root->name, "p")) { 1119 } else if (!strcmp(root->name, "p")) {
1120 msim_markup_p_to_html(session, root, begin, end); 1120 msim_markup_p_to_html(session, root, begin, end);
1125 } else if (!strcmp(root->name, "i")) { 1125 } else if (!strcmp(root->name, "i")) {
1126 msim_markup_i_to_html(session, root, begin, end); 1126 msim_markup_i_to_html(session, root, begin, end);
1127 } else { 1127 } else {
1128 purple_debug_info("msim", "msim_markup_tag_to_html: " 1128 purple_debug_info("msim", "msim_markup_tag_to_html: "
1129 "unknown tag name=%s, ignoring", 1129 "unknown tag name=%s, ignoring",
1130 (root && root->name) ? root->name : "(NULL)"); 1130 (root && root->name) ? root->name : "(NULL)");
1131 *begin = g_strdup(""); 1131 *begin = g_strdup("");
1132 *end = g_strdup(""); 1132 *end = g_strdup("");
1133 } 1133 }
1134 } 1134 }
1135 1135
1136 /** Convert an individual HTML tag to msim markup. */ 1136 /** Convert an individual HTML tag to msim markup. */
1137 static void 1137 static void
1138 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, 1138 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin,
1139 gchar **end) 1139 gchar **end)
1140 { 1140 {
1141 /* TODO: Coalesce nested tags into one <f> tag! 1141 /* TODO: Coalesce nested tags into one <f> tag!
1142 * Currently, the 's' value will be overwritten when b/i/u is nested 1142 * Currently, the 's' value will be overwritten when b/i/u is nested
1143 * within another one, and only the inner-most formatting will be 1143 * within another one, and only the inner-most formatting will be
1144 * applied to the text. */ 1144 * applied to the text. */
1145 if (!strcmp(root->name, "root")) { 1145 if (!strcmp(root->name, "root")) {
1146 *begin = g_strdup(""); 1146 *begin = g_strdup("");
1147 *end = g_strdup(""); 1147 *end = g_strdup("");
1148 } else if (!strcmp(root->name, "b")) { 1148 } else if (!strcmp(root->name, "b")) {
1149 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); 1149 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD);
1150 *end = g_strdup("</f>"); 1150 *end = g_strdup("</f>");
1151 } else if (!strcmp(root->name, "i")) { 1151 } else if (!strcmp(root->name, "i")) {
1152 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); 1152 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC);
1153 *end = g_strdup("</f>"); 1153 *end = g_strdup("</f>");
1154 } else if (!strcmp(root->name, "u")) { 1154 } else if (!strcmp(root->name, "u")) {
1155 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE); 1155 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE);
1156 *end = g_strdup("</f>"); 1156 *end = g_strdup("</f>");
1157 } else if (!strcmp(root->name, "font")) { 1157 } else if (!strcmp(root->name, "font")) {
1158 const gchar *size; 1158 const gchar *size;
1159 const gchar *face; 1159 const gchar *face;
1160 1160
1161 size = xmlnode_get_attrib(root, "size"); 1161 size = xmlnode_get_attrib(root, "size");
1162 face = xmlnode_get_attrib(root, "face"); 1162 face = xmlnode_get_attrib(root, "face");
1163 1163
1164 if (face && size) 1164 if (face && size) {
1165 { 1165 *begin = g_strdup_printf("<f f='%s' h='%d'>", face,
1166 *begin = g_strdup_printf("<f f='%s' h='%d'>", face, 1166 msim_point_to_height(session,
1167 msim_point_to_height(session, 1167 msim_purple_size_to_point(session, atoi(size))));
1168 msim_purple_size_to_point(session, atoi(size)))); 1168 } else if (face) {
1169 } else if (face) { 1169 *begin = g_strdup_printf("<f f='%s'>", face);
1170 *begin = g_strdup_printf("<f f='%s'>", face); 1170 } else if (size) {
1171 } else if (size) { 1171 *begin = g_strdup_printf("<f h='%d'>",
1172 *begin = g_strdup_printf("<f h='%d'>", 1172 msim_point_to_height(session,
1173 msim_point_to_height(session, 1173 msim_purple_size_to_point(session, atoi(size))));
1174 msim_purple_size_to_point(session, atoi(size)))); 1174 } else {
1175 } else { 1175 *begin = g_strdup("<f>");
1176 *begin = g_strdup("<f>"); 1176 }
1177 } 1177
1178 1178 *end = g_strdup("</f>");
1179 *end = g_strdup("</f>"); 1179
1180 1180 /* TODO: color (bg uses <body>), emoticons */
1181 /* TODO: color (bg uses <body>), emoticons */ 1181 } else {
1182 } else { 1182 *begin = g_strdup_printf("[%s]", root->name);
1183 *begin = g_strdup_printf("[%s]", root->name); 1183 *end = g_strdup_printf("[/%s]", root->name);
1184 *end = g_strdup_printf("[/%s]", root->name); 1184 }
1185 }
1186 } 1185 }
1187 1186
1188 /** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. 1187 /** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup.
1189 * 1188 *
1190 * @param f Function to convert tags. 1189 * @param f Function to convert tags.
1194 static gchar * 1193 static gchar *
1195 msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f) 1194 msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f)
1196 { 1195 {
1197 xmlnode *node; 1196 xmlnode *node;
1198 gchar *begin, *inner, *end; 1197 gchar *begin, *inner, *end;
1199 GString *final; 1198 GString *final;
1200 1199
1201 if (!root || !root->name) { 1200 if (!root || !root->name) {
1202 return g_strdup(""); 1201 return g_strdup("");
1203 } 1202 }
1204 1203
1205 purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", 1204 purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n",
1206 root->name); 1205 root->name);
1207 1206
1208 begin = inner = end = NULL; 1207 begin = inner = end = NULL;
1209 1208
1210 final = g_string_new(""); 1209 final = g_string_new("");
1211 1210
1212 f(session, root, &begin, &end); 1211 f(session, root, &begin, &end);
1213 1212
1214 g_string_append(final, begin); 1213 g_string_append(final, begin);
1215 1214
1216 /* Loop over all child nodes. */ 1215 /* Loop over all child nodes. */
1217 for (node = root->child; node != NULL; node = node->next) { 1216 for (node = root->child; node != NULL; node = node->next) {
1218 switch (node->type) { 1217 switch (node->type) {
1219 case XMLNODE_TYPE_ATTRIB: 1218 case XMLNODE_TYPE_ATTRIB:
1220 /* Attributes handled above. */ 1219 /* Attributes handled above. */
1221 break; 1220 break;
1222 1221
1223 case XMLNODE_TYPE_TAG: 1222 case XMLNODE_TYPE_TAG:
1224 /* A tag or tag with attributes. Recursively descend. */ 1223 /* A tag or tag with attributes. Recursively descend. */
1225 inner = msim_convert_xmlnode(session, node, f); 1224 inner = msim_convert_xmlnode(session, node, f);
1226 g_return_val_if_fail(inner != NULL, NULL); 1225 g_return_val_if_fail(inner != NULL, NULL);
1227 1226
1228 purple_debug_info("msim", " ** node name=%s\n", 1227 purple_debug_info("msim", " ** node name=%s\n",
1229 (node && node->name) ? node->name : "(NULL)"); 1228 (node && node->name) ? node->name : "(NULL)");
1230 break; 1229 break;
1231 1230
1232 case XMLNODE_TYPE_DATA: 1231 case XMLNODE_TYPE_DATA:
1233 /* Literal text. */ 1232 /* Literal text. */
1234 inner = g_new0(char, node->data_sz + 1); 1233 inner = g_new0(char, node->data_sz + 1);
1235 strncpy(inner, node->data, node->data_sz); 1234 strncpy(inner, node->data, node->data_sz);
1236 inner[node->data_sz] = 0; 1235 inner[node->data_sz] = 0;
1237 1236
1238 purple_debug_info("msim", " ** node data=%s\n", 1237 purple_debug_info("msim", " ** node data=%s\n",
1239 inner ? inner : "(NULL)"); 1238 inner ? inner : "(NULL)");
1240 break; 1239 break;
1241 1240
1242 default: 1241 default:
1243 purple_debug_info("msim", 1242 purple_debug_info("msim",
1244 "msim_convert_xmlnode: strange node\n"); 1243 "msim_convert_xmlnode: strange node\n");
1245 inner = g_strdup(""); 1244 inner = g_strdup("");
1246 } 1245 }
1247 1246
1248 if (inner) { 1247 if (inner) {
1249 g_string_append(final, inner); 1248 g_string_append(final, inner);
1250 } 1249 }
1251 } 1250 }
1252 1251
1253 /* TODO: Note that msim counts each piece of text enclosed by <f> as 1252 /* TODO: Note that msim counts each piece of text enclosed by <f> as
1254 * a paragraph and will display each on its own line. You actually have 1253 * a paragraph and will display each on its own line. You actually have
1255 * to _nest_ <f> tags to intersperse different text in one paragraph! 1254 * to _nest_ <f> tags to intersperse different text in one paragraph!
1256 * Comment out this line below to see. */ 1255 * Comment out this line below to see. */
1257 g_string_append(final, end); 1256 g_string_append(final, end);
1258 1257
1259 purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n", 1258 purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n",
1260 (final && final->str) ? final->str : "(NULL)"); 1259 (final && final->str) ? final->str : "(NULL)");
1261 1260
1262 return final->str; 1261 return final->str;
1266 static gchar * 1265 static gchar *
1267 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) 1266 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f)
1268 { 1267 {
1269 xmlnode *root; 1268 xmlnode *root;
1270 gchar *str; 1269 gchar *str;
1271 gchar *enclosed_raw; 1270 gchar *enclosed_raw;
1272 1271
1273 g_return_val_if_fail(raw != NULL, NULL); 1272 g_return_val_if_fail(raw != NULL, NULL);
1274 1273
1275 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ 1274 /* Enclose text in one root tag, to try to make it valid XML for parsing. */
1276 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); 1275 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL);
1277 1276
1278 root = xmlnode_from_str(enclosed_raw, -1); 1277 root = xmlnode_from_str(enclosed_raw, -1);
1279 1278
1280 if (!root) { 1279 if (!root) {
1281 purple_debug_info("msim", "msim_markup_to_html: couldn't parse " 1280 purple_debug_info("msim", "msim_markup_to_html: couldn't parse "
1282 "%s as XML, returning raw: %s\n", enclosed_raw, raw); 1281 "%s as XML, returning raw: %s\n", enclosed_raw, raw);
1283 /* TODO: msim_unrecognized */ 1282 /* TODO: msim_unrecognized */
1284 g_free(enclosed_raw); 1283 g_free(enclosed_raw);
1285 return g_strdup(raw); 1284 return g_strdup(raw);
1286 } 1285 }
1287 1286
1288 g_free(enclosed_raw); 1287 g_free(enclosed_raw);
1289 1288
1290 str = msim_convert_xmlnode(session, root, f); 1289 str = msim_convert_xmlnode(session, root, f);
1291 g_return_val_if_fail(str != NULL, NULL); 1290 g_return_val_if_fail(str != NULL, NULL);
1292 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str); 1291 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str);
1293 1292
1294 xmlnode_free(root); 1293 xmlnode_free(root);
1295 1294
1296 return str; 1295 return str;
1302 * @return A new string with <i> tags, if applicable. Must be g_free()'d. 1301 * @return A new string with <i> tags, if applicable. Must be g_free()'d.
1303 */ 1302 */
1304 static gchar * 1303 static gchar *
1305 msim_convert_smileys_to_markup(gchar *before) 1304 msim_convert_smileys_to_markup(gchar *before)
1306 { 1305 {
1307 gchar *old, *new, *replacement; 1306 gchar *old, *new, *replacement;
1308 guint i; 1307 guint i;
1309 struct MSIM_EMOTICON *emote; 1308 struct MSIM_EMOTICON *emote;
1310 1309
1311 old = before; 1310 old = before;
1312 new = NULL; 1311 new = NULL;
1313 1312
1314 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { 1313 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
1315 gchar *name, *symbol; 1314 gchar *name, *symbol;
1316 1315
1317 name = emote->name; 1316 name = emote->name;
1318 symbol = emote->symbol; 1317 symbol = emote->symbol;
1319 1318
1320 replacement = g_strdup_printf("<i n=\"%s\"/>", name); 1319 replacement = g_strdup_printf("<i n=\"%s\"/>", name);
1321 1320
1322 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", 1321 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n",
1323 symbol ? symbol : "(NULL)", 1322 symbol ? symbol : "(NULL)",
1324 replacement ? replacement : "(NULL)"); 1323 replacement ? replacement : "(NULL)");
1325 new = str_replace(old, symbol, replacement); 1324 new = str_replace(old, symbol, replacement);
1326 1325
1327 g_free(replacement); 1326 g_free(replacement);
1328 g_free(old); 1327 g_free(old);
1329 1328
1330 old = new; 1329 old = new;
1331 } 1330 }
1332 1331
1333 return new; 1332 return new;
1334 } 1333 }
1335 1334
1336 1335
1337 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup. 1336 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup.
1338 * 1337 *
1339 * @return Purple markup string, must be g_free()'d. */ 1338 * @return Purple markup string, must be g_free()'d. */
1340 static gchar * 1339 static gchar *
1341 msim_markup_to_html(MsimSession *session, const gchar *raw) 1340 msim_markup_to_html(MsimSession *session, const gchar *raw)
1342 { 1341 {
1343 return msim_convert_xml(session, raw, 1342 return msim_convert_xml(session, raw,
1344 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); 1343 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html));
1345 } 1344 }
1346 1345
1347 /** High-level function to convert Purple (HTML) to MySpaceIM markup. 1346 /** High-level function to convert Purple (HTML) to MySpaceIM markup.
1348 * 1347 *
1349 * @return HTML markup string, must be g_free()'d. */ 1348 * @return HTML markup string, must be g_free()'d. */
1350 static gchar * 1349 static gchar *
1351 html_to_msim_markup(MsimSession *session, const gchar *raw) 1350 html_to_msim_markup(MsimSession *session, const gchar *raw)
1352 { 1351 {
1353 gchar *markup; 1352 gchar *markup;
1354 1353
1355 markup = msim_convert_xml(session, raw, 1354 markup = msim_convert_xml(session, raw,
1356 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); 1355 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup));
1357 1356
1358 if (purple_account_get_bool(session->account, "emoticons", TRUE)) { 1357 if (purple_account_get_bool(session->account, "emoticons", TRUE)) {
1359 /* Frees markup and allocates a new one. */ 1358 /* Frees markup and allocates a new one. */
1360 markup = msim_convert_smileys_to_markup(markup); 1359 markup = msim_convert_smileys_to_markup(markup);
1361 } 1360 }
1362 1361
1363 return markup; 1362 return markup;
1364 } 1363 }
1365 1364
1366 /** Record the client version in the buddy list, from an incoming message. */ 1365 /** Record the client version in the buddy list, from an incoming message. */
1367 static gboolean 1366 static gboolean
1368 msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg) 1367 msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg)
1369 { 1368 {
1370 gchar *username, *cv; 1369 gchar *username, *cv;
1371 gboolean ret; 1370 gboolean ret;
1372 PurpleBuddy *buddy; 1371 PurpleBuddy *buddy;
1373 1372
1374 username = msim_msg_get_string(msg, "_username"); 1373 username = msim_msg_get_string(msg, "_username");
1375 cv = msim_msg_get_string(msg, "cv"); 1374 cv = msim_msg_get_string(msg, "cv");
1376 1375
1377 g_return_val_if_fail(username != NULL, FALSE); 1376 g_return_val_if_fail(username != NULL, FALSE);
1378 g_return_val_if_fail(cv != NULL, FALSE); 1377 g_return_val_if_fail(cv != NULL, FALSE);
1379 1378
1380 buddy = purple_find_buddy(session->account, username); 1379 buddy = purple_find_buddy(session->account, username);
1381 1380
1382 if (buddy) { 1381 if (buddy) {
1383 purple_blist_node_set_int(&buddy->node, "client_cv", atol(cv)); 1382 purple_blist_node_set_int(&buddy->node, "client_cv", atol(cv));
1384 ret = TRUE; 1383 ret = TRUE;
1385 } else { 1384 } else {
1386 ret = FALSE; 1385 ret = FALSE;
1387 } 1386 }
1388 1387
1389 g_free(username); 1388 g_free(username);
1390 g_free(cv); 1389 g_free(cv);
1391 1390
1392 return ret; 1391 return ret;
1393 } 1392 }
1394 1393
1395 /** Handle an incoming buddy message. */ 1394 /** Handle an incoming buddy message. */
1396 static gboolean 1395 static gboolean
1397 msim_incoming_bm(MsimSession *session, MsimMessage *msg) 1396 msim_incoming_bm(MsimSession *session, MsimMessage *msg)
1398 { 1397 {
1399 guint bm; 1398 guint bm;
1400 1399
1401 bm = msim_msg_get_integer(msg, "bm"); 1400 bm = msim_msg_get_integer(msg, "bm");
1402 1401
1403 msim_incoming_bm_record_cv(session, msg); 1402 msim_incoming_bm_record_cv(session, msg);
1404 1403
1405 switch (bm) { 1404 switch (bm) {
1406 case MSIM_BM_STATUS: 1405 case MSIM_BM_STATUS:
1407 return msim_incoming_status(session, msg); 1406 return msim_incoming_status(session, msg);
1408 case MSIM_BM_INSTANT: 1407 case MSIM_BM_INSTANT:
1409 return msim_incoming_im(session, msg); 1408 return msim_incoming_im(session, msg);
1410 case MSIM_BM_ACTION: 1409 case MSIM_BM_ACTION:
1411 return msim_incoming_action(session, msg); 1410 return msim_incoming_action(session, msg);
1412 case MSIM_BM_MEDIA: 1411 case MSIM_BM_MEDIA:
1413 return msim_incoming_media(session, msg); 1412 return msim_incoming_media(session, msg);
1414 case MSIM_BM_UNOFFICIAL_CLIENT: 1413 case MSIM_BM_UNOFFICIAL_CLIENT:
1415 return msim_incoming_unofficial_client(session, msg); 1414 return msim_incoming_unofficial_client(session, msg);
1416 default: 1415 default:
1417 /* Not really an IM, but show it for informational 1416 /* Not really an IM, but show it for informational
1418 * purposes during development. */ 1417 * purposes during development. */
1419 return msim_incoming_im(session, msg); 1418 return msim_incoming_im(session, msg);
1420 } 1419 }
1421 } 1420 }
1422 1421
1423 /** 1422 /**
1424 * Handle an incoming instant message. 1423 * Handle an incoming instant message.
1425 * 1424 *
1426 * @param session The session 1425 * @param session The session
1427 * @param msg Message from the server, containing 'f' (userid from) and 'msg'. 1426 * @param msg Message from the server, containing 'f' (userid from) and 'msg'.
1428 * Should also contain username in _username from preprocessing. 1427 * Should also contain username in _username from preprocessing.
1429 * 1428 *
1430 * @return TRUE if successful. 1429 * @return TRUE if successful.
1431 */ 1430 */
1432 static gboolean 1431 static gboolean
1433 msim_incoming_im(MsimSession *session, MsimMessage *msg) 1432 msim_incoming_im(MsimSession *session, MsimMessage *msg)
1434 { 1433 {
1435 gchar *username, *msg_msim_markup, *msg_purple_markup; 1434 gchar *username, *msg_msim_markup, *msg_purple_markup;
1436 1435
1437 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 1436 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
1438 g_return_val_if_fail(msg != NULL, FALSE); 1437 g_return_val_if_fail(msg != NULL, FALSE);
1439 1438
1440 username = msim_msg_get_string(msg, "_username"); 1439 username = msim_msg_get_string(msg, "_username");
1441 g_return_val_if_fail(username != NULL, FALSE); 1440 g_return_val_if_fail(username != NULL, FALSE);
1442 1441
1443 msg_msim_markup = msim_msg_get_string(msg, "msg"); 1442 msg_msim_markup = msim_msg_get_string(msg, "msg");
1444 g_return_val_if_fail(msg_msim_markup != NULL, FALSE); 1443 g_return_val_if_fail(msg_msim_markup != NULL, FALSE);
1445 1444
1446 msg_purple_markup = msim_markup_to_html(session, msg_msim_markup); 1445 msg_purple_markup = msim_markup_to_html(session, msg_msim_markup);
1447 g_free(msg_msim_markup); 1446 g_free(msg_msim_markup);
1448 1447
1449 serv_got_im(session->gc, username, msg_purple_markup, 1448 serv_got_im(session->gc, username, msg_purple_markup,
1450 PURPLE_MESSAGE_RECV, time(NULL)); 1449 PURPLE_MESSAGE_RECV, time(NULL));
1451 1450
1452 g_free(username); 1451 g_free(username);
1453 g_free(msg_purple_markup); 1452 g_free(msg_purple_markup);
1454 1453
1464 */ 1463 */
1465 static void 1464 static void
1466 msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note) 1465 msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note)
1467 { 1466 {
1468 /* TODO: Some more context, outwardly equivalent to a backtrace, 1467 /* TODO: Some more context, outwardly equivalent to a backtrace,
1469 * for helping figure out what this msg is for. What was going on? 1468 * for helping figure out what this msg is for. What was going on?
1470 * But not too much information so that a user 1469 * But not too much information so that a user
1471 * posting this dump reveals confidential information. 1470 * posting this dump reveals confidential information.
1472 */ 1471 */
1473 1472
1474 /* TODO: dump unknown msgs to file, so user can send them to me 1473 /* TODO: dump unknown msgs to file, so user can send them to me
1475 * if they wish, to help add support for new messages (inspired 1474 * if they wish, to help add support for new messages (inspired
1476 * by Alexandr Shutko, who maintains OSCAR protocol documentation). */ 1475 * by Alexandr Shutko, who maintains OSCAR protocol documentation). */
1477 1476
1478 purple_debug_info("msim", "Unrecognized data on account for %s\n", 1477 purple_debug_info("msim", "Unrecognized data on account for %s\n",
1479 session->account->username ? session->account->username 1478 session->account->username ? session->account->username
1480 : "(NULL)"); 1479 : "(NULL)");
1481 if (note) { 1480 if (note) {
1482 purple_debug_info("msim", "(Note: %s)\n", note); 1481 purple_debug_info("msim", "(Note: %s)\n", note);
1483 } 1482 }
1484 1483
1485 if (msg) { 1484 if (msg) {
1486 msim_msg_dump("Unrecognized message dump: %s\n", msg); 1485 msim_msg_dump("Unrecognized message dump: %s\n", msg);
1487 } 1486 }
1488 } 1487 }
1489 1488
1490 /** Process an incoming zap. */ 1489 /** Process an incoming zap. */
1491 static gboolean 1490 static gboolean
1492 msim_incoming_zap(MsimSession *session, MsimMessage *msg) 1491 msim_incoming_zap(MsimSession *session, MsimMessage *msg)
1493 { 1492 {
1494 gchar *msg_text, *username, *zap_text; 1493 gchar *msg_text, *username, *zap_text;
1495 gint zap; 1494 gint zap;
1496 const gchar *zap_past_tense[10]; 1495 const gchar *zap_past_tense[10];
1497 1496
1498 zap_past_tense[0] = _("zapped"); 1497 zap_past_tense[0] = _("zapped");
1499 zap_past_tense[1] = _("whacked"); 1498 zap_past_tense[1] = _("whacked");
1500 zap_past_tense[2] = _("torched"); 1499 zap_past_tense[2] = _("torched");
1501 zap_past_tense[3] = _("smooched"); 1500 zap_past_tense[3] = _("smooched");
1502 zap_past_tense[4] = _("hugged"); 1501 zap_past_tense[4] = _("hugged");
1503 zap_past_tense[5] = _("bslapped"); 1502 zap_past_tense[5] = _("bslapped");
1504 zap_past_tense[6] = _("goosed"); 1503 zap_past_tense[6] = _("goosed");
1505 zap_past_tense[7] = _("hi-fived"); 1504 zap_past_tense[7] = _("hi-fived");
1506 zap_past_tense[8] = _("punk'd"); 1505 zap_past_tense[8] = _("punk'd");
1507 zap_past_tense[9] = _("raspberried"); 1506 zap_past_tense[9] = _("raspberried");
1508 1507
1509 msg_text = msim_msg_get_string(msg, "msg"); 1508 msg_text = msim_msg_get_string(msg, "msg");
1510 username = msim_msg_get_string(msg, "_username"); 1509 username = msim_msg_get_string(msg, "_username");
1511 1510
1512 g_return_val_if_fail(msg_text != NULL, FALSE); 1511 g_return_val_if_fail(msg_text != NULL, FALSE);
1513 g_return_val_if_fail(username != NULL, FALSE); 1512 g_return_val_if_fail(username != NULL, FALSE);
1514 1513
1515 g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE); 1514 g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
1516 1515
1517 zap = CLAMP(zap, 0, sizeof(zap_past_tense) / sizeof(zap_past_tense[0])); 1516 zap = CLAMP(zap, 0, sizeof(zap_past_tense) / sizeof(zap_past_tense[0]));
1518 1517
1519 zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]); 1518 zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]);
1520 1519
1521 serv_got_im(session->gc, username, zap_text, 1520 serv_got_im(session->gc, username, zap_text,
1522 PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL)); 1521 PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL));
1523 1522
1524 g_free(zap_text); 1523 g_free(zap_text);
1525 g_free(msg_text); 1524 g_free(msg_text);
1526 g_free(username); 1525 g_free(username);
1527 1526
1528 return TRUE; 1527 return TRUE;
1529 } 1528 }
1530 1529
1531 /** 1530 /**
1532 * Handle an incoming action message. 1531 * Handle an incoming action message.
1533 * 1532 *
1541 msim_incoming_action(MsimSession *session, MsimMessage *msg) 1540 msim_incoming_action(MsimSession *session, MsimMessage *msg)
1542 { 1541 {
1543 gchar *msg_text, *username; 1542 gchar *msg_text, *username;
1544 gboolean rc; 1543 gboolean rc;
1545 1544
1546 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 1545 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
1547 g_return_val_if_fail(msg != NULL, FALSE); 1546 g_return_val_if_fail(msg != NULL, FALSE);
1548 1547
1549 msg_text = msim_msg_get_string(msg, "msg"); 1548 msg_text = msim_msg_get_string(msg, "msg");
1550 g_return_val_if_fail(msg_text != NULL, FALSE); 1549 g_return_val_if_fail(msg_text != NULL, FALSE);
1551 1550
1552 username = msim_msg_get_string(msg, "_username"); 1551 username = msim_msg_get_string(msg, "_username");
1553 g_return_val_if_fail(username != NULL, FALSE); 1552 g_return_val_if_fail(username != NULL, FALSE);
1554 1553
1555 purple_debug_info("msim", "msim_incoming_action: action <%s> from <%d>\n", 1554 purple_debug_info("msim", "msim_incoming_action: action <%s> from <%d>\n",
1556 msg_text, username); 1555 msg_text, username);
1557 1556
1558 if (strcmp(msg_text, "%typing%") == 0) { 1557 if (strcmp(msg_text, "%typing%") == 0) {
1559 /* TODO: find out if msim repeatedly sends typing messages, so we can 1558 /* TODO: find out if msim repeatedly sends typing messages, so we can
1560 * give it a timeout. Right now, there does seem to be an inordinately 1559 * give it a timeout. Right now, there does seem to be an inordinately
1561 * amount of time between typing stopped-typing notifications. */ 1560 * amount of time between typing stopped-typing notifications. */
1562 serv_got_typing(session->gc, username, 0, PURPLE_TYPING); 1561 serv_got_typing(session->gc, username, 0, PURPLE_TYPING);
1563 rc = TRUE; 1562 rc = TRUE;
1564 } else if (strcmp(msg_text, "%stoptyping%") == 0) { 1563 } else if (strcmp(msg_text, "%stoptyping%") == 0) {
1565 serv_got_typing_stopped(session->gc, username); 1564 serv_got_typing_stopped(session->gc, username);
1566 rc = TRUE; 1565 rc = TRUE;
1567 } else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) { 1566 } else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) {
1568 rc = msim_incoming_zap(session, msg); 1567 rc = msim_incoming_zap(session, msg);
1569 } else { 1568 } else {
1570 msim_unrecognized(session, msg, 1569 msim_unrecognized(session, msg,
1571 "got to msim_incoming_action but unrecognized value for 'msg'"); 1570 "got to msim_incoming_action but unrecognized value for 'msg'");
1572 rc = FALSE; 1571 rc = FALSE;
1573 } 1572 }
1574 1573
1575 g_free(msg_text); 1574 g_free(msg_text);
1576 g_free(username); 1575 g_free(username);
1580 1579
1581 /* Process an incoming media (buddy icon) message. */ 1580 /* Process an incoming media (buddy icon) message. */
1582 static gboolean 1581 static gboolean
1583 msim_incoming_media(MsimSession *session, MsimMessage *msg) 1582 msim_incoming_media(MsimSession *session, MsimMessage *msg)
1584 { 1583 {
1585 gchar *username, *text; 1584 gchar *username, *text;
1586 1585
1587 username = msim_msg_get_string(msg, "_username"); 1586 username = msim_msg_get_string(msg, "_username");
1588 text = msim_msg_get_string(msg, "msg"); 1587 text = msim_msg_get_string(msg, "msg");
1589 1588
1590 g_return_val_if_fail(username != NULL, FALSE); 1589 g_return_val_if_fail(username != NULL, FALSE);
1591 g_return_val_if_fail(text != NULL, FALSE); 1590 g_return_val_if_fail(text != NULL, FALSE);
1592 1591
1593 purple_debug_info("msim", "msim_incoming_media: from %s, got msg=%s\n", username, text); 1592 purple_debug_info("msim", "msim_incoming_media: from %s, got msg=%s\n", username, text);
1594 1593
1595 /* Media messages are sent when the user opens a window to someone. 1594 /* Media messages are sent when the user opens a window to someone.
1596 * Tell libpurple they started typing and stopped typing, to inform the Psychic 1595 * Tell libpurple they started typing and stopped typing, to inform the Psychic
1597 * Mode plugin so it too can open a window to the user. */ 1596 * Mode plugin so it too can open a window to the user. */
1598 serv_got_typing(session->gc, username, 0, PURPLE_TYPING); 1597 serv_got_typing(session->gc, username, 0, PURPLE_TYPING);
1599 serv_got_typing_stopped(session->gc, username); 1598 serv_got_typing_stopped(session->gc, username);
1600 1599
1601 g_free(username); 1600 g_free(username);
1602 1601
1603 return TRUE; 1602 return TRUE;
1604 } 1603 }
1605 1604
1606 /* Process an incoming "unofficial client" message. The plugin for 1605 /* Process an incoming "unofficial client" message. The plugin for
1607 * Miranda IM sends this message with the plugin information. */ 1606 * Miranda IM sends this message with the plugin information. */
1608 static gboolean 1607 static gboolean
1609 msim_incoming_unofficial_client(MsimSession *session, MsimMessage *msg) 1608 msim_incoming_unofficial_client(MsimSession *session, MsimMessage *msg)
1610 { 1609 {
1611 PurpleBuddy *buddy; 1610 PurpleBuddy *buddy;
1612 gchar *username, *client_info; 1611 gchar *username, *client_info;
1613 1612
1614 username = msim_msg_get_string(msg, "_username"); 1613 username = msim_msg_get_string(msg, "_username");
1615 client_info = msim_msg_get_string(msg, "msg"); 1614 client_info = msim_msg_get_string(msg, "msg");
1616 1615
1617 g_return_val_if_fail(username != NULL, FALSE); 1616 g_return_val_if_fail(username != NULL, FALSE);
1618 g_return_val_if_fail(client_info != NULL, FALSE); 1617 g_return_val_if_fail(client_info != NULL, FALSE);
1619 1618
1620 purple_debug_info("msim", "msim_incoming_unofficial_client: %s is using client %s\n", 1619 purple_debug_info("msim", "msim_incoming_unofficial_client: %s is using client %s\n",
1621 username, client_info); 1620 username, client_info);
1622 1621
1623 buddy = purple_find_buddy(session->account, username); 1622 buddy = purple_find_buddy(session->account, username);
1624 1623
1625 g_return_val_if_fail(buddy != NULL, FALSE); 1624 g_return_val_if_fail(buddy != NULL, FALSE);
1626 1625
1627 purple_blist_node_remove_setting(&buddy->node, "client"); 1626 purple_blist_node_remove_setting(&buddy->node, "client");
1628 purple_blist_node_set_string(&buddy->node, "client", client_info); 1627 purple_blist_node_set_string(&buddy->node, "client", client_info);
1629 1628
1630 g_free(username); 1629 g_free(username);
1631 /* Do not free client_info - the blist now owns it. */ 1630 /* Do not free client_info - the blist now owns it. */
1632 1631
1633 return TRUE; 1632 return TRUE;
1634 } 1633 }
1635 1634
1636 1635
1637 #ifdef MSIM_SEND_CLIENT_VERSION 1636 #ifdef MSIM_SEND_CLIENT_VERSION
1638 /** Send our client version to another unofficial client that understands it. */ 1637 /** Send our client version to another unofficial client that understands it. */
1639 static gboolean 1638 static gboolean
1640 msim_send_unofficial_client(MsimSession *session, gchar *username) 1639 msim_send_unofficial_client(MsimSession *session, gchar *username)
1641 { 1640 {
1642 gchar *our_info; 1641 gchar *our_info;
1643 gboolean ret; 1642 gboolean ret;
1644 1643
1645 our_info = g_strdup_printf("Libpurple %d.%d.%d - msimprpl %s", 1644 our_info = g_strdup_printf("Libpurple %d.%d.%d - msimprpl %s",
1646 PURPLE_MAJOR_VERSION, 1645 PURPLE_MAJOR_VERSION,
1647 PURPLE_MINOR_VERSION, 1646 PURPLE_MINOR_VERSION,
1648 PURPLE_MICRO_VERSION, 1647 PURPLE_MICRO_VERSION,
1649 MSIM_PRPL_VERSION_STRING); 1648 MSIM_PRPL_VERSION_STRING);
1650 1649
1651 ret = msim_send_bm(session, username, our_info, MSIM_BM_UNOFFICIAL_CLIENT); 1650 ret = msim_send_bm(session, username, our_info, MSIM_BM_UNOFFICIAL_CLIENT);
1652 1651
1653 return ret; 1652 return ret;
1654 } 1653 }
1655 #endif 1654 #endif
1656 1655
1657 /** 1656 /**
1658 * Handle when our user starts or stops typing to another user. 1657 * Handle when our user starts or stops typing to another user.
1663 * 1662 *
1664 * @return 0 1663 * @return 0
1665 */ 1664 */
1666 unsigned int 1665 unsigned int
1667 msim_send_typing(PurpleConnection *gc, const gchar *name, 1666 msim_send_typing(PurpleConnection *gc, const gchar *name,
1668 PurpleTypingState state) 1667 PurpleTypingState state)
1669 { 1668 {
1670 const gchar *typing_str; 1669 const gchar *typing_str;
1671 MsimSession *session; 1670 MsimSession *session;
1672 1671
1673 g_return_val_if_fail(gc != NULL, 0); 1672 g_return_val_if_fail(gc != NULL, 0);
1674 g_return_val_if_fail(name != NULL, 0); 1673 g_return_val_if_fail(name != NULL, 0);
1675 1674
1676 session = (MsimSession *)gc->proto_data; 1675 session = (MsimSession *)gc->proto_data;
1677 1676
1678 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0); 1677 g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
1679 1678
1680 switch (state) { 1679 switch (state) {
1681 case PURPLE_TYPING: 1680 case PURPLE_TYPING:
1682 typing_str = "%typing%"; 1681 typing_str = "%typing%";
1683 break; 1682 break;
1684 1683
1685 case PURPLE_TYPED: 1684 case PURPLE_TYPED:
1695 } 1694 }
1696 1695
1697 /** Callback for msim_get_info(), for when user info is received. */ 1696 /** Callback for msim_get_info(), for when user info is received. */
1698 static void 1697 static void
1699 msim_get_info_cb(MsimSession *session, MsimMessage *user_info_msg, 1698 msim_get_info_cb(MsimSession *session, MsimMessage *user_info_msg,
1700 gpointer data) 1699 gpointer data)
1701 { 1700 {
1702 GHashTable *body; 1701 GHashTable *body;
1703 gchar *body_str; 1702 gchar *body_str;
1704 MsimMessage *msg; 1703 MsimMessage *msg;
1705 gchar *user; 1704 gchar *user;
1706 PurpleNotifyUserInfo *user_info; 1705 PurpleNotifyUserInfo *user_info;
1707 PurpleBuddy *buddy; 1706 PurpleBuddy *buddy;
1708 const gchar *str, *str2; 1707 const gchar *str, *str2;
1709 1708
1710 g_return_if_fail(MSIM_SESSION_VALID(session)); 1709 g_return_if_fail(MSIM_SESSION_VALID(session));
1711 1710
1712 /* Get user{name,id} from msim_get_info, passed as an MsimMessage for 1711 /* Get user{name,id} from msim_get_info, passed as an MsimMessage for
1713 orthogonality. */ 1712 orthogonality. */
1714 msg = (MsimMessage *)data; 1713 msg = (MsimMessage *)data;
1715 g_return_if_fail(msg != NULL); 1714 g_return_if_fail(msg != NULL);
1716 1715
1717 user = msim_msg_get_string(msg, "user"); 1716 user = msim_msg_get_string(msg, "user");
1718 if (!user) { 1717 if (!user) {
1719 purple_debug_info("msim", "msim_get_info_cb: no 'user' in msg"); 1718 purple_debug_info("msim", "msim_get_info_cb: no 'user' in msg");
1720 return; 1719 return;
1722 1721
1723 msim_msg_free(msg); 1722 msim_msg_free(msg);
1724 purple_debug_info("msim", "msim_get_info_cb: got for user: %s\n", user); 1723 purple_debug_info("msim", "msim_get_info_cb: got for user: %s\n", user);
1725 1724
1726 body_str = msim_msg_get_string(user_info_msg, "body"); 1725 body_str = msim_msg_get_string(user_info_msg, "body");
1727 g_return_if_fail(body_str != NULL); 1726 g_return_if_fail(body_str != NULL);
1728 body = msim_parse_body(body_str); 1727 body = msim_parse_body(body_str);
1729 g_free(body_str); 1728 g_free(body_str);
1730 1729
1731 buddy = purple_find_buddy(session->account, user); 1730 buddy = purple_find_buddy(session->account, user);
1732 /* Note: don't assume buddy is non-NULL; will be if lookup random user 1731 /* Note: don't assume buddy is non-NULL; will be if lookup random user
1733 * not on blist. */ 1732 * not on blist. */
1734 1733
1735 user_info = purple_notify_user_info_new(); 1734 user_info = purple_notify_user_info_new();
1736 1735
1737 /* Identification */ 1736 /* Identification */
1738 purple_notify_user_info_add_pair(user_info, _("User"), user); 1737 purple_notify_user_info_add_pair(user_info, _("User"), user);
1741 str = g_hash_table_lookup(body, "UserID"); 1740 str = g_hash_table_lookup(body, "UserID");
1742 if (str) 1741 if (str)
1743 purple_notify_user_info_add_pair(user_info, _("User ID"), 1742 purple_notify_user_info_add_pair(user_info, _("User ID"),
1744 g_strdup(str)); 1743 g_strdup(str));
1745 1744
1746 /* a/s/l...the vitals */ 1745 /* a/s/l...the vitals */
1747 str = g_hash_table_lookup(body, "Age"); 1746 str = g_hash_table_lookup(body, "Age");
1748 if (str) 1747 if (str)
1749 purple_notify_user_info_add_pair(user_info, _("Age"), g_strdup(str)); 1748 purple_notify_user_info_add_pair(user_info, _("Age"), g_strdup(str));
1750 1749
1751 str = g_hash_table_lookup(body, "Gender"); 1750 str = g_hash_table_lookup(body, "Gender");
1758 g_strdup(str)); 1757 g_strdup(str));
1759 1758
1760 /* Other information */ 1759 /* Other information */
1761 1760
1762 if (buddy) { 1761 if (buddy) {
1763 /* Headline comes from buddy status messages */ 1762 /* Headline comes from buddy status messages */
1764 str = purple_blist_node_get_string(&buddy->node, "Headline"); 1763 str = purple_blist_node_get_string(&buddy->node, "Headline");
1765 if (str) 1764 if (str)
1766 purple_notify_user_info_add_pair(user_info, "Headline", str); 1765 purple_notify_user_info_add_pair(user_info, "Headline", str);
1767 } 1766 }
1768 1767
1780 /* Total friends only available if looked up by uid, not username. */ 1779 /* Total friends only available if looked up by uid, not username. */
1781 str = g_hash_table_lookup(body, "TotalFriends"); 1780 str = g_hash_table_lookup(body, "TotalFriends");
1782 if (str) { 1781 if (str) {
1783 purple_notify_user_info_add_pair(user_info, _("Total Friends"), 1782 purple_notify_user_info_add_pair(user_info, _("Total Friends"),
1784 g_strdup(str)); 1783 g_strdup(str));
1785 } 1784 }
1786 1785
1787 if (buddy) { 1786 if (buddy) {
1788 gint cv; 1787 gint cv;
1789 1788
1790 str = purple_blist_node_get_string(&buddy->node, "client"); 1789 str = purple_blist_node_get_string(&buddy->node, "client");
1791 cv = purple_blist_node_get_int(&buddy->node, "client_cv"); 1790 cv = purple_blist_node_get_int(&buddy->node, "client_cv");
1792 1791
1793 if (str) { 1792 if (str) {
1794 purple_notify_user_info_add_pair(user_info, _("Client Version"), 1793 purple_notify_user_info_add_pair(user_info, _("Client Version"),
1795 g_strdup_printf("%s (build %d)", str, cv)); 1794 g_strdup_printf("%s (build %d)", str, cv));
1796 } 1795 }
1797 } 1796 }
1798 1797
1799 purple_notify_userinfo(session->gc, user, user_info, NULL, NULL); 1798 purple_notify_userinfo(session->gc, user, user_info, NULL, NULL);
1800 purple_debug_info("msim", "msim_get_info_cb: username=%s\n", user); 1799 purple_debug_info("msim", "msim_get_info_cb: username=%s\n", user);
1801 //purple_notify_user_info_destroy(user_info); 1800 //purple_notify_user_info_destroy(user_info);
1802 /* Do not free username, since it will be used by user_info. */ 1801 /* Do not free username, since it will be used by user_info. */
1812 MsimSession *session; 1811 MsimSession *session;
1813 guint uid; 1812 guint uid;
1814 gchar *user_to_lookup; 1813 gchar *user_to_lookup;
1815 MsimMessage *user_msg; 1814 MsimMessage *user_msg;
1816 1815
1817 g_return_if_fail(gc != NULL); 1816 g_return_if_fail(gc != NULL);
1818 g_return_if_fail(user != NULL); 1817 g_return_if_fail(user != NULL);
1819 1818
1820 session = (MsimSession *)gc->proto_data; 1819 session = (MsimSession *)gc->proto_data;
1821 1820
1822 g_return_if_fail(MSIM_SESSION_VALID(session)); 1821 g_return_if_fail(MSIM_SESSION_VALID(session));
1823 1822
1824 /* Obtain uid of buddy. */ 1823 /* Obtain uid of buddy. */
1825 buddy = purple_find_buddy(session->account, user); 1824 buddy = purple_find_buddy(session->account, user);
1826 if (buddy) { 1825 if (buddy) {
1827 uid = purple_blist_node_get_int(&buddy->node, "UserID"); 1826 uid = purple_blist_node_get_int(&buddy->node, "UserID");
1861 void 1860 void
1862 msim_set_status(PurpleAccount *account, PurpleStatus *status) 1861 msim_set_status(PurpleAccount *account, PurpleStatus *status)
1863 { 1862 {
1864 PurpleStatusType *type; 1863 PurpleStatusType *type;
1865 MsimSession *session; 1864 MsimSession *session;
1866 guint status_code; 1865 guint status_code;
1867 const gchar *statstring; 1866 const gchar *statstring;
1868 1867
1869 session = (MsimSession *)account->gc->proto_data; 1868 session = (MsimSession *)account->gc->proto_data;
1870 1869
1871 g_return_if_fail(MSIM_SESSION_VALID(session)); 1870 g_return_if_fail(MSIM_SESSION_VALID(session));
1872 1871
1873 type = purple_status_get_type(status); 1872 type = purple_status_get_type(status);
1874 1873
1875 switch (purple_status_type_get_primitive(type)) { 1874 switch (purple_status_type_get_primitive(type)) {
1876 case PURPLE_STATUS_AVAILABLE: 1875 case PURPLE_STATUS_AVAILABLE:
1877 purple_debug_info("msim", "msim_set_status: available (%d->%d)\n", PURPLE_STATUS_AVAILABLE, 1876 purple_debug_info("msim", "msim_set_status: available (%d->%d)\n", PURPLE_STATUS_AVAILABLE,
1878 MSIM_STATUS_CODE_ONLINE); 1877 MSIM_STATUS_CODE_ONLINE);
1879 status_code = MSIM_STATUS_CODE_ONLINE; 1878 status_code = MSIM_STATUS_CODE_ONLINE;
1880 break; 1879 break;
1881 1880
1882 case PURPLE_STATUS_INVISIBLE: 1881 case PURPLE_STATUS_INVISIBLE:
1883 purple_debug_info("msim", "msim_set_status: invisible (%d->%d)\n", PURPLE_STATUS_INVISIBLE, 1882 purple_debug_info("msim", "msim_set_status: invisible (%d->%d)\n", PURPLE_STATUS_INVISIBLE,
1884 MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN); 1883 MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN);
1885 status_code = MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN; 1884 status_code = MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN;
1886 break; 1885 break;
1887 1886
1888 case PURPLE_STATUS_AWAY: 1887 case PURPLE_STATUS_AWAY:
1889 purple_debug_info("msim", "msim_set_status: away (%d->%d)\n", PURPLE_STATUS_AWAY, 1888 purple_debug_info("msim", "msim_set_status: away (%d->%d)\n", PURPLE_STATUS_AWAY,
1890 MSIM_STATUS_CODE_AWAY); 1889 MSIM_STATUS_CODE_AWAY);
1891 status_code = MSIM_STATUS_CODE_AWAY; 1890 status_code = MSIM_STATUS_CODE_AWAY;
1892 break; 1891 break;
1893 1892
1894 default: 1893 default:
1895 purple_debug_info("msim", "msim_set_status: unknown " 1894 purple_debug_info("msim", "msim_set_status: unknown "
1896 "status interpreting as online"); 1895 "status interpreting as online");
1897 status_code = MSIM_STATUS_CODE_ONLINE; 1896 status_code = MSIM_STATUS_CODE_ONLINE;
1898 break; 1897 break;
1899 } 1898 }
1900 1899
1901 statstring = purple_status_get_attr_string(status, "message"); 1900 statstring = purple_status_get_attr_string(status, "message");
1902 1901
1903 if (!statstring) { 1902 if (!statstring) {
1904 statstring = g_strdup(""); 1903 statstring = g_strdup("");
1905 } 1904 }
1906 1905
1907 msim_set_status_code(session, status_code, g_strdup(statstring)); 1906 msim_set_status_code(session, status_code, g_strdup(statstring));
1908 } 1907 }
1909 1908
1910 /** Go idle. */ 1909 /** Go idle. */
1911 void 1910 void
1912 msim_set_idle(PurpleConnection *gc, int time) 1911 msim_set_idle(PurpleConnection *gc, int time)
1913 { 1912 {
1914 MsimSession *session; 1913 MsimSession *session;
1915 1914
1916 g_return_if_fail(gc != NULL); 1915 g_return_if_fail(gc != NULL);
1917 1916
1918 session = (MsimSession *)gc->proto_data; 1917 session = (MsimSession *)gc->proto_data;
1919 1918
1920 g_return_if_fail(MSIM_SESSION_VALID(session)); 1919 g_return_if_fail(MSIM_SESSION_VALID(session));
1921 1920
1922 if (time == 0) { 1921 if (time == 0) {
1923 /* Going back from idle. In msim, idle is mutually exclusive 1922 /* Going back from idle. In msim, idle is mutually exclusive
1924 * from the other states (you can only be away or idle, but not 1923 * from the other states (you can only be away or idle, but not
1925 * both, for example), so by going non-idle I go online. 1924 * both, for example), so by going non-idle I go online.
1926 */ 1925 */
1927 /* TODO: find out how to keep old status string? */ 1926 /* TODO: find out how to keep old status string? */
1928 msim_set_status_code(session, MSIM_STATUS_CODE_ONLINE, g_strdup("")); 1927 msim_set_status_code(session, MSIM_STATUS_CODE_ONLINE, g_strdup(""));
1929 } else { 1928 } else {
1930 /* msim doesn't support idle time, so just go idle */ 1929 /* msim doesn't support idle time, so just go idle */
1931 msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, g_strdup("")); 1930 msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, g_strdup(""));
1932 } 1931 }
1933 } 1932 }
1934 1933
1935 /** Set status using an MSIM_STATUS_CODE_* value. 1934 /** Set status using an MSIM_STATUS_CODE_* value.
1936 * @param status_code An MSIM_STATUS_CODE_* value. 1935 * @param status_code An MSIM_STATUS_CODE_* value.
1937 * @param statstring Status string, must be a dynamic string (will be freed by msim_send). 1936 * @param statstring Status string, must be a dynamic string (will be freed by msim_send).
1938 */ 1937 */
1939 static void 1938 static void
1940 msim_set_status_code(MsimSession *session, guint status_code, gchar *statstring) 1939 msim_set_status_code(MsimSession *session, guint status_code, gchar *statstring)
1941 { 1940 {
1942 g_return_if_fail(MSIM_SESSION_VALID(session)); 1941 g_return_if_fail(MSIM_SESSION_VALID(session));
1943 g_return_if_fail(statstring != NULL); 1942 g_return_if_fail(statstring != NULL);
1944 1943
1945 purple_debug_info("msim", "msim_set_status_code: going to set status to code=%d,str=%s\n", 1944 purple_debug_info("msim", "msim_set_status_code: going to set status to code=%d,str=%s\n",
1946 status_code, statstring); 1945 status_code, statstring);
1947 1946
1948 if (!msim_send(session, 1947 if (!msim_send(session,
1949 "status", MSIM_TYPE_INTEGER, status_code, 1948 "status", MSIM_TYPE_INTEGER, status_code,
1950 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 1949 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
1951 "statstring", MSIM_TYPE_STRING, statstring, 1950 "statstring", MSIM_TYPE_STRING, statstring,
1952 "locstring", MSIM_TYPE_STRING, g_strdup(""), 1951 "locstring", MSIM_TYPE_STRING, g_strdup(""),
1953 NULL)) 1952 NULL))
1954 { 1953 {
1955 purple_debug_info("msim", "msim_set_status: failed to set status"); 1954 purple_debug_info("msim", "msim_set_status: failed to set status");
1956 } 1955 }
1957 1956
1958 } 1957 }
1970 gchar *body_str; 1969 gchar *body_str;
1971 GHashTable *body; 1970 GHashTable *body;
1972 gchar *username; 1971 gchar *username;
1973 MsimMessage *msg; 1972 MsimMessage *msg;
1974 1973
1975 g_return_if_fail(MSIM_SESSION_VALID(session)); 1974 g_return_if_fail(MSIM_SESSION_VALID(session));
1976 g_return_if_fail(userinfo != NULL); 1975 g_return_if_fail(userinfo != NULL);
1977 1976
1978 body_str = msim_msg_get_string(userinfo, "body"); 1977 body_str = msim_msg_get_string(userinfo, "body");
1979 g_return_if_fail(body_str != NULL); 1978 g_return_if_fail(body_str != NULL);
1980 body = msim_parse_body(body_str); 1979 body = msim_parse_body(body_str);
1981 g_return_if_fail(body != NULL); 1980 g_return_if_fail(body != NULL);
1984 username = g_hash_table_lookup(body, "UserName"); 1983 username = g_hash_table_lookup(body, "UserName");
1985 g_return_if_fail(username != NULL); 1984 g_return_if_fail(username != NULL);
1986 1985
1987 1986
1988 msg = (MsimMessage *)data; 1987 msg = (MsimMessage *)data;
1989 g_return_if_fail(msg != NULL); 1988 g_return_if_fail(msg != NULL);
1990 1989
1991 /* TODO: more elegant solution than below. attach whole message? */ 1990 /* TODO: more elegant solution than below. attach whole message? */
1992 /* Special elements name beginning with '_', we'll use internally within the 1991 /* Special elements name beginning with '_', we'll use internally within the
1993 * program (did not come directly from the wire). */ 1992 * program (did not come directly from the wire). */
1994 msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username)); 1993 msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username));
1995 1994
1996 /* TODO: attach more useful information, like ImageURL */ 1995 /* TODO: attach more useful information, like ImageURL */
1997 1996
1998 msim_process(session, msg); 1997 msim_process(session, msg);
1999 1998
2000 /* TODO: Free copy cloned from msim_preprocess_incoming(). */ 1999 /* TODO: Free copy cloned from msim_preprocess_incoming(). */
2001 //XXX msim_msg_free(msg); 2000 //XXX msim_msg_free(msg);
2037 //node = cur->data; 2036 //node = cur->data;
2038 2037
2039 uid = purple_blist_node_get_int(&buddy->node, "UserID"); 2038 uid = purple_blist_node_get_int(&buddy->node, "UserID");
2040 //uid = purple_blist_node_get_int(node, "UserID"); 2039 //uid = purple_blist_node_get_int(node, "UserID");
2041 2040
2042 /* name = buddy->name; */ /* crash */ 2041 /* name = buddy->name; */ /* crash */
2043 /* name = PURPLE_BLIST_NODE_NAME(&buddy->node); */ /* crash */ 2042 /* name = PURPLE_BLIST_NODE_NAME(&buddy->node); */ /* crash */
2044 2043
2045 /* XXX Is this right? Memory corruption here somehow. Happens only 2044 /* XXX Is this right? Memory corruption here somehow. Happens only
2046 * when return one of these values. */ 2045 * when return one of these values. */
2047 name = purple_buddy_get_name(buddy); /* crash */ 2046 name = purple_buddy_get_name(buddy); /* crash */
2048 //name = purple_buddy_get_name((PurpleBuddy *)node); /* crash */ 2047 //name = purple_buddy_get_name((PurpleBuddy *)node); /* crash */
2049 /* return name; */ /* crash (with above) */ 2048 /* return name; */ /* crash (with above) */
2050 2049
2051 /* name = NULL; */ /* no crash */ 2050 /* name = NULL; */ /* no crash */
2052 /* return NULL; */ /* no crash (with anything) */ 2051 /* return NULL; */ /* no crash (with anything) */
2053 2052
2054 /* crash = 2053 /* crash =
2055 *** glibc detected *** pidgin: realloc(): invalid pointer: 0x0000000000d2aec0 *** 2054 *** glibc detected *** pidgin: realloc(): invalid pointer: 0x0000000000d2aec0 ***
2056 ======= Backtrace: ========= 2055 ======= Backtrace: =========
2057 /lib/libc.so.6(__libc_realloc+0x323)[0x2b7bfc012e03] 2056 /lib/libc.so.6(__libc_realloc+0x323)[0x2b7bfc012e03]
2078 #4 0x00002b4074f0d6dd in malloc () from /lib/libc.so.6 2077 #4 0x00002b4074f0d6dd in malloc () from /lib/libc.so.6
2079 #5 0x00002b4074974b5b in g_malloc () from /usr/lib/libglib-2.0.so.0 2078 #5 0x00002b4074974b5b in g_malloc () from /usr/lib/libglib-2.0.so.0
2080 #6 0x00002b40749868bf in g_strdup () from /usr/lib/libglib-2.0.so.0 2079 #6 0x00002b40749868bf in g_strdup () from /usr/lib/libglib-2.0.so.0
2081 #7 0x00002b407810969f in msim_parse ( 2080 #7 0x00002b407810969f in msim_parse (
2082 raw=0xd2a910 "\\bm\\100\\f\\3656574\\msg\\|s|0|ss|Offline") 2081 raw=0xd2a910 "\\bm\\100\\f\\3656574\\msg\\|s|0|ss|Offline")
2083 at message.c:648 2082 at message.c:648
2084 #8 0x00002b407810889c in msim_input_cb (gc_uncasted=0xcf92c0, 2083 #8 0x00002b407810889c in msim_input_cb (gc_uncasted=0xcf92c0,
2085 source=<value optimized out>, cond=<value optimized out>) at myspace.c:1478 2084 source=<value optimized out>, cond=<value optimized out>) at myspace.c:1478
2086 2085
2087 2086
2088 Why is it crashing in msim_parse()'s g_strdup()? 2087 Why is it crashing in msim_parse()'s g_strdup()?
2089 */ 2088 */
2090 purple_debug_info("msim", "msim_uid2username_from_blist: %s's uid=%d (want %d)\n", 2089 purple_debug_info("msim", "msim_uid2username_from_blist: %s's uid=%d (want %d)\n",
2091 name, uid, wanted_uid); 2090 name, uid, wanted_uid);
2092 2091
2093 if (uid == wanted_uid) 2092 if (uid == wanted_uid)
2113 * @param msg MsimMessage *, freed by caller. 2112 * @param msg MsimMessage *, freed by caller.
2114 */ 2113 */
2115 static gboolean 2114 static gboolean
2116 msim_preprocess_incoming(MsimSession *session, MsimMessage *msg) 2115 msim_preprocess_incoming(MsimSession *session, MsimMessage *msg)
2117 { 2116 {
2118 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2117 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2119 g_return_val_if_fail(msg != NULL, FALSE); 2118 g_return_val_if_fail(msg != NULL, FALSE);
2120 2119
2121 if (msim_msg_get(msg, "bm") && msim_msg_get(msg, "f")) { 2120 if (msim_msg_get(msg, "bm") && msim_msg_get(msg, "f")) {
2122 guint uid; 2121 guint uid;
2123 const gchar *username; 2122 const gchar *username;
2124 2123
2162 #ifdef MSIM_USE_KEEPALIVE 2161 #ifdef MSIM_USE_KEEPALIVE
2163 /** Check if the connection is still alive, based on last communication. */ 2162 /** Check if the connection is still alive, based on last communication. */
2164 static gboolean 2163 static gboolean
2165 msim_check_alive(gpointer data) 2164 msim_check_alive(gpointer data)
2166 { 2165 {
2167 MsimSession *session; 2166 MsimSession *session;
2168 time_t delta; 2167 time_t delta;
2169 gchar *errmsg; 2168 gchar *errmsg;
2170 2169
2171 session = (MsimSession *)data; 2170 session = (MsimSession *)data;
2172 2171
2173 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2172 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2174 2173
2175 delta = time(NULL) - session->last_comm; 2174 delta = time(NULL) - session->last_comm;
2176 //purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); 2175 //purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta);
2177 if (delta >= MSIM_KEEPALIVE_INTERVAL) { 2176 if (delta >= MSIM_KEEPALIVE_INTERVAL) {
2178 errmsg = g_strdup_printf(_("Connection to server lost (no data received within %d seconds)"), (int)delta); 2177 errmsg = g_strdup_printf(_("Connection to server lost (no data received within %d seconds)"), (int)delta);
2179 2178
2180 purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n", 2179 purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
2181 errmsg, MSIM_KEEPALIVE_INTERVAL); 2180 errmsg, MSIM_KEEPALIVE_INTERVAL);
2182 purple_connection_error(session->gc, errmsg); 2181 purple_connection_error(session->gc, errmsg);
2183 2182
2184 purple_notify_error(session->gc, NULL, errmsg, NULL); 2183 purple_notify_error(session->gc, NULL, errmsg, NULL);
2185 2184
2186 g_free(errmsg); 2185 g_free(errmsg);
2187 2186
2188 return FALSE; 2187 return FALSE;
2189 } 2188 }
2190 2189
2191 return TRUE; 2190 return TRUE;
2192 } 2191 }
2193 #endif 2192 #endif
2194 2193
2195 /** Handle mail reply checks. */ 2194 /** Handle mail reply checks. */
2196 static void 2195 static void
2197 msim_check_inbox_cb(MsimSession *session, MsimMessage *reply, gpointer data) 2196 msim_check_inbox_cb(MsimSession *session, MsimMessage *reply, gpointer data)
2198 { 2197 {
2199 GHashTable *body; 2198 GHashTable *body;
2200 gchar *body_str; 2199 gchar *body_str;
2201 GString *notification; 2200 GString *notification;
2202 guint old_inbox_status; 2201 guint old_inbox_status;
2203 guint i, n; 2202 guint i, n;
2204 const gchar *froms[5], *tos[5], *urls[5], *subjects[5]; 2203 const gchar *froms[5], *tos[5], *urls[5], *subjects[5];
2205 2204
2206 /* Three parallel arrays for each new inbox message type. */ 2205 /* Three parallel arrays for each new inbox message type. */
2207 static const gchar *inbox_keys[] = 2206 static const gchar *inbox_keys[] =
2208 { 2207 {
2209 "Mail", 2208 "Mail",
2210 "BlogComment", 2209 "BlogComment",
2211 "ProfileComment", 2210 "ProfileComment",
2212 "FriendRequest", 2211 "FriendRequest",
2213 "PictureComment" 2212 "PictureComment"
2214 }; 2213 };
2215 2214
2216 static const guint inbox_bits[] = 2215 static const guint inbox_bits[] =
2217 { 2216 {
2218 MSIM_INBOX_MAIL, 2217 MSIM_INBOX_MAIL,
2219 MSIM_INBOX_BLOG_COMMENT, 2218 MSIM_INBOX_BLOG_COMMENT,
2220 MSIM_INBOX_PROFILE_COMMENT, 2219 MSIM_INBOX_PROFILE_COMMENT,
2221 MSIM_INBOX_FRIEND_REQUEST, 2220 MSIM_INBOX_FRIEND_REQUEST,
2222 MSIM_INBOX_PICTURE_COMMENT 2221 MSIM_INBOX_PICTURE_COMMENT
2223 }; 2222 };
2224 2223
2225 static const gchar *inbox_urls[] = 2224 static const gchar *inbox_urls[] =
2226 { 2225 {
2227 "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox", 2226 "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox",
2228 "http://blog.myspace.com/index.cfm?fuseaction=blog", 2227 "http://blog.myspace.com/index.cfm?fuseaction=blog",
2229 "http://home.myspace.com/index.cfm?fuseaction=user", 2228 "http://home.myspace.com/index.cfm?fuseaction=user",
2230 "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", 2229 "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests",
2231 "http://home.myspace.com/index.cfm?fuseaction=user" 2230 "http://home.myspace.com/index.cfm?fuseaction=user"
2232 }; 2231 };
2233 2232
2234 static const gchar *inbox_text[5]; 2233 static const gchar *inbox_text[5];
2235 2234
2236 /* Can't write _()'d strings in array initializers. Workaround. */ 2235 /* Can't write _()'d strings in array initializers. Workaround. */
2237 inbox_text[0] = _("New mail messages"); 2236 inbox_text[0] = _("New mail messages");
2238 inbox_text[1] = _("New blog comments"); 2237 inbox_text[1] = _("New blog comments");
2239 inbox_text[2] = _("New profile comments"); 2238 inbox_text[2] = _("New profile comments");
2240 inbox_text[3] = _("New friend requests!"); 2239 inbox_text[3] = _("New friend requests!");
2241 inbox_text[4] = _("New picture comments"); 2240 inbox_text[4] = _("New picture comments");
2242 2241
2243 g_return_if_fail(reply != NULL); 2242 g_return_if_fail(reply != NULL);
2244 2243
2245 msim_msg_dump("msim_check_inbox_cb: reply=%s\n", reply); 2244 msim_msg_dump("msim_check_inbox_cb: reply=%s\n", reply);
2246 2245
2247 body_str = msim_msg_get_string(reply, "body"); 2246 body_str = msim_msg_get_string(reply, "body");
2248 g_return_if_fail(body_str != NULL); 2247 g_return_if_fail(body_str != NULL);
2249 2248
2250 body = msim_parse_body(body_str); 2249 body = msim_parse_body(body_str);
2251 g_free(body_str); 2250 g_free(body_str);
2252 2251
2253 notification = g_string_new(""); 2252 notification = g_string_new("");
2254 2253
2255 old_inbox_status = session->inbox_status; 2254 old_inbox_status = session->inbox_status;
2256 2255
2257 n = 0; 2256 n = 0;
2258 2257
2259 for (i = 0; i < sizeof(inbox_keys) / sizeof(inbox_keys[0]); ++i) { 2258 for (i = 0; i < sizeof(inbox_keys) / sizeof(inbox_keys[0]); ++i) {
2260 const gchar *key; 2259 const gchar *key;
2261 guint bit; 2260 guint bit;
2262 2261
2263 key = inbox_keys[i]; 2262 key = inbox_keys[i];
2264 bit = inbox_bits[i]; 2263 bit = inbox_bits[i];
2265 2264
2266 if (g_hash_table_lookup(body, key)) { 2265 if (g_hash_table_lookup(body, key)) {
2267 /* Notify only on when _changes_ from no mail -> has mail 2266 /* Notify only on when _changes_ from no mail -> has mail
2268 * (edge triggered) */ 2267 * (edge triggered) */
2269 if (!(session->inbox_status & bit)) { 2268 if (!(session->inbox_status & bit)) {
2270 purple_debug_info("msim", "msim_check_inbox_cb: got %s, at %d\n", 2269 purple_debug_info("msim", "msim_check_inbox_cb: got %s, at %d\n",
2271 key ? key : "(NULL)", n); 2270 key ? key : "(NULL)", n);
2272 2271
2273 subjects[n] = inbox_text[i]; 2272 subjects[n] = inbox_text[i];
2274 froms[n] = _("MySpace"); 2273 froms[n] = _("MySpace");
2275 tos[n] = session->username; 2274 tos[n] = session->username;
2276 /* TODO: append token, web challenge, so automatically logs in. 2275 /* TODO: append token, web challenge, so automatically logs in.
2277 * Would also need to free strings because they won't be static 2276 * Would also need to free strings because they won't be static
2278 */ 2277 */
2279 urls[n] = inbox_urls[i]; 2278 urls[n] = inbox_urls[i];
2280 2279
2281 ++n; 2280 ++n;
2282 } else { 2281 } else {
2283 purple_debug_info("msim", 2282 purple_debug_info("msim",
2284 "msim_check_inbox_cb: already notified of %s\n", 2283 "msim_check_inbox_cb: already notified of %s\n",
2285 key ? key : "(NULL)"); 2284 key ? key : "(NULL)");
2286 } 2285 }
2287 2286
2288 session->inbox_status |= bit; 2287 session->inbox_status |= bit;
2289 } 2288 }
2290 } 2289 }
2291 2290
2292 if (n) { 2291 if (n) {
2293 purple_debug_info("msim", 2292 purple_debug_info("msim",
2294 "msim_check_inbox_cb: notifying of %d\n", n); 2293 "msim_check_inbox_cb: notifying of %d\n", n);
2295 2294
2296 /* TODO: free strings with callback _if_ change to dynamic (w/ token) */ 2295 /* TODO: free strings with callback _if_ change to dynamic (w/ token) */
2297 purple_notify_emails(session->gc, /* handle */ 2296 purple_notify_emails(session->gc, /* handle */
2298 n, /* count */ 2297 n, /* count */
2299 TRUE, /* detailed */ 2298 TRUE, /* detailed */
2300 subjects, froms, tos, urls, 2299 subjects, froms, tos, urls,
2301 NULL, /* PurpleNotifyCloseCallback cb */ 2300 NULL, /* PurpleNotifyCloseCallback cb */
2302 NULL); /* gpointer user_data */ 2301 NULL); /* gpointer user_data */
2303 2302
2304 } 2303 }
2305 2304
2306 g_hash_table_destroy(body); 2305 g_hash_table_destroy(body);
2307 } 2306 }
2308 2307
2309 /* Send request to check if there is new mail. */ 2308 /* Send request to check if there is new mail. */
2310 static gboolean 2309 static gboolean
2311 msim_check_inbox(gpointer data) 2310 msim_check_inbox(gpointer data)
2312 { 2311 {
2313 MsimSession *session; 2312 MsimSession *session;
2314 2313
2315 session = (MsimSession *)data; 2314 session = (MsimSession *)data;
2316 2315
2317 purple_debug_info("msim", "msim_check_inbox: checking mail\n"); 2316 purple_debug_info("msim", "msim_check_inbox: checking mail\n");
2318 g_return_val_if_fail(msim_send(session, 2317 g_return_val_if_fail(msim_send(session,
2319 "persist", MSIM_TYPE_INTEGER, 1, 2318 "persist", MSIM_TYPE_INTEGER, 1,
2320 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 2319 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
2321 "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, 2320 "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
2322 "dsn", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_DSN, 2321 "dsn", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_DSN,
2323 "lid", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_LID, 2322 "lid", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_LID,
2324 "uid", MSIM_TYPE_INTEGER, session->userid, 2323 "uid", MSIM_TYPE_INTEGER, session->userid,
2325 "rid", MSIM_TYPE_INTEGER, 2324 "rid", MSIM_TYPE_INTEGER,
2326 msim_new_reply_callback(session, msim_check_inbox_cb, NULL), 2325 msim_new_reply_callback(session, msim_check_inbox_cb, NULL),
2327 "body", MSIM_TYPE_STRING, g_strdup(""), 2326 "body", MSIM_TYPE_STRING, g_strdup(""),
2328 NULL), TRUE); 2327 NULL), TRUE);
2329 2328
2330 /* Always return true, so that we keep checking for mail. */ 2329 /* Always return true, so that we keep checking for mail. */
2331 return TRUE; 2330 return TRUE;
2332 } 2331 }
2333 2332
2334 /** Called when the session key arrives. */ 2333 /** Called when the session key arrives. */
2335 static gboolean 2334 static gboolean
2336 msim_we_are_logged_on(MsimSession *session, MsimMessage *msg) 2335 msim_we_are_logged_on(MsimSession *session, MsimMessage *msg)
2337 { 2336 {
2338 MsimMessage *body; 2337 MsimMessage *body;
2339 2338
2340 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2339 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2341 g_return_val_if_fail(msg != NULL, FALSE); 2340 g_return_val_if_fail(msg != NULL, FALSE);
2342 2341
2343 purple_connection_update_progress(session->gc, _("Connected"), 3, 4); 2342 purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
2344 purple_connection_set_state(session->gc, PURPLE_CONNECTED); 2343 purple_connection_set_state(session->gc, PURPLE_CONNECTED);
2345 2344
2346 session->sesskey = msim_msg_get_integer(msg, "sesskey"); 2345 session->sesskey = msim_msg_get_integer(msg, "sesskey");
2347 purple_debug_info("msim", "SESSKEY=<%d>\n", session->sesskey); 2346 purple_debug_info("msim", "SESSKEY=<%d>\n", session->sesskey);
2348 2347
2349 /* What is proof? Used to be uid, but now is 52 base64'd bytes... */ 2348 /* What is proof? Used to be uid, but now is 52 base64'd bytes... */
2350 2349
2351 /* Comes with: proof,profileid,userid,uniquenick -- all same values 2350 /* Comes with: proof,profileid,userid,uniquenick -- all same values
2352 * some of the time, but can vary. This is our own user ID. */ 2351 * some of the time, but can vary. This is our own user ID. */
2353 session->userid = msim_msg_get_integer(msg, "userid"); 2352 session->userid = msim_msg_get_integer(msg, "userid");
2354 2353
2355 /* Not sure what profileid is used for. */ 2354 /* Not sure what profileid is used for. */
2356 if (msim_msg_get_integer(msg, "profileid") != session->userid) { 2355 if (msim_msg_get_integer(msg, "profileid") != session->userid) {
2357 msim_unrecognized(session, msg, 2356 msim_unrecognized(session, msg,
2358 "Profile ID didn't match user ID, don't know why"); 2357 "Profile ID didn't match user ID, don't know why");
2359 } 2358 }
2360 2359
2361 /* We now know are our own username, only after we're logged in.. 2360 /* We now know are our own username, only after we're logged in..
2362 * which is weird, but happens because you login with your email 2361 * which is weird, but happens because you login with your email
2363 * address and not username. Will be freed in msim_session_destroy(). */ 2362 * address and not username. Will be freed in msim_session_destroy(). */
2364 session->username = msim_msg_get_string(msg, "uniquenick"); 2363 session->username = msim_msg_get_string(msg, "uniquenick");
2365 2364
2366 if (msim_msg_get_integer(msg, "uniquenick") == session->userid) { 2365 if (msim_msg_get_integer(msg, "uniquenick") == session->userid) {
2367 purple_debug_info("msim_we_are_logged_on", "TODO: pick username"); 2366 purple_debug_info("msim_we_are_logged_on", "TODO: pick username");
2368 } 2367 }
2369 2368
2370 body = msim_msg_new(TRUE, 2369 body = msim_msg_new(TRUE,
2371 "UserID", MSIM_TYPE_INTEGER, session->userid, 2370 "UserID", MSIM_TYPE_INTEGER, session->userid,
2372 NULL); 2371 NULL);
2373 2372
2374 /* Request IM info about ourself. */ 2373 /* Request IM info about ourself. */
2375 msim_send(session, 2374 msim_send(session,
2376 "persist", MSIM_TYPE_STRING, g_strdup("persist"), 2375 "persist", MSIM_TYPE_STRING, g_strdup("persist"),
2377 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 2376 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
2378 "dsn", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_DSN, 2377 "dsn", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_DSN,
2379 "uid", MSIM_TYPE_INTEGER, session->userid, 2378 "uid", MSIM_TYPE_INTEGER, session->userid,
2380 "lid", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_LID, 2379 "lid", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_LID,
2381 "rid", MSIM_TYPE_INTEGER, session->next_rid++, 2380 "rid", MSIM_TYPE_INTEGER, session->next_rid++,
2382 "body", MSIM_TYPE_DICTIONARY, body, 2381 "body", MSIM_TYPE_DICTIONARY, body,
2383 NULL); 2382 NULL);
2384 2383
2385 /* Request MySpace info about ourself. */ 2384 /* Request MySpace info about ourself. */
2386 msim_send(session, 2385 msim_send(session,
2387 "persist", MSIM_TYPE_STRING, g_strdup("persist"), 2386 "persist", MSIM_TYPE_STRING, g_strdup("persist"),
2388 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 2387 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
2389 "dsn", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_DSN, 2388 "dsn", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_DSN,
2390 "uid", MSIM_TYPE_INTEGER, session->userid, 2389 "uid", MSIM_TYPE_INTEGER, session->userid,
2391 "lid", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_LID, 2390 "lid", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_LID,
2392 "rid", MSIM_TYPE_INTEGER, session->next_rid++, 2391 "rid", MSIM_TYPE_INTEGER, session->next_rid++,
2393 "body", MSIM_TYPE_STRING, g_strdup(""), 2392 "body", MSIM_TYPE_STRING, g_strdup(""),
2394 NULL); 2393 NULL);
2395 2394
2396 /* TODO: set options (persist cmd=514,dsn=1,lid=10) */ 2395 /* TODO: set options (persist cmd=514,dsn=1,lid=10) */
2397 /* TODO: set blocklist */ 2396 /* TODO: set blocklist */
2398 2397
2399 /* Notify servers of our current status. */ 2398 /* Notify servers of our current status. */
2400 purple_debug_info("msim", "msim_we_are_logged_on: notifying servers of status\n"); 2399 purple_debug_info("msim", "msim_we_are_logged_on: notifying servers of status\n");
2401 msim_set_status(session->account, 2400 msim_set_status(session->account,
2402 purple_account_get_active_status(session->account)); 2401 purple_account_get_active_status(session->account));
2403 2402
2404 /* TODO: setinfo */ 2403 /* TODO: setinfo */
2405 /* 2404 /*
2406 body = msim_msg_new(TRUE, 2405 body = msim_msg_new(TRUE,
2407 "TotalFriends", MSIM_TYPE_INTEGER, 666, 2406 "TotalFriends", MSIM_TYPE_INTEGER, 666,
2408 NULL); 2407 NULL);
2409 msim_send(session, 2408 msim_send(session,
2410 "setinfo", MSIM_TYPE_BOOLEAN, TRUE, 2409 "setinfo", MSIM_TYPE_BOOLEAN, TRUE,
2411 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 2410 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
2412 "info", MSIM_TYPE_DICTIONARY, body, 2411 "info", MSIM_TYPE_DICTIONARY, body,
2413 NULL); 2412 NULL);
2414 */ 2413 */
2415 2414
2416 /* Disable due to problems with timeouts. TODO: fix. */ 2415 /* Disable due to problems with timeouts. TODO: fix. */
2417 #ifdef MSIM_USE_KEEPALIVE 2416 #ifdef MSIM_USE_KEEPALIVE
2418 purple_timeout_add(MSIM_KEEPALIVE_INTERVAL_CHECK, 2417 purple_timeout_add(MSIM_KEEPALIVE_INTERVAL_CHECK,
2419 (GSourceFunc)msim_check_alive, session); 2418 (GSourceFunc)msim_check_alive, session);
2420 #endif 2419 #endif
2421 2420
2422 purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, 2421 purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK,
2423 (GSourceFunc)msim_check_inbox, session); 2422 (GSourceFunc)msim_check_inbox, session);
2424 2423
2425 msim_check_inbox(session); 2424 msim_check_inbox(session);
2426 2425
2427 return TRUE; 2426 return TRUE;
2428 } 2427 }
2429 2428
2430 /** 2429 /**
2431 * Process a message. 2430 * Process a message.
2432 * 2431 *
2436 * @return TRUE if successful. FALSE if processing failed. 2435 * @return TRUE if successful. FALSE if processing failed.
2437 */ 2436 */
2438 static gboolean 2437 static gboolean
2439 msim_process(MsimSession *session, MsimMessage *msg) 2438 msim_process(MsimSession *session, MsimMessage *msg)
2440 { 2439 {
2441 g_return_val_if_fail(session != NULL, FALSE); 2440 g_return_val_if_fail(session != NULL, FALSE);
2442 g_return_val_if_fail(msg != NULL, FALSE); 2441 g_return_val_if_fail(msg != NULL, FALSE);
2443 2442
2444 #ifdef MSIM_DEBUG_MSG 2443 #ifdef MSIM_DEBUG_MSG
2445 msim_msg_dump("ready to process: %s\n", msg); 2444 msim_msg_dump("ready to process: %s\n", msg);
2446 #endif 2445 #endif
2447 2446
2448 if (msim_msg_get_integer(msg, "lc") == 1) { 2447 if (msim_msg_get_integer(msg, "lc") == 1) {
2449 return msim_login_challenge(session, msg); 2448 return msim_login_challenge(session, msg);
2450 } else if (msim_msg_get_integer(msg, "lc") == 2) { 2449 } else if (msim_msg_get_integer(msg, "lc") == 2) {
2451 return msim_we_are_logged_on(session, msg); 2450 return msim_we_are_logged_on(session, msg);
2452 } else if (msim_msg_get(msg, "bm")) { 2451 } else if (msim_msg_get(msg, "bm")) {
2453 return msim_incoming_bm(session, msg); 2452 return msim_incoming_bm(session, msg);
2454 } else if (msim_msg_get(msg, "rid")) { 2453 } else if (msim_msg_get(msg, "rid")) {
2455 return msim_process_reply(session, msg); 2454 return msim_process_reply(session, msg);
2456 } else if (msim_msg_get(msg, "error")) { 2455 } else if (msim_msg_get(msg, "error")) {
2457 return msim_error(session, msg); 2456 return msim_error(session, msg);
2458 } else if (msim_msg_get(msg, "ka")) { 2457 } else if (msim_msg_get(msg, "ka")) {
2459 return TRUE; 2458 return TRUE;
2460 } else { 2459 } else {
2461 msim_unrecognized(session, msg, "in msim_process"); 2460 msim_unrecognized(session, msg, "in msim_process");
2462 return FALSE; 2461 return FALSE;
2463 } 2462 }
2464 } 2463 }
2465 2464
2466 /** Store an field of information about a buddy. */ 2465 /** Store an field of information about a buddy. */
2467 static void 2466 static void
2468 msim_store_buddy_info_each(gpointer key, gpointer value, gpointer user_data) 2467 msim_store_buddy_info_each(gpointer key, gpointer value, gpointer user_data)
2499 GHashTable *body; 2498 GHashTable *body;
2500 gchar *username, *body_str, *uid; 2499 gchar *username, *body_str, *uid;
2501 PurpleBuddy *buddy; 2500 PurpleBuddy *buddy;
2502 guint rid; 2501 guint rid;
2503 2502
2504 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2503 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2505 g_return_val_if_fail(msg != NULL, FALSE); 2504 g_return_val_if_fail(msg != NULL, FALSE);
2506 2505
2507 rid = msim_msg_get_integer(msg, "rid"); 2506 rid = msim_msg_get_integer(msg, "rid");
2508 2507
2509 g_return_val_if_fail(rid != 0, FALSE); 2508 g_return_val_if_fail(rid != 0, FALSE);
2510 2509
2517 username = g_hash_table_lookup(body, "UserName"); 2516 username = g_hash_table_lookup(body, "UserName");
2518 2517
2519 if (!username) { 2518 if (!username) {
2520 purple_debug_info("msim", 2519 purple_debug_info("msim",
2521 "msim_process_reply: not caching body, no UserName\n"); 2520 "msim_process_reply: not caching body, no UserName\n");
2522 g_hash_table_destroy(body); 2521 g_hash_table_destroy(body);
2523 return FALSE; 2522 return FALSE;
2524 } 2523 }
2525 2524
2526 uid = g_hash_table_lookup(body, "UserID"); 2525 uid = g_hash_table_lookup(body, "UserID");
2527 if (!uid) { 2526 if (!uid) {
2528 g_hash_table_destroy(body); 2527 g_hash_table_destroy(body);
2529 g_return_val_if_fail(uid, FALSE); 2528 g_return_val_if_fail(uid, FALSE);
2530 } 2529 }
2531 2530
2532 purple_debug_info("msim", "associating uid %s with username %s\n", uid, username); 2531 purple_debug_info("msim", "associating uid %s with username %s\n", uid, username);
2533 2532
2534 buddy = purple_find_buddy(session->account, username); 2533 buddy = purple_find_buddy(session->account, username);
2535 if (buddy) { 2534 if (buddy) {
2536 g_hash_table_foreach(body, msim_store_buddy_info_each, buddy); 2535 g_hash_table_foreach(body, msim_store_buddy_info_each, buddy);
2537 } 2536 }
2538 2537
2539 if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN && 2538 if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
2540 msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) { 2539 msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
2541 /* TODO: do something with our own IM info, if we need it for some 2540 /* TODO: do something with our own IM info, if we need it for some
2542 * specific purpose. Otherwise it is available on the buddy list, 2541 * specific purpose. Otherwise it is available on the buddy list,
2543 * if the user has themselves as their own buddy. */ 2542 * if the user has themselves as their own buddy. */
2544 } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN && 2543 } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
2545 msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) { 2544 msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
2546 /* TODO: same as above, but for MySpace info. */ 2545 /* TODO: same as above, but for MySpace info. */
2547 } 2546 }
2548 2547
2549 g_hash_table_destroy(body); 2548 g_hash_table_destroy(body);
2550 2549
2551 return TRUE; 2550 return TRUE;
2552 } 2551 }
2553 2552
2554 /** Process the initial server information from the server. */ 2553 /** Process the initial server information from the server. */
2555 static gboolean 2554 static gboolean
2556 msim_process_server_info(MsimSession *session, MsimMessage *msg) 2555 msim_process_server_info(MsimSession *session, MsimMessage *msg)
2557 { 2556 {
2558 gchar *body_str; 2557 gchar *body_str;
2559 GHashTable *body; 2558 GHashTable *body;
2560 2559
2561 body_str = msim_msg_get_string(msg, "body"); 2560 body_str = msim_msg_get_string(msg, "body");
2562 g_return_val_if_fail(body_str != NULL, FALSE); 2561 g_return_val_if_fail(body_str != NULL, FALSE);
2563 body = msim_parse_body(body_str); 2562 body = msim_parse_body(body_str);
2564 g_free(body_str); 2563 g_free(body_str);
2565 g_return_val_if_fail(body != NULL, FALSE); 2564 g_return_val_if_fail(body != NULL, FALSE);
2566 2565
2567 /* Example body: 2566 /* Example body:
2568 AdUnitRefreshInterval=10. 2567 AdUnitRefreshInterval=10.
2569 AlertPollInterval=360. 2568 AlertPollInterval=360.
2570 AllowChatRoomEmoticonSharing=False. 2569 AllowChatRoomEmoticonSharing=False.
2571 ChatRoomUserIDs=78744676;163733130;1300326231;123521495;142663391. 2570 ChatRoomUserIDs=78744676;163733130;1300326231;123521495;142663391.
2572 CurClientVersion=673. 2571 CurClientVersion=673.
2580 MySpaceNowTimer=720. 2579 MySpaceNowTimer=720.
2581 PersistenceDataTimeout=900. 2580 PersistenceDataTimeout=900.
2582 UseWebChallenge=1. 2581 UseWebChallenge=1.
2583 WebTicketGoHome=False 2582 WebTicketGoHome=False
2584 2583
2585 Anything useful? TODO: use what is useful, and use it. 2584 Anything useful? TODO: use what is useful, and use it.
2586 */ 2585 */
2587 purple_debug_info("msim_process_server_info", 2586 purple_debug_info("msim_process_server_info",
2588 "maximum contacts: %s\n", 2587 "maximum contacts: %s\n",
2589 g_hash_table_lookup(body, "MaxContacts") ? 2588 g_hash_table_lookup(body, "MaxContacts") ?
2590 g_hash_table_lookup(body, "MaxContacts") : "(NULL)"); 2589 g_hash_table_lookup(body, "MaxContacts") : "(NULL)");
2591 2590
2592 session->server_info = body; 2591 session->server_info = body;
2593 /* session->server_info freed in msim_session_destroy */ 2592 /* session->server_info freed in msim_session_destroy */
2594 2593
2595 return TRUE; 2594 return TRUE;
2596 } 2595 }
2597 2596
2598 /** Process a web challenge, used to login to the web site. */ 2597 /** Process a web challenge, used to login to the web site. */
2599 static gboolean 2598 static gboolean
2600 msim_web_challenge(MsimSession *session, MsimMessage *msg) 2599 msim_web_challenge(MsimSession *session, MsimMessage *msg)
2601 { 2600 {
2602 /* TODO: web challenge, store token */ 2601 /* TODO: web challenge, store token */
2603 return FALSE; 2602 return FALSE;
2604 } 2603 }
2605 2604
2606 /** 2605 /**
2607 * Process a persistance message reply from the server. 2606 * Process a persistance message reply from the server.
2608 * 2607 *
2614 * msim_lookup_user sets callback for here 2613 * msim_lookup_user sets callback for here
2615 */ 2614 */
2616 static gboolean 2615 static gboolean
2617 msim_process_reply(MsimSession *session, MsimMessage *msg) 2616 msim_process_reply(MsimSession *session, MsimMessage *msg)
2618 { 2617 {
2619 MSIM_USER_LOOKUP_CB cb; 2618 MSIM_USER_LOOKUP_CB cb;
2620 gpointer data; 2619 gpointer data;
2621 guint rid, cmd, dsn, lid; 2620 guint rid, cmd, dsn, lid;
2622 2621
2623 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2622 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2624 g_return_val_if_fail(msg != NULL, FALSE); 2623 g_return_val_if_fail(msg != NULL, FALSE);
2625 2624
2626 msim_store_buddy_info(session, msg); 2625 msim_store_buddy_info(session, msg);
2627 2626
2628 rid = msim_msg_get_integer(msg, "rid"); 2627 rid = msim_msg_get_integer(msg, "rid");
2629 cmd = msim_msg_get_integer(msg, "cmd"); 2628 cmd = msim_msg_get_integer(msg, "cmd");
2630 dsn = msim_msg_get_integer(msg, "dsn"); 2629 dsn = msim_msg_get_integer(msg, "dsn");
2631 lid = msim_msg_get_integer(msg, "lid"); 2630 lid = msim_msg_get_integer(msg, "lid");
2632 2631
2633 /* Unsolicited messages */ 2632 /* Unsolicited messages */
2634 if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET)) { 2633 if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET)) {
2635 if (dsn == MG_SERVER_INFO_DSN && lid == MG_SERVER_INFO_LID) { 2634 if (dsn == MG_SERVER_INFO_DSN && lid == MG_SERVER_INFO_LID) {
2636 return msim_process_server_info(session, msg); 2635 return msim_process_server_info(session, msg);
2637 } else if (dsn == MG_WEB_CHALLENGE_DSN && lid == MG_WEB_CHALLENGE_LID) { 2636 } else if (dsn == MG_WEB_CHALLENGE_DSN && lid == MG_WEB_CHALLENGE_LID) {
2638 return msim_web_challenge(session, msg); 2637 return msim_web_challenge(session, msg);
2639 } 2638 }
2640 } 2639 }
2641 2640
2642 /* If a callback is registered for this userid lookup, call it. */ 2641 /* If a callback is registered for this userid lookup, call it. */
2643 cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid)); 2642 cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid));
2644 data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); 2643 data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid));
2645 2644
2646 if (cb) { 2645 if (cb) {
2647 purple_debug_info("msim", 2646 purple_debug_info("msim",
2648 "msim_process_body: calling callback now\n"); 2647 "msim_process_body: calling callback now\n");
2649 /* Clone message, so that the callback 'cb' can use it (needs to free it also). */ 2648 /* Clone message, so that the callback 'cb' can use it (needs to free it also). */
2650 cb(session, msim_msg_clone(msg), data); 2649 cb(session, msim_msg_clone(msg), data);
2651 g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid)); 2650 g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid));
2652 g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); 2651 g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid));
2653 } else { 2652 } else {
2654 purple_debug_info("msim", 2653 purple_debug_info("msim",
2655 "msim_process_body: no callback for rid %d\n", rid); 2654 "msim_process_body: no callback for rid %d\n", rid);
2656 } 2655 }
2657 2656
2658 return TRUE; 2657 return TRUE;
2659 } 2658 }
2660 2659
2661 /** 2660 /**
2667 * @return TRUE if successfully reported error. 2666 * @return TRUE if successfully reported error.
2668 */ 2667 */
2669 static gboolean 2668 static gboolean
2670 msim_error(MsimSession *session, MsimMessage *msg) 2669 msim_error(MsimSession *session, MsimMessage *msg)
2671 { 2670 {
2672 gchar *errmsg, *full_errmsg; 2671 gchar *errmsg, *full_errmsg;
2673 guint err; 2672 guint err;
2674 2673
2675 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); 2674 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2676 g_return_val_if_fail(msg != NULL, FALSE); 2675 g_return_val_if_fail(msg != NULL, FALSE);
2677 2676
2678 err = msim_msg_get_integer(msg, "err"); 2677 err = msim_msg_get_integer(msg, "err");
2679 errmsg = msim_msg_get_string(msg, "errmsg"); 2678 errmsg = msim_msg_get_string(msg, "errmsg");
2680 2679
2681 full_errmsg = g_strdup_printf(_("Protocol error, code %d: %s"), err, 2680 full_errmsg = g_strdup_printf(_("Protocol error, code %d: %s"), err,
2682 errmsg ? errmsg : "no 'errmsg' given"); 2681 errmsg ? errmsg : "no 'errmsg' given");
2683 2682
2684 g_free(errmsg); 2683 g_free(errmsg);
2685 2684
2686 purple_debug_info("msim", "msim_error: %s\n", full_errmsg); 2685 purple_debug_info("msim", "msim_error: %s\n", full_errmsg);
2687 2686
2688 purple_notify_error(session->account, g_strdup(_("MySpaceIM Error")), 2687 purple_notify_error(session->account, g_strdup(_("MySpaceIM Error")),
2689 full_errmsg, NULL); 2688 full_errmsg, NULL);
2690 2689
2691 /* Destroy session if fatal. */ 2690 /* Destroy session if fatal. */
2692 if (msim_msg_get(msg, "fatal")) { 2691 if (msim_msg_get(msg, "fatal")) {
2693 purple_debug_info("msim", "fatal error, closing\n"); 2692 purple_debug_info("msim", "fatal error, closing\n");
2694 purple_connection_error(session->gc, full_errmsg); 2693 purple_connection_error(session->gc, full_errmsg);
2695 } 2694 }
2696 2695
2697 return TRUE; 2696 return TRUE;
2698 } 2697 }
2699 2698
2700 /** 2699 /**
2701 * Process incoming status messages. 2700 * Process incoming status messages.
2702 * 2701 *
2706 * @return TRUE if successful. 2705 * @return TRUE if successful.
2707 */ 2706 */
2708 static gboolean 2707 static gboolean
2709 msim_incoming_status(MsimSession *session, MsimMessage *msg) 2708 msim_incoming_status(MsimSession *session, MsimMessage *msg)
2710 { 2709 {
2711 PurpleBuddyList *blist; 2710 PurpleBuddyList *blist;
2712 PurpleBuddy *buddy; 2711 PurpleBuddy *buddy;
2713 //PurpleStatus *status; 2712 GList *list;
2714 //gchar **status_array; 2713 gchar *status_headline;
2715 GList *list; 2714 gint status_code, purple_status_code;
2716 gchar *status_headline; 2715 gchar *username;
2717 //gchar *status_str; 2716
2718 //gint i; 2717 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2719 gint status_code, purple_status_code; 2718 g_return_val_if_fail(msg != NULL, FALSE);
2720 gchar *username;
2721
2722 g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
2723 g_return_val_if_fail(msg != NULL, FALSE);
2724 2719
2725 msim_msg_dump("msim_status msg=%s\n", msg); 2720 msim_msg_dump("msim_status msg=%s\n", msg);
2726 2721
2727 /* Helpfully looked up by msim_incoming_resolve() for us. */ 2722 /* Helpfully looked up by msim_incoming_resolve() for us. */
2728 username = msim_msg_get_string(msg, "_username"); 2723 username = msim_msg_get_string(msg, "_username");
2729 g_return_val_if_fail(username != NULL, FALSE); 2724 g_return_val_if_fail(username != NULL, FALSE);
2730 2725
2731 { 2726 {
2732 gchar *ss; 2727 gchar *ss;
2733 2728
2734 ss = msim_msg_get_string(msg, "msg"); 2729 ss = msim_msg_get_string(msg, "msg");
2735 purple_debug_info("msim", 2730 purple_debug_info("msim",
2736 "msim_status: updating status for <%s> to <%s>\n", 2731 "msim_status: updating status for <%s> to <%s>\n",
2737 username, ss ? ss : "(NULL)"); 2732 username, ss ? ss : "(NULL)");
2738 g_free(ss); 2733 g_free(ss);
2739 } 2734 }
2740 2735
2741 /* Example fields: 2736 /* Example fields:
2742 * |s|0|ss|Offline 2737 * |s|0|ss|Offline
2743 * |s|1|ss|:-)|ls||ip|0|p|0 2738 * |s|1|ss|:-)|ls||ip|0|p|0
2744 */ 2739 */
2745 list = msim_msg_get_list(msg, "msg"); 2740 list = msim_msg_get_list(msg, "msg");
2746 2741
2747 status_code = atoi(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE)); 2742 status_code = atoi(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE));
2748 purple_debug_info("msim", "msim_status: %s's status code = %d\n", username, status_code); 2743 purple_debug_info("msim", "msim_status: %s's status code = %d\n", username, status_code);
2749 status_headline = g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE); 2744 status_headline = g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE);
2750 2745
2751 blist = purple_get_blist(); 2746 blist = purple_get_blist();
2752 2747
2753 /* Add buddy if not found */ 2748 /* Add buddy if not found */
2754 buddy = purple_find_buddy(session->account, username); 2749 buddy = purple_find_buddy(session->account, username);
2755 if (!buddy) { 2750 if (!buddy) {
2756 purple_debug_info("msim", 2751 purple_debug_info("msim",
2757 "msim_status: making new buddy for %s\n", username); 2752 "msim_status: making new buddy for %s\n", username);
2758 buddy = purple_buddy_new(session->account, username, NULL); 2753 buddy = purple_buddy_new(session->account, username, NULL);
2759 2754
2760 purple_blist_add_buddy(buddy, NULL, NULL, NULL); 2755 purple_blist_add_buddy(buddy, NULL, NULL, NULL);
2761 2756
2762 /* All buddies on list should have 'uid' integer associated with them. */ 2757 /* All buddies on list should have 'uid' integer associated with them. */
2763 purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f")); 2758 purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f"));
2764 2759
2765 msim_store_buddy_info(session, msg); 2760 msim_store_buddy_info(session, msg);
2766 } else { 2761 } else {
2767 purple_debug_info("msim", "msim_status: found buddy %s\n", username); 2762 purple_debug_info("msim", "msim_status: found buddy %s\n", username);
2768 } 2763 }
2769 2764
2770 purple_blist_node_set_string(&buddy->node, "Headline", status_headline); 2765 purple_blist_node_set_string(&buddy->node, "Headline", status_headline);
2771 2766
2772 /* Set user status */ 2767 /* Set user status */
2773 switch (status_code) { 2768 switch (status_code) {
2774 case MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN: 2769 case MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN:
2775 purple_status_code = PURPLE_STATUS_OFFLINE; 2770 purple_status_code = PURPLE_STATUS_OFFLINE;
2776 break; 2771 break;
2777 2772
2778 case MSIM_STATUS_CODE_ONLINE: 2773 case MSIM_STATUS_CODE_ONLINE:
2779 purple_status_code = PURPLE_STATUS_AVAILABLE; 2774 purple_status_code = PURPLE_STATUS_AVAILABLE;
2780 break; 2775 break;
2781 2776
2782 case MSIM_STATUS_CODE_AWAY: 2777 case MSIM_STATUS_CODE_AWAY:
2783 purple_status_code = PURPLE_STATUS_AWAY; 2778 purple_status_code = PURPLE_STATUS_AWAY;
2784 break; 2779 break;
2785 2780
2786 case MSIM_STATUS_CODE_IDLE: 2781 case MSIM_STATUS_CODE_IDLE:
2787 /* will be handled below */ 2782 /* will be handled below */
2788 purple_status_code = -1; 2783 purple_status_code = -1;
2789 break; 2784 break;
2790 2785
2791 default: 2786 default:
2792 purple_debug_info("msim", "msim_status for %s, unknown status code %d, treating as available\n", 2787 purple_debug_info("msim", "msim_status for %s, unknown status code %d, treating as available\n",
2793 username, status_code); 2788 username, status_code);
2794 purple_status_code = PURPLE_STATUS_AVAILABLE; 2789 purple_status_code = PURPLE_STATUS_AVAILABLE;
2795 } 2790 }
2796 2791
2797 purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL); 2792 purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL);
2798 2793
2799 if (status_code == MSIM_STATUS_CODE_IDLE) { 2794 if (status_code == MSIM_STATUS_CODE_IDLE) {
2800 purple_debug_info("msim", "msim_status: got idle: %s\n", username); 2795 purple_debug_info("msim", "msim_status: got idle: %s\n", username);
2801 purple_prpl_got_user_idle(session->account, username, TRUE, time(NULL)); 2796 purple_prpl_got_user_idle(session->account, username, TRUE, time(NULL));
2802 } else { 2797 } else {
2803 /* All other statuses indicate going back to non-idle. */ 2798 /* All other statuses indicate going back to non-idle. */
2804 purple_prpl_got_user_idle(session->account, username, FALSE, time(NULL)); 2799 purple_prpl_got_user_idle(session->account, username, FALSE, time(NULL));
2805 } 2800 }
2806 2801
2807 #ifdef MSIM_SEND_CLIENT_VERSION 2802 #ifdef MSIM_SEND_CLIENT_VERSION
2808 if (status_code == MSIM_STATUS_CODE_ONLINE) { 2803 if (status_code == MSIM_STATUS_CODE_ONLINE) {
2809 /* Secretly whisper to unofficial clients our own version as they come online */ 2804 /* Secretly whisper to unofficial clients our own version as they come online */
2810 msim_send_unofficial_client(session, username); 2805 msim_send_unofficial_client(session, username);
2811 } 2806 }
2812 #endif 2807 #endif
2813 2808
2814 g_free(username); 2809 g_free(username);
2815 msim_msg_list_free(list); 2810 msim_msg_list_free(list);
2816 2811
2817 return TRUE; 2812 return TRUE;
2818 } 2813 }
2819 2814
2820 /** Add a buddy to user's buddy list. */ 2815 /** Add a buddy to user's buddy list. */
2821 void 2816 void
2822 msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) 2817 msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
2823 { 2818 {
2824 MsimSession *session; 2819 MsimSession *session;
2825 MsimMessage *msg; 2820 MsimMessage *msg;
2826 MsimMessage *msg_persist; 2821 MsimMessage *msg_persist;
2827 MsimMessage *body; 2822 MsimMessage *body;
2828 2823
2829 session = (MsimSession *)gc->proto_data; 2824 session = (MsimSession *)gc->proto_data;
2830 purple_debug_info("msim", "msim_add_buddy: want to add %s to %s\n", 2825 purple_debug_info("msim", "msim_add_buddy: want to add %s to %s\n",
2831 buddy->name, (group && group->name) ? group->name : "(no group)"); 2826 buddy->name, (group && group->name) ? group->name : "(no group)");
2832 2827
2833 msg = msim_msg_new(TRUE, 2828 msg = msim_msg_new(TRUE,
2834 "addbuddy", MSIM_TYPE_BOOLEAN, TRUE, 2829 "addbuddy", MSIM_TYPE_BOOLEAN, TRUE,
2835 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 2830 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
2836 /* "newprofileid" will be inserted here with uid. */ 2831 /* "newprofileid" will be inserted here with uid. */
2845 msim_msg_free(msg); 2840 msim_msg_free(msg);
2846 2841
2847 /* TODO: if addbuddy fails ('error' message is returned), delete added buddy from 2842 /* TODO: if addbuddy fails ('error' message is returned), delete added buddy from
2848 * buddy list since Purple adds it locally. */ 2843 * buddy list since Purple adds it locally. */
2849 2844
2850 body = msim_msg_new(TRUE, 2845 body = msim_msg_new(TRUE,
2851 "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"), 2846 "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"),
2852 "GroupName", MSIM_TYPE_STRING, g_strdup(group->name), 2847 "GroupName", MSIM_TYPE_STRING, g_strdup(group->name),
2853 "Position", MSIM_TYPE_INTEGER, 1000, 2848 "Position", MSIM_TYPE_INTEGER, 1000,
2854 "Visibility", MSIM_TYPE_INTEGER, 1, 2849 "Visibility", MSIM_TYPE_INTEGER, 1,
2855 "NickName", MSIM_TYPE_STRING, g_strdup(""), 2850 "NickName", MSIM_TYPE_STRING, g_strdup(""),
2856 "NameSelect", MSIM_TYPE_INTEGER, 0, 2851 "NameSelect", MSIM_TYPE_INTEGER, 0,
2857 NULL); 2852 NULL);
2858 2853
2859 /* TODO: Update blocklist. */ 2854 /* TODO: Update blocklist. */
2860 2855
2861 #if 0 2856 #if 0
2862 msg_persist = msim_msg_new(TRUE, 2857 msg_persist = msim_msg_new(TRUE,
2866 "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN, 2861 "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN,
2867 "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID, 2862 "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID,
2868 /* TODO: Use msim_new_reply_callback to get rid. */ 2863 /* TODO: Use msim_new_reply_callback to get rid. */
2869 "rid", MSIM_TYPE_INTEGER, session->next_rid++, 2864 "rid", MSIM_TYPE_INTEGER, session->next_rid++,
2870 "body", MSIM_TYPE_DICTIONARY, body, 2865 "body", MSIM_TYPE_DICTIONARY, body,
2871 NULL); 2866 NULL);
2872 2867
2873 if (!msim_postprocess_outgoing(session, msg_persist, buddy->name, "body", NULL)) 2868 if (!msim_postprocess_outgoing(session, msg_persist, buddy->name, "body", NULL))
2874 { 2869 {
2875 purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("persist command failed")); 2870 purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("persist command failed"));
2876 msim_msg_free(msg_persist); 2871 msim_msg_free(msg_persist);
2897 * Does not handle sending, or scheduling userid lookup. For that, see msim_postprocess_outgoing(). 2892 * Does not handle sending, or scheduling userid lookup. For that, see msim_postprocess_outgoing().
2898 */ 2893 */
2899 static MsimMessage * 2894 static MsimMessage *
2900 msim_do_postprocessing(MsimMessage *msg, const gchar *uid_before, 2895 msim_do_postprocessing(MsimMessage *msg, const gchar *uid_before,
2901 const gchar *uid_field_name, guint uid) 2896 const gchar *uid_field_name, guint uid)
2902 { 2897 {
2903 msim_msg_dump("msim_do_postprocessing msg: %s\n", msg); 2898 msim_msg_dump("msim_do_postprocessing msg: %s\n", msg);
2904 2899
2905 /* First, check - if the field already exists, replace <uid> within it */ 2900 /* First, check - if the field already exists, replace <uid> within it */
2906 if (msim_msg_get(msg, uid_field_name)) { 2901 if (msim_msg_get(msg, uid_field_name)) {
2907 MsimMessageElement *elem; 2902 MsimMessageElement *elem;
2908 gchar *fmt_string; 2903 gchar *fmt_string;
2909 gchar *uid_str, *new_str; 2904 gchar *uid_str, *new_str;
2910 2905
2911 /* Warning: this is a delicate, but safe, operation */ 2906 /* Warning: this is a delicate, but safe, operation */
2912 2907
2913 elem = msim_msg_get(msg, uid_field_name); 2908 elem = msim_msg_get(msg, uid_field_name);
2914 2909
2915 /* Get the packed element, flattening it. This allows <uid> to be 2910 /* Get the packed element, flattening it. This allows <uid> to be
2916 * replaced within nested data structures, since the replacement is done 2911 * replaced within nested data structures, since the replacement is done
2917 * on the linear, packed data, not on a complicated data structure. 2912 * on the linear, packed data, not on a complicated data structure.
2918 * 2913 *
2919 * For example, if the field was originally a dictionary or a list, you 2914 * For example, if the field was originally a dictionary or a list, you
2920 * would have to iterate over all the items in it to see what needs to 2915 * would have to iterate over all the items in it to see what needs to
2921 * be replaced. But by packing it first, the <uid> marker is easily replaced 2916 * be replaced. But by packing it first, the <uid> marker is easily replaced
2922 * just by a string replacement. 2917 * just by a string replacement.
2923 */ 2918 */
2924 fmt_string = msim_msg_pack_element_data(elem); 2919 fmt_string = msim_msg_pack_element_data(elem);
2925 2920
2926 uid_str = g_strdup_printf("%d", uid); 2921 uid_str = g_strdup_printf("%d", uid);
2927 new_str = str_replace(fmt_string, "<uid>", uid_str); 2922 new_str = str_replace(fmt_string, "<uid>", uid_str);
2928 g_free(uid_str); 2923 g_free(uid_str);
2929 g_free(fmt_string); 2924 g_free(fmt_string);
2930 2925
2931 /* Free the old element data */ 2926 /* Free the old element data */
2932 msim_msg_free_element_data(elem->data); 2927 msim_msg_free_element_data(elem->data);
2933 2928
2934 /* Replace it with our new data */ 2929 /* Replace it with our new data */
2935 elem->data = new_str; 2930 elem->data = new_str;
2936 elem->type = MSIM_TYPE_RAW; 2931 elem->type = MSIM_TYPE_RAW;
2937 2932
2938 } else { 2933 } else {
2939 /* Otherwise, insert new field into outgoing message. */ 2934 /* Otherwise, insert new field into outgoing message. */
2940 msg = msim_msg_insert_before(msg, uid_before, uid_field_name, MSIM_TYPE_INTEGER, GUINT_TO_POINTER(uid)); 2935 msg = msim_msg_insert_before(msg, uid_before, uid_field_name, MSIM_TYPE_INTEGER, GUINT_TO_POINTER(uid));
2941 } 2936 }
2942 2937
2943 msim_msg_dump("msim_postprocess_outgoing_cb: postprocessed msg=%s\n", msg); 2938 msim_msg_dump("msim_postprocess_outgoing_cb: postprocessed msg=%s\n", msg);
2944 2939
2945 return msg; 2940 return msg;
2946 } 2941 }
2947 2942
2948 /** Callback for msim_postprocess_outgoing() to add a userid to a message, and send it (once receiving userid). 2943 /** Callback for msim_postprocess_outgoing() to add a userid to a message, and send it (once receiving userid).
2949 * 2944 *
2950 * @param session 2945 * @param session
2951 * @param userinfo The user information reply message, containing the user ID 2946 * @param userinfo The user information reply message, containing the user ID
2958 * 2953 *
2959 * 2954 *
2960 */ 2955 */
2961 static void 2956 static void
2962 msim_postprocess_outgoing_cb(MsimSession *session, MsimMessage *userinfo, 2957 msim_postprocess_outgoing_cb(MsimSession *session, MsimMessage *userinfo,
2963 gpointer data) 2958 gpointer data)
2964 { 2959 {
2965 gchar *body_str; 2960 gchar *body_str;
2966 GHashTable *body; 2961 GHashTable *body;
2967 gchar *uid, *uid_field_name, *uid_before; 2962 gchar *uid, *uid_field_name, *uid_before;
2968 MsimMessage *msg; 2963 MsimMessage *msg;
2995 * field names - instead, treats them as static strings (which they usually are). 2990 * field names - instead, treats them as static strings (which they usually are).
2996 */ 2991 */
2997 g_free(uid_field_name); 2992 g_free(uid_field_name);
2998 g_free(uid_before); 2993 g_free(uid_before);
2999 2994
3000 g_hash_table_destroy(body); 2995 g_hash_table_destroy(body);
3001 2996
3002 //msim_msg_free(msg); 2997 //msim_msg_free(msg);
3003 } 2998 }
3004 2999
3005 /** Postprocess and send a message. 3000 /** Postprocess and send a message.
3015 gboolean 3010 gboolean
3016 msim_postprocess_outgoing(MsimSession *session, MsimMessage *msg, 3011 msim_postprocess_outgoing(MsimSession *session, MsimMessage *msg,
3017 const gchar *username, const gchar *uid_field_name, 3012 const gchar *username, const gchar *uid_field_name,
3018 const gchar *uid_before) 3013 const gchar *uid_before)
3019 { 3014 {
3020 PurpleBuddy *buddy; 3015 PurpleBuddy *buddy;
3021 guint uid; 3016 guint uid;
3022 gboolean rc; 3017 gboolean rc;
3023 3018
3024 g_return_val_if_fail(msg != NULL, FALSE); 3019 g_return_val_if_fail(msg != NULL, FALSE);
3025 3020
3026 /* Store information for msim_postprocess_outgoing_cb(). */ 3021 /* Store information for msim_postprocess_outgoing_cb(). */
3027 msim_msg_dump("msim_postprocess_outgoing: msg before=%s\n", msg); 3022 msim_msg_dump("msim_postprocess_outgoing: msg before=%s\n", msg);
3028 msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username)); 3023 msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username));
3029 msg = msim_msg_append(msg, "_uid_field_name", MSIM_TYPE_STRING, g_strdup(uid_field_name)); 3024 msg = msim_msg_append(msg, "_uid_field_name", MSIM_TYPE_STRING, g_strdup(uid_field_name));
3030 msg = msim_msg_append(msg, "_uid_before", MSIM_TYPE_STRING, g_strdup(uid_before)); 3025 msg = msim_msg_append(msg, "_uid_before", MSIM_TYPE_STRING, g_strdup(uid_before));
3031 3026
3032 /* First, try the most obvious. If numeric userid is given, use that directly. */ 3027 /* First, try the most obvious. If numeric userid is given, use that directly. */
3033 if (msim_is_userid(username)) { 3028 if (msim_is_userid(username)) {
3034 uid = atol(username); 3029 uid = atol(username);
3035 } else { 3030 } else {
3036 /* Next, see if on buddy list and know uid. */ 3031 /* Next, see if on buddy list and know uid. */
3037 buddy = purple_find_buddy(session->account, username); 3032 buddy = purple_find_buddy(session->account, username);
3038 if (buddy) { 3033 if (buddy) {
3039 uid = purple_blist_node_get_int(&buddy->node, "UserID"); 3034 uid = purple_blist_node_get_int(&buddy->node, "UserID");
3040 } else { 3035 } else {
3047 purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n", 3042 purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n",
3048 username ? username : "(NULL)"); 3043 username ? username : "(NULL)");
3049 msim_msg_dump("msim_postprocess_outgoing - scheduling lookup, msg=%s\n", msg); 3044 msim_msg_dump("msim_postprocess_outgoing - scheduling lookup, msg=%s\n", msg);
3050 /* TODO: where is cloned message freed? Should be in _cb. */ 3045 /* TODO: where is cloned message freed? Should be in _cb. */
3051 msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg)); 3046 msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg));
3052 return TRUE; /* not sure of status yet - haven't sent! */ 3047 return TRUE; /* not sure of status yet - haven't sent! */
3053 } 3048 }
3054 } 3049 }
3055 3050
3056 /* Already have uid, postprocess and send msg immediately. */ 3051 /* Already have uid, postprocess and send msg immediately. */
3057 purple_debug_info("msim", "msim_postprocess_outgoing: found username %s has uid %d\n", 3052 purple_debug_info("msim", "msim_postprocess_outgoing: found username %s has uid %d\n",
3074 { 3069 {
3075 MsimSession *session; 3070 MsimSession *session;
3076 MsimMessage *delbuddy_msg; 3071 MsimMessage *delbuddy_msg;
3077 MsimMessage *persist_msg; 3072 MsimMessage *persist_msg;
3078 MsimMessage *blocklist_msg; 3073 MsimMessage *blocklist_msg;
3079 GList *blocklist_updates; 3074 GList *blocklist_updates;
3080 3075
3081 session = (MsimSession *)gc->proto_data; 3076 session = (MsimSession *)gc->proto_data;
3082 3077
3083 delbuddy_msg = msim_msg_new(TRUE, 3078 delbuddy_msg = msim_msg_new(TRUE,
3084 "delbuddy", MSIM_TYPE_BOOLEAN, TRUE, 3079 "delbuddy", MSIM_TYPE_BOOLEAN, TRUE,
3086 /* 'delprofileid' with uid will be inserted here. */ 3081 /* 'delprofileid' with uid will be inserted here. */
3087 NULL); 3082 NULL);
3088 3083
3089 if (!msim_postprocess_outgoing(session, delbuddy_msg, buddy->name, "delprofileid", NULL)) { 3084 if (!msim_postprocess_outgoing(session, delbuddy_msg, buddy->name, "delprofileid", NULL)) {
3090 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("'delbuddy' command failed")); 3085 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("'delbuddy' command failed"));
3091 msim_msg_free(delbuddy_msg); 3086 msim_msg_free(delbuddy_msg);
3092 return; 3087 return;
3093 } 3088 }
3094 msim_msg_free(delbuddy_msg); 3089 msim_msg_free(delbuddy_msg);
3095 3090
3096 persist_msg = msim_msg_new(TRUE, 3091 persist_msg = msim_msg_new(TRUE,
3097 "persist", MSIM_TYPE_INTEGER, 1, 3092 "persist", MSIM_TYPE_INTEGER, 1,
3098 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 3093 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
3099 "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE, 3094 "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE,
3104 /* <uid> will be replaced by postprocessing */ 3099 /* <uid> will be replaced by postprocessing */
3105 "body", MSIM_TYPE_STRING, g_strdup("ContactID=<uid>"), 3100 "body", MSIM_TYPE_STRING, g_strdup("ContactID=<uid>"),
3106 NULL); 3101 NULL);
3107 3102
3108 if (!msim_postprocess_outgoing(session, persist_msg, buddy->name, "body", NULL)) { 3103 if (!msim_postprocess_outgoing(session, persist_msg, buddy->name, "body", NULL)) {
3109 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("persist command failed")); 3104 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("persist command failed"));
3110 msim_msg_free(persist_msg); 3105 msim_msg_free(persist_msg);
3111 return; 3106 return;
3112 } 3107 }
3113 msim_msg_free(persist_msg); 3108 msim_msg_free(persist_msg);
3114 3109
3115 blocklist_updates = NULL; 3110 blocklist_updates = NULL;
3116 blocklist_updates = g_list_prepend(blocklist_updates, "a-"); 3111 blocklist_updates = g_list_prepend(blocklist_updates, "a-");
3117 blocklist_updates = g_list_prepend(blocklist_updates, "<uid>"); 3112 blocklist_updates = g_list_prepend(blocklist_updates, "<uid>");
3118 blocklist_updates = g_list_prepend(blocklist_updates, "b-"); 3113 blocklist_updates = g_list_prepend(blocklist_updates, "b-");
3119 blocklist_updates = g_list_prepend(blocklist_updates, "<uid>"); 3114 blocklist_updates = g_list_prepend(blocklist_updates, "<uid>");
3120 blocklist_updates = g_list_reverse(blocklist_updates); 3115 blocklist_updates = g_list_reverse(blocklist_updates);
3121 3116
3122 blocklist_msg = msim_msg_new(TRUE, 3117 blocklist_msg = msim_msg_new(TRUE,
3123 "blocklist", MSIM_TYPE_BOOLEAN, TRUE, 3118 "blocklist", MSIM_TYPE_BOOLEAN, TRUE,
3124 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 3119 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
3125 /* TODO: MsimMessage lists. Currently <uid> isn't replaced in lists. */ 3120 /* TODO: MsimMessage lists. Currently <uid> isn't replaced in lists. */
3126 //"idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"), 3121 //"idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"),
3127 "idlist", MSIM_TYPE_LIST, blocklist_updates, 3122 "idlist", MSIM_TYPE_LIST, blocklist_updates,
3128 NULL); 3123 NULL);
3129 3124
3130 if (!msim_postprocess_outgoing(session, blocklist_msg, buddy->name, "idlist", NULL)) { 3125 if (!msim_postprocess_outgoing(session, blocklist_msg, buddy->name, "idlist", NULL)) {
3131 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("blocklist command failed")); 3126 purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("blocklist command failed"));
3132 msim_msg_free(blocklist_msg); 3127 msim_msg_free(blocklist_msg);
3133 return; 3128 return;
3134 } 3129 }
3135 msim_msg_free(blocklist_msg); 3130 msim_msg_free(blocklist_msg);
3136 } 3131 }
3137 3132
3138 /** Return whether the buddy can be messaged while offline. 3133 /** Return whether the buddy can be messaged while offline.
3139 * 3134 *
3140 * The protocol supports offline messages in just the same way as online 3135 * The protocol supports offline messages in just the same way as online
3156 * Reads the input, and calls msim_preprocess_incoming() to handle it. 3151 * Reads the input, and calls msim_preprocess_incoming() to handle it.
3157 */ 3152 */
3158 static void 3153 static void
3159 msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond) 3154 msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond)
3160 { 3155 {
3161 PurpleConnection *gc; 3156 PurpleConnection *gc;
3162 PurpleAccount *account; 3157 PurpleAccount *account;
3163 MsimSession *session; 3158 MsimSession *session;
3164 gchar *end; 3159 gchar *end;
3165 int n; 3160 int n;
3166 3161
3167 g_return_if_fail(gc_uncasted != NULL); 3162 g_return_if_fail(gc_uncasted != NULL);
3168 g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */ 3163 g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */
3169 3164
3170 gc = (PurpleConnection *)(gc_uncasted); 3165 gc = (PurpleConnection *)(gc_uncasted);
3171 account = purple_connection_get_account(gc); 3166 account = purple_connection_get_account(gc);
3172 session = gc->proto_data; 3167 session = gc->proto_data;
3173 3168
3174 g_return_if_fail(cond == PURPLE_INPUT_READ); 3169 g_return_if_fail(cond == PURPLE_INPUT_READ);
3175 g_return_if_fail(MSIM_SESSION_VALID(session)); 3170 g_return_if_fail(MSIM_SESSION_VALID(session));
3176 3171
3177 /* Mark down that we got data, so don't timeout. */ 3172 /* Mark down that we got data, so don't timeout. */
3178 session->last_comm = time(NULL); 3173 session->last_comm = time(NULL);
3179 3174
3180 /* Only can handle so much data at once... 3175 /* Only can handle so much data at once...
3181 * If this happens, try recompiling with a higher MSIM_READ_BUF_SIZE. 3176 * If this happens, try recompiling with a higher MSIM_READ_BUF_SIZE.
3182 * Should be large enough to hold the largest protocol message. 3177 * Should be large enough to hold the largest protocol message.
3183 */ 3178 */
3184 if (session->rxoff >= MSIM_READ_BUF_SIZE) { 3179 if (session->rxoff >= MSIM_READ_BUF_SIZE) {
3185 purple_debug_error("msim", 3180 purple_debug_error("msim",
3186 "msim_input_cb: %d-byte read buffer full! rxoff=%d\n", 3181 "msim_input_cb: %d-byte read buffer full! rxoff=%d\n",
3187 MSIM_READ_BUF_SIZE, session->rxoff); 3182 MSIM_READ_BUF_SIZE, session->rxoff);
3188 purple_connection_error(gc, _("Read buffer full")); 3183 purple_connection_error(gc, _("Read buffer full"));
3189 return; 3184 return;
3190 } 3185 }
3191 3186
3192 purple_debug_info("msim", "buffer at %d (max %d), reading up to %d\n", 3187 purple_debug_info("msim", "buffer at %d (max %d), reading up to %d\n",
3193 session->rxoff, MSIM_READ_BUF_SIZE, 3188 session->rxoff, MSIM_READ_BUF_SIZE,
3194 MSIM_READ_BUF_SIZE - session->rxoff); 3189 MSIM_READ_BUF_SIZE - session->rxoff);
3195 3190
3196 /* Read into buffer. On Win32, need recv() not read(). session->fd also holds 3191 /* Read into buffer. On Win32, need recv() not read(). session->fd also holds
3197 * the file descriptor, but it sometimes differs from the 'source' parameter. 3192 * the file descriptor, but it sometimes differs from the 'source' parameter.
3198 */ 3193 */
3199 n = recv(session->fd, session->rxbuf + session->rxoff, MSIM_READ_BUF_SIZE - session->rxoff, 0); 3194 n = recv(session->fd, session->rxbuf + session->rxoff, MSIM_READ_BUF_SIZE - session->rxoff, 0);
3200 3195
3201 if (n < 0 && errno == EAGAIN) { 3196 if (n < 0 && errno == EAGAIN) {
3202 return; 3197 return;
3203 } else if (n < 0) { 3198 } else if (n < 0) {
3204 purple_debug_error("msim", "msim_input_cb: read error, ret=%d, " 3199 purple_debug_error("msim", "msim_input_cb: read error, ret=%d, "
3205 "error=%s, source=%d, fd=%d (%X))\n", 3200 "error=%s, source=%d, fd=%d (%X))\n",
3206 n, strerror(errno), source, session->fd, session->fd); 3201 n, strerror(errno), source, session->fd, session->fd);
3207 purple_connection_error(gc, _("Read error")); 3202 purple_connection_error(gc, _("Read error"));
3208 return; 3203 return;
3209 } else if (n == 0) { 3204 } else if (n == 0) {
3210 purple_debug_info("msim", "msim_input_cb: server disconnected\n"); 3205 purple_debug_info("msim", "msim_input_cb: server disconnected\n");
3211 purple_connection_error(gc, _("Server has disconnected")); 3206 purple_connection_error(gc, _("Server has disconnected"));
3212 return; 3207 return;
3213 } 3208 }
3214 3209
3215 if (n + session->rxoff >= MSIM_READ_BUF_SIZE) { 3210 if (n + session->rxoff >= MSIM_READ_BUF_SIZE) {
3216 purple_debug_info("msim_input_cb", "received %d bytes, pushing rxoff to %d, over buffer size of %d\n", 3211 purple_debug_info("msim_input_cb", "received %d bytes, pushing rxoff to %d, over buffer size of %d\n",
3217 n, n + session->rxoff, MSIM_READ_BUF_SIZE); 3212 n, n + session->rxoff, MSIM_READ_BUF_SIZE);
3218 /* TODO: g_realloc like msn, yahoo, irc, jabber? */ 3213 /* TODO: g_realloc like msn, yahoo, irc, jabber? */
3219 purple_connection_error(gc, _("Read buffer full")); 3214 purple_connection_error(gc, _("Read buffer full"));
3220 } 3215 }
3221 3216
3222 /* Null terminate */ 3217 /* Null terminate */
3223 purple_debug_info("msim", "msim_input_cb: going to null terminate " 3218 purple_debug_info("msim", "msim_input_cb: going to null terminate "
3224 "at n=%d\n", n); 3219 "at n=%d\n", n);
3225 session->rxbuf[session->rxoff + n] = 0; 3220 session->rxbuf[session->rxoff + n] = 0;
3226 3221
3227 #ifdef MSIM_CHECK_EMBEDDED_NULLS 3222 #ifdef MSIM_CHECK_EMBEDDED_NULLS
3228 /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */ 3223 /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */
3229 if (strlen(session->rxbuf + session->rxoff) != n) { 3224 if (strlen(session->rxbuf + session->rxoff) != n) {
3230 /* Occurs after login, but it is not a null byte. */ 3225 /* Occurs after login, but it is not a null byte. */
3231 purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes" 3226 purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes"
3232 "--null byte encountered?\n", 3227 "--null byte encountered?\n",
3233 strlen(session->rxbuf + session->rxoff), n); 3228 strlen(session->rxbuf + session->rxoff), n);
3234 //purple_connection_error(gc, "Invalid message - null byte on input"); 3229 //purple_connection_error(gc, "Invalid message - null byte on input");
3235 return; 3230 return;
3236 } 3231 }
3237 #endif 3232 #endif
3238 3233
3239 session->rxoff += n; 3234 session->rxoff += n;
3240 purple_debug_info("msim", "msim_input_cb: read=%d\n", n); 3235 purple_debug_info("msim", "msim_input_cb: read=%d\n", n);
3241 3236
3242 #ifdef MSIM_DEBUG_RXBUF 3237 #ifdef MSIM_DEBUG_RXBUF
3243 purple_debug_info("msim", "buf=<%s>\n", session->rxbuf); 3238 purple_debug_info("msim", "buf=<%s>\n", session->rxbuf);
3244 #endif 3239 #endif
3245 3240
3246 /* Look for \\final\\ end markers. If found, process message. */ 3241 /* Look for \\final\\ end markers. If found, process message. */
3247 while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) { 3242 while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) {
3248 MsimMessage *msg; 3243 MsimMessage *msg;
3249 3244
3250 #ifdef MSIM_DEBUG_RXBUF 3245 #ifdef MSIM_DEBUG_RXBUF
3251 purple_debug_info("msim", "in loop: buf=<%s>\n", session->rxbuf); 3246 purple_debug_info("msim", "in loop: buf=<%s>\n", session->rxbuf);
3252 #endif 3247 #endif
3253 *end = 0; 3248 *end = 0;
3254 msg = msim_parse(g_strdup(session->rxbuf)); 3249 msg = msim_parse(g_strdup(session->rxbuf));
3255 if (!msg) { 3250 if (!msg) {
3256 purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n"); 3251 purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n");
3257 purple_connection_error(gc, _("Unparseable message")); 3252 purple_connection_error(gc, _("Unparseable message"));
3258 } else { 3253 } else {
3259 /* Process message and then free it (processing function should 3254 /* Process message and then free it (processing function should
3260 * clone message if it wants to keep it afterwards.) */ 3255 * clone message if it wants to keep it afterwards.) */
3261 if (!msim_preprocess_incoming(session, msg)) { 3256 if (!msim_preprocess_incoming(session, msg)) {
3262 msim_msg_dump("msim_input_cb: preprocessing message failed on msg: %s\n", msg); 3257 msim_msg_dump("msim_input_cb: preprocessing message failed on msg: %s\n", msg);
3263 } 3258 }
3264 msim_msg_free(msg); 3259 msim_msg_free(msg);
3265 } 3260 }
3266 3261
3267 /* Move remaining part of buffer to beginning. */ 3262 /* Move remaining part of buffer to beginning. */
3268 session->rxoff -= strlen(session->rxbuf) + strlen(MSIM_FINAL_STRING); 3263 session->rxoff -= strlen(session->rxbuf) + strlen(MSIM_FINAL_STRING);
3269 memmove(session->rxbuf, end + strlen(MSIM_FINAL_STRING), 3264 memmove(session->rxbuf, end + strlen(MSIM_FINAL_STRING),
3270 MSIM_READ_BUF_SIZE - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf)); 3265 MSIM_READ_BUF_SIZE - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf));
3271 3266
3272 /* Clear end of buffer */ 3267 /* Clear end of buffer */
3273 //memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf)); 3268 //memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf));
3274 } 3269 }
3275 } 3270 }
3276 3271
3277 /* Setup a callback, to be called when a reply is received with the returned rid. 3272 /* Setup a callback, to be called when a reply is received with the returned rid.
3278 * 3273 *
3279 * @param cb The callback, an MSIM_USER_LOOKUP_CB. 3274 * @param cb The callback, an MSIM_USER_LOOKUP_CB.
3280 * @param data Arbitrary user data to be passed to callback (probably an MsimMessage *). 3275 * @param data Arbitrary user data to be passed to callback (probably an MsimMessage *).
3281 * 3276 *
3282 * @return The request/reply ID, used to link replies with requests, or -1. 3277 * @return The request/reply ID, used to link replies with requests, or -1.
3283 * Put the rid in your request, 'rid' field. 3278 * Put the rid in your request, 'rid' field.
3284 * 3279 *
3285 * TODO: Make more generic and more specific: 3280 * TODO: Make more generic and more specific:
3286 * 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup 3281 * 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup
3287 * 2) data - make it an MsimMessage? 3282 * 2) data - make it an MsimMessage?
3288 */ 3283 */
3290 msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, 3285 msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb,
3291 gpointer data) 3286 gpointer data)
3292 { 3287 {
3293 guint rid; 3288 guint rid;
3294 3289
3295 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1); 3290 g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
3296 3291
3297 rid = session->next_rid++; 3292 rid = session->next_rid++;
3298 3293
3299 g_hash_table_insert(session->user_lookup_cb, GUINT_TO_POINTER(rid), cb); 3294 g_hash_table_insert(session->user_lookup_cb, GUINT_TO_POINTER(rid), cb);
3300 g_hash_table_insert(session->user_lookup_cb_data, GUINT_TO_POINTER(rid), data); 3295 g_hash_table_insert(session->user_lookup_cb_data, GUINT_TO_POINTER(rid), data);
3301 3296
3302 return rid; 3297 return rid;
3303 } 3298 }
3304 3299
3305 /** 3300 /**
3310 * @param error_message 3305 * @param error_message
3311 */ 3306 */
3312 static void 3307 static void
3313 msim_connect_cb(gpointer data, gint source, const gchar *error_message) 3308 msim_connect_cb(gpointer data, gint source, const gchar *error_message)
3314 { 3309 {
3315 PurpleConnection *gc; 3310 PurpleConnection *gc;
3316 MsimSession *session; 3311 MsimSession *session;
3317 3312
3318 g_return_if_fail(data != NULL); 3313 g_return_if_fail(data != NULL);
3319 3314
3320 gc = (PurpleConnection *)data; 3315 gc = (PurpleConnection *)data;
3321 session = (MsimSession *)gc->proto_data; 3316 session = (MsimSession *)gc->proto_data;
3322 3317
3323 if (source < 0) { 3318 if (source < 0) {
3324 purple_connection_error(gc, _("Couldn't connect to host")); 3319 purple_connection_error(gc, _("Couldn't connect to host"));
3325 purple_connection_error(gc, g_strdup_printf( 3320 purple_connection_error(gc, g_strdup_printf(
3326 _("Couldn't connect to host: %s (%d)"), 3321 _("Couldn't connect to host: %s (%d)"),
3327 error_message ? error_message : "no message given", 3322 error_message ? error_message : "no message given",
3328 source)); 3323 source));
3329 return; 3324 return;
3330 } 3325 }
3331 3326
3332 session->fd = source; 3327 session->fd = source;
3333 3328
3334 gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc); 3329 gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc);
3335 } 3330 }
3336 3331
3337 /* Session methods */ 3332 /* Session methods */
3338 3333
3339 /** 3334 /**
3344 * @return Pointer to a new session. Free with msim_session_destroy. 3339 * @return Pointer to a new session. Free with msim_session_destroy.
3345 */ 3340 */
3346 MsimSession * 3341 MsimSession *
3347 msim_session_new(PurpleAccount *acct) 3342 msim_session_new(PurpleAccount *acct)
3348 { 3343 {
3349 MsimSession *session; 3344 MsimSession *session;
3350 3345
3351 g_return_val_if_fail(acct != NULL, NULL); 3346 g_return_val_if_fail(acct != NULL, NULL);
3352 3347
3353 session = g_new0(MsimSession, 1); 3348 session = g_new0(MsimSession, 1);
3354 3349
3355 session->magic = MSIM_SESSION_STRUCT_MAGIC; 3350 session->magic = MSIM_SESSION_STRUCT_MAGIC;
3356 session->account = acct; 3351 session->account = acct;
3357 session->gc = purple_account_get_connection(acct); 3352 session->gc = purple_account_get_connection(acct);
3358 session->sesskey = 0; 3353 session->sesskey = 0;
3359 session->userid = 0; 3354 session->userid = 0;
3360 session->username = NULL; 3355 session->username = NULL;
3361 session->fd = -1; 3356 session->fd = -1;
3362 3357
3363 /* TODO: Remove. */ 3358 /* TODO: Remove. */
3364 session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, 3359 session->user_lookup_cb = g_hash_table_new_full(g_direct_hash,
3365 g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */ 3360 g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */
3366 session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, 3361 session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash,
3367 g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are, 3362 g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
3368 they could be integers inside gpointers 3363 they could be integers inside gpointers
3369 or strings, so I don't freed them. 3364 or strings, so I don't freed them.
3370 Figure this out, once free cache. */ 3365 Figure this out, once free cache. */
3371 3366
3372 /* Created in msim_process_server_info() */ 3367 /* Created in msim_process_server_info() */
3373 session->server_info = NULL; 3368 session->server_info = NULL;
3374 3369
3375 session->rxoff = 0; 3370 session->rxoff = 0;
3376 session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE); 3371 session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE);
3377 session->next_rid = 1; 3372 session->next_rid = 1;
3378 session->last_comm = time(NULL); 3373 session->last_comm = time(NULL);
3379 session->inbox_status = 0; 3374 session->inbox_status = 0;
3380 3375
3381 return session; 3376 return session;
3382 } 3377 }
3383 3378
3384 /** 3379 /**
3385 * Free a session. 3380 * Free a session.
3386 * 3381 *
3387 * @param session The session to destroy. 3382 * @param session The session to destroy.
3388 */ 3383 */
3389 void 3384 void
3390 msim_session_destroy(MsimSession *session) 3385 msim_session_destroy(MsimSession *session)
3391 { 3386 {
3392 g_return_if_fail(MSIM_SESSION_VALID(session)); 3387 g_return_if_fail(MSIM_SESSION_VALID(session));
3393 3388
3394 session->magic = -1; 3389 session->magic = -1;
3395 3390
3396 g_free(session->rxbuf); 3391 g_free(session->rxbuf);
3397 g_free(session->username); 3392 g_free(session->username);
3398 3393
3399 /* TODO: Remove. */ 3394 /* TODO: Remove. */
3400 g_hash_table_destroy(session->user_lookup_cb); 3395 g_hash_table_destroy(session->user_lookup_cb);
3401 g_hash_table_destroy(session->user_lookup_cb_data); 3396 g_hash_table_destroy(session->user_lookup_cb_data);
3402 3397
3403 if (session->server_info) { 3398 if (session->server_info) {
3404 g_hash_table_destroy(session->server_info); 3399 g_hash_table_destroy(session->server_info);
3405 } 3400 }
3406 3401
3407 g_free(session); 3402 g_free(session);
3408 } 3403 }
3409 3404
3410 /** 3405 /**
3411 * Close the connection. 3406 * Close the connection.
3412 * 3407 *
3413 * @param gc The connection. 3408 * @param gc The connection.
3414 */ 3409 */
3417 { 3412 {
3418 MsimSession *session; 3413 MsimSession *session;
3419 3414
3420 if (gc == NULL) { 3415 if (gc == NULL) {
3421 return; 3416 return;
3422 } 3417 }
3423 3418
3424 session = (MsimSession *)gc->proto_data; 3419 session = (MsimSession *)gc->proto_data;
3425 if (session == NULL) 3420 if (session == NULL)
3426 return; 3421 return;
3427 3422
3428 gc->proto_data = NULL; 3423 gc->proto_data = NULL;
3429 3424
3430 if (!MSIM_SESSION_VALID(session)) { 3425 if (!MSIM_SESSION_VALID(session)) {
3431 return; 3426 return;
3432 } 3427 }
3433 3428
3434 if (session->gc->inpa) { 3429 if (session->gc->inpa) {
3435 purple_input_remove(session->gc->inpa); 3430 purple_input_remove(session->gc->inpa);
3436 } 3431 }
3437 3432
3438 msim_session_destroy(session); 3433 msim_session_destroy(session);
3439 } 3434 }
3440 3435
3441 3436
3442 /** 3437 /**
3443 * Check if a string is a userid (all numeric). 3438 * Check if a string is a userid (all numeric).
3447 * @return TRUE if is userid, FALSE if not. 3442 * @return TRUE if is userid, FALSE if not.
3448 */ 3443 */
3449 static gboolean 3444 static gboolean
3450 msim_is_userid(const gchar *user) 3445 msim_is_userid(const gchar *user)
3451 { 3446 {
3452 g_return_val_if_fail(user != NULL, FALSE); 3447 g_return_val_if_fail(user != NULL, FALSE);
3453 3448
3454 return strspn(user, "0123456789") == strlen(user); 3449 return strspn(user, "0123456789") == strlen(user);
3455 } 3450 }
3456 3451
3457 /** 3452 /**
3458 * Check if a string is an email address (contains an @). 3453 * Check if a string is an email address (contains an @).
3459 * 3454 *
3467 * other forms of identification. 3462 * other forms of identification.
3468 */ 3463 */
3469 static gboolean 3464 static gboolean
3470 msim_is_email(const gchar *user) 3465 msim_is_email(const gchar *user)
3471 { 3466 {
3472 g_return_val_if_fail(user != NULL, FALSE); 3467 g_return_val_if_fail(user != NULL, FALSE);
3473 3468
3474 return strchr(user, '@') != NULL; 3469 return strchr(user, '@') != NULL;
3475 } 3470 }
3476 3471
3477 3472
3478 /** 3473 /**
3479 * Asynchronously lookup user information, calling callback when receive result. 3474 * Asynchronously lookup user information, calling callback when receive result.
3486 /* TODO: change to not use callbacks */ 3481 /* TODO: change to not use callbacks */
3487 static void 3482 static void
3488 msim_lookup_user(MsimSession *session, const gchar *user, 3483 msim_lookup_user(MsimSession *session, const gchar *user,
3489 MSIM_USER_LOOKUP_CB cb, gpointer data) 3484 MSIM_USER_LOOKUP_CB cb, gpointer data)
3490 { 3485 {
3491 MsimMessage *body; 3486 MsimMessage *body;
3492 gchar *field_name; 3487 gchar *field_name;
3493 guint rid, cmd, dsn, lid; 3488 guint rid, cmd, dsn, lid;
3494 3489
3495 g_return_if_fail(MSIM_SESSION_VALID(session)); 3490 g_return_if_fail(MSIM_SESSION_VALID(session));
3496 g_return_if_fail(user != NULL); 3491 g_return_if_fail(user != NULL);
3497 g_return_if_fail(cb != NULL); 3492 g_return_if_fail(cb != NULL);
3498 3493
3499 purple_debug_info("msim", "msim_lookup_userid: " 3494 purple_debug_info("msim", "msim_lookup_userid: "
3500 "asynchronously looking up <%s>\n", user); 3495 "asynchronously looking up <%s>\n", user);
3501 3496
3502 msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data); 3497 msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data);
3503 3498
3504 /* Setup callback. Response will be associated with request using 'rid'. */ 3499 /* Setup callback. Response will be associated with request using 'rid'. */
3505 rid = msim_new_reply_callback(session, cb, data); 3500 rid = msim_new_reply_callback(session, cb, data);
3506 3501
3507 /* Send request */ 3502 /* Send request */
3508 3503
3509 cmd = MSIM_CMD_GET; 3504 cmd = MSIM_CMD_GET;
3510 3505
3511 if (msim_is_userid(user)) { 3506 if (msim_is_userid(user)) {
3512 field_name = "UserID"; 3507 field_name = "UserID";
3513 dsn = MG_MYSPACE_INFO_BY_ID_DSN; 3508 dsn = MG_MYSPACE_INFO_BY_ID_DSN;
3514 lid = MG_MYSPACE_INFO_BY_ID_LID; 3509 lid = MG_MYSPACE_INFO_BY_ID_LID;
3515 } else if (msim_is_email(user)) { 3510 } else if (msim_is_email(user)) {
3516 field_name = "Email"; 3511 field_name = "Email";
3517 dsn = MG_MYSPACE_INFO_BY_STRING_DSN; 3512 dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
3518 lid = MG_MYSPACE_INFO_BY_STRING_LID; 3513 lid = MG_MYSPACE_INFO_BY_STRING_LID;
3519 } else { 3514 } else {
3520 field_name = "UserName"; 3515 field_name = "UserName";
3521 dsn = MG_MYSPACE_INFO_BY_STRING_DSN; 3516 dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
3522 lid = MG_MYSPACE_INFO_BY_STRING_LID; 3517 lid = MG_MYSPACE_INFO_BY_STRING_LID;
3523 } 3518 }
3524 3519
3525 body = msim_msg_new(TRUE, 3520 body = msim_msg_new(TRUE,
3526 field_name, MSIM_TYPE_STRING, g_strdup(user), 3521 field_name, MSIM_TYPE_STRING, g_strdup(user),
3527 NULL); 3522 NULL);
3528 3523
3529 g_return_if_fail(msim_send(session, 3524 g_return_if_fail(msim_send(session,
3530 "persist", MSIM_TYPE_INTEGER, 1, 3525 "persist", MSIM_TYPE_INTEGER, 1,
3531 "sesskey", MSIM_TYPE_INTEGER, session->sesskey, 3526 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
3532 "cmd", MSIM_TYPE_INTEGER, 1, 3527 "cmd", MSIM_TYPE_INTEGER, 1,
3548 * 3543 *
3549 */ 3544 */
3550 char * 3545 char *
3551 msim_status_text(PurpleBuddy *buddy) 3546 msim_status_text(PurpleBuddy *buddy)
3552 { 3547 {
3553 MsimSession *session; 3548 MsimSession *session;
3554 const gchar *display_name, *headline; 3549 const gchar *display_name, *headline;
3555 3550
3556 g_return_val_if_fail(buddy != NULL, NULL); 3551 g_return_val_if_fail(buddy != NULL, NULL);
3557 3552
3558 session = (MsimSession *)buddy->account->gc->proto_data; 3553 session = (MsimSession *)buddy->account->gc->proto_data;
3559 g_return_val_if_fail(MSIM_SESSION_VALID(session), NULL); 3554 g_return_val_if_fail(MSIM_SESSION_VALID(session), NULL);
3560 3555
3561 display_name = headline = NULL; 3556 display_name = headline = NULL;
3562 3557
3563 /* Retrieve display name and/or headline, depending on user preference. */ 3558 /* Retrieve display name and/or headline, depending on user preference. */
3564 if (purple_account_get_bool(session->account, "show_display_name", TRUE)) { 3559 if (purple_account_get_bool(session->account, "show_display_name", TRUE)) {
3565 display_name = purple_blist_node_get_string(&buddy->node, "DisplayName"); 3560 display_name = purple_blist_node_get_string(&buddy->node, "DisplayName");
3566 } 3561 }
3567 3562
3568 if (purple_account_get_bool(session->account, "show_headline", FALSE)) { 3563 if (purple_account_get_bool(session->account, "show_headline", FALSE)) {
3569 headline = purple_blist_node_get_string(&buddy->node, "Headline"); 3564 headline = purple_blist_node_get_string(&buddy->node, "Headline");
3570 } 3565 }
3571 3566
3572 /* Return appropriate combination of display name and/or headline, or neither. */ 3567 /* Return appropriate combination of display name and/or headline, or neither. */
3573 3568
3574 if (display_name && headline) { 3569 if (display_name && headline) {
3575 return g_strconcat(display_name, " ", headline, NULL); 3570 return g_strconcat(display_name, " ", headline, NULL);
3576 } 3571 }
3577 3572
3578 if (display_name) { 3573 if (display_name) {
3579 return g_strdup(display_name); 3574 return g_strdup(display_name);
3580 } 3575 }
3581 3576
3582 if (headline) { 3577 if (headline) {
3583 return g_strdup(headline); 3578 return g_strdup(headline);
3584 } 3579 }
3585 3580
3586 return NULL; 3581 return NULL;
3587 } 3582 }
3588 3583
3589 /** 3584 /**
3599 gboolean full) 3594 gboolean full)
3600 { 3595 {
3601 const gchar *str, *str2; 3596 const gchar *str, *str2;
3602 gint n; 3597 gint n;
3603 3598
3604 g_return_if_fail(buddy != NULL); 3599 g_return_if_fail(buddy != NULL);
3605 g_return_if_fail(user_info != NULL); 3600 g_return_if_fail(user_info != NULL);
3606 3601
3607 if (PURPLE_BUDDY_IS_ONLINE(buddy)) { 3602 if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
3608 MsimSession *session; 3603 MsimSession *session;
3609 3604
3610 session = (MsimSession *)buddy->account->gc->proto_data; 3605 session = (MsimSession *)buddy->account->gc->proto_data;
3611 3606
3612 g_return_if_fail(MSIM_SESSION_VALID(session)); 3607 g_return_if_fail(MSIM_SESSION_VALID(session));
3613 3608
3614 /* TODO: if (full), do something different */ 3609 /* TODO: if (full), do something different */
3615 3610
3616 /* Useful to identify the account the tooltip refers to. 3611 /* Useful to identify the account the tooltip refers to.
3617 * Other prpls show this. */ 3612 * Other prpls show this. */
3618 str = purple_blist_node_get_string(&buddy->node, "UserName"); 3613 str = purple_blist_node_get_string(&buddy->node, "UserName");
3619 if (str) { 3614 if (str) {
3620 purple_notify_user_info_add_pair(user_info, _("User Name"), str); 3615 purple_notify_user_info_add_pair(user_info, _("User Name"), str);
3621 } 3616 }
3622 3617
3623 /* a/s/l...the vitals */ 3618 /* a/s/l...the vitals */
3624 n = purple_blist_node_get_int(&buddy->node, "Age"); 3619 n = purple_blist_node_get_int(&buddy->node, "Age");
3625 if (n) { 3620 if (n) {
3626 purple_notify_user_info_add_pair(user_info, _("Age"), 3621 purple_notify_user_info_add_pair(user_info, _("Age"),
3627 g_strdup_printf("%d", n)); 3622 g_strdup_printf("%d", n));
3628 } 3623 }
3629 3624
3630 str = purple_blist_node_get_string(&buddy->node, "Gender"); 3625 str = purple_blist_node_get_string(&buddy->node, "Gender");
3631 if (str) { 3626 if (str) {
3632 purple_notify_user_info_add_pair(user_info, _("Gender"), str); 3627 purple_notify_user_info_add_pair(user_info, _("Gender"), str);
3633 } 3628 }
3634 3629
3635 str = purple_blist_node_get_string(&buddy->node, "Location"); 3630 str = purple_blist_node_get_string(&buddy->node, "Location");
3636 if (str) { 3631 if (str) {
3637 purple_notify_user_info_add_pair(user_info, _("Location"), str); 3632 purple_notify_user_info_add_pair(user_info, _("Location"), str);
3638 } 3633 }
3639 3634
3640 /* Other information */ 3635 /* Other information */
3641 str = purple_blist_node_get_string(&buddy->node, "Headline"); 3636 str = purple_blist_node_get_string(&buddy->node, "Headline");
3642 if (str) { 3637 if (str) {
3643 purple_notify_user_info_add_pair(user_info, _("Headline"), str); 3638 purple_notify_user_info_add_pair(user_info, _("Headline"), str);
3644 } 3639 }
3645 3640
3646 str = purple_blist_node_get_string(&buddy->node, "BandName"); 3641 str = purple_blist_node_get_string(&buddy->node, "BandName");
3647 str2 = purple_blist_node_get_string(&buddy->node, "SongName"); 3642 str2 = purple_blist_node_get_string(&buddy->node, "SongName");
3648 if (str || str2) { 3643 if (str || str2) {
3649 purple_notify_user_info_add_pair(user_info, _("Song"), 3644 purple_notify_user_info_add_pair(user_info, _("Song"),
3650 g_strdup_printf("%s - %s", 3645 g_strdup_printf("%s - %s",
3651 str ? str : _("Unknown Artist"), 3646 str ? str : _("Unknown Artist"),
3652 str2 ? str2 : _("Unknown Song"))); 3647 str2 ? str2 : _("Unknown Song")));
3653 } 3648 }
3654 3649
3655 n = purple_blist_node_get_int(&buddy->node, "TotalFriends"); 3650 n = purple_blist_node_get_int(&buddy->node, "TotalFriends");
3656 if (n) { 3651 if (n) {
3657 purple_notify_user_info_add_pair(user_info, _("Total Friends"), 3652 purple_notify_user_info_add_pair(user_info, _("Total Friends"),
3658 g_strdup_printf("%d", n)); 3653 g_strdup_printf("%d", n));
3659 } 3654 }
3660 3655
3661 } 3656 }
3662 } 3657 }
3663 3658
3664 /** Actions menu for account. */ 3659 /** Actions menu for account. */
3665 GList * 3660 GList *
3666 msim_actions(PurplePlugin *plugin, gpointer context) 3661 msim_actions(PurplePlugin *plugin, gpointer context)
3667 { 3662 {
3668 PurpleConnection *gc; 3663 PurpleConnection *gc;
3669 GList *menu; 3664 GList *menu;
3670 //PurplePluginAction *act; 3665 //PurplePluginAction *act;
3671 3666
3672 gc = (PurpleConnection *)context; 3667 gc = (PurpleConnection *)context;
3673 3668
3674 menu = NULL; 3669 menu = NULL;
3675 3670
3676 #if 0 3671 #if 0
3677 /* TODO: find out how */ 3672 /* TODO: find out how */
3678 act = purple_plugin_action_new(_("Find people..."), msim_); 3673 act = purple_plugin_action_new(_("Find people..."), msim_);
3679 menu = g_list_append(menu, act); 3674 menu = g_list_append(menu, act);
3680 3675
3681 act = purple_plugin_action_new(_("Import friends..."), NULL); 3676 act = purple_plugin_action_new(_("Import friends..."), NULL);
3682 menu = g_list_append(menu, act); 3677 menu = g_list_append(menu, act);
3683 3678
3684 act = purple_plugin_action_new(_("Change IM name..."), NULL); 3679 act = purple_plugin_action_new(_("Change IM name..."), NULL);
3685 menu = g_list_append(menu, act); 3680 menu = g_list_append(menu, act);
3686 #endif 3681 #endif
3687 3682
3688 return menu; 3683 return menu;
3689 } 3684 }
3690 3685
3691 /** Callbacks called by Purple, to access this plugin. */ 3686 /** Callbacks called by Purple, to access this plugin. */
3692 PurplePluginProtocolInfo prpl_info = { 3687 PurplePluginProtocolInfo prpl_info = {
3693 /* options */ 3688 /* options */
3694 OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */ 3689 OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */
3695 | OPT_PROTO_MAIL_CHECK, 3690 | OPT_PROTO_MAIL_CHECK,
3696 3691
3697 /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */ 3692 /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */
3698 NULL, /* user_splits */ 3693 NULL, /* user_splits */
3699 NULL, /* protocol_options */ 3694 NULL, /* protocol_options */
3700 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ 3695 NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */
3701 msim_list_icon, /* list_icon */ 3696 msim_list_icon, /* list_icon */
3702 NULL, /* list_emblems */ 3697 NULL, /* list_emblems */
3703 msim_status_text, /* status_text */ 3698 msim_status_text, /* status_text */
3704 msim_tooltip_text, /* tooltip_text */ 3699 msim_tooltip_text, /* tooltip_text */
3705 msim_status_types, /* status_types */ 3700 msim_status_types, /* status_types */
3706 msim_blist_node_menu, /* blist_node_menu */ 3701 msim_blist_node_menu, /* blist_node_menu */
3707 NULL, /* chat_info */ 3702 NULL, /* chat_info */
3708 NULL, /* chat_info_defaults */ 3703 NULL, /* chat_info_defaults */
3709 msim_login, /* login */ 3704 msim_login, /* login */
3710 msim_close, /* close */ 3705 msim_close, /* close */
3711 msim_send_im, /* send_im */ 3706 msim_send_im, /* send_im */
3712 NULL, /* set_info */ 3707 NULL, /* set_info */
3713 msim_send_typing, /* send_typing */ 3708 msim_send_typing, /* send_typing */
3714 msim_get_info, /* get_info */ 3709 msim_get_info, /* get_info */
3715 msim_set_status, /* set_status */ 3710 msim_set_status, /* set_status */
3716 msim_set_idle, /* set_idle */ 3711 msim_set_idle, /* set_idle */
3717 NULL, /* change_passwd */ 3712 NULL, /* change_passwd */
3718 msim_add_buddy, /* add_buddy */ 3713 msim_add_buddy, /* add_buddy */
3719 NULL, /* add_buddies */ 3714 NULL, /* add_buddies */
3720 msim_remove_buddy, /* remove_buddy */ 3715 msim_remove_buddy, /* remove_buddy */
3721 NULL, /* remove_buddies */ 3716 NULL, /* remove_buddies */
3722 NULL, /* add_permit */ 3717 NULL, /* add_permit */
3723 NULL, /* add_deny */ 3718 NULL, /* add_deny */
3724 NULL, /* rem_permit */ 3719 NULL, /* rem_permit */
3725 NULL, /* rem_deny */ 3720 NULL, /* rem_deny */
3726 NULL, /* set_permit_deny */ 3721 NULL, /* set_permit_deny */
3727 NULL, /* join_chat */ 3722 NULL, /* join_chat */
3728 NULL, /* reject chat invite */ 3723 NULL, /* reject chat invite */
3729 NULL, /* get_chat_name */ 3724 NULL, /* get_chat_name */
3730 NULL, /* chat_invite */ 3725 NULL, /* chat_invite */
3731 NULL, /* chat_leave */ 3726 NULL, /* chat_leave */
3732 NULL, /* chat_whisper */ 3727 NULL, /* chat_whisper */
3733 NULL, /* chat_send */ 3728 NULL, /* chat_send */
3734 NULL, /* keepalive */ 3729 NULL, /* keepalive */
3735 NULL, /* register_user */ 3730 NULL, /* register_user */
3736 NULL, /* get_cb_info */ 3731 NULL, /* get_cb_info */
3737 NULL, /* get_cb_away */ 3732 NULL, /* get_cb_away */
3738 NULL, /* alias_buddy */ 3733 NULL, /* alias_buddy */
3739 NULL, /* group_buddy */ 3734 NULL, /* group_buddy */
3740 NULL, /* rename_group */ 3735 NULL, /* rename_group */
3741 NULL, /* buddy_free */ 3736 NULL, /* buddy_free */
3742 NULL, /* convo_closed */ 3737 NULL, /* convo_closed */
3743 NULL, /* normalize */ 3738 NULL, /* normalize */
3744 NULL, /* set_buddy_icon */ 3739 NULL, /* set_buddy_icon */
3745 NULL, /* remove_group */ 3740 NULL, /* remove_group */
3746 NULL, /* get_cb_real_name */ 3741 NULL, /* get_cb_real_name */
3747 NULL, /* set_chat_topic */ 3742 NULL, /* set_chat_topic */
3748 NULL, /* find_blist_chat */ 3743 NULL, /* find_blist_chat */
3749 NULL, /* roomlist_get_list */ 3744 NULL, /* roomlist_get_list */
3750 NULL, /* roomlist_cancel */ 3745 NULL, /* roomlist_cancel */
3751 NULL, /* roomlist_expand_category */ 3746 NULL, /* roomlist_expand_category */
3752 NULL, /* can_receive_file */ 3747 NULL, /* can_receive_file */
3753 NULL, /* send_file */ 3748 NULL, /* send_file */
3754 NULL, /* new_xfer */ 3749 NULL, /* new_xfer */
3755 msim_offline_message, /* offline_message */ 3750 msim_offline_message, /* offline_message */
3756 NULL, /* whiteboard_prpl_ops */ 3751 NULL, /* whiteboard_prpl_ops */
3757 msim_send_really_raw, /* send_raw */ 3752 msim_send_really_raw, /* send_raw */
3758 NULL, /* roomlist_room_serialize */ 3753 NULL, /* roomlist_room_serialize */
3759 NULL, /* _purple_reserved1 */ 3754 NULL, /* _purple_reserved1 */
3760 NULL, /* _purple_reserved2 */ 3755 NULL, /* _purple_reserved2 */
3761 NULL, /* _purple_reserved3 */ 3756 NULL, /* _purple_reserved3 */
3762 NULL /* _purple_reserved4 */ 3757 NULL /* _purple_reserved4 */
3763 }; 3758 };
3764 3759
3765 3760
3766 3761
3767 /** Based on MSN's plugin info comments. */ 3762 /** Based on MSN's plugin info comments. */
3768 PurplePluginInfo info = { 3763 PurplePluginInfo info = {
3769 PURPLE_PLUGIN_MAGIC, 3764 PURPLE_PLUGIN_MAGIC,
3770 PURPLE_MAJOR_VERSION, 3765 PURPLE_MAJOR_VERSION,
3771 PURPLE_MINOR_VERSION, 3766 PURPLE_MINOR_VERSION,
3772 PURPLE_PLUGIN_PROTOCOL, /**< type */ 3767 PURPLE_PLUGIN_PROTOCOL, /**< type */
3773 NULL, /**< ui_requirement */ 3768 NULL, /**< ui_requirement */
3774 0, /**< flags */ 3769 0, /**< flags */
3775 NULL, /**< dependencies */ 3770 NULL, /**< dependencies */
3776 PURPLE_PRIORITY_DEFAULT, /**< priority */ 3771 PURPLE_PRIORITY_DEFAULT, /**< priority */
3777 3772
3778 "prpl-myspace", /**< id */ 3773 "prpl-myspace", /**< id */
3779 "MySpaceIM", /**< name */ 3774 "MySpaceIM", /**< name */
3780 MSIM_PRPL_VERSION_STRING, /**< version */ 3775 MSIM_PRPL_VERSION_STRING, /**< version */
3781 /** summary */ 3776 /** summary */
3782 "MySpaceIM Protocol Plugin", 3777 "MySpaceIM Protocol Plugin",
3783 /** description */ 3778 /** description */
3784 "MySpaceIM Protocol Plugin", 3779 "MySpaceIM Protocol Plugin",
3785 "Jeff Connelly <jeff2@soc.pidgin.im>", /**< author */ 3780 "Jeff Connelly <jeff2@soc.pidgin.im>", /**< author */
3786 "http://developer.pidgin.im/wiki/MySpaceIM/", /**< homepage */ 3781 "http://developer.pidgin.im/wiki/MySpaceIM/", /**< homepage */
3787 3782
3788 msim_load, /**< load */ 3783 msim_load, /**< load */
3789 NULL, /**< unload */ 3784 NULL, /**< unload */
3790 NULL, /**< destroy */ 3785 NULL, /**< destroy */
3791 NULL, /**< ui_info */ 3786 NULL, /**< ui_info */
3792 &prpl_info, /**< extra_info */ 3787 &prpl_info, /**< extra_info */
3793 NULL, /**< prefs_info */ 3788 NULL, /**< prefs_info */
3794 msim_actions, /**< msim_actions */ 3789 msim_actions, /**< msim_actions */
3795 NULL, /**< reserved1 */ 3790 NULL, /**< reserved1 */
3796 NULL, /**< reserved2 */ 3791 NULL, /**< reserved2 */
3797 NULL, /**< reserved3 */ 3792 NULL, /**< reserved3 */
3798 NULL /**< reserved4 */ 3793 NULL /**< reserved4 */
3799 }; 3794 };
3800 3795
3801 3796
3802 #ifdef MSIM_SELF_TEST 3797 #ifdef MSIM_SELF_TEST
3803 /** Test functions. 3798 /** Test functions.
3825 /** Test MsimMessage for basic functionality. */ 3820 /** Test MsimMessage for basic functionality. */
3826 int 3821 int
3827 msim_test_msg(void) 3822 msim_test_msg(void)
3828 { 3823 {
3829 MsimMessage *msg, *msg_cloned, *msg2; 3824 MsimMessage *msg, *msg_cloned, *msg2;
3830 GList *list; 3825 GList *list;
3831 gchar *packed, *packed_expected, *packed_cloned; 3826 gchar *packed, *packed_expected, *packed_cloned;
3832 guint failures; 3827 guint failures;
3833 3828
3834 failures = 0; 3829 failures = 0;
3835 3830
3836 purple_debug_info("msim", "\n\nTesting MsimMessage\n"); 3831 purple_debug_info("msim", "\n\nTesting MsimMessage\n");
3837 msg = msim_msg_new(FALSE); /* Create a new, empty message. */ 3832 msg = msim_msg_new(FALSE); /* Create a new, empty message. */
3838 3833
3839 /* Append some new elements. */ 3834 /* Append some new elements. */
3840 msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len(g_strdup("XXX"), 3)); 3835 msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len(g_strdup("XXX"), 3));
3841 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1")); 3836 msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1"));
3842 msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(42)); 3837 msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(42));
3869 g_free(packed); 3864 g_free(packed);
3870 g_free(packed_cloned); 3865 g_free(packed_cloned);
3871 msim_msg_free(msg_cloned); 3866 msim_msg_free(msg_cloned);
3872 msim_msg_free(msg); 3867 msim_msg_free(msg);
3873 3868
3874 /* Try some of the more advanced functionality */ 3869 /* Try some of the more advanced functionality */
3875 list = NULL; 3870 list = NULL;
3876 3871
3877 list = g_list_prepend(list, "item3"); 3872 list = g_list_prepend(list, "item3");
3878 list = g_list_prepend(list, "item2"); 3873 list = g_list_prepend(list, "item2");
3879 list = g_list_prepend(list, "item1"); 3874 list = g_list_prepend(list, "item1");
3880 list = g_list_prepend(list, "item0"); 3875 list = g_list_prepend(list, "item0");
3881 3876
3882 msg = msim_msg_new(FALSE); 3877 msg = msim_msg_new(FALSE);
3883 msg = msim_msg_append(msg, "string", MSIM_TYPE_STRING, g_strdup("string value")); 3878 msg = msim_msg_append(msg, "string", MSIM_TYPE_STRING, g_strdup("string value"));
3884 msg = msim_msg_append(msg, "raw", MSIM_TYPE_RAW, g_strdup("raw value")); 3879 msg = msim_msg_append(msg, "raw", MSIM_TYPE_RAW, g_strdup("raw value"));
3885 msg = msim_msg_append(msg, "integer", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(3140)); 3880 msg = msim_msg_append(msg, "integer", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(3140));
3886 msg = msim_msg_append(msg, "boolean", MSIM_TYPE_BOOLEAN, GUINT_TO_POINTER(FALSE)); 3881 msg = msim_msg_append(msg, "boolean", MSIM_TYPE_BOOLEAN, GUINT_TO_POINTER(FALSE));
3887 msg = msim_msg_append(msg, "list", MSIM_TYPE_LIST, list); 3882 msg = msim_msg_append(msg, "list", MSIM_TYPE_LIST, list);
3888 3883
3889 msim_msg_dump("msg with list=%s\n", msg); 3884 msim_msg_dump("msg with list=%s\n", msg);
3890 purple_debug_info("msim", "msg with list packed=%s\n", msim_msg_pack(msg)); 3885 purple_debug_info("msim", "msg with list packed=%s\n", msim_msg_pack(msg));
3891 3886
3892 msg2 = msim_msg_new(FALSE); 3887 msg2 = msim_msg_new(FALSE);
3893 msg2 = msim_msg_append(msg2, "outer", MSIM_TYPE_STRING, g_strdup("outer value")); 3888 msg2 = msim_msg_append(msg2, "outer", MSIM_TYPE_STRING, g_strdup("outer value"));
3894 msg2 = msim_msg_append(msg2, "body", MSIM_TYPE_DICTIONARY, msg); 3889 msg2 = msim_msg_append(msg2, "body", MSIM_TYPE_DICTIONARY, msg);
3895 msim_msg_dump("msg with dict=%s\n", msg2); /* msg2 now 'owns' msg */ 3890 msim_msg_dump("msg with dict=%s\n", msg2); /* msg2 now 'owns' msg */
3896 purple_debug_info("msim", "msg with dict packed=%s\n", msim_msg_pack(msg2)); 3891 purple_debug_info("msim", "msg with dict packed=%s\n", msim_msg_pack(msg2));
3897 3892
3898 msim_msg_free(msg2); 3893 msim_msg_free(msg2);
3899 3894
3900 return failures; 3895 return failures;
3901 } 3896 }
3902 3897
3903 /** Test protocol-level escaping/unescaping. */ 3898 /** Test protocol-level escaping/unescaping. */
3926 g_free(escaped); 3921 g_free(escaped);
3927 purple_debug_info("msim", "msim_test_escaping: unescaped=%s\n", unescaped); 3922 purple_debug_info("msim", "msim_test_escaping: unescaped=%s\n", unescaped);
3928 if (0 != strcmp(raw, unescaped)) { 3923 if (0 != strcmp(raw, unescaped)) {
3929 purple_debug_info("msim", "!!!(%d), msim_unescape failed: %s != %s\n", 3924 purple_debug_info("msim", "!!!(%d), msim_unescape failed: %s != %s\n",
3930 ++failures, raw, unescaped); 3925 ++failures, raw, unescaped);
3931 } 3926 }
3932 3927
3933 return failures; 3928 return failures;
3934 } 3929 }
3935 #endif 3930 #endif
3936 3931
3955 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 3950 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3956 3951
3957 option = purple_account_option_bool_new(_("Show headline in status text"), "show_headline", TRUE); 3952 option = purple_account_option_bool_new(_("Show headline in status text"), "show_headline", TRUE);
3958 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 3953 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3959 3954
3960 option = purple_account_option_bool_new(_("Send emoticons"), "emoticons", FALSE); 3955 option = purple_account_option_bool_new(_("Send emoticons"), "emoticons", FALSE);
3961 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 3956 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3962 3957
3963 #ifdef MSIM_USER_REALLY_CARES_ABOUT_PRECISE_FONT_SIZES 3958 #ifdef MSIM_USER_REALLY_CARES_ABOUT_PRECISE_FONT_SIZES
3964 option = purple_account_option_int_new(_("Screen resolution (dots per inch)"), "dpi", MSIM_DEFAULT_DPI); 3959 option = purple_account_option_int_new(_("Screen resolution (dots per inch)"), "dpi", MSIM_DEFAULT_DPI);
3965 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 3960 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3966 3961
3967 option = purple_account_option_int_new(_("Base font size (points)"), "base_font_size", MSIM_BASE_FONT_POINT_SIZE); 3962 option = purple_account_option_int_new(_("Base font size (points)"), "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
3968 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 3963 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3969 #endif 3964 #endif
3970 } 3965 }
3971 3966
3972 PURPLE_INIT_PLUGIN(myspace, init_plugin, info); 3967 PURPLE_INIT_PLUGIN(myspace, init_plugin, info);