Mercurial > pidgin
comparison src/protocols/msn/switchboard.c @ 4542:86b0a0243be8
[gaim-migrate @ 4821]
Split up the MSN module a bit and added file receive support.
Caution: I lost some data in a large file that was sent to me. Don't rely
on this yet. I'll get it all fixed tomorrow.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Thu, 06 Feb 2003 10:13:18 +0000 |
parents | |
children | a951bb590857 |
comparison
equal
deleted
inserted
replaced
4541:0626ec2f2feb | 4542:86b0a0243be8 |
---|---|
1 /** | |
2 * @file switchboard.c MSN switchboard functions | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
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 | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
23 #include "msn.h" | |
24 | |
25 static char * | |
26 msn_parse_format(char *mime) | |
27 { | |
28 char *cur; | |
29 GString *ret = g_string_new(NULL); | |
30 guint colorbuf; | |
31 char *colors = (char *)(&colorbuf); | |
32 | |
33 | |
34 cur = strstr(mime, "FN="); | |
35 if (cur && (*(cur = cur + 3) != ';')) { | |
36 ret = g_string_append(ret, "<FONT FACE=\""); | |
37 while (*cur && *cur != ';') { | |
38 ret = g_string_append_c(ret, *cur); | |
39 cur++; | |
40 } | |
41 ret = g_string_append(ret, "\">"); | |
42 } | |
43 | |
44 cur = strstr(mime, "EF="); | |
45 if (cur && (*(cur = cur + 3) != ';')) { | |
46 while (*cur && *cur != ';') { | |
47 ret = g_string_append_c(ret, '<'); | |
48 ret = g_string_append_c(ret, *cur); | |
49 ret = g_string_append_c(ret, '>'); | |
50 cur++; | |
51 } | |
52 } | |
53 | |
54 cur = strstr(mime, "CO="); | |
55 if (cur && (*(cur = cur + 3) != ';')) { | |
56 if (sscanf (cur, "%x;", &colorbuf) == 1) { | |
57 char tag[64]; | |
58 g_snprintf(tag, sizeof(tag), "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", colors[0], colors[1], colors[2]); | |
59 ret = g_string_append(ret, tag); | |
60 } | |
61 } | |
62 | |
63 cur = url_decode(ret->str); | |
64 g_string_free(ret, TRUE); | |
65 return cur; | |
66 } | |
67 | |
68 static int | |
69 msn_process_switch(struct msn_switchboard *ms, char *buf) | |
70 { | |
71 struct gaim_connection *gc = ms->gc; | |
72 char sendbuf[MSN_BUF_LEN]; | |
73 static int id = 0; | |
74 | |
75 if (!g_strncasecmp(buf, "ACK", 3)) { | |
76 } else if (!g_strncasecmp(buf, "ANS", 3)) { | |
77 if (ms->chat) | |
78 gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); | |
79 } else if (!g_strncasecmp(buf, "BYE", 3)) { | |
80 char *user, *tmp = buf; | |
81 GET_NEXT(tmp); | |
82 user = tmp; | |
83 | |
84 if (ms->chat) { | |
85 gaim_chat_remove_user(GAIM_CHAT(ms->chat), user, NULL); | |
86 } else { | |
87 char msgbuf[256]; | |
88 const char *username; | |
89 struct gaim_conversation *cnv; | |
90 struct buddy *b; | |
91 | |
92 if ((b = find_buddy(gc->account, user)) != NULL) | |
93 username = get_buddy_alias(b); | |
94 else | |
95 username = user; | |
96 | |
97 g_snprintf(msgbuf, sizeof(msgbuf), | |
98 _("%s has closed the conversation window"), username); | |
99 | |
100 if ((cnv = gaim_find_conversation(user))) | |
101 gaim_conversation_write(cnv, NULL, msgbuf, -1, | |
102 WFLAG_SYSTEM, time(NULL)); | |
103 | |
104 msn_kill_switch(ms); | |
105 return 0; | |
106 } | |
107 } else if (!g_strncasecmp(buf, "CAL", 3)) { | |
108 } else if (!g_strncasecmp(buf, "IRO", 3)) { | |
109 char *tot, *user, *tmp = buf; | |
110 | |
111 GET_NEXT(tmp); | |
112 GET_NEXT(tmp); | |
113 GET_NEXT(tmp); | |
114 tot = tmp; | |
115 GET_NEXT(tmp); | |
116 ms->total = atoi(tot); | |
117 user = tmp; | |
118 GET_NEXT(tmp); | |
119 | |
120 if (ms->total > 1) { | |
121 if (!ms->chat) | |
122 ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat"); | |
123 | |
124 gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL); | |
125 } | |
126 } else if (!g_strncasecmp(buf, "JOI", 3)) { | |
127 char *user, *tmp = buf; | |
128 GET_NEXT(tmp); | |
129 user = tmp; | |
130 GET_NEXT(tmp); | |
131 | |
132 if (ms->total == 1) { | |
133 ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat"); | |
134 gaim_chat_add_user(GAIM_CHAT(ms->chat), ms->user, NULL); | |
135 gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); | |
136 g_free(ms->user); | |
137 ms->user = NULL; | |
138 } | |
139 if (ms->chat) | |
140 gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL); | |
141 ms->total++; | |
142 while (ms->txqueue) { | |
143 char *send = add_cr(ms->txqueue->data); | |
144 g_snprintf(sendbuf, sizeof(sendbuf), | |
145 "MSG %u N %d\r\n%s%s", ++ms->trId, | |
146 strlen(MIME_HEADER) + strlen(send), | |
147 MIME_HEADER, send); | |
148 | |
149 g_free(ms->txqueue->data); | |
150 ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); | |
151 | |
152 if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { | |
153 msn_kill_switch(ms); | |
154 return 0; | |
155 } | |
156 | |
157 debug_printf("\n"); | |
158 } | |
159 } else if (!g_strncasecmp(buf, "MSG", 3)) { | |
160 char *user, *tmp = buf; | |
161 int length; | |
162 | |
163 GET_NEXT(tmp); | |
164 user = tmp; | |
165 | |
166 GET_NEXT(tmp); | |
167 | |
168 GET_NEXT(tmp); | |
169 length = atoi(tmp); | |
170 | |
171 ms->msg = TRUE; | |
172 ms->msguser = g_strdup(user); | |
173 ms->msglen = length; | |
174 } else if (!g_strncasecmp(buf, "NAK", 3)) { | |
175 do_error_dialog(_("An MSN message may not have been received."), NULL, GAIM_ERROR); | |
176 } else if (!g_strncasecmp(buf, "NLN", 3)) { | |
177 } else if (!g_strncasecmp(buf, "OUT", 3)) { | |
178 if (ms->chat) | |
179 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); | |
180 msn_kill_switch(ms); | |
181 return 0; | |
182 } else if (!g_strncasecmp(buf, "USR", 3)) { | |
183 /* good, we got USR, now we need to find out who we want to talk to */ | |
184 struct msn_switchboard *ms = msn_find_writable_switch(gc); | |
185 | |
186 if (!ms) | |
187 return 0; | |
188 | |
189 g_snprintf(sendbuf, sizeof(sendbuf), "CAL %u %s\r\n", | |
190 ++ms->trId, ms->user); | |
191 | |
192 if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { | |
193 msn_kill_switch(ms); | |
194 return 0; | |
195 } | |
196 } else if (isdigit(*buf)) { | |
197 handle_errcode(buf, TRUE); | |
198 | |
199 if (atoi(buf) == 217) | |
200 msn_kill_switch(ms); | |
201 | |
202 } else { | |
203 debug_printf("Unhandled message!\n"); | |
204 } | |
205 | |
206 return 1; | |
207 } | |
208 | |
209 static void | |
210 msn_process_switch_msg(struct msn_switchboard *ms, char *msg) | |
211 { | |
212 char *content, *agent, *format; | |
213 char *message = NULL; | |
214 int flags = 0; | |
215 | |
216 agent = strstr(msg, "User-Agent: "); | |
217 if (agent) { | |
218 if (!g_strncasecmp(agent, "User-Agent: Gaim", | |
219 strlen("User-Agent: Gaim"))) | |
220 flags |= IM_FLAG_GAIMUSER; | |
221 } | |
222 | |
223 format = strstr(msg, "X-MMS-IM-Format: "); | |
224 if (format) { | |
225 format = msn_parse_format(format); | |
226 } else { | |
227 format = NULL; | |
228 } | |
229 | |
230 content = strstr(msg, "Content-Type: "); | |
231 if (!content) | |
232 return; | |
233 if (!g_strncasecmp(content, "Content-Type: text/x-msmsgscontrol\r\n", | |
234 strlen( "Content-Type: text/x-msmsgscontrol\r\n"))) { | |
235 if (strstr(content,"TypingUser: ") && !ms->chat) { | |
236 serv_got_typing(ms->gc, ms->msguser, | |
237 MSN_TYPING_RECV_TIMEOUT, TYPING); | |
238 return; | |
239 } | |
240 | |
241 } else if (!g_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;", | |
242 strlen("Content-Type: text/x-msmsgsinvite;"))) { | |
243 | |
244 /* | |
245 * NOTE: Other things, such as voice communication, would go in | |
246 * here too (since they send the same Content-Type). However, | |
247 * this is the best check for file transfer messages, so I'm | |
248 * calling msn_process_ft_invite_msg(). If anybody adds support | |
249 * for anything else that sends a text/x-msmsgsinvite, perhaps | |
250 * this should be changed. For now, it stays. | |
251 */ | |
252 msn_process_ft_msg(ms, content); | |
253 | |
254 } else if (!g_strncasecmp(content, "Content-Type: text/plain", | |
255 strlen("Content-Type: text/plain"))) { | |
256 | |
257 char *skiphead = strstr(msg, "\r\n\r\n"); | |
258 | |
259 if (!skiphead || !skiphead[4]) { | |
260 return; | |
261 } | |
262 | |
263 skiphead += 4; | |
264 strip_linefeed(skiphead); | |
265 | |
266 if (format) { | |
267 message = g_strdup_printf("%s%s", format, skiphead); | |
268 } else { | |
269 message = g_strdup(skiphead); | |
270 } | |
271 | |
272 if (ms->chat) | |
273 serv_got_chat_in(ms->gc, gaim_chat_get_id(GAIM_CHAT(ms->chat)), | |
274 ms->msguser, flags, message, time(NULL)); | |
275 else | |
276 serv_got_im(ms->gc, ms->msguser, message, flags, time(NULL), -1); | |
277 | |
278 g_free(message); | |
279 } | |
280 } | |
281 | |
282 static void | |
283 msn_switchboard_callback(gpointer data, gint source, GaimInputCondition cond) | |
284 { | |
285 struct msn_switchboard *ms = data; | |
286 char buf[MSN_BUF_LEN]; | |
287 int cont = 1; | |
288 int len; | |
289 | |
290 ms->fd = source; | |
291 len = read(ms->fd, buf, sizeof(buf)); | |
292 if (len <= 0) { | |
293 msn_kill_switch(ms); | |
294 return; | |
295 } | |
296 | |
297 ms->rxqueue = g_realloc(ms->rxqueue, len + ms->rxlen); | |
298 memcpy(ms->rxqueue + ms->rxlen, buf, len); | |
299 ms->rxlen += len; | |
300 | |
301 while (cont) { | |
302 if (!ms->rxlen) | |
303 return; | |
304 | |
305 if (ms->msg) { | |
306 char *msg; | |
307 if (ms->msglen > ms->rxlen) | |
308 return; | |
309 msg = ms->rxqueue; | |
310 ms->rxlen -= ms->msglen; | |
311 if (ms->rxlen) { | |
312 ms->rxqueue = g_memdup(msg + ms->msglen, ms->rxlen); | |
313 } else { | |
314 ms->rxqueue = NULL; | |
315 msg = g_realloc(msg, ms->msglen + 1); | |
316 } | |
317 msg[ms->msglen] = 0; | |
318 ms->msglen = 0; | |
319 ms->msg = FALSE; | |
320 | |
321 msn_process_switch_msg(ms, msg); | |
322 | |
323 g_free(ms->msguser); | |
324 g_free(msg); | |
325 } else { | |
326 char *end = ms->rxqueue; | |
327 int cmdlen; | |
328 char *cmd; | |
329 int i = 0; | |
330 | |
331 while (i + 1 < ms->rxlen) { | |
332 if (*end == '\r' && end[1] == '\n') | |
333 break; | |
334 end++; i++; | |
335 } | |
336 if (i + 1 == ms->rxlen) | |
337 return; | |
338 | |
339 cmdlen = end - ms->rxqueue + 2; | |
340 cmd = ms->rxqueue; | |
341 ms->rxlen -= cmdlen; | |
342 if (ms->rxlen) { | |
343 ms->rxqueue = g_memdup(cmd + cmdlen, ms->rxlen); | |
344 } else { | |
345 ms->rxqueue = NULL; | |
346 cmd = g_realloc(cmd, cmdlen + 1); | |
347 } | |
348 cmd[cmdlen] = 0; | |
349 | |
350 debug_printf("MSN S: %s", cmd); | |
351 g_strchomp(cmd); | |
352 cont = msn_process_switch(ms, cmd); | |
353 | |
354 g_free(cmd); | |
355 } | |
356 } | |
357 } | |
358 | |
359 void | |
360 msn_rng_connect(gpointer data, gint source, GaimInputCondition cond) | |
361 { | |
362 struct msn_switchboard *ms = data; | |
363 struct gaim_connection *gc = ms->gc; | |
364 struct msn_data *md; | |
365 char buf[MSN_BUF_LEN]; | |
366 | |
367 if (source == -1 || !g_slist_find(connections, gc)) { | |
368 close(source); | |
369 g_free(ms->sessid); | |
370 g_free(ms->auth); | |
371 g_free(ms); | |
372 return; | |
373 } | |
374 | |
375 md = gc->proto_data; | |
376 | |
377 if (ms->fd != source) | |
378 ms->fd = source; | |
379 | |
380 g_snprintf(buf, sizeof(buf), "ANS %u %s %s %s\r\n", ++ms->trId, gc->username, ms->auth, ms->sessid); | |
381 if (msn_write(ms->fd, buf, strlen(buf)) < 0) { | |
382 close(ms->fd); | |
383 g_free(ms->sessid); | |
384 g_free(ms->auth); | |
385 g_free(ms); | |
386 return; | |
387 } | |
388 | |
389 md->switches = g_slist_append(md->switches, ms); | |
390 ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, | |
391 msn_switchboard_callback, ms); | |
392 } | |
393 | |
394 static void | |
395 msn_ss_xfr_connect(gpointer data, gint source, GaimInputCondition cond) | |
396 { | |
397 struct msn_switchboard *ms = data; | |
398 struct gaim_connection *gc = ms->gc; | |
399 char buf[MSN_BUF_LEN]; | |
400 | |
401 if (source == -1 || !g_slist_find(connections, gc)) { | |
402 close(source); | |
403 if (g_slist_find(connections, gc)) { | |
404 msn_kill_switch(ms); | |
405 do_error_dialog(_("Gaim was unable to send an MSN message"), | |
406 _("Gaim encountered an error communicating with the " | |
407 "MSN switchboard server. Please try again later."), | |
408 GAIM_ERROR); | |
409 } | |
410 | |
411 return; | |
412 } | |
413 | |
414 if (ms->fd != source) | |
415 ms->fd = source; | |
416 | |
417 g_snprintf(buf, sizeof(buf), "USR %u %s %s\r\n", | |
418 ++ms->trId, gc->username, ms->auth); | |
419 | |
420 if (msn_write(ms->fd, buf, strlen(buf)) < 0) { | |
421 g_free(ms->auth); | |
422 g_free(ms); | |
423 return; | |
424 } | |
425 | |
426 ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, | |
427 msn_switchboard_callback, ms); | |
428 } | |
429 | |
430 struct msn_switchboard * | |
431 msn_find_switch(struct gaim_connection *gc, const char *username) | |
432 { | |
433 struct msn_data *md = (struct msn_data *)gc->proto_data; | |
434 GSList *m = md->switches; | |
435 | |
436 for (m = md->switches; m != NULL; m = m->next) { | |
437 struct msn_switchboard *ms = (struct msn_switchboard *)m->data; | |
438 | |
439 if (ms->total <= 1 && !g_strcasecmp(ms->user, username)) | |
440 return ms; | |
441 } | |
442 | |
443 return NULL; | |
444 } | |
445 | |
446 struct msn_switchboard * | |
447 msn_find_switch_by_id(struct gaim_connection *gc, int chat_id) | |
448 { | |
449 struct msn_data *md = (struct msn_data *)gc->proto_data; | |
450 GSList *m; | |
451 | |
452 for (m = md->switches; m != NULL; m = m->next) { | |
453 struct msn_switchboard *ms = (struct msn_switchboard *)m->data; | |
454 | |
455 if (ms->chat && gaim_chat_get_id(GAIM_CHAT(ms->chat)) == chat_id) | |
456 return ms; | |
457 } | |
458 | |
459 return NULL; | |
460 } | |
461 | |
462 struct msn_switchboard * | |
463 msn_find_writable_switch(struct gaim_connection *gc) | |
464 { | |
465 struct msn_data *md = (struct msn_data *)gc->proto_data; | |
466 GSList *m; | |
467 | |
468 for (m = md->switches; m != NULL; m = m->next) { | |
469 struct msn_switchboard *ms = (struct msn_switchboard *)m->data; | |
470 | |
471 if (ms->txqueue != NULL) | |
472 return ms; | |
473 } | |
474 | |
475 return NULL; | |
476 } | |
477 | |
478 void | |
479 msn_kill_switch(struct msn_switchboard *ms) | |
480 { | |
481 struct gaim_connection *gc = ms->gc; | |
482 struct msn_data *md = gc->proto_data; | |
483 | |
484 if (ms->inpa) | |
485 gaim_input_remove(ms->inpa); | |
486 | |
487 close(ms->fd); | |
488 g_free(ms->rxqueue); | |
489 | |
490 if (ms->msg) g_free(ms->msguser); | |
491 if (ms->user) g_free(ms->user); | |
492 if (ms->sessid) g_free(ms->sessid); | |
493 | |
494 g_free(ms->auth); | |
495 | |
496 while (ms->txqueue) { | |
497 g_free(ms->txqueue->data); | |
498 ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); | |
499 } | |
500 | |
501 if (ms->chat) | |
502 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); | |
503 | |
504 md->switches = g_slist_remove(md->switches, ms); | |
505 | |
506 g_free(ms); | |
507 } | |
508 | |
509 struct msn_switchboard * | |
510 msn_switchboard_connect(struct gaim_connection *gc, const char *host, int port) | |
511 { | |
512 struct msn_switchboard *ms; | |
513 | |
514 if (host == NULL || port == 0) | |
515 return NULL; | |
516 | |
517 ms = msn_find_writable_switch(gc); | |
518 | |
519 if (ms == NULL) | |
520 return NULL; | |
521 | |
522 if (proxy_connect((char *)host, port, msn_ss_xfr_connect, ms) != 0) { | |
523 msn_kill_switch(ms); | |
524 | |
525 return NULL; | |
526 } | |
527 | |
528 return ms; | |
529 } |