comparison src/protocols/msn/switchboard.c @ 5309:e2e53316a21d

[gaim-migrate @ 5681] Announcing the new MSN prpl! It probably has some bugs, and for the time being, there is no file transfer. That's good though, because the current MSN file transfer is a little broken. I've had many corrupted files. I'll commit new file transfer code when it's written. I want this heavily tested before 0.63! If you use MSN, please talk to people on it. Let me know of any oddities, crashes, bugs, whatever. I'll fix things as I find them. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Tue, 06 May 2003 02:06:56 +0000
parents abe4d103e300
children d5690ed70085
comparison
equal deleted inserted replaced
5308:6aa785e55d0f 5309:e2e53316a21d
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details. 16 * GNU General Public License for more details.
17 * 17 *
18 * You should have received a copy of the GNU General Public License 18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software 19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */ 21 */
23 #include "msn.h" 22 #include "msn.h"
24 23 #include "switchboard.h"
25 #ifdef _WIN32 24 #include "utils.h"
26 #include "win32dep.h" 25
27 #endif 26 static GHashTable *switchboard_commands = NULL;
28 27 static GHashTable *switchboard_msg_types = NULL;
29 G_MODULE_IMPORT GSList *connections; 28
30 29
31 static char * 30 /**************************************************************************
32 msn_parse_format(char *mime) 31 * Catch-all commands
33 { 32 **************************************************************************/
34 char *cur; 33 static gboolean
35 GString *ret = g_string_new(NULL); 34 __blank_cmd(MsnServConn *servconn, const char *command, const char **params,
36 guint colorbuf; 35 size_t param_count)
37 char *colors = (char *)(&colorbuf); 36 {
38 37 return TRUE;
39 38 }
40 cur = strstr(mime, "FN="); 39
41 if (cur && (*(cur = cur + 3) != ';')) { 40 static gboolean
42 ret = g_string_append(ret, "<FONT FACE=\""); 41 __unknown_cmd(MsnServConn *servconn, const char *command, const char **params,
43 while (*cur && *cur != ';') { 42 size_t param_count)
44 ret = g_string_append_c(ret, *cur); 43 {
45 cur++; 44 gaim_debug(GAIM_DEBUG_ERROR, "msg",
45 "Handled switchboard message: %s\n", command);
46
47 return FALSE;
48 }
49
50 /**************************************************************************
51 * Switchboard Commands
52 **************************************************************************/
53 static gboolean
54 __ans_cmd(MsnServConn *servconn, const char *command, const char **params,
55 size_t param_count)
56 {
57 struct gaim_connection *gc = servconn->session->account->gc;
58 MsnSwitchBoard *swboard = servconn->data;
59
60 if (swboard->chat != NULL)
61 gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL);
62
63 return TRUE;
64 }
65
66 static gboolean
67 __bye_cmd(MsnServConn *servconn, const char *command, const char **params,
68 size_t param_count)
69 {
70 struct gaim_connection *gc = servconn->session->account->gc;
71 MsnSwitchBoard *swboard = servconn->data;
72 const char *user = params[0];
73
74 if (swboard->chat != NULL)
75 gaim_chat_remove_user(GAIM_CHAT(swboard->chat), user, NULL);
76 else {
77 const char *username;
78 struct gaim_conversation *conv;
79 struct buddy *b;
80 char buf[MSN_BUF_LEN];
81
82 if ((b = gaim_find_buddy(gc->account, user)) != NULL)
83 username = gaim_get_buddy_alias(b);
84 else
85 username = user;
86
87 g_snprintf(buf, sizeof(buf),
88 _("%s has closed the conversation window."), username);
89
90 if ((conv = gaim_find_conversation(user)) != NULL)
91 gaim_conversation_write(conv, NULL, buf, -1, WFLAG_SYSTEM,
92 time(NULL));
93
94 msn_switchboard_destroy(swboard);
95
96 return FALSE;
97 }
98
99 return TRUE;
100 }
101
102 static gboolean
103 __iro_cmd(MsnServConn *servconn, const char *command, const char **params,
104 size_t param_count)
105 {
106 struct gaim_connection *gc = servconn->session->account->gc;
107 MsnSwitchBoard *swboard = servconn->data;
108
109 swboard->total_users = atoi(params[2]);
110
111 if (swboard->total_users > 1) {
112 if (swboard->chat == NULL)
113 swboard->chat = serv_got_joined_chat(gc, ++swboard->chat_id,
114 "MSN Chat");
115
116 gaim_chat_add_user(GAIM_CHAT(swboard->chat), params[3], NULL);
117 }
118
119 if (swboard->chat != NULL)
120 gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL);
121
122 return TRUE;
123 }
124
125 static gboolean
126 __joi_cmd(MsnServConn *servconn, const char *command, const char **params,
127 size_t param_count)
128 {
129 struct gaim_connection *gc = servconn->session->account->gc;
130 MsnSwitchBoard *swboard = servconn->data;
131 const char *passport;
132
133 passport = params[0];
134
135 if (swboard->total_users == 1) {
136 swboard->chat = serv_got_joined_chat(gc, ++swboard->chat_id,
137 "MSN Chat");
138 gaim_chat_add_user(GAIM_CHAT(swboard->chat),
139 msn_user_get_passport(swboard->user), NULL);
140 gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL);
141
142 msn_user_unref(swboard->user);
143 }
144
145 if (swboard->chat != NULL)
146 gaim_chat_add_user(GAIM_CHAT(swboard->chat), passport, NULL);
147
148 swboard->total_users++;
149
150 while (servconn->txqueue) {
151 char *buf = servconn->txqueue->data;
152
153 servconn->txqueue = g_slist_remove(servconn->txqueue, buf);
154
155 if (msn_servconn_write(swboard->servconn, buf, strlen(buf)) < 0) {
156 msn_switchboard_destroy(swboard);
157
158 return FALSE;
46 } 159 }
47 ret = g_string_append(ret, "\">"); 160 }
48 } 161
49 162 return TRUE;
50 cur = strstr(mime, "EF="); 163 }
51 if (cur && (*(cur = cur + 3) != ';')) { 164
52 while (*cur && *cur != ';') { 165 static gboolean
53 ret = g_string_append_c(ret, '<'); 166 __msg_cmd(MsnServConn *servconn, const char *command, const char **params,
54 ret = g_string_append_c(ret, *cur); 167 size_t param_count)
55 ret = g_string_append_c(ret, '>'); 168 {
56 cur++; 169 gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n");
170
171 servconn->parsing_msg = TRUE;
172 servconn->msg_passport = g_strdup(params[0]);
173 servconn->msg_friendly = g_strdup(params[1]);
174 servconn->msg_len = atoi(params[2]);
175
176 return TRUE;
177 }
178
179 static gboolean
180 __nak_cmd(MsnServConn *servconn, const char *command, const char **params,
181 size_t param_count)
182 {
183 /*
184 * TODO: Investigate this, as it seems to occur frequently with
185 * the old prpl.
186 */
187 do_error_dialog(_("An MSN message may not have been received."),
188 NULL, GAIM_ERROR);
189
190 return TRUE;
191 }
192
193 static gboolean
194 __out_cmd(MsnServConn *servconn, const char *command, const char **params,
195 size_t param_count)
196 {
197 struct gaim_connection *gc = servconn->session->account->gc;
198 MsnSwitchBoard *swboard = servconn->data;
199
200 if (swboard->chat != NULL)
201 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(swboard->chat)));
202
203 msn_switchboard_destroy(swboard);
204
205 return FALSE;
206 }
207
208 static gboolean
209 __usr_cmd(MsnServConn *servconn, const char *command, const char **params,
210 size_t param_count)
211 {
212 MsnSwitchBoard *swboard = servconn->data;
213
214 if (!msn_switchboard_send_command(swboard, "CAL",
215 msn_user_get_passport(swboard->user))) {
216 msn_switchboard_destroy(swboard);
217
218 return FALSE;
219 }
220
221 return TRUE;
222 }
223
224 /**************************************************************************
225 * Message Types
226 **************************************************************************/
227 static gboolean
228 __plain_msg(MsnServConn *servconn, const MsnMessage *msg)
229 {
230 struct gaim_connection *gc = servconn->session->account->gc;
231 MsnSwitchBoard *swboard = servconn->data;
232 char *body;
233 const char *value;
234 char *format;
235 int flags = 0;
236
237 body = g_strdup(msn_message_get_body(msg));
238
239 if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) {
240 if (!g_ascii_strncasecmp(value, "Gaim", 4))
241 flags |= IM_FLAG_GAIMUSER;
242 }
243
244 if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) {
245 format = msn_parse_format(value);
246
247 body = g_strdup_printf("%s%s", format, body);
248
249 g_free(format);
250 }
251
252 if (swboard->chat != NULL)
253 serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(swboard->chat)),
254 servconn->msg_passport, flags, body, time(NULL));
255 else
256 serv_got_im(gc, servconn->msg_passport, body, flags, time(NULL), -1);
257
258 g_free(body);
259
260 return TRUE;
261 }
262
263 static gboolean
264 __control_msg(MsnServConn *servconn, const MsnMessage *msg)
265 {
266 struct gaim_connection *gc = servconn->session->account->gc;
267 MsnSwitchBoard *swboard = servconn->data;
268 const char *value;
269
270 if (swboard->chat == NULL &&
271 (value = msn_message_get_attr(msg, "TypingUser")) != NULL) {
272
273 serv_got_typing(gc, servconn->msg_passport, MSN_TYPING_RECV_TIMEOUT,
274 TYPING);
275 }
276
277 return TRUE;
278 }
279
280 /**************************************************************************
281 * Connect stuff
282 **************************************************************************/
283 static gboolean
284 __connect_cb(gpointer data, gint source, GaimInputCondition cond)
285 {
286 MsnServConn *servconn = data;
287 MsnSwitchBoard *swboard = servconn->data;
288 char outparams[MSN_BUF_LEN];
289
290 if (servconn->fd != source)
291 servconn->fd = source;
292
293 swboard->in_use = TRUE;
294
295 gaim_debug(GAIM_DEBUG_INFO, "msn", "Connecting to switchboard...\n");
296
297 if (msn_switchboard_is_invited(swboard)) {
298 g_snprintf(outparams, sizeof(outparams), "%s %s %s",
299 servconn->session->account->gc->username,
300 swboard->auth_key, swboard->session_id);
301
302 if (!msn_switchboard_send_command(swboard, "ANS", outparams)) {
303 msn_switchboard_destroy(swboard);
304
305 return FALSE;
57 } 306 }
58 } 307 }
59 308 else {
60 cur = strstr(mime, "CO="); 309 g_snprintf(outparams, sizeof(outparams), "%s %s",
61 if (cur && (*(cur = cur + 3) != ';')) { 310 servconn->session->account->gc->username, swboard->auth_key);
62 if (sscanf (cur, "%x;", &colorbuf) == 1) { 311
63 char tag[64]; 312 if (!msn_switchboard_send_command(swboard, "USR", outparams)) {
64 g_snprintf(tag, sizeof(tag), "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", colors[0], colors[1], colors[2]); 313 msn_switchboard_destroy(swboard);
65 ret = g_string_append(ret, tag); 314
315 return FALSE;
66 } 316 }
67 } 317 }
68 318
69 cur = url_decode(ret->str); 319 return TRUE;
70 g_string_free(ret, TRUE);
71 return cur;
72 }
73
74 static int
75 msn_process_switch(struct msn_switchboard *ms, char *buf)
76 {
77 struct gaim_connection *gc = ms->gc;
78 char sendbuf[MSN_BUF_LEN];
79 static int id = 0;
80
81 if (!g_ascii_strncasecmp(buf, "ACK", 3)) {
82 } else if (!g_ascii_strncasecmp(buf, "ANS", 3)) {
83 if (ms->chat)
84 gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL);
85 } else if (!g_ascii_strncasecmp(buf, "BYE", 3)) {
86 char *user, *tmp = buf;
87 GET_NEXT(tmp);
88 user = tmp;
89
90 if (ms->chat) {
91 gaim_chat_remove_user(GAIM_CHAT(ms->chat), user, NULL);
92 } else {
93 char msgbuf[256];
94 const char *username;
95 struct gaim_conversation *cnv;
96 struct buddy *b;
97
98 if ((b = gaim_find_buddy(gc->account, user)) != NULL)
99 username = gaim_get_buddy_alias(b);
100 else
101 username = user;
102
103 g_snprintf(msgbuf, sizeof(msgbuf),
104 _("%s has closed the conversation window"), username);
105
106 if ((cnv = gaim_find_conversation(user)))
107 gaim_conversation_write(cnv, NULL, msgbuf, -1,
108 WFLAG_SYSTEM, time(NULL));
109
110 msn_kill_switch(ms);
111 return 0;
112 }
113 } else if (!g_ascii_strncasecmp(buf, "CAL", 3)) {
114 } else if (!g_ascii_strncasecmp(buf, "IRO", 3)) {
115 char *tot, *user, *tmp = buf;
116
117 GET_NEXT(tmp);
118 GET_NEXT(tmp);
119 GET_NEXT(tmp);
120 tot = tmp;
121 GET_NEXT(tmp);
122 ms->total = atoi(tot);
123 user = tmp;
124 GET_NEXT(tmp);
125
126 if (ms->total > 1) {
127 if (!ms->chat)
128 ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat");
129
130 gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL);
131 }
132 } else if (!g_ascii_strncasecmp(buf, "JOI", 3)) {
133 char *user, *tmp = buf;
134 GET_NEXT(tmp);
135 user = tmp;
136 GET_NEXT(tmp);
137
138 if (ms->total == 1) {
139 ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat");
140 gaim_chat_add_user(GAIM_CHAT(ms->chat), ms->user, NULL);
141 gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL);
142 g_free(ms->user);
143 ms->user = NULL;
144 }
145 if (ms->chat)
146 gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL);
147 ms->total++;
148 while (ms->txqueue) {
149 char *send = add_cr(ms->txqueue->data);
150 g_snprintf(sendbuf, sizeof(sendbuf),
151 "MSG %u N %d\r\n%s%s", ++ms->trId,
152 strlen(MIME_HEADER) + strlen(send),
153 MIME_HEADER, send);
154
155 g_free(ms->txqueue->data);
156 ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data);
157
158 if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) {
159 msn_kill_switch(ms);
160 return 0;
161 }
162 }
163 } else if (!g_ascii_strncasecmp(buf, "MSG", 3)) {
164 char *user, *tmp = buf;
165 int length;
166
167 GET_NEXT(tmp);
168 user = tmp;
169
170 GET_NEXT(tmp);
171
172 GET_NEXT(tmp);
173 length = atoi(tmp);
174
175 ms->msg = TRUE;
176 ms->msguser = g_strdup(user);
177 ms->msglen = length;
178 } else if (!g_ascii_strncasecmp(buf, "NAK", 3)) {
179 do_error_dialog(_("An MSN message may not have been received."), NULL, GAIM_ERROR);
180 } else if (!g_ascii_strncasecmp(buf, "NLN", 3)) {
181 } else if (!g_ascii_strncasecmp(buf, "OUT", 3)) {
182 if (ms->chat)
183 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat)));
184 msn_kill_switch(ms);
185 return 0;
186 } else if (!g_ascii_strncasecmp(buf, "USR", 3)) {
187 /* good, we got USR, now we need to find out who we want to talk to */
188 struct msn_switchboard *ms = msn_find_writable_switch(gc);
189
190 if (!ms)
191 return 0;
192
193 g_snprintf(sendbuf, sizeof(sendbuf), "CAL %u %s\r\n",
194 ++ms->trId, ms->user);
195
196 if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) {
197 msn_kill_switch(ms);
198 return 0;
199 }
200 } else if (isdigit(*buf)) {
201 handle_errcode(buf, TRUE);
202
203 if (atoi(buf) == 217)
204 msn_kill_switch(ms);
205
206 } else {
207 gaim_debug(GAIM_DEBUG_WARNING, "msn", "Unhandled message!\n");
208 }
209
210 return 1;
211 } 320 }
212 321
213 static void 322 static void
214 msn_process_switch_msg(struct msn_switchboard *ms, char *msg) 323 __failed_read_cb(gpointer data, gint source, GaimInputCondition cond)
215 { 324 {
216 char *content, *agent, *format; 325 MsnServConn *servconn = data;
217 char *message = NULL; 326
218 int flags = 0; 327 msn_switchboard_destroy(servconn->data);
219 328 }
220 agent = strstr(msg, "User-Agent: "); 329
221 if (agent) { 330 MsnSwitchBoard *
222 if (!g_ascii_strncasecmp(agent, "User-Agent: Gaim", 331 msn_switchboard_new(MsnSession *session)
223 strlen("User-Agent: Gaim"))) 332 {
224 flags |= IM_FLAG_GAIMUSER; 333 MsnSwitchBoard *swboard;
225 } 334 MsnServConn *servconn;
226 335
227 format = strstr(msg, "X-MMS-IM-Format: "); 336 g_return_val_if_fail(session != NULL, NULL);
228 if (format) { 337
229 format = msn_parse_format(format); 338 swboard = g_new0(MsnSwitchBoard, 1);
230 } else { 339
231 format = NULL; 340 swboard->servconn = servconn = msn_servconn_new(session);
232 } 341 msn_servconn_set_connect_cb(servconn, __connect_cb);
233 342 msn_servconn_set_failed_read_cb(servconn, __failed_read_cb);
234 content = strstr(msg, "Content-Type: "); 343
235 if (!content) 344 servconn->data = swboard;
236 return; 345
237 if (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgscontrol\r\n", 346 session->switches = g_list_append(session->switches, swboard);
238 strlen( "Content-Type: text/x-msmsgscontrol\r\n"))) { 347
239 if (strstr(content,"TypingUser: ") && !ms->chat) { 348 if (switchboard_commands == NULL) {
240 serv_got_typing(ms->gc, ms->msguser, 349 /* Register the command callbacks. */
241 MSN_TYPING_RECV_TIMEOUT, TYPING); 350 msn_servconn_register_command(servconn, "ACK", __blank_cmd);
242 return; 351 msn_servconn_register_command(servconn, "ANS", __ans_cmd);
243 } 352 msn_servconn_register_command(servconn, "BYE", __bye_cmd);
244 353 msn_servconn_register_command(servconn, "CAL", __blank_cmd);
245 } else if (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;", 354 msn_servconn_register_command(servconn, "IRO", __iro_cmd);
246 strlen("Content-Type: text/x-msmsgsinvite;"))) { 355 msn_servconn_register_command(servconn, "JOI", __joi_cmd);
247 356 msn_servconn_register_command(servconn, "MSG", __msg_cmd);
248 /* 357 msn_servconn_register_command(servconn, "NAK", __nak_cmd);
249 * NOTE: Other things, such as voice communication, would go in 358 msn_servconn_register_command(servconn, "NLN", __blank_cmd);
250 * here too (since they send the same Content-Type). However, 359 msn_servconn_register_command(servconn, "OUT", __out_cmd);
251 * this is the best check for file transfer messages, so I'm 360 msn_servconn_register_command(servconn, "USR", __usr_cmd);
252 * calling msn_process_ft_invite_msg(). If anybody adds support 361 msn_servconn_register_command(servconn, "_unknown_", __unknown_cmd);
253 * for anything else that sends a text/x-msmsgsinvite, perhaps 362
254 * this should be changed. For now, it stays. 363 /* Register the message type callbacks. */
255 */ 364 msn_servconn_register_msg_type(servconn, "text/plain", __plain_msg);
256 msn_process_ft_msg(ms, content); 365 msn_servconn_register_msg_type(servconn, "text/x-msmsgscontrol",
257 366 __control_msg);
258 } else if (!g_ascii_strncasecmp(content, "Content-Type: text/plain", 367
259 strlen("Content-Type: text/plain"))) { 368 /* Save these for future use. */
260 369 switchboard_commands = servconn->commands;
261 char *skiphead = strstr(msg, "\r\n\r\n"); 370 switchboard_msg_types = servconn->msg_types;
262 371 }
263 if (!skiphead || !skiphead[4]) { 372 else {
264 return; 373 g_hash_table_destroy(servconn->commands);
265 } 374 g_hash_table_destroy(servconn->msg_types);
266 375
267 skiphead += 4; 376 servconn->commands = switchboard_commands;
268 strip_linefeed(skiphead); 377 servconn->msg_types = switchboard_msg_types;
269 378 }
270 if (format) { 379
271 message = g_strdup_printf("%s%s", format, skiphead); 380 return swboard;
272 } else { 381 }
273 message = g_strdup(skiphead); 382
274 } 383 void
275 384 msn_switchboard_destroy(MsnSwitchBoard *swboard)
276 if (ms->chat) 385 {
277 serv_got_chat_in(ms->gc, gaim_chat_get_id(GAIM_CHAT(ms->chat)), 386 MsnSession *session;
278 ms->msguser, flags, message, time(NULL)); 387
279 else 388 g_return_if_fail(swboard != NULL);
280 serv_got_im(ms->gc, ms->msguser, message, flags, time(NULL), -1); 389
281 390 session = swboard->servconn->session;
282 g_free(message); 391
283 } 392 if (swboard->servconn->connected)
284 } 393 msn_switchboard_disconnect(swboard);
285 394
286 static void 395 if (swboard->user != NULL)
287 msn_switchboard_callback(gpointer data, gint source, GaimInputCondition cond) 396 msn_user_unref(swboard->user);
288 { 397
289 struct msn_switchboard *ms = data; 398 if (swboard->auth_key != NULL)
399 g_free(swboard->auth_key);
400
401 if (swboard->session_id != NULL)
402 g_free(swboard->session_id);
403
404 session->switches = g_list_remove(session->switches, swboard);
405
406 msn_servconn_destroy(swboard->servconn);
407
408 g_free(swboard);
409 }
410
411 void
412 msn_switchboard_set_user(MsnSwitchBoard *swboard, MsnUser *user)
413 {
414 g_return_if_fail(swboard != NULL);
415
416 swboard->user = user;
417
418 msn_user_ref(user);
419 }
420
421 MsnUser *
422 msn_switchboard_get_user(const MsnSwitchBoard *swboard)
423 {
424 g_return_val_if_fail(swboard != NULL, NULL);
425
426 return swboard->user;
427 }
428
429 void
430 msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key)
431 {
432 g_return_if_fail(swboard != NULL);
433 g_return_if_fail(key != NULL);
434
435 swboard->auth_key = g_strdup(key);
436 }
437
438 const char *
439 msn_switchboard_get_auth_key(const MsnSwitchBoard *swboard)
440 {
441 g_return_val_if_fail(swboard != NULL, NULL);
442
443 return swboard->auth_key;
444 }
445
446 void
447 msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id)
448 {
449 g_return_if_fail(swboard != NULL);
450 g_return_if_fail(id != NULL);
451
452 if (swboard->session_id != NULL)
453 g_free(swboard->session_id);
454
455 swboard->session_id = g_strdup(id);
456 }
457
458 const char *
459 msn_switchboard_get_session_id(const MsnSwitchBoard *swboard)
460 {
461 g_return_val_if_fail(swboard != NULL, NULL);
462
463 return swboard->session_id;
464 }
465
466 void
467 msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited)
468 {
469 g_return_if_fail(swboard != NULL);
470
471 swboard->invited = invited;
472 }
473
474 gboolean
475 msn_switchboard_is_invited(const MsnSwitchBoard *swboard)
476 {
477 g_return_val_if_fail(swboard != NULL, FALSE);
478
479 return swboard->invited;
480 }
481
482 gboolean
483 msn_switchboard_connect(MsnSwitchBoard *swboard, const char *server, int port)
484 {
485 g_return_val_if_fail(swboard != NULL, FALSE);
486
487 msn_servconn_set_server(swboard->servconn, server, port);
488
489 if (msn_servconn_connect(swboard->servconn))
490 swboard->in_use = TRUE;
491
492 return swboard->in_use;
493 }
494
495 void
496 msn_switchboard_disconnect(MsnSwitchBoard *swboard)
497 {
498 g_return_if_fail(swboard != NULL);
499 g_return_if_fail(swboard->servconn->connected);
500
501 msn_servconn_disconnect(swboard->servconn);
502
503 swboard->in_use = FALSE;
504 }
505
506 gboolean
507 msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
508 {
509 char *buf;
510 int ret;
511
512 g_return_val_if_fail(swboard != NULL, FALSE);
513 g_return_val_if_fail(msg != NULL, FALSE);
514
515 msn_message_set_transaction_id(msg, ++swboard->trId);
516 buf = msn_message_build_string(msg);
517
518 if (swboard->servconn->txqueue != NULL || !swboard->in_use) {
519 gaim_debug(GAIM_DEBUG_INFO, "msn", "Appending message to queue.\n");
520
521 swboard->servconn->txqueue =
522 g_slist_append(swboard->servconn->txqueue, buf);
523
524 return TRUE;
525 }
526
527 ret = msn_servconn_write(swboard->servconn, buf, strlen(buf));
528
529 g_free(buf);
530
531 return (ret > 0);
532 }
533
534 gboolean
535 msn_switchboard_send_command(MsnSwitchBoard *swboard, const char *command,
536 const char *params)
537 {
290 char buf[MSN_BUF_LEN]; 538 char buf[MSN_BUF_LEN];
291 int cont = 1; 539
292 int len; 540 g_return_val_if_fail(swboard != NULL, FALSE);
293 541 g_return_val_if_fail(command != NULL, FALSE);
294 ms->fd = source; 542
295 len = read(ms->fd, buf, sizeof(buf)); 543 if (params == NULL)
296 if (len <= 0) { 544 g_snprintf(buf, sizeof(buf), "%s %u\r\n", command,
297 msn_kill_switch(ms); 545 ++swboard->trId);
298 return; 546 else
299 } 547 g_snprintf(buf, sizeof(buf), "%s %u %s\r\n",
300 548 command, ++swboard->trId, params);
301 ms->rxqueue = g_realloc(ms->rxqueue, len + ms->rxlen); 549
302 memcpy(ms->rxqueue + ms->rxlen, buf, len); 550 return (msn_servconn_write(swboard->servconn, buf, strlen(buf)) > 0);
303 ms->rxlen += len; 551 }
304
305 while (cont) {
306 if (!ms->rxlen)
307 return;
308
309 if (ms->msg) {
310 char *msg;
311 if (ms->msglen > ms->rxlen)
312 return;
313 msg = ms->rxqueue;
314 ms->rxlen -= ms->msglen;
315 if (ms->rxlen) {
316 ms->rxqueue = g_memdup(msg + ms->msglen, ms->rxlen);
317 } else {
318 ms->rxqueue = NULL;
319 msg = g_realloc(msg, ms->msglen + 1);
320 }
321 msg[ms->msglen] = 0;
322 ms->msglen = 0;
323 ms->msg = FALSE;
324
325 msn_process_switch_msg(ms, msg);
326
327 g_free(ms->msguser);
328 g_free(msg);
329 } else {
330 char *end = ms->rxqueue;
331 int cmdlen;
332 char *cmd;
333 int i = 0;
334
335 while (i + 1 < ms->rxlen) {
336 if (*end == '\r' && end[1] == '\n')
337 break;
338 end++; i++;
339 }
340 if (i + 1 == ms->rxlen)
341 return;
342
343 cmdlen = end - ms->rxqueue + 2;
344 cmd = ms->rxqueue;
345 ms->rxlen -= cmdlen;
346 if (ms->rxlen) {
347 ms->rxqueue = g_memdup(cmd + cmdlen, ms->rxlen);
348 } else {
349 ms->rxqueue = NULL;
350 cmd = g_realloc(cmd, cmdlen + 1);
351 }
352 cmd[cmdlen] = 0;
353
354 gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd);
355 g_strchomp(cmd);
356 cont = msn_process_switch(ms, cmd);
357
358 g_free(cmd);
359 }
360 }
361 }
362
363 void
364 msn_rng_connect(gpointer data, gint source, GaimInputCondition cond)
365 {
366 struct msn_switchboard *ms = data;
367 struct gaim_connection *gc = ms->gc;
368 struct msn_data *md;
369 char buf[MSN_BUF_LEN];
370
371 if (source == -1 || !g_slist_find(connections, gc)) {
372 close(source);
373 g_free(ms->sessid);
374 g_free(ms->auth);
375 g_free(ms);
376 return;
377 }
378
379 md = gc->proto_data;
380
381 if (ms->fd != source)
382 ms->fd = source;
383
384 g_snprintf(buf, sizeof(buf), "ANS %u %s %s %s\r\n", ++ms->trId, gc->username, ms->auth, ms->sessid);
385 if (msn_write(ms->fd, buf, strlen(buf)) < 0) {
386 close(ms->fd);
387 g_free(ms->sessid);
388 g_free(ms->auth);
389 g_free(ms);
390 return;
391 }
392
393 md->switches = g_slist_append(md->switches, ms);
394 ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ,
395 msn_switchboard_callback, ms);
396 }
397
398 static void
399 msn_ss_xfr_connect(gpointer data, gint source, GaimInputCondition cond)
400 {
401 struct msn_switchboard *ms = data;
402 struct gaim_connection *gc = ms->gc;
403 char buf[MSN_BUF_LEN];
404
405 if (source == -1 || !g_slist_find(connections, gc)) {
406 close(source);
407 if (g_slist_find(connections, gc)) {
408 msn_kill_switch(ms);
409 do_error_dialog(_("Gaim was unable to send an MSN message"),
410 _("Gaim encountered an error communicating with the "
411 "MSN switchboard server. Please try again later."),
412 GAIM_ERROR);
413 }
414
415 return;
416 }
417
418 if (ms->fd != source)
419 ms->fd = source;
420
421 g_snprintf(buf, sizeof(buf), "USR %u %s %s\r\n",
422 ++ms->trId, gc->username, ms->auth);
423
424 if (msn_write(ms->fd, buf, strlen(buf)) < 0) {
425 g_free(ms->auth);
426 g_free(ms);
427 return;
428 }
429
430 ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ,
431 msn_switchboard_callback, ms);
432 }
433
434 struct msn_switchboard *
435 msn_find_switch(struct gaim_connection *gc, const char *username)
436 {
437 struct msn_data *md = (struct msn_data *)gc->proto_data;
438 GSList *m = md->switches;
439
440 for (m = md->switches; m != NULL; m = m->next) {
441 struct msn_switchboard *ms = (struct msn_switchboard *)m->data;
442
443 if (ms->total <= 1 && !gaim_utf8_strcasecmp(ms->user, username))
444 return ms;
445 }
446
447 return NULL;
448 }
449
450 struct msn_switchboard *
451 msn_find_switch_by_id(struct gaim_connection *gc, int chat_id)
452 {
453 struct msn_data *md = (struct msn_data *)gc->proto_data;
454 GSList *m;
455
456 for (m = md->switches; m != NULL; m = m->next) {
457 struct msn_switchboard *ms = (struct msn_switchboard *)m->data;
458
459 if (ms->chat && gaim_chat_get_id(GAIM_CHAT(ms->chat)) == chat_id)
460 return ms;
461 }
462
463 return NULL;
464 }
465
466 struct msn_switchboard *
467 msn_find_writable_switch(struct gaim_connection *gc)
468 {
469 struct msn_data *md = (struct msn_data *)gc->proto_data;
470 GSList *m;
471
472 for (m = md->switches; m != NULL; m = m->next) {
473 struct msn_switchboard *ms = (struct msn_switchboard *)m->data;
474
475 if (ms->txqueue != NULL)
476 return ms;
477 }
478
479 return NULL;
480 }
481
482 void
483 msn_kill_switch(struct msn_switchboard *ms)
484 {
485 struct gaim_connection *gc = ms->gc;
486 struct msn_data *md = gc->proto_data;
487
488 if (ms->inpa)
489 gaim_input_remove(ms->inpa);
490
491 close(ms->fd);
492 g_free(ms->rxqueue);
493
494 if (ms->msg) g_free(ms->msguser);
495 if (ms->user) g_free(ms->user);
496 if (ms->sessid) g_free(ms->sessid);
497
498 g_free(ms->auth);
499
500 while (ms->txqueue) {
501 g_free(ms->txqueue->data);
502 ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data);
503 }
504
505 if (ms->chat)
506 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat)));
507
508 md->switches = g_slist_remove(md->switches, ms);
509
510 g_free(ms);
511 }
512
513 struct msn_switchboard *
514 msn_switchboard_connect(struct gaim_connection *gc, const char *host, int port)
515 {
516 struct msn_switchboard *ms;
517
518 if (host == NULL || port == 0)
519 return NULL;
520
521 ms = msn_find_writable_switch(gc);
522
523 if (ms == NULL)
524 return NULL;
525
526 if (proxy_connect(gc->account, (char *)host, port, msn_ss_xfr_connect,
527 ms) != 0) {
528 msn_kill_switch(ms);
529
530 return NULL;
531 }
532
533 return ms;
534 }