Mercurial > pidgin
comparison src/protocols/msn/servconn.c @ 8646:1e211dde3cae
[gaim-migrate @ 9398]
Added a patch by shx to clean up the message-handling code and split the
command stuff from it, among a few other things. Also, I fixed a crash in
message parsing, which I think may close a couple bug reports.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Tue, 13 Apr 2004 04:08:22 +0000 |
parents | fc27237783ee |
children | c47a90a0a3b1 |
comparison
equal
deleted
inserted
replaced
8645:47f647dd2ac3 | 8646:1e211dde3cae |
---|---|
30 char *command; | 30 char *command; |
31 MsnMessage *msg; | 31 MsnMessage *msg; |
32 | 32 |
33 } MsnQueueEntry; | 33 } MsnQueueEntry; |
34 | 34 |
35 static gboolean | 35 gboolean |
36 process_message(MsnServConn *servconn, MsnMessage *msg) | 36 msn_servconn_process_message(MsnServConn *servconn, MsnMessage *msg) |
37 { | 37 { |
38 MsnServConnMsgCb cb; | 38 MsnServConnMsgCb cb; |
39 | 39 |
40 cb = g_hash_table_lookup(servconn->msg_types, | 40 cb = g_hash_table_lookup(servconn->msg_types, |
41 msn_message_get_content_type(msg)); | 41 msn_message_get_content_type(msg)); |
42 | 42 |
43 if (cb == NULL) { | 43 if (cb == NULL) |
44 { | |
44 gaim_debug(GAIM_DEBUG_WARNING, "msn", | 45 gaim_debug(GAIM_DEBUG_WARNING, "msn", |
45 "Unhandled content-type '%s': %s\n", | 46 "Unhandled content-type '%s': %s\n", |
46 msn_message_get_content_type(msg), | 47 msn_message_get_content_type(msg), |
47 msn_message_get_body(msg)); | 48 msn_message_get_body(msg)); |
48 | 49 |
52 cb(servconn, msg); | 53 cb(servconn, msg); |
53 | 54 |
54 return TRUE; | 55 return TRUE; |
55 } | 56 } |
56 | 57 |
57 static gboolean | |
58 process_single_line(MsnServConn *servconn, char *str) | |
59 { | |
60 MsnSession *session = servconn->session; | |
61 MsnServConnCommandCb cb; | |
62 GSList *l, *l_next = NULL; | |
63 gboolean result; | |
64 size_t param_count = 0; | |
65 char *command, *param_start; | |
66 char **params = NULL; | |
67 | |
68 command = str; | |
69 | |
70 /** | |
71 * See how many spaces we have in this. | |
72 */ | |
73 param_start = strchr(command, ' '); | |
74 | |
75 if (param_start != NULL) { | |
76 params = g_strsplit(param_start + 1, " ", 0); | |
77 | |
78 for (param_count = 0; params[param_count] != NULL; param_count++) | |
79 ; | |
80 | |
81 *param_start = '\0'; | |
82 } | |
83 | |
84 cb = g_hash_table_lookup(servconn->commands, command); | |
85 | |
86 if (cb == NULL) { | |
87 cb = g_hash_table_lookup(servconn->commands, "_unknown_"); | |
88 | |
89 if (cb == NULL) { | |
90 if (isdigit(*str)) | |
91 msn_error_handle(session, atoi(str)); | |
92 else | |
93 gaim_debug(GAIM_DEBUG_WARNING, "msn", | |
94 "Unhandled command '%s'\n", str); | |
95 | |
96 if (params != NULL) | |
97 g_strfreev(params); | |
98 | |
99 return TRUE; | |
100 } | |
101 } | |
102 | |
103 result = cb(servconn, command, (const char **)params, param_count); | |
104 | |
105 if (params != NULL) | |
106 g_strfreev(params); | |
107 | |
108 if (g_list_find(session->servconns, servconn) == NULL) | |
109 return result; | |
110 | |
111 /* Process all queued messages that are waiting on this command. */ | |
112 for (l = servconn->msg_queue; l != NULL; l = l_next) { | |
113 MsnQueueEntry *entry = l->data; | |
114 MsnMessage *msg; | |
115 | |
116 l_next = l->next; | |
117 | |
118 if (entry->command == NULL || | |
119 !g_ascii_strcasecmp(entry->command, command)) { | |
120 | |
121 MsnUser *sender; | |
122 | |
123 msg = entry->msg; | |
124 | |
125 msn_message_ref(msg); | |
126 | |
127 sender = msn_message_get_sender(msg); | |
128 | |
129 servconn->msg_passport = g_strdup(msn_user_get_passport(sender)); | |
130 servconn->msg_friendly = g_strdup(msn_user_get_name(sender)); | |
131 | |
132 process_message(servconn, msg); | |
133 | |
134 g_free(servconn->msg_passport); | |
135 g_free(servconn->msg_friendly); | |
136 | |
137 msn_servconn_unqueue_message(servconn, entry->msg); | |
138 | |
139 msn_message_destroy(msg); | |
140 entry->msg = NULL; | |
141 } | |
142 } | |
143 | |
144 return result; | |
145 } | |
146 | |
147 static gboolean | |
148 process_multi_line(MsnServConn *servconn, char *buffer) | |
149 { | |
150 char msg_str[MSN_BUF_LEN]; | |
151 gboolean result = TRUE; | |
152 | |
153 if (servconn->multiline_type == MSN_MULTILINE_MSG) { | |
154 MsnMessage *msg; | |
155 size_t header_len; | |
156 | |
157 g_snprintf(msg_str, sizeof(msg_str), | |
158 "MSG %s %s %d\r\n", | |
159 servconn->msg_passport, servconn->msg_friendly, | |
160 servconn->multiline_len); | |
161 | |
162 header_len = strlen(msg_str); | |
163 | |
164 memcpy(msg_str + header_len, buffer, servconn->multiline_len); | |
165 | |
166 gaim_debug(GAIM_DEBUG_MISC, "msn", | |
167 "Message: {%s}\n", buffer); | |
168 | |
169 msg = msn_message_new_from_str(servconn->session, msg_str); | |
170 | |
171 result = process_message(servconn, msg); | |
172 | |
173 msn_message_destroy(msg); | |
174 } | |
175 else if (servconn->multiline_type == MSN_MULTILINE_IPG) { | |
176 g_snprintf(msg_str, sizeof(msg_str), | |
177 "IPG %d\r\n%s", | |
178 servconn->multiline_len, buffer); | |
179 | |
180 gaim_debug(GAIM_DEBUG_MISC, "msn", | |
181 "Incoming Page: {%s}\n", buffer); | |
182 } | |
183 else if (servconn->multiline_type == MSN_MULTILINE_NOT) { | |
184 g_snprintf(msg_str, sizeof(msg_str), | |
185 "NOT %d\r\n%s", | |
186 servconn->multiline_len, buffer); | |
187 | |
188 gaim_debug(GAIM_DEBUG_MISC, "msn", | |
189 "Notification: {%s}\n", buffer); | |
190 } | |
191 | |
192 return result; | |
193 } | |
194 | |
195 static void | 58 static void |
196 connect_cb(gpointer data, gint source, GaimInputCondition cond) | 59 connect_cb(gpointer data, gint source, GaimInputCondition cond) |
197 { | 60 { |
198 MsnServConn *servconn = data; | 61 MsnServConn *servconn = data; |
199 | 62 |
282 g_return_if_fail(servconn != NULL); | 145 g_return_if_fail(servconn != NULL); |
283 g_return_if_fail(servconn->connected); | 146 g_return_if_fail(servconn->connected); |
284 | 147 |
285 session = servconn->session; | 148 session = servconn->session; |
286 | 149 |
287 if (servconn->inpa) | 150 if (servconn->inpa > 0) |
151 { | |
288 gaim_input_remove(servconn->inpa); | 152 gaim_input_remove(servconn->inpa); |
153 servconn->inpa = 0; | |
154 } | |
289 | 155 |
290 close(servconn->fd); | 156 close(servconn->fd); |
291 | |
292 servconn->connected = FALSE; | |
293 | 157 |
294 if (servconn->http_data != NULL) | 158 if (servconn->http_data != NULL) |
295 { | 159 { |
296 if (servconn->http_data->session_id != NULL) | 160 if (servconn->http_data->session_id != NULL) |
297 g_free(servconn->http_data->session_id); | 161 g_free(servconn->http_data->session_id); |
306 gaim_timeout_remove(servconn->http_data->timer); | 170 gaim_timeout_remove(servconn->http_data->timer); |
307 | 171 |
308 g_free(servconn->http_data); | 172 g_free(servconn->http_data); |
309 } | 173 } |
310 | 174 |
311 if (servconn->rxqueue != NULL) | 175 servconn->rx_len = 0; |
312 g_free(servconn->rxqueue); | 176 servconn->payload_len = 0; |
313 | 177 |
314 while (servconn->txqueue != NULL) { | 178 while (servconn->txqueue != NULL) { |
315 g_free(servconn->txqueue->data); | 179 g_free(servconn->txqueue->data); |
316 | 180 |
317 servconn->txqueue = g_slist_remove(servconn->txqueue, | 181 servconn->txqueue = g_slist_remove(servconn->txqueue, |
322 MsnQueueEntry *entry = servconn->msg_queue->data; | 186 MsnQueueEntry *entry = servconn->msg_queue->data; |
323 | 187 |
324 msn_servconn_unqueue_message(servconn, entry->msg); | 188 msn_servconn_unqueue_message(servconn, entry->msg); |
325 } | 189 } |
326 | 190 |
327 if (servconn->disconnect_cb) | 191 if (servconn->disconnect_cb != NULL) |
328 servconn->disconnect_cb(servconn); | 192 servconn->disconnect_cb(servconn); |
193 | |
194 servconn->connected = FALSE; | |
329 } | 195 } |
330 | 196 |
331 void | 197 void |
332 msn_servconn_destroy(MsnServConn *servconn) | 198 msn_servconn_destroy(MsnServConn *servconn) |
333 { | 199 { |
524 | 390 |
525 msn_servconn_disconnect(servconn); | 391 msn_servconn_disconnect(servconn); |
526 } | 392 } |
527 | 393 |
528 static void | 394 static void |
395 process_payload(MsnServConn *servconn, char *payload, int payload_len) | |
396 { | |
397 g_return_if_fail(servconn != NULL); | |
398 g_return_if_fail(servconn->payload_cb != NULL); | |
399 | |
400 servconn->payload_cb(servconn, payload, payload_len); | |
401 } | |
402 | |
403 static int | |
404 process_cmd_text(MsnServConn *servconn, const char *command) | |
405 { | |
406 MsnSession *session = servconn->session; | |
407 MsnServConnCommandCb cb; | |
408 GSList *l, *l_next = NULL; | |
409 gboolean result; | |
410 size_t param_count = 0; | |
411 char *param_start; | |
412 char **params = NULL; | |
413 | |
414 #if 0 | |
415 if (servconn->last_cmd != NULL) | |
416 gfree(servconn->last_cmd); | |
417 | |
418 servconn->last_cmd = gstrdup(command); | |
419 #endif | |
420 | |
421 /** | |
422 * See how many spaces we have in this. | |
423 */ | |
424 param_start = strchr(command, ' '); | |
425 | |
426 if (param_start != NULL) { | |
427 params = g_strsplit(param_start + 1, " ", 0); | |
428 | |
429 for (param_count = 0; params[param_count] != NULL; param_count++) | |
430 ; | |
431 | |
432 *param_start = '\0'; | |
433 } | |
434 | |
435 cb = g_hash_table_lookup(servconn->commands, command); | |
436 | |
437 if (cb == NULL) { | |
438 cb = g_hash_table_lookup(servconn->commands, "_unknown_"); | |
439 | |
440 if (cb == NULL) { | |
441 gaim_debug(GAIM_DEBUG_WARNING, "msn", | |
442 "Unhandled command '%s'\n", command); | |
443 | |
444 if (params != NULL) | |
445 g_strfreev(params); | |
446 | |
447 return TRUE; | |
448 } | |
449 } | |
450 | |
451 result = cb(servconn, command, (const char **)params, param_count); | |
452 | |
453 if (params != NULL) | |
454 g_strfreev(params); | |
455 | |
456 if (g_list_find(session->servconns, servconn) == NULL) | |
457 return result; | |
458 | |
459 /* Process all queued messages that are waiting on this command. */ | |
460 for (l = servconn->msg_queue; l != NULL; l = l_next) { | |
461 MsnQueueEntry *entry = l->data; | |
462 MsnMessage *msg; | |
463 | |
464 l_next = l->next; | |
465 | |
466 if (entry->command == NULL || | |
467 !g_ascii_strcasecmp(entry->command, command)) { | |
468 | |
469 msg = entry->msg; | |
470 | |
471 msn_servconn_process_message(servconn, msg); | |
472 | |
473 msn_servconn_unqueue_message(servconn, msg); | |
474 | |
475 entry->msg = NULL; | |
476 } | |
477 } | |
478 | |
479 return result; | |
480 } | |
481 | |
482 static void | |
529 read_cb(gpointer data, gint source, GaimInputCondition cond) | 483 read_cb(gpointer data, gint source, GaimInputCondition cond) |
530 { | 484 { |
531 MsnServConn *servconn = (MsnServConn *)data; | 485 MsnServConn *servconn = (MsnServConn *)data; |
532 MsnSession *session = servconn->session; | 486 MsnSession *session = servconn->session; |
533 char buf[MSN_BUF_LEN]; | 487 char buf[MSN_BUF_LEN]; |
534 gboolean cont = TRUE; | 488 char *cur, *end, *old_rx_buf; |
535 int len; | 489 int len, cur_len; |
536 | 490 |
537 len = read(servconn->fd, buf, sizeof(buf)); | 491 len = read(servconn->fd, buf, sizeof(buf) - 1); |
538 | 492 |
539 if (len <= 0) | 493 if (len <= 0) |
540 { | 494 { |
541 failed_io(servconn); | 495 failed_io(servconn); |
542 | 496 |
543 return; | 497 return; |
544 } | 498 } |
545 | 499 |
546 servconn->rxqueue = g_realloc(servconn->rxqueue, len + servconn->rxlen); | 500 servconn->rx_buf = g_realloc(servconn->rx_buf, len + servconn->rx_len); |
547 memcpy(servconn->rxqueue + servconn->rxlen, buf, len); | 501 memcpy(servconn->rx_buf + servconn->rx_len, buf, len); |
548 servconn->rxlen += len; | 502 servconn->rx_len += len; |
549 | 503 |
550 if (session->http_method) | 504 if (session->http_method) |
551 { | 505 { |
552 char *result_msg = NULL; | 506 char *result_msg = NULL; |
553 size_t result_len = 0; | 507 size_t result_len = 0; |
554 gboolean error; | 508 gboolean error; |
555 char *tmp; | 509 char *tmp; |
556 | 510 |
557 tmp = g_strndup(servconn->rxqueue, servconn->rxlen); | 511 tmp = g_strndup(servconn->rx_buf, servconn->rx_len); |
558 | 512 |
559 if (!msn_http_servconn_parse_data(servconn, tmp, | 513 if (!msn_http_servconn_parse_data(servconn, tmp, |
560 servconn->rxlen, &result_msg, | 514 servconn->rx_len, &result_msg, |
561 &result_len, &error)) | 515 &result_len, &error)) |
562 { | 516 { |
563 g_free(tmp); | 517 g_free(tmp); |
564 return; | 518 return; |
565 } | 519 } |
605 if (i == 0) | 559 if (i == 0) |
606 servconn->connected = TRUE; | 560 servconn->connected = TRUE; |
607 } | 561 } |
608 #endif | 562 #endif |
609 | 563 |
610 g_free(servconn->rxqueue); | 564 g_free(servconn->rx_buf); |
611 servconn->rxqueue = result_msg; | 565 servconn->rx_buf = result_msg; |
612 servconn->rxlen = result_len; | 566 servconn->rx_len = result_len; |
613 } | 567 } |
614 | 568 |
615 while (cont) | 569 end = old_rx_buf = servconn->rx_buf; |
616 { | 570 |
617 if (servconn->parsing_multiline) | 571 do |
618 { | 572 { |
619 char *msg; | 573 cur = end; |
620 | 574 |
621 if (servconn->rxlen == 0) | 575 if (servconn->payload_len) |
576 { | |
577 if (servconn->payload_len > servconn->rx_len) | |
578 /* The payload is still not complete. */ | |
622 break; | 579 break; |
623 | 580 |
624 if (servconn->multiline_len > servconn->rxlen) | 581 cur_len = servconn->payload_len; |
582 end += cur_len; | |
583 } | |
584 else | |
585 { | |
586 end = strstr(cur, "\r\n"); | |
587 | |
588 if (!end) | |
589 /* The command is still not complete. */ | |
625 break; | 590 break; |
626 | 591 |
627 msg = servconn->rxqueue; | 592 *end = '\0'; |
628 servconn->rxlen -= servconn->multiline_len; | 593 cur_len = end - cur + 2; |
629 | 594 end += 2; |
630 if (servconn->rxlen) { | 595 } |
631 servconn->rxqueue = g_memdup(msg + servconn->multiline_len, | 596 |
632 servconn->rxlen); | 597 servconn->rx_len -= cur_len; |
633 } | 598 |
634 else { | 599 if (servconn->payload_len) |
635 servconn->rxqueue = NULL; | 600 { |
636 msg = g_realloc(msg, servconn->multiline_len + 1); | 601 process_payload(servconn, cur, cur_len); |
637 } | 602 servconn->payload_len = 0; |
638 | 603 } |
639 msg[servconn->multiline_len] = '\0'; | 604 else |
640 servconn->parsing_multiline = FALSE; | 605 { |
641 | 606 gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s\n", cur); |
642 process_multi_line(servconn, msg); | 607 process_cmd_text(servconn, cur); |
643 | 608 } |
644 if (g_list_find(session->servconns, servconn) != NULL) { | 609 } while (servconn->connected && servconn->rx_len); |
645 servconn->multiline_len = 0; | 610 |
646 | 611 if (servconn->connected) |
647 if (servconn->msg_passport != NULL) | 612 { |
648 g_free(servconn->msg_passport); | 613 if (servconn->rx_len) |
649 | 614 servconn->rx_buf = g_memdup(cur, servconn->rx_len); |
650 if (servconn->msg_friendly != NULL) | 615 else |
651 g_free(servconn->msg_friendly); | 616 servconn->rx_buf = NULL; |
652 } | 617 } |
653 else | 618 |
654 cont = 0; | 619 if (servconn->wasted) |
655 | 620 msn_servconn_destroy(servconn); |
656 g_free(msg); | 621 |
657 } | 622 g_free(old_rx_buf); |
658 else { | 623 } |
659 char *end = servconn->rxqueue; | |
660 char *cmd; | |
661 int cmdlen, i; | |
662 | |
663 if (!servconn->rxlen) | |
664 return; | |
665 | |
666 for (i = 0; i < servconn->rxlen - 1; end++, i++) { | |
667 if (*end == '\r' && end[1] == '\n') | |
668 break; | |
669 } | |
670 | |
671 if (i == servconn->rxlen - 1) | |
672 return; | |
673 | |
674 cmdlen = end - servconn->rxqueue + 2; | |
675 cmd = servconn->rxqueue; | |
676 servconn->rxlen -= cmdlen; | |
677 | |
678 if (servconn->rxlen) | |
679 servconn->rxqueue = g_memdup(cmd + cmdlen, servconn->rxlen); | |
680 else { | |
681 servconn->rxqueue = NULL; | |
682 cmd = g_realloc(cmd, cmdlen + 1); | |
683 } | |
684 | |
685 cmd[cmdlen] = '\0'; | |
686 | |
687 gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd); | |
688 | |
689 g_strchomp(cmd); | |
690 | |
691 cont = process_single_line(servconn, cmd); | |
692 | |
693 g_free(cmd); | |
694 } | |
695 } | |
696 } |