From 3c440f70c66953735d01841453fa6a7ed244ef1c Mon Sep 17 00:00:00 2001
From: Mike Macgirvin <mike@macgirvin.com>
Date: Tue, 12 Oct 2010 19:32:15 -0700
Subject: [PATCH] Provide the ability to subscribe to our user from other
 federated sites. This is a read-only relationship until the rest of the
 salmon magic-envelope stuff lands

---
 boot.php              |  12 +-
 mod/dfrn_request.php  | 264 +++++++++++++++++++++++-------------------
 view/dfrn_request.tpl |   5 +-
 3 files changed, 161 insertions(+), 120 deletions(-)

diff --git a/boot.php b/boot.php
index d8ebc0299..dcfb8a46a 100644
--- a/boot.php
+++ b/boot.php
@@ -2,10 +2,11 @@
 
 set_time_limit(0);
 
-define ( 'BUILD_ID' , 1007 );
+define ( 'BUILD_ID',             1007 );
+define ( 'DFRN_PROTOCOL_VERSION', '2.0');
 
-define ( 'EOL', "<br />\r\n");
-define ( 'ATOM_TIME',  'Y-m-d\TH:i:s\Z' );
+define ( 'EOL',                  "<br />\r\n");
+define ( 'ATOM_TIME',            'Y-m-d\TH:i:s\Z' );
 
 define ( 'REGISTER_CLOSED',  0);
 define ( 'REGISTER_APPROVE', 1);
@@ -34,6 +35,7 @@ define ( 'NAMESPACE_TOMB' ,           'http://purl.org/atompub/tombstones/1.0' )
 define ( 'NAMESPACE_ACTIVITY',        'http://activitystrea.ms/spec/1.0/' );
 define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/');
 define ( 'NAMESPACE_SALMON_ME',       'http://salmon-protocol.org/ns/magic-env');
+define ( 'NAMESPACE_OSTATUSSUB',      'http://ostatus.org/schema/1.0/subscribe');
 
 define ( 'ACTIVITY_LIKE',        NAMESPACE_ACTIVITY_SCHEMA . 'like' );
 define ( 'ACTIVITY_DISLIKE',     NAMESPACE_DFRN            . '/dislike' );
@@ -848,6 +850,10 @@ function webfinger_dfrn($s) {
 		foreach($links as $link)
 			if($link['@attributes']['rel'] === NAMESPACE_DFRN)
 				return $link['@attributes']['href'];
+		foreach($links as $link)
+			if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
+				return 'stat:' . $link['@attributes']['template'];
+		
 	}
 	return '';
 }}
diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php
index 3cb2a16a4..f6a85fbab 100644
--- a/mod/dfrn_request.php
+++ b/mod/dfrn_request.php
@@ -183,143 +183,175 @@ function dfrn_request_post(&$a) {
 
 		$url = webfinger_dfrn($url);
 
+		if(substr($url,0,5) === 'stat:') {
+			$network = 'stat';
+			$url = substr($url,5);
+		}
+		else {
+			$network = 'dfrn';
+		}
+
 		if(! strlen($url)) {
 			notice( t("Unable to resolve your name at the provided location.") . EOL);			
 			return;
 		}
 
-		$ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", 
-			intval($uid),
-			dbesc($url)
-		);
 
-		if(count($ret)) {
-			if(strlen($ret[0]['issued-id'])) {
-				notice( t('You have already introduced yourself here.') . EOL );
-				return;
-			}
-			else {
-				$contact_record = $ret[0];
-				$parms = array('dfrn-request' => $ret[0]['request']);
-			}
-		}
-		$issued_id = random_string();
-
-		if(is_array($contact_record)) {
-			// There is a contact record but no issued-id, so this
-			// is a reciprocal introduction from a known contact
-			$r = q("UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d LIMIT 1",
-				dbesc($issued_id),
-				intval($contact_record['id'])
+		if($network == 'dfrn') {
+			$ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", 
+				intval($uid),
+				dbesc($url)
 			);
-		}
-		else {
-			if(! validate_url($url)) {
-				notice( t('Invalid profile URL.') . EOL);
-				goaway($a->get_baseurl() . '/' . $a->cmd);
-				return; // NOTREACHED
-			}
-
-			if(! allowed_url($url)) {
-				notice( t('Disallowed profile URL.') . EOL);
-				goaway($a->get_baseurl() . '/' . $a->cmd);
-				return; // NOTREACHED
-			}
-			
-
-			require_once('Scrape.php');
-
-			$parms = scrape_dfrn($url);
-
-			if(! count($parms)) {
-				notice( t('Profile location is not valid or does not contain profile information.') . EOL );
-				goaway($a->get_baseurl() . '/' . $a->cmd);
-			}
-			else {
-				if(! x($parms,'fn'))
-					notice( t('Warning: profile location has no identifiable owner name.') . EOL );
-				if(! x($parms,'photo'))
-					notice( t('Warning: profile location has no profile photo.') . EOL );
-				$invalid = validate_dfrn($parms);		
-				if($invalid) {
-					notice( $invalid . t(' required parameter') 
-						. (($invalid == 1) ? t(" was ") : t("s were ") )
-						. t("not found at the given location.") . EOL ) ;
 
+			if(count($ret)) {
+				if(strlen($ret[0]['issued-id'])) {
+					notice( t('You have already introduced yourself here.') . EOL );
 					return;
 				}
+				else {
+					$contact_record = $ret[0];
+					$parms = array('dfrn-request' => $ret[0]['request']);
+				}
 			}
+			$issued_id = random_string();
 
-
-			$parms['url'] = $url;
-			$parms['issued-id'] = $issued_id;
-
-
-			dbesc_array($parms);
-			$r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `issued-id`, `photo`, `site-pubkey`,
-				`request`, `confirm`, `notify`, `poll` )
-				VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
-				intval($uid),
-				datetime_convert(),
-				$parms['url'],
-				$parms['fn'],
-				$parms['issued-id'],
-				$parms['photo'],
-				$parms['key'],
-				$parms['dfrn-request'],
-				$parms['dfrn-confirm'],
-				$parms['dfrn-notify'],
-				$parms['dfrn-poll']
-			);
-
-			// find the contact record we just created
-			if($r) {	
-				$r = q("SELECT `id` FROM `contact` 
-					WHERE `uid` = %d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1",
-					intval($uid),
-					$parms['url'],
-					$parms['issued-id']
+			if(is_array($contact_record)) {
+				// There is a contact record but no issued-id, so this
+				// is a reciprocal introduction from a known contact
+				$r = q("UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d LIMIT 1",
+					dbesc($issued_id),
+					intval($contact_record['id'])
+				);
+			}
+			else {
+				if(! validate_url($url)) {
+					notice( t('Invalid profile URL.') . EOL);
+					goaway($a->get_baseurl() . '/' . $a->cmd);
+					return; // NOTREACHED
+				}
+
+				if(! allowed_url($url)) {
+					notice( t('Disallowed profile URL.') . EOL);
+					goaway($a->get_baseurl() . '/' . $a->cmd);
+					return; // NOTREACHED
+				}
+			
+
+				require_once('Scrape.php');
+
+				$parms = scrape_dfrn($url);
+
+				if(! count($parms)) {
+					notice( t('Profile location is not valid or does not contain profile information.') . EOL );
+					goaway($a->get_baseurl() . '/' . $a->cmd);
+				}
+				else {
+					if(! x($parms,'fn'))
+						notice( t('Warning: profile location has no identifiable owner name.') . EOL );
+					if(! x($parms,'photo'))
+						notice( t('Warning: profile location has no profile photo.') . EOL );
+					$invalid = validate_dfrn($parms);		
+					if($invalid) {
+						notice( $invalid . t(' required parameter') 
+							. (($invalid == 1) ? t(" was ") : t("s were ") )
+							. t("not found at the given location.") . EOL ) ;
+	
+						return;
+					}
+				}
+
+
+				$parms['url'] = $url;
+				$parms['issued-id'] = $issued_id;
+
+
+				dbesc_array($parms);
+				$r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `issued-id`, `photo`, `site-pubkey`,
+					`request`, `confirm`, `notify`, `poll` )
+					VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
+					intval($uid),
+					datetime_convert(),
+					$parms['url'],
+					$parms['fn'],
+					$parms['issued-id'],
+					$parms['photo'],
+					$parms['key'],
+					$parms['dfrn-request'],
+					$parms['dfrn-confirm'],
+					$parms['dfrn-notify'],
+					$parms['dfrn-poll']
+				);
+
+				// find the contact record we just created
+				if($r) {	
+					$r = q("SELECT `id` FROM `contact` 
+						WHERE `uid` = %d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1",
+						intval($uid),
+						$parms['url'],
+						$parms['issued-id']
+					);
+					if(count($r)) 
+						$contact_record = $r[0];
+				}
+	
+			}
+			if($r === false) {
+				notice( t('Failed to update contact record.') . EOL );
+				return;
+			}
+
+			$hash = random_string() . (string) time();   // Generate a confirm_key
+	
+			if(is_array($contact_record)) {
+				$ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
+					VALUES ( %d, %d, 1, %d, '%s', '%s', '%s' )",
+					intval($uid),
+					intval($contact_record['id']),
+					((x($_POST,'knowyou') && ($_POST['knowyou'] == 1)) ? 1 : 0),
+					dbesc(notags(trim($_POST['dfrn-request-message']))),
+					dbesc($hash),
+					dbesc(datetime_convert())
 				);
-				if(count($r)) 
-					$contact_record = $r[0];
 			}
 	
-		}
-		if($r === false) {
-			notice( t('Failed to update contact record.') . EOL );
-			return;
-		}
 
-		$hash = random_string() . (string) time();   // Generate a confirm_key
+			// This notice will only be seen by the requestor if  the requestor and requestee are on the same server.
 
-		if(is_array($contact_record)) {
-			$ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
-				VALUES ( %d, %d, 1, %d, '%s', '%s', '%s' )",
-				intval($uid),
-				intval($contact_record['id']),
-				((x($_POST,'knowyou') && ($_POST['knowyou'] == 1)) ? 1 : 0),
-				dbesc(notags(trim($_POST['dfrn-request-message']))),
-				dbesc($hash),
-				dbesc(datetime_convert())
+			if(! $failed) 
+				notice( t('Your introduction has been sent.') . EOL );
+
+			// "Homecoming" - send the requestor back to their site to record the introduction.
+
+			$dfrn_url = bin2hex($a->get_baseurl() . '/profile/' . $nickname);
+			$aes_allow = ((function_exists('openssl_encrypt')) ? 1 : 0);
+
+			goaway($parms['dfrn-request'] . "?dfrn_url=$dfrn_url" 
+				. '&dfrn_version=' . DFRN_PROTOCOL_VERSION 
+				. '&confirm_key='  . $hash 
+				. (($aes_allow) ? "&aes_allow=1" : "")
 			);
+			// NOTREACHED
+			// END $network === 'dfrn'
 		}
-	
+		elseif($network === 'stat') {
+			
+			/**
+			*
+			* OStatus network
+			* Check contact existence
+			* Try and scrape together enough information to create a contact record, with us as REL_VIP
+			* Substitute our user's feed URL into $url template
+			* Send the subscriber home to subscribe
+			*
+			**/
 
-		// This notice will only be seen by the requestor if  the requestor and requestee are on the same server.
+			$url = str_replace('{uri}', $a->get_baseurl() . '/dfrn_poll/' . $nickname, $url);
+			goaway($url);
+			// NOTREACHED
+			// END $network === 'stat'
+		}
 
-		if(! $failed) 
-			notice( t('Your introduction has been sent.') . EOL );
-
-		// "Homecoming" - send the requestor back to their site to record the introduction.
-
-		$dfrn_url = bin2hex($a->get_baseurl() . '/profile/' . $nickname);
-		$aes_allow = ((function_exists('openssl_encrypt')) ? 1 : 0);
-
-		goaway($parms['dfrn-request'] . "?dfrn_url=$dfrn_url" . '&confirm_key=' . $hash . (($aes_allow) ? "&aes_allow=1" : ""));
-		return; // NOTREACHED
-
-	}
-	return;
+	}	return;
 }}
 
 
diff --git a/view/dfrn_request.tpl b/view/dfrn_request.tpl
index 0b02982d8..df7e3830c 100644
--- a/view/dfrn_request.tpl
+++ b/view/dfrn_request.tpl
@@ -4,7 +4,10 @@
 <p id="dfrn-request-intro">
 You may request a connection with this member if you have a valid profile address<br />
 on one of the following social networks:<br />
-<ul id="dfrn-request-networks"><li><a href="http://dfrn.org">mistpark/DFRN</a></li></ul>
+<ul id="dfrn-request-networks">
+<li><a href="http://dfrn.org">Mistpark/DFRN</a> (fully supported)</li>
+<li><a href="http://ostatus.org">Federation/OStatus/Diaspora/GNU-social</a> (limited - experimental)</li>
+</ul>
 </p>
 
 <form action="dfrn_request/$nickname" method="post" />