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 }