Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

814 lines
25 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
  1. <?php
  2. /**
  3. * @file mod/dfrn_confirm.php
  4. * @brief Module: dfrn_confirm
  5. * Purpose: Friendship acceptance for DFRN contacts
  6. *.
  7. * There are two possible entry points and three scenarios.
  8. *.
  9. * 1. A form was submitted by our user approving a friendship that originated elsewhere.
  10. * This may also be called from dfrn_request to automatically approve a friendship.
  11. *
  12. * 2. We may be the target or other side of the conversation to scenario 1, and will
  13. * interact with that process on our own user's behalf.
  14. *.
  15. * @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf
  16. * You also find a graphic which describes the confirmation process at
  17. * https://github.com/friendica/friendica/blob/master/spec/dfrn2_contact_confirmation.png
  18. */
  19. require_once('include/enotify.php');
  20. require_once('include/group.php');
  21. require_once('include/Probe.php');
  22. function dfrn_confirm_post(&$a,$handsfree = null) {
  23. if(is_array($handsfree)) {
  24. /*
  25. * We were called directly from dfrn_request due to automatic friend acceptance.
  26. * Any $_POST parameters we may require are supplied in the $handsfree array.
  27. *
  28. */
  29. $node = $handsfree['node'];
  30. $a->interactive = false; // notice() becomes a no-op since nobody is there to see it
  31. }
  32. else {
  33. if($a->argc > 1)
  34. $node = $a->argv[1];
  35. }
  36. /*
  37. *
  38. * Main entry point. Scenario 1. Our user received a friend request notification (perhaps
  39. * from another site) and clicked 'Approve'.
  40. * $POST['source_url'] is not set. If it is, it indicates Scenario 2.
  41. *
  42. * We may also have been called directly from dfrn_request ($handsfree != null) due to
  43. * this being a page type which supports automatic friend acceptance. That is also Scenario 1
  44. * since we are operating on behalf of our registered user to approve a friendship.
  45. *
  46. */
  47. if(! x($_POST,'source_url')) {
  48. $uid = ((is_array($handsfree)) ? $handsfree['uid'] : local_user());
  49. if(! $uid) {
  50. notice( t('Permission denied.') . EOL );
  51. return;
  52. }
  53. $user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
  54. intval($uid)
  55. );
  56. if(! $user) {
  57. notice( t('Profile not found.') . EOL );
  58. return;
  59. }
  60. // These data elements may come from either the friend request notification form or $handsfree array.
  61. if(is_array($handsfree)) {
  62. logger('Confirm in handsfree mode');
  63. $dfrn_id = $handsfree['dfrn_id'];
  64. $intro_id = $handsfree['intro_id'];
  65. $duplex = $handsfree['duplex'];
  66. $hidden = ((array_key_exists('hidden',$handsfree)) ? intval($handsfree['hidden']) : 0 );
  67. $activity = ((array_key_exists('activity',$handsfree)) ? intval($handsfree['activity']) : 0 );
  68. }
  69. else {
  70. $dfrn_id = ((x($_POST,'dfrn_id')) ? notags(trim($_POST['dfrn_id'])) : "");
  71. $intro_id = ((x($_POST,'intro_id')) ? intval($_POST['intro_id']) : 0 );
  72. $duplex = ((x($_POST,'duplex')) ? intval($_POST['duplex']) : 0 );
  73. $cid = ((x($_POST,'contact_id')) ? intval($_POST['contact_id']) : 0 );
  74. $hidden = ((x($_POST,'hidden')) ? intval($_POST['hidden']) : 0 );
  75. $activity = ((x($_POST,'activity')) ? intval($_POST['activity']) : 0 );
  76. }
  77. /*
  78. *
  79. * Ensure that dfrn_id has precedence when we go to find the contact record.
  80. * We only want to search based on contact id if there is no dfrn_id,
  81. * e.g. for OStatus network followers.
  82. *
  83. */
  84. if(strlen($dfrn_id))
  85. $cid = 0;
  86. logger('Confirming request for dfrn_id (issued) ' . $dfrn_id);
  87. if($cid)
  88. logger('Confirming follower with contact_id: ' . $cid);
  89. /*
  90. *
  91. * The other person will have been issued an ID when they first requested friendship.
  92. * Locate their record. At this time, their record will have both pending and blocked set to 1.
  93. * There won't be any dfrn_id if this is a network follower, so use the contact_id instead.
  94. *
  95. */
  96. $r = q("SELECT * FROM `contact` WHERE ( ( `issued-id` != '' AND `issued-id` = '%s' ) OR ( `id` = %d AND `id` != 0 ) ) AND `uid` = %d AND `duplex` = 0 LIMIT 1",
  97. dbesc($dfrn_id),
  98. intval($cid),
  99. intval($uid)
  100. );
  101. if (! dbm::is_result($r)) {
  102. logger('Contact not found in DB.');
  103. notice( t('Contact not found.') . EOL );
  104. notice( t('This may occasionally happen if contact was requested by both persons and it has already been approved.') . EOL );
  105. return;
  106. }
  107. $contact = $r[0];
  108. $contact_id = $contact['id'];
  109. $relation = $contact['rel'];
  110. $site_pubkey = $contact['site-pubkey'];
  111. $dfrn_confirm = $contact['confirm'];
  112. $aes_allow = $contact['aes_allow'];
  113. $network = ((strlen($contact['issued-id'])) ? NETWORK_DFRN : NETWORK_OSTATUS);
  114. if($contact['network'])
  115. $network = $contact['network'];
  116. if($network === NETWORK_DFRN) {
  117. /*
  118. *
  119. * Generate a key pair for all further communications with this person.
  120. * We have a keypair for every contact, and a site key for unknown people.
  121. * This provides a means to carry on relationships with other people if
  122. * any single key is compromised. It is a robust key. We're much more
  123. * worried about key leakage than anybody cracking it.
  124. *
  125. */
  126. require_once('include/crypto.php');
  127. $res = new_keypair(4096);
  128. $private_key = $res['prvkey'];
  129. $public_key = $res['pubkey'];
  130. // Save the private key. Send them the public key.
  131. $r = q("UPDATE `contact` SET `prvkey` = '%s' WHERE `id` = %d AND `uid` = %d",
  132. dbesc($private_key),
  133. intval($contact_id),
  134. intval($uid)
  135. );
  136. $params = array();
  137. /*
  138. *
  139. * Per the DFRN protocol, we will verify both ends by encrypting the dfrn_id with our
  140. * site private key (person on the other end can decrypt it with our site public key).
  141. * Then encrypt our profile URL with the other person's site public key. They can decrypt
  142. * it with their site private key. If the decryption on the other end fails for either
  143. * item, it indicates tampering or key failure on at least one site and we will not be
  144. * able to provide a secure communication pathway.
  145. *
  146. * If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3
  147. * or later) then we encrypt the personal public key we send them using AES-256-CBC and a
  148. * random key which is encrypted with their site public key.
  149. *
  150. */
  151. $src_aes_key = random_string();
  152. $result = '';
  153. openssl_private_encrypt($dfrn_id,$result,$user[0]['prvkey']);
  154. $params['dfrn_id'] = bin2hex($result);
  155. $params['public_key'] = $public_key;
  156. $my_url = App::get_baseurl() . '/profile/' . $user[0]['nickname'];
  157. openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey);
  158. $params['source_url'] = bin2hex($params['source_url']);
  159. if($aes_allow && function_exists('openssl_encrypt')) {
  160. openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey);
  161. $params['aes_key'] = bin2hex($params['aes_key']);
  162. $params['public_key'] = bin2hex(openssl_encrypt($public_key,'AES-256-CBC',$src_aes_key));
  163. }
  164. $params['dfrn_version'] = DFRN_PROTOCOL_VERSION ;
  165. if($duplex == 1)
  166. $params['duplex'] = 1;
  167. if($user[0]['page-flags'] == PAGE_COMMUNITY)
  168. $params['page'] = 1;
  169. if($user[0]['page-flags'] == PAGE_PRVGROUP)
  170. $params['page'] = 2;
  171. logger('Confirm: posting data to ' . $dfrn_confirm . ': ' . print_r($params,true), LOGGER_DATA);
  172. /*
  173. *
  174. * POST all this stuff to the other site.
  175. * Temporarily raise the network timeout to 120 seconds because the default 60
  176. * doesn't always give the other side quite enough time to decrypt everything.
  177. *
  178. */
  179. $a->config['system']['curl_timeout'] = 120;
  180. $res = post_url($dfrn_confirm,$params);
  181. logger(' Confirm: received data: ' . $res, LOGGER_DATA);
  182. // Now figure out what they responded. Try to be robust if the remote site is
  183. // having difficulty and throwing up errors of some kind.
  184. $leading_junk = substr($res,0,strpos($res,'<?xml'));
  185. $res = substr($res,strpos($res,'<?xml'));
  186. if(! strlen($res)) {
  187. // No XML at all, this exchange is messed up really bad.
  188. // We shouldn't proceed, because the xml parser might choke,
  189. // and $status is going to be zero, which indicates success.
  190. // We can hardly call this a success.
  191. notice( t('Response from remote site was not understood.') . EOL);
  192. return;
  193. }
  194. if(strlen($leading_junk) && get_config('system','debugging')) {
  195. // This might be more common. Mixed error text and some XML.
  196. // If we're configured for debugging, show the text. Proceed in either case.
  197. notice( t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL );
  198. }
  199. if(stristr($res, "<status")===false) {
  200. // wrong xml! stop here!
  201. notice( t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res) . EOL );
  202. return;
  203. }
  204. $xml = parse_xml_string($res);
  205. $status = (int) $xml->status;
  206. $message = unxmlify($xml->message); // human readable text of what may have gone wrong.
  207. switch($status) {
  208. case 0:
  209. info( t("Confirmation completed successfully.") . EOL);
  210. if(strlen($message))
  211. notice( t('Remote site reported: ') . $message . EOL);
  212. break;
  213. case 1:
  214. // birthday paradox - generate new dfrn-id and fall through.
  215. $new_dfrn_id = random_string();
  216. $r = q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d",
  217. dbesc($new_dfrn_id),
  218. intval($contact_id),
  219. intval($uid)
  220. );
  221. case 2:
  222. notice( t("Temporary failure. Please wait and try again.") . EOL);
  223. if(strlen($message))
  224. notice( t('Remote site reported: ') . $message . EOL);
  225. break;
  226. case 3:
  227. notice( t("Introduction failed or was revoked.") . EOL);
  228. if(strlen($message))
  229. notice( t('Remote site reported: ') . $message . EOL);
  230. break;
  231. }
  232. if(($status == 0) && ($intro_id)) {
  233. // Success. Delete the notification.
  234. $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d",
  235. intval($intro_id),
  236. intval($uid)
  237. );
  238. }
  239. if($status != 0)
  240. return;
  241. }
  242. /*
  243. *
  244. * We have now established a relationship with the other site.
  245. * Let's make our own personal copy of their profile photo so we don't have
  246. * to always load it from their site.
  247. *
  248. * We will also update the contact record with the nature and scope of the relationship.
  249. *
  250. */
  251. require_once('include/Photo.php');
  252. update_contact_avatar($contact['photo'],$uid,$contact_id);
  253. logger('dfrn_confirm: confirm - imported photos');
  254. if($network === NETWORK_DFRN) {
  255. $new_relation = CONTACT_IS_FOLLOWER;
  256. if(($relation == CONTACT_IS_SHARING) || ($duplex))
  257. $new_relation = CONTACT_IS_FRIEND;
  258. if(($relation == CONTACT_IS_SHARING) && ($duplex))
  259. $duplex = 0;
  260. $r = q("UPDATE `contact` SET `rel` = %d,
  261. `name-date` = '%s',
  262. `uri-date` = '%s',
  263. `blocked` = 0,
  264. `pending` = 0,
  265. `duplex` = %d,
  266. `hidden` = %d,
  267. `network` = '%s' WHERE `id` = %d
  268. ",
  269. intval($new_relation),
  270. dbesc(datetime_convert()),
  271. dbesc(datetime_convert()),
  272. intval($duplex),
  273. intval($hidden),
  274. dbesc(NETWORK_DFRN),
  275. intval($contact_id)
  276. );
  277. }
  278. else {
  279. // $network !== NETWORK_DFRN
  280. $network = (($contact['network']) ? $contact['network'] : NETWORK_OSTATUS);
  281. $notify = (($contact['notify']) ? $contact['notify'] : '');
  282. $poll = (($contact['poll']) ? $contact['poll'] : '');
  283. if((! $contact['notify']) || (! $contact['poll'])) {
  284. $arr = Probe::lrdd($contact['url']);
  285. if(count($arr)) {
  286. foreach($arr as $link) {
  287. if($link['@attributes']['rel'] === 'salmon')
  288. $notify = $link['@attributes']['href'];
  289. if($link['@attributes']['rel'] === NAMESPACE_FEED)
  290. $poll = $link['@attributes']['href'];
  291. }
  292. }
  293. }
  294. $new_relation = $contact['rel'];
  295. $writable = $contact['writable'];
  296. if($network === NETWORK_DIASPORA) {
  297. if($duplex)
  298. $new_relation = CONTACT_IS_FRIEND;
  299. else
  300. $new_relation = CONTACT_IS_FOLLOWER;
  301. if($new_relation != CONTACT_IS_FOLLOWER)
  302. $writable = 1;
  303. }
  304. $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d",
  305. intval($intro_id),
  306. intval($uid)
  307. );
  308. $r = q("UPDATE `contact` SET `name-date` = '%s',
  309. `uri-date` = '%s',
  310. `notify` = '%s',
  311. `poll` = '%s',
  312. `blocked` = 0,
  313. `pending` = 0,
  314. `network` = '%s',
  315. `writable` = %d,
  316. `hidden` = %d,
  317. `rel` = %d
  318. WHERE `id` = %d
  319. ",
  320. dbesc(datetime_convert()),
  321. dbesc(datetime_convert()),
  322. dbesc($notify),
  323. dbesc($poll),
  324. dbesc($network),
  325. intval($writable),
  326. intval($hidden),
  327. intval($new_relation),
  328. intval($contact_id)
  329. );
  330. }
  331. /// @TODO is dbm::is_result() working here?
  332. if ($r === false) {
  333. notice( t('Unable to set contact photo.') . EOL);
  334. }
  335. // reload contact info
  336. $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
  337. intval($contact_id)
  338. );
  339. if (dbm::is_result($r)) {
  340. $contact = $r[0];
  341. } else {
  342. $contact = null;
  343. }
  344. if ((isset($new_relation) && $new_relation == CONTACT_IS_FRIEND)) {
  345. if (($contact) && ($contact['network'] === NETWORK_DIASPORA)) {
  346. require_once('include/diaspora.php');
  347. $ret = Diaspora::send_share($user[0],$r[0]);
  348. logger('share returns: ' . $ret);
  349. }
  350. // Send a new friend post if we are allowed to...
  351. $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1",
  352. intval($uid)
  353. );
  354. if((dbm::is_result($r)) && ($r[0]['hide-friends'] == 0) && ($activity) && (! $hidden)) {
  355. require_once('include/items.php');
  356. $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
  357. intval($uid)
  358. );
  359. if(count($self)) {
  360. $arr = array();
  361. $arr['guid'] = get_guid(32);
  362. $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $uid);
  363. $arr['uid'] = $uid;
  364. $arr['contact-id'] = $self[0]['id'];
  365. $arr['wall'] = 1;
  366. $arr['type'] = 'wall';
  367. $arr['gravity'] = 0;
  368. $arr['origin'] = 1;
  369. $arr['author-name'] = $arr['owner-name'] = $self[0]['name'];
  370. $arr['author-link'] = $arr['owner-link'] = $self[0]['url'];
  371. $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb'];
  372. $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]';
  373. $APhoto = '[url=' . $self[0]['url'] . ']' . '[img]' . $self[0]['thumb'] . '[/img][/url]';
  374. $B = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
  375. $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]';
  376. $arr['verb'] = ACTIVITY_FRIEND;
  377. $arr['object-type'] = ACTIVITY_OBJ_PERSON;
  378. $arr['body'] = sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$BPhoto;
  379. $arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>'
  380. . '<id>' . $contact['url'] . '/' . $contact['name'] . '</id>';
  381. $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $contact['url'] . '" />' . "\n");
  382. $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $contact['thumb'] . '" />' . "\n");
  383. $arr['object'] .= '</link></object>' . "\n";
  384. $arr['last-child'] = 1;
  385. $arr['allow_cid'] = $user[0]['allow_cid'];
  386. $arr['allow_gid'] = $user[0]['allow_gid'];
  387. $arr['deny_cid'] = $user[0]['deny_cid'];
  388. $arr['deny_gid'] = $user[0]['deny_gid'];
  389. $i = item_store($arr);
  390. if($i)
  391. proc_run(PRIORITY_HIGH, "include/notifier.php", "activity", $i);
  392. }
  393. }
  394. }
  395. $def_gid = get_default_group($uid, $contact["network"]);
  396. if($contact && intval($def_gid))
  397. group_add_member($uid, '', $contact['id'], $def_gid);
  398. // Let's send our user to the contact editor in case they want to
  399. // do anything special with this new friend.
  400. if ($handsfree === null) {
  401. goaway(App::get_baseurl() . '/contacts/' . intval($contact_id));
  402. } else {
  403. return;
  404. }
  405. //NOTREACHED
  406. }
  407. /*
  408. *
  409. *
  410. * End of Scenario 1. [Local confirmation of remote friend request].
  411. *
  412. * Begin Scenario 2. This is the remote response to the above scenario.
  413. * This will take place on the site that originally initiated the friend request.
  414. * In the section above where the confirming party makes a POST and
  415. * retrieves xml status information, they are communicating with the following code.
  416. *
  417. */
  418. if (x($_POST,'source_url')) {
  419. // We are processing an external confirmation to an introduction created by our user.
  420. $public_key = ((x($_POST,'public_key')) ? $_POST['public_key'] : '');
  421. $dfrn_id = ((x($_POST,'dfrn_id')) ? hex2bin($_POST['dfrn_id']) : '');
  422. $source_url = ((x($_POST,'source_url')) ? hex2bin($_POST['source_url']) : '');
  423. $aes_key = ((x($_POST,'aes_key')) ? $_POST['aes_key'] : '');
  424. $duplex = ((x($_POST,'duplex')) ? intval($_POST['duplex']) : 0 );
  425. $page = ((x($_POST,'page')) ? intval($_POST['page']) : 0 );
  426. $version_id = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0);
  427. $forum = (($page == 1) ? 1 : 0);
  428. $prv = (($page == 2) ? 1 : 0);
  429. logger('dfrn_confirm: requestee contacted: ' . $node);
  430. logger('dfrn_confirm: request: POST=' . print_r($_POST,true), LOGGER_DATA);
  431. // If $aes_key is set, both of these items require unpacking from the hex transport encoding.
  432. if (x($aes_key)) {
  433. $aes_key = hex2bin($aes_key);
  434. $public_key = hex2bin($public_key);
  435. }
  436. // Find our user's account
  437. $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' LIMIT 1",
  438. dbesc($node));
  439. if (! dbm::is_result($r)) {
  440. $message = sprintf(t('No user record found for \'%s\' '), $node);
  441. xml_status(3,$message); // failure
  442. // NOTREACHED
  443. }
  444. $my_prvkey = $r[0]['prvkey'];
  445. $local_uid = $r[0]['uid'];
  446. if(! strstr($my_prvkey,'PRIVATE KEY')) {
  447. $message = t('Our site encryption key is apparently messed up.');
  448. xml_status(3,$message);
  449. }
  450. // verify everything
  451. $decrypted_source_url = "";
  452. openssl_private_decrypt($source_url,$decrypted_source_url,$my_prvkey);
  453. if(! strlen($decrypted_source_url)) {
  454. $message = t('Empty site URL was provided or URL could not be decrypted by us.');
  455. xml_status(3,$message);
  456. // NOTREACHED
  457. }
  458. $ret = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
  459. dbesc($decrypted_source_url),
  460. intval($local_uid)
  461. );
  462. if(! count($ret)) {
  463. if(strstr($decrypted_source_url,'http:'))
  464. $newurl = str_replace('http:','https:',$decrypted_source_url);
  465. else
  466. $newurl = str_replace('https:','http:',$decrypted_source_url);
  467. $ret = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
  468. dbesc($newurl),
  469. intval($local_uid)
  470. );
  471. if(! count($ret)) {
  472. // this is either a bogus confirmation (?) or we deleted the original introduction.
  473. $message = t('Contact record was not found for you on our site.');
  474. xml_status(3,$message);
  475. return; // NOTREACHED
  476. }
  477. }
  478. $relation = $ret[0]['rel'];
  479. // Decrypt all this stuff we just received
  480. $foreign_pubkey = $ret[0]['site-pubkey'];
  481. $dfrn_record = $ret[0]['id'];
  482. if(! $foreign_pubkey) {
  483. $message = sprintf( t('Site public key not available in contact record for URL %s.'), $newurl);
  484. xml_status(3,$message);
  485. }
  486. $decrypted_dfrn_id = "";
  487. openssl_public_decrypt($dfrn_id,$decrypted_dfrn_id,$foreign_pubkey);
  488. if(strlen($aes_key)) {
  489. $decrypted_aes_key = "";
  490. openssl_private_decrypt($aes_key,$decrypted_aes_key,$my_prvkey);
  491. $dfrn_pubkey = openssl_decrypt($public_key,'AES-256-CBC',$decrypted_aes_key);
  492. }
  493. else {
  494. $dfrn_pubkey = $public_key;
  495. }
  496. $r = q("SELECT * FROM `contact` WHERE `dfrn-id` = '%s' LIMIT 1",
  497. dbesc($decrypted_dfrn_id)
  498. );
  499. if (dbm::is_result($r)) {
  500. $message = t('The ID provided by your system is a duplicate on our system. It should work if you try again.');
  501. xml_status(1,$message); // Birthday paradox - duplicate dfrn-id
  502. // NOTREACHED
  503. }
  504. $r = q("UPDATE `contact` SET `dfrn-id` = '%s', `pubkey` = '%s' WHERE `id` = %d",
  505. dbesc($decrypted_dfrn_id),
  506. dbesc($dfrn_pubkey),
  507. intval($dfrn_record)
  508. );
  509. if (! dbm::is_result($r)) {
  510. $message = t('Unable to set your contact credentials on our system.');
  511. xml_status(3,$message);
  512. }
  513. // It's possible that the other person also requested friendship.
  514. // If it is a duplex relationship, ditch the issued-id if one exists.
  515. if($duplex) {
  516. $r = q("UPDATE `contact` SET `issued-id` = '' WHERE `id` = %d",
  517. intval($dfrn_record)
  518. );
  519. }
  520. // We're good but now we have to scrape the profile photo and send notifications.
  521. $r = q("SELECT `photo` FROM `contact` WHERE `id` = %d LIMIT 1",
  522. intval($dfrn_record));
  523. if (dbm::is_result($r)) {
  524. $photo = $r[0]['photo'];
  525. } else {
  526. $photo = App::get_baseurl() . '/images/person-175.jpg';
  527. }
  528. require_once("include/Photo.php");
  529. update_contact_avatar($photo,$local_uid,$dfrn_record);
  530. logger('dfrn_confirm: request - photos imported');
  531. $new_relation = CONTACT_IS_SHARING;
  532. if (($relation == CONTACT_IS_FOLLOWER) || ($duplex)) {
  533. $new_relation = CONTACT_IS_FRIEND;
  534. }
  535. if (($relation == CONTACT_IS_FOLLOWER) && ($duplex)) {
  536. $duplex = 0;
  537. }
  538. $r = q("UPDATE `contact` SET
  539. `rel` = %d,
  540. `name-date` = '%s',
  541. `uri-date` = '%s',
  542. `blocked` = 0,
  543. `pending` = 0,
  544. `duplex` = %d,
  545. `forum` = %d,
  546. `prv` = %d,
  547. `network` = '%s' WHERE `id` = %d
  548. ",
  549. intval($new_relation),
  550. dbesc(datetime_convert()),
  551. dbesc(datetime_convert()),
  552. intval($duplex),
  553. intval($forum),
  554. intval($prv),
  555. dbesc(NETWORK_DFRN),
  556. intval($dfrn_record)
  557. );
  558. if ($r === false) { // indicates schema is messed up or total db failure
  559. $message = t('Unable to update your contact profile details on our system');
  560. xml_status(3,$message);
  561. }
  562. // Otherwise everything seems to have worked and we are almost done. Yay!
  563. // Send an email notification
  564. logger('dfrn_confirm: request: info updated');
  565. $r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
  566. WHERE `contact`.`id` = %d LIMIT 1",
  567. intval($dfrn_record)
  568. );
  569. if (dbm::is_result($r))
  570. $combined = $r[0];
  571. if((dbm::is_result($r)) && ($r[0]['notify-flags'] & NOTIFY_CONFIRM)) {
  572. $mutual = ($new_relation == CONTACT_IS_FRIEND);
  573. notification(array(
  574. 'type' => NOTIFY_CONFIRM,
  575. 'notify_flags' => $r[0]['notify-flags'],
  576. 'language' => $r[0]['language'],
  577. 'to_name' => $r[0]['username'],
  578. 'to_email' => $r[0]['email'],
  579. 'uid' => $r[0]['uid'],
  580. 'link' => App::get_baseurl() . '/contacts/' . $dfrn_record,
  581. 'source_name' => ((strlen(stripslashes($r[0]['name']))) ? stripslashes($r[0]['name']) : t('[Name Withheld]')),
  582. 'source_link' => $r[0]['url'],
  583. 'source_photo' => $r[0]['photo'],
  584. 'verb' => ($mutual?ACTIVITY_FRIEND:ACTIVITY_FOLLOW),
  585. 'otype' => 'intro'
  586. ));
  587. }
  588. // Send a new friend post if we are allowed to...
  589. if($page && intval(get_pconfig($local_uid,'system','post_joingroup'))) {
  590. $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1",
  591. intval($local_uid)
  592. );
  593. if((dbm::is_result($r)) && ($r[0]['hide-friends'] == 0)) {
  594. require_once('include/items.php');
  595. $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
  596. intval($local_uid)
  597. );
  598. if(count($self)) {
  599. $arr = array();
  600. $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $local_uid);
  601. $arr['uid'] = $local_uid;
  602. $arr['contact-id'] = $self[0]['id'];
  603. $arr['wall'] = 1;
  604. $arr['type'] = 'wall';
  605. $arr['gravity'] = 0;
  606. $arr['origin'] = 1;
  607. $arr['author-name'] = $arr['owner-name'] = $self[0]['name'];
  608. $arr['author-link'] = $arr['owner-link'] = $self[0]['url'];
  609. $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb'];
  610. $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]';
  611. $APhoto = '[url=' . $self[0]['url'] . ']' . '[img]' . $self[0]['thumb'] . '[/img][/url]';
  612. $B = '[url=' . $combined['url'] . ']' . $combined['name'] . '[/url]';
  613. $BPhoto = '[url=' . $combined['url'] . ']' . '[img]' . $combined['thumb'] . '[/img][/url]';
  614. $arr['verb'] = ACTIVITY_JOIN;
  615. $arr['object-type'] = ACTIVITY_OBJ_GROUP;
  616. $arr['body'] = sprintf( t('%1$s has joined %2$s'), $A, $B)."\n\n\n" .$BPhoto;
  617. $arr['object'] = '<object><type>' . ACTIVITY_OBJ_GROUP . '</type><title>' . $combined['name'] . '</title>'
  618. . '<id>' . $combined['url'] . '/' . $combined['name'] . '</id>';
  619. $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $combined['url'] . '" />' . "\n");
  620. $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $combined['thumb'] . '" />' . "\n");
  621. $arr['object'] .= '</link></object>' . "\n";
  622. $arr['last-child'] = 1;
  623. $arr['allow_cid'] = $user[0]['allow_cid'];
  624. $arr['allow_gid'] = $user[0]['allow_gid'];
  625. $arr['deny_cid'] = $user[0]['deny_cid'];
  626. $arr['deny_gid'] = $user[0]['deny_gid'];
  627. $i = item_store($arr);
  628. if($i)
  629. proc_run(PRIORITY_HIGH, "include/notifier.php", "activity", $i);
  630. }
  631. }
  632. }
  633. xml_status(0); // Success
  634. return; // NOTREACHED
  635. ////////////////////// End of this scenario ///////////////////////////////////////////////
  636. }
  637. // somebody arrived here by mistake or they are fishing. Send them to the homepage.
  638. goaway(z_root());
  639. // NOTREACHED
  640. }