Mercurial > pidgin
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); |