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.

192 lines
5.0 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. <?php
  2. require_once('include/salmon.php');
  3. require_once('include/ostatus.php');
  4. require_once('include/crypto.php');
  5. require_once('include/items.php');
  6. require_once('include/follow.php');
  7. function salmon_return($val) {
  8. if($val >= 400)
  9. $err = 'Error';
  10. if($val >= 200 && $val < 300)
  11. $err = 'OK';
  12. logger('mod-salmon returns ' . $val);
  13. header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err);
  14. killme();
  15. }
  16. function salmon_post(App $a) {
  17. $xml = file_get_contents('php://input');
  18. logger('mod-salmon: new salmon ' . $xml, LOGGER_DATA);
  19. $nick = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
  20. $mentions = (($a->argc > 2 && $a->argv[2] === 'mention') ? true : false);
  21. $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1",
  22. dbesc($nick)
  23. );
  24. if (! dbm::is_result($r)) {
  25. http_status_exit(500);
  26. }
  27. $importer = $r[0];
  28. // parse the xml
  29. $dom = simplexml_load_string($xml,'SimpleXMLElement',0,NAMESPACE_SALMON_ME);
  30. // figure out where in the DOM tree our data is hiding
  31. if($dom->provenance->data)
  32. $base = $dom->provenance;
  33. elseif($dom->env->data)
  34. $base = $dom->env;
  35. elseif($dom->data)
  36. $base = $dom;
  37. if(! $base) {
  38. logger('mod-salmon: unable to locate salmon data in xml ');
  39. http_status_exit(400);
  40. }
  41. // Stash the signature away for now. We have to find their key or it won't be good for anything.
  42. $signature = base64url_decode($base->sig);
  43. // unpack the data
  44. // strip whitespace so our data element will return to one big base64 blob
  45. $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
  46. // stash away some other stuff for later
  47. $type = $base->data[0]->attributes()->type[0];
  48. $keyhash = $base->sig[0]->attributes()->keyhash[0];
  49. $encoding = $base->encoding;
  50. $alg = $base->alg;
  51. // Salmon magic signatures have evolved and there is no way of knowing ahead of time which
  52. // flavour we have. We'll try and verify it regardless.
  53. $stnet_signed_data = $data;
  54. $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg);
  55. $compliant_format = str_replace('=', '', $signed_data);
  56. // decode the data
  57. $data = base64url_decode($data);
  58. $author = ostatus::salmon_author($data,$importer);
  59. $author_link = $author["author-link"];
  60. if(! $author_link) {
  61. logger('mod-salmon: Could not retrieve author URI.');
  62. http_status_exit(400);
  63. }
  64. // Once we have the author URI, go to the web and try to find their public key
  65. logger('mod-salmon: Fetching key for ' . $author_link);
  66. $key = get_salmon_key($author_link,$keyhash);
  67. if(! $key) {
  68. logger('mod-salmon: Could not retrieve author key.');
  69. http_status_exit(400);
  70. }
  71. $key_info = explode('.',$key);
  72. $m = base64url_decode($key_info[1]);
  73. $e = base64url_decode($key_info[2]);
  74. logger('mod-salmon: key details: ' . print_r($key_info,true), LOGGER_DEBUG);
  75. $pubkey = metopem($m,$e);
  76. // We should have everything we need now. Let's see if it verifies.
  77. // Try GNU Social format
  78. $verify = rsa_verify($signed_data, $signature, $pubkey);
  79. $mode = 1;
  80. if (! $verify) {
  81. logger('mod-salmon: message did not verify using protocol. Trying compliant format.');
  82. $verify = rsa_verify($compliant_format, $signature, $pubkey);
  83. $mode = 2;
  84. }
  85. if (! $verify) {
  86. logger('mod-salmon: message did not verify using padding. Trying old statusnet format.');
  87. $verify = rsa_verify($stnet_signed_data, $signature, $pubkey);
  88. $mode = 3;
  89. }
  90. if (! $verify) {
  91. logger('mod-salmon: Message did not verify. Discarding.');
  92. http_status_exit(400);
  93. }
  94. logger('mod-salmon: Message verified with mode '.$mode);
  95. /*
  96. *
  97. * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
  98. *
  99. */
  100. $r = q("SELECT * FROM `contact` WHERE `network` IN ('%s', '%s')
  101. AND (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s')
  102. AND `uid` = %d LIMIT 1",
  103. dbesc(NETWORK_OSTATUS),
  104. dbesc(NETWORK_DFRN),
  105. dbesc(normalise_link($author_link)),
  106. dbesc($author_link),
  107. dbesc(normalise_link($author_link)),
  108. intval($importer['uid'])
  109. );
  110. if (! dbm::is_result($r)) {
  111. logger('mod-salmon: Author unknown to us.');
  112. if(get_pconfig($importer['uid'],'system','ostatus_autofriend')) {
  113. $result = new_contact($importer['uid'],$author_link);
  114. if($result['success']) {
  115. $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s')
  116. AND `uid` = %d LIMIT 1",
  117. dbesc(NETWORK_OSTATUS),
  118. dbesc($author_link),
  119. dbesc($author_link),
  120. intval($importer['uid'])
  121. );
  122. }
  123. }
  124. }
  125. // Have we ignored the person?
  126. // If so we can not accept this post.
  127. //if((dbm::is_result($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == CONTACT_IS_FOLLOWER) || ($r[0]['blocked']))) {
  128. if (dbm::is_result($r) && $r[0]['blocked']) {
  129. logger('mod-salmon: Ignoring this author.');
  130. http_status_exit(202);
  131. // NOTREACHED
  132. }
  133. // Placeholder for hub discovery.
  134. $hub = '';
  135. $contact_rec = ((dbm::is_result($r)) ? $r[0] : null);
  136. ostatus::import($data,$importer,$contact_rec, $hub);
  137. http_status_exit(200);
  138. }