diff libpurple/protocols/simple/simple.c @ 21418:38cc722159ff

propagate from branch 'im.pidgin.pidgin' (head d8103be97302efb404e2f6922925f661c807ad23) to branch 'im.pidgin.cpw.resiak.disconnectreason' (head 0ac25a1f38ae28654c967caa143f1c0d12ef2e1c)
author Will Thompson <will.thompson@collabora.co.uk>
date Sat, 10 Nov 2007 12:10:04 +0000
parents e747ac0c42d6 b2d9f859663e
children c38d72677c8a 60f5abc6cf0c
line wrap: on
line diff
--- a/libpurple/protocols/simple/simple.c	Wed Nov 07 11:23:03 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Sat Nov 10 12:10:04 2007 +0000
@@ -321,7 +321,7 @@
 	return retval;
 }
 
-static void fill_auth(struct simple_account_data *sip, gchar *hdr, struct sip_auth *auth) {
+static void fill_auth(struct simple_account_data *sip, const gchar *hdr, struct sip_auth *auth) {
 	int i = 0;
 	const char *authuser;
 	char *tmp;
@@ -596,7 +596,7 @@
 static struct transaction *transactions_find(struct simple_account_data *sip, struct sipmsg *msg) {
 	struct transaction *trans;
 	GSList *transactions = sip->transactions;
-	gchar *cseq = sipmsg_find_header(msg, "CSeq");
+	const gchar *cseq = sipmsg_find_header(msg, "CSeq");
 
 	if (cseq) {
 		while(transactions) {
@@ -694,19 +694,15 @@
 }
 
 static char *get_contact(struct simple_account_data  *sip) {
-	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");
+	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");
 }
 
 static void do_register_exp(struct simple_account_data *sip, int expire) {
 	char *uri, *to, *contact, *hdr;
 
-	/* Set our default expiration to 900,
-	 * as done in the initialization of the simple_account_data
-	 * structure.
-	 */
-	if (!expire)
-		expire = 900;
-
 	sip->reregister = time(NULL) + expire - 50;
 
 	uri = g_strdup_printf("sip:%s", sip->servername);
@@ -758,11 +754,49 @@
 	purple_debug_info("simple", "got %s\n", from);
 	return from;
 }
+static gchar *find_tag(const gchar *);
 
 static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
-	gchar *to;
+	gchar *to = NULL;
+	struct simple_buddy *b = NULL;
+	gchar *theirtag = NULL, *ourtag = NULL;
+	const gchar *callid = NULL;
+
+	purple_debug_info("simple", "process subscribe response\n");
 
 	if(msg->response == 200 || msg->response == 202) {
+		if ( (to = parse_from(sipmsg_find_header(msg, "To"))) &&
+		      (b = g_hash_table_lookup(sip->buddies, to)) &&
+		       !(b->dialog))
+		{
+			purple_debug_info("simple", "creating dialog"
+				" information for a subscription.\n");
+
+			theirtag = find_tag(sipmsg_find_header(msg, "To"));
+			ourtag = find_tag(sipmsg_find_header(msg, "From"));
+			callid = sipmsg_find_header(msg, "Call-ID");
+
+			if (theirtag && ourtag && callid)
+			{
+				b->dialog = g_new0(struct sip_dialog, 1);
+				b->dialog->ourtag = g_strdup(ourtag);
+				b->dialog->theirtag = g_strdup(theirtag);
+				b->dialog->callid = g_strdup(callid);
+
+				purple_debug_info("simple", "ourtag: %s\n", 
+					ourtag);
+				purple_debug_info("simple", "theirtag: %s\n", 
+					theirtag);
+				purple_debug_info("simple", "callid: %s\n", 
+					callid);
+				g_free(theirtag);
+				g_free(ourtag);
+			}
+		}
+		else
+		{
+			purple_debug_info("simple", "cannot create dialog!\n");
+		}
 		return TRUE;
 	}
 
@@ -775,10 +809,14 @@
 	return TRUE;
 }
 
-static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) {
-	gchar *contact = "Expires: 1200\r\nAccept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n";
-	gchar *to;
-	gchar *tmp;
+static void simple_subscribe_exp(struct simple_account_data *sip, struct simple_buddy *buddy, int expiration) {
+	gchar *contact, *to, *tmp, *tmp2;
+
+	tmp2 = g_strdup_printf(
+		"Expires: %d\r\n"
+		"Accept: application/pidf+xml, application/xpidf+xml\r\n"
+		"Event: presence\r\n",
+		expiration);
 
 	if(strstr(buddy->name, "sip:"))
 		to = g_strdup(buddy->name);
@@ -786,25 +824,38 @@
 		to = g_strdup_printf("sip:%s", buddy->name);
 
 	tmp = get_contact(sip);
-	contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
+	contact = g_strdup_printf("%sContact: %s\r\n", tmp2, tmp);
 	g_free(tmp);
+	g_free(tmp2);
 
-	/* subscribe to buddy presence
-	 * we dont need to know the status so we do not need a callback */
-
-	send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
-		process_subscribe_response);
+	send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact,"",buddy->dialog,
+			 (expiration > 0) ? process_subscribe_response : NULL);
 
 	g_free(to);
 	g_free(contact);
 
 	/* resubscribe before subscription expires */
 	/* add some jitter */
-	buddy->resubscribe = time(NULL)+1140+(rand()%50);
+	if (expiration > 60)
+		buddy->resubscribe = time(NULL) + (expiration - 60) + (rand() % 50);
+	else if (expiration > 0)
+		buddy->resubscribe = time(NULL) + ((int) (expiration / 2));
+}
+
+static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) {
+	simple_subscribe_exp(sip, buddy, SUBSCRIBE_EXPIRATION);
+}
+
+static void simple_unsubscribe(char *name, struct simple_buddy *buddy, struct simple_account_data *sip) {
+	if (buddy->dialog)
+	{
+		purple_debug_info("simple", "Unsubscribing from %s\n", name);
+		simple_subscribe_exp(sip, buddy, 0);
+	}
 }
 
 static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
-	gchar *tmp;
+	const gchar *tmp;
 	xmlnode *item, *group, *isc;
 	const char *name_group;
 	PurpleBuddy *b;
@@ -916,11 +967,20 @@
 	if(sip->reregister < curtime) {
 		do_register(sip);
 	}
+
+	/* publish status again if our last update is about to expire. */
+	if (sip->republish != -1 && 
+		sip->republish < curtime &&
+		purple_account_get_bool(sip->account, "dopublish", TRUE))
+	{
+		purple_debug_info("simple", "subscribe_timeout: republishing status.\n");
+		send_open_publish(sip);
+	}
+
 	/* check for every subscription if we need to resubscribe */
 	g_hash_table_foreach(sip->buddies, (GHFunc)simple_buddy_resub, (gpointer)sip);
 
 	/* remove a timed out suscriber */
-
 	tmp = sip->watcher;
 	while(tmp) {
 		struct simple_watcher *watcher = tmp->data;
@@ -964,7 +1024,7 @@
 
 static void process_incoming_message(struct simple_account_data *sip, struct sipmsg *msg) {
 	gchar *from;
-	gchar *contenttype;
+	const gchar *contenttype;
 	gboolean found = FALSE;
 
 	from = parse_from(sipmsg_find_header(msg, "From"));
@@ -1019,7 +1079,7 @@
 
 
 gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
-	gchar *tmp;
+	const gchar *tmp;
 	purple_debug(PURPLE_DEBUG_MISC, "simple", "in process register response response: %d\n", msg->response);
 	switch (msg->response) {
 		case 200:
@@ -1075,23 +1135,96 @@
 	return TRUE;
 }
 
+static gboolean dialog_match(struct sip_dialog *dialog, struct sipmsg *msg)
+{
+	const gchar *fromhdr;
+	const gchar *tohdr;
+	const gchar *callid;
+	gchar *ourtag, *theirtag;
+	gboolean match = FALSE;
+
+	fromhdr = sipmsg_find_header(msg, "From");
+	tohdr = sipmsg_find_header(msg, "To");
+	callid = sipmsg_find_header(msg, "Call-ID");
+
+	if (!fromhdr || !tohdr || !callid)
+		return FALSE;
+
+	ourtag = find_tag(tohdr);
+	theirtag = find_tag(fromhdr);
+
+	if (ourtag && theirtag &&
+			!strcmp(dialog->callid, callid) &&
+			!strcmp(dialog->ourtag, ourtag) &&
+			!strcmp(dialog->theirtag, theirtag))
+		match = TRUE;
+
+	g_free(ourtag);
+	g_free(theirtag);
+
+	return match;
+}
+
 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) {
 	gchar *from;
-	gchar *fromhdr;
+	const gchar *fromhdr;
 	gchar *basicstatus_data;
 	xmlnode *pidf;
 	xmlnode *basicstatus = NULL, *tuple, *status;
 	gboolean isonline = FALSE;
+	struct simple_buddy *b = NULL;
+	const gchar *sshdr = NULL;
 
 	fromhdr = sipmsg_find_header(msg, "From");
 	from = parse_from(fromhdr);
 	if(!from) return;
 
+	b = g_hash_table_lookup(sip->buddies, from);
+	if (!b)
+	{
+		g_free(from);
+		purple_debug_info("simple", "Could not find the buddy.\n");
+		return;
+	}
+
+	if (b->dialog && !dialog_match(b->dialog, msg))
+	{
+		/* We only accept notifies from people that
+		 * we already have a dialog with.
+		 */
+		purple_debug_info("simple","No corresponding dialog for notify--discard\n");
+		g_free(from);
+		return;
+	}
+
 	pidf = xmlnode_from_str(msg->body, msg->bodylen);
 
 	if(!pidf) {
 		purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n");
-		purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+		sshdr = sipmsg_find_header(msg, "Subscription-State");
+		if (sshdr)
+		{
+			int i = 0;
+			gchar **ssparts = g_strsplit(sshdr, ":", 0);
+			while (ssparts[i])
+			{
+				g_strchug(ssparts[i]);
+				if (g_str_has_prefix(ssparts[i], "terminated"))
+				{
+					purple_debug_info("simple", "Subscription expired!");
+					g_free(b->dialog->ourtag);
+					g_free(b->dialog->theirtag);
+					g_free(b->dialog->callid);
+					g_free(b->dialog);
+					b->dialog = NULL;
+
+					purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+					break;
+				}
+				i++;
+			}
+			g_strfreev(ssparts);
+		}
 		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		g_free(from);
 		return;
@@ -1231,15 +1364,22 @@
 }
 
 static void send_open_publish(struct simple_account_data *sip) {
+	gchar *add_headers = NULL;
 	gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
 	gchar *doc = gen_pidf(sip, TRUE);
+
+	add_headers = g_strdup_printf("%s%d%s",
+		"Expires: ",
+		PUBLISH_EXPIRATION,
+		"\r\nEvent: presence\r\n"
+		"Content-Type: application/pidf+xml\r\n");
+
 	send_sip_request(sip->gc, "PUBLISH", uri, uri,
-		"Expires: 600\r\nEvent: presence\r\n"
-		"Content-Type: application/pidf+xml\r\n",
-		doc, NULL, process_publish_response);
-	sip->republish = time(NULL) + 500;
+		add_headers, doc, NULL, process_publish_response);
+	sip->republish = time(NULL) + PUBLISH_EXPIRATION - 50;
 	g_free(uri);
 	g_free(doc);
+	g_free(add_headers);
 }
 
 static void send_closed_publish(struct simple_account_data *sip) {
@@ -1260,8 +1400,8 @@
 	gchar *theirtag = find_tag(from_hdr);
 	gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
 	gboolean tagadded = FALSE;
-	gchar *callid = sipmsg_find_header(msg, "Call-ID");
-	gchar *expire = sipmsg_find_header(msg, "Expire");
+	const gchar *callid = sipmsg_find_header(msg, "Call-ID");
+	const gchar *expire = sipmsg_find_header(msg, "Expire");
 	gchar *tmp;
 	struct simple_watcher *watcher = watcher_find(sip, from);
 	if(!ourtag) {
@@ -1269,14 +1409,14 @@
 		ourtag = gentag();
 	}
 	if(!watcher) { /* new subscription */
-		gchar *acceptheader = sipmsg_find_header(msg, "Accept");
+		const gchar *acceptheader = sipmsg_find_header(msg, "Accept");
 		gboolean needsxpidf = FALSE;
 		if(!purple_privacy_check(sip->account, from)) {
 			send_sip_response(sip->gc, msg, 202, "Ok", NULL);
 			goto privend;
 		}
 		if(acceptheader) {
-			gchar *tmp = acceptheader;
+			const gchar *tmp = acceptheader;
 			gboolean foundpidf = FALSE;
 			gboolean foundxpidf = FALSE;
 			while(tmp && tmp < acceptheader + strlen(acceptheader)) {
@@ -1294,7 +1434,6 @@
 					tmp = 0;
 			}
 			if(!foundpidf && foundxpidf) needsxpidf = TRUE;
-			g_free(acceptheader);
 		}
 		watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
 	}
@@ -1319,8 +1458,6 @@
 	g_free(from);
 	g_free(theirtag);
 	g_free(ourtag);
-	g_free(callid);
-	g_free(expire);
 }
 
 static void process_input_message(struct simple_account_data *sip, struct sipmsg *msg) {
@@ -1342,7 +1479,8 @@
 		struct transaction *trans = transactions_find(sip, msg);
 		if(trans) {
 			if(msg->response == 407) {
-				gchar *resend, *auth, *ptmp;
+				gchar *resend, *auth;
+				const gchar *ptmp;
 
 				if(sip->proxy.retries > 3) return;
 				sip->proxy.retries++;
@@ -1386,7 +1524,8 @@
 							/* This is encountered when a generic (MESSAGE, NOTIFY, etc)
 							 * was denied until further authorization is provided.
 							 */
-							gchar *resend, *auth, *ptmp;
+							gchar *resend, *auth;
+							const gchar *ptmp;
 
 							if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) return;
 							sip->registrar.retries++;
@@ -1775,11 +1914,14 @@
 		/* unregister */
 		if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
 		{
-			if(purple_account_get_bool(sip->account, 
-				"dopublish", 
-				TRUE))
+			g_hash_table_foreach(sip->buddies,
+				(GHFunc)simple_unsubscribe,
+				(gpointer)sip);
+
+			if(purple_account_get_bool(sip->account,
+						   "dopublish", TRUE))
 				send_closed_publish(sip);
-			
+
 			do_register_exp(sip, 0);
 		}
 		connection_free_all(sip);
@@ -1911,7 +2053,7 @@
 
 	"prpl-simple",                                    /**< id             */
 	"SIMPLE",                                         /**< name           */
-	VERSION,                                          /**< version        */
+	DISPLAY_VERSION,                                  /**< version        */
 	N_("SIP/SIMPLE Protocol Plugin"),                 /**  summary        */
 	N_("The SIP/SIMPLE Protocol Plugin"),             /**  description    */
 	"Thomas Butter <butter@uni-mannheim.de>",         /**< author         */