comparison libpurple/protocols/simple/simple.c @ 21149:b2d9f859663e

Patch from Will Hawkins to make the SIMPLE prpl more standards compliant by keeping all subscribe/notify messaging within a dialog. This didn't apply cleanly, so I had to manually apply it - hopefully I didn't break anything during the process. Fixes #3778.
author Daniel Atallah <daniel.atallah@gmail.com>
date Tue, 06 Nov 2007 02:52:41 +0000
parents 3cc856ca2338
children 38cc722159ff
comparison
equal deleted inserted replaced
21148:3635ddf4170f 21149:b2d9f859663e
688 688
689 g_free(buf); 689 g_free(buf);
690 } 690 }
691 691
692 static char *get_contact(struct simple_account_data *sip) { 692 static char *get_contact(struct simple_account_data *sip) {
693 return g_strdup_printf("<sip:%s@%s:%d;transport=%s>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"", sip->username, purple_network_get_my_ip(-1), sip->listenport, sip->udp ? "udp" : "tcp"); 693 return g_strdup_printf("<sip:%s@%s:%d;transport=%s>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"",
694 sip->username, purple_network_get_my_ip(-1),
695 sip->listenport,
696 sip->udp ? "udp" : "tcp");
694 } 697 }
695 698
696 static void do_register_exp(struct simple_account_data *sip, int expire) { 699 static void do_register_exp(struct simple_account_data *sip, int expire) {
697 char *uri, *to, *contact, *hdr; 700 char *uri, *to, *contact, *hdr;
698
699 /* Set our default expiration to 900,
700 * as done in the initialization of the simple_account_data
701 * structure.
702 */
703 if (!expire)
704 expire = 900;
705 701
706 sip->reregister = time(NULL) + expire - 50; 702 sip->reregister = time(NULL) + expire - 50;
707 703
708 uri = g_strdup_printf("sip:%s", sip->servername); 704 uri = g_strdup_printf("sip:%s", sip->servername);
709 to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); 705 to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
752 } 748 }
753 } 749 }
754 purple_debug_info("simple", "got %s\n", from); 750 purple_debug_info("simple", "got %s\n", from);
755 return from; 751 return from;
756 } 752 }
753 static gchar *find_tag(const gchar *);
757 754
758 static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { 755 static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
759 gchar *to; 756 gchar *to = NULL;
757 struct simple_buddy *b = NULL;
758 gchar *theirtag = NULL, *ourtag = NULL;
759 const gchar *callid = NULL;
760
761 purple_debug_info("simple", "process subscribe response\n");
760 762
761 if(msg->response == 200 || msg->response == 202) { 763 if(msg->response == 200 || msg->response == 202) {
764 if ( (to = parse_from(sipmsg_find_header(msg, "To"))) &&
765 (b = g_hash_table_lookup(sip->buddies, to)) &&
766 !(b->dialog))
767 {
768 purple_debug_info("simple", "creating dialog"
769 " information for a subscription.\n");
770
771 theirtag = find_tag(sipmsg_find_header(msg, "To"));
772 ourtag = find_tag(sipmsg_find_header(msg, "From"));
773 callid = sipmsg_find_header(msg, "Call-ID");
774
775 if (theirtag && ourtag && callid)
776 {
777 b->dialog = g_new0(struct sip_dialog, 1);
778 b->dialog->ourtag = g_strdup(ourtag);
779 b->dialog->theirtag = g_strdup(theirtag);
780 b->dialog->callid = g_strdup(callid);
781
782 purple_debug_info("simple", "ourtag: %s\n",
783 ourtag);
784 purple_debug_info("simple", "theirtag: %s\n",
785 theirtag);
786 purple_debug_info("simple", "callid: %s\n",
787 callid);
788 g_free(theirtag);
789 g_free(ourtag);
790 }
791 }
792 else
793 {
794 purple_debug_info("simple", "cannot create dialog!\n");
795 }
762 return TRUE; 796 return TRUE;
763 } 797 }
764 798
765 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */ 799 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
766 800
769 purple_prpl_got_user_status(sip->account, to, "offline", NULL); 803 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
770 g_free(to); 804 g_free(to);
771 return TRUE; 805 return TRUE;
772 } 806 }
773 807
774 static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) { 808 static void simple_subscribe_exp(struct simple_account_data *sip, struct simple_buddy *buddy, int expiration) {
775 gchar *contact = "Expires: 1200\r\nAccept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n"; 809 gchar *contact, *to, *tmp, *tmp2;
776 gchar *to; 810
777 gchar *tmp; 811 tmp2 = g_strdup_printf(
812 "Expires: %d\r\n"
813 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
814 "Event: presence\r\n",
815 expiration);
778 816
779 if(strstr(buddy->name, "sip:")) 817 if(strstr(buddy->name, "sip:"))
780 to = g_strdup(buddy->name); 818 to = g_strdup(buddy->name);
781 else 819 else
782 to = g_strdup_printf("sip:%s", buddy->name); 820 to = g_strdup_printf("sip:%s", buddy->name);
783 821
784 tmp = get_contact(sip); 822 tmp = get_contact(sip);
785 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp); 823 contact = g_strdup_printf("%sContact: %s\r\n", tmp2, tmp);
786 g_free(tmp); 824 g_free(tmp);
787 825 g_free(tmp2);
788 /* subscribe to buddy presence 826
789 * we dont need to know the status so we do not need a callback */ 827 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact,"",buddy->dialog,
790 828 (expiration > 0) ? process_subscribe_response : NULL);
791 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
792 process_subscribe_response);
793 829
794 g_free(to); 830 g_free(to);
795 g_free(contact); 831 g_free(contact);
796 832
797 /* resubscribe before subscription expires */ 833 /* resubscribe before subscription expires */
798 /* add some jitter */ 834 /* add some jitter */
799 buddy->resubscribe = time(NULL)+1140+(rand()%50); 835 if (expiration > 60)
836 buddy->resubscribe = time(NULL) + (expiration - 60) + (rand() % 50);
837 else if (expiration > 0)
838 buddy->resubscribe = time(NULL) + ((int) (expiration / 2));
839 }
840
841 static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) {
842 simple_subscribe_exp(sip, buddy, SUBSCRIBE_EXPIRATION);
843 }
844
845 static void simple_unsubscribe(char *name, struct simple_buddy *buddy, struct simple_account_data *sip) {
846 if (buddy->dialog)
847 {
848 purple_debug_info("simple", "Unsubscribing from %s\n", name);
849 simple_subscribe_exp(sip, buddy, 0);
850 }
800 } 851 }
801 852
802 static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { 853 static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
803 const gchar *tmp; 854 const gchar *tmp;
804 xmlnode *item, *group, *isc; 855 xmlnode *item, *group, *isc;
910 time_t curtime = time(NULL); 961 time_t curtime = time(NULL);
911 /* register again if first registration expires */ 962 /* register again if first registration expires */
912 if(sip->reregister < curtime) { 963 if(sip->reregister < curtime) {
913 do_register(sip); 964 do_register(sip);
914 } 965 }
966
967 /* publish status again if our last update is about to expire. */
968 if (sip->republish != -1 &&
969 sip->republish < curtime &&
970 purple_account_get_bool(sip->account, "dopublish", TRUE))
971 {
972 purple_debug_info("simple", "subscribe_timeout: republishing status.\n");
973 send_open_publish(sip);
974 }
975
915 /* check for every subscription if we need to resubscribe */ 976 /* check for every subscription if we need to resubscribe */
916 g_hash_table_foreach(sip->buddies, (GHFunc)simple_buddy_resub, (gpointer)sip); 977 g_hash_table_foreach(sip->buddies, (GHFunc)simple_buddy_resub, (gpointer)sip);
917 978
918 /* remove a timed out suscriber */ 979 /* remove a timed out suscriber */
919
920 tmp = sip->watcher; 980 tmp = sip->watcher;
921 while(tmp) { 981 while(tmp) {
922 struct simple_watcher *watcher = tmp->data; 982 struct simple_watcher *watcher = tmp->data;
923 if(watcher->expire < curtime) { 983 if(watcher->expire < curtime) {
924 watcher_remove(sip, watcher->name); 984 watcher_remove(sip, watcher->name);
1068 break; 1128 break;
1069 } 1129 }
1070 return TRUE; 1130 return TRUE;
1071 } 1131 }
1072 1132
1133 static gboolean dialog_match(struct sip_dialog *dialog, struct sipmsg *msg)
1134 {
1135 const gchar *fromhdr;
1136 const gchar *tohdr;
1137 const gchar *callid;
1138 gchar *ourtag, *theirtag;
1139 gboolean match = FALSE;
1140
1141 fromhdr = sipmsg_find_header(msg, "From");
1142 tohdr = sipmsg_find_header(msg, "To");
1143 callid = sipmsg_find_header(msg, "Call-ID");
1144
1145 if (!fromhdr || !tohdr || !callid)
1146 return FALSE;
1147
1148 ourtag = find_tag(tohdr);
1149 theirtag = find_tag(fromhdr);
1150
1151 if (ourtag && theirtag &&
1152 !strcmp(dialog->callid, callid) &&
1153 !strcmp(dialog->ourtag, ourtag) &&
1154 !strcmp(dialog->theirtag, theirtag))
1155 match = TRUE;
1156
1157 g_free(ourtag);
1158 g_free(theirtag);
1159
1160 return match;
1161 }
1162
1073 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) { 1163 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) {
1074 gchar *from; 1164 gchar *from;
1075 const gchar *fromhdr; 1165 const gchar *fromhdr;
1076 gchar *basicstatus_data; 1166 gchar *basicstatus_data;
1077 xmlnode *pidf; 1167 xmlnode *pidf;
1078 xmlnode *basicstatus = NULL, *tuple, *status; 1168 xmlnode *basicstatus = NULL, *tuple, *status;
1079 gboolean isonline = FALSE; 1169 gboolean isonline = FALSE;
1170 struct simple_buddy *b = NULL;
1171 const gchar *sshdr = NULL;
1080 1172
1081 fromhdr = sipmsg_find_header(msg, "From"); 1173 fromhdr = sipmsg_find_header(msg, "From");
1082 from = parse_from(fromhdr); 1174 from = parse_from(fromhdr);
1083 if(!from) return; 1175 if(!from) return;
1084 1176
1177 b = g_hash_table_lookup(sip->buddies, from);
1178 if (!b)
1179 {
1180 g_free(from);
1181 purple_debug_info("simple", "Could not find the buddy.\n");
1182 return;
1183 }
1184
1185 if (b->dialog && !dialog_match(b->dialog, msg))
1186 {
1187 /* We only accept notifies from people that
1188 * we already have a dialog with.
1189 */
1190 purple_debug_info("simple","No corresponding dialog for notify--discard\n");
1191 g_free(from);
1192 return;
1193 }
1194
1085 pidf = xmlnode_from_str(msg->body, msg->bodylen); 1195 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1086 1196
1087 if(!pidf) { 1197 if(!pidf) {
1088 purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n"); 1198 purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n");
1089 purple_prpl_got_user_status(sip->account, from, "offline", NULL); 1199 sshdr = sipmsg_find_header(msg, "Subscription-State");
1200 if (sshdr)
1201 {
1202 int i = 0;
1203 gchar **ssparts = g_strsplit(sshdr, ":", 0);
1204 while (ssparts[i])
1205 {
1206 g_strchug(ssparts[i]);
1207 if (g_str_has_prefix(ssparts[i], "terminated"))
1208 {
1209 purple_debug_info("simple", "Subscription expired!");
1210 g_free(b->dialog->ourtag);
1211 g_free(b->dialog->theirtag);
1212 g_free(b->dialog->callid);
1213 g_free(b->dialog);
1214 b->dialog = NULL;
1215
1216 purple_prpl_got_user_status(sip->account, from, "offline", NULL);
1217 break;
1218 }
1219 i++;
1220 }
1221 g_strfreev(ssparts);
1222 }
1090 send_sip_response(sip->gc, msg, 200, "OK", NULL); 1223 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1091 g_free(from); 1224 g_free(from);
1092 return; 1225 return;
1093 } 1226 }
1094 1227
1224 } 1357 }
1225 return TRUE; 1358 return TRUE;
1226 } 1359 }
1227 1360
1228 static void send_open_publish(struct simple_account_data *sip) { 1361 static void send_open_publish(struct simple_account_data *sip) {
1362 gchar *add_headers = NULL;
1229 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); 1363 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
1230 gchar *doc = gen_pidf(sip, TRUE); 1364 gchar *doc = gen_pidf(sip, TRUE);
1365
1366 add_headers = g_strdup_printf("%s%d%s",
1367 "Expires: ",
1368 PUBLISH_EXPIRATION,
1369 "\r\nEvent: presence\r\n"
1370 "Content-Type: application/pidf+xml\r\n");
1371
1231 send_sip_request(sip->gc, "PUBLISH", uri, uri, 1372 send_sip_request(sip->gc, "PUBLISH", uri, uri,
1232 "Expires: 600\r\nEvent: presence\r\n" 1373 add_headers, doc, NULL, process_publish_response);
1233 "Content-Type: application/pidf+xml\r\n", 1374 sip->republish = time(NULL) + PUBLISH_EXPIRATION - 50;
1234 doc, NULL, process_publish_response);
1235 sip->republish = time(NULL) + 500;
1236 g_free(uri); 1375 g_free(uri);
1237 g_free(doc); 1376 g_free(doc);
1377 g_free(add_headers);
1238 } 1378 }
1239 1379
1240 static void send_closed_publish(struct simple_account_data *sip) { 1380 static void send_closed_publish(struct simple_account_data *sip) {
1241 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); 1381 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
1242 gchar *doc = gen_pidf(sip, FALSE); 1382 gchar *doc = gen_pidf(sip, FALSE);
1750 1890
1751 if(sip) { 1891 if(sip) {
1752 /* unregister */ 1892 /* unregister */
1753 if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE) 1893 if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
1754 { 1894 {
1755 if(purple_account_get_bool(sip->account, 1895 g_hash_table_foreach(sip->buddies,
1756 "dopublish", 1896 (GHFunc)simple_unsubscribe,
1757 TRUE)) 1897 (gpointer)sip);
1898
1899 if(purple_account_get_bool(sip->account,
1900 "dopublish", TRUE))
1758 send_closed_publish(sip); 1901 send_closed_publish(sip);
1759 1902
1760 do_register_exp(sip, 0); 1903 do_register_exp(sip, 0);
1761 } 1904 }
1762 connection_free_all(sip); 1905 connection_free_all(sip);
1763 1906
1764 if (sip->query_data != NULL) 1907 if (sip->query_data != NULL)