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.

548 lines
15 KiB

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
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
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
  1. <?php
  2. require_once('include/items.php');
  3. require_once('include/auth.php');
  4. function dfrn_poll_init(&$a) {
  5. $dfrn_id = ((x($_GET,'dfrn_id')) ? $_GET['dfrn_id'] : '');
  6. $type = ((x($_GET,'type')) ? $_GET['type'] : 'data');
  7. $last_update = ((x($_GET,'last_update')) ? $_GET['last_update'] : '');
  8. $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url'] : '');
  9. $challenge = ((x($_GET,'challenge')) ? $_GET['challenge'] : '');
  10. $sec = ((x($_GET,'sec')) ? $_GET['sec'] : '');
  11. $dfrn_version = ((x($_GET,'dfrn_version')) ? (float) $_GET['dfrn_version'] : 2.0);
  12. $perm = ((x($_GET,'perm')) ? $_GET['perm'] : 'r');
  13. $direction = (-1);
  14. if(strpos($dfrn_id,':') == 1) {
  15. $direction = intval(substr($dfrn_id,0,1));
  16. $dfrn_id = substr($dfrn_id,2);
  17. }
  18. if(($dfrn_id === '') && (! x($_POST,'dfrn_id')) && ($a->argc > 1)) {
  19. if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
  20. killme();
  21. }
  22. $r = q("SELECT `hidewall` FROM `user` WHERE `user`.`nickname` = '%s' LIMIT 1",
  23. dbesc($a->argv[1])
  24. );
  25. if(count($r) && $r[0]['hidewall'])
  26. killme();
  27. logger('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] );
  28. header("Content-type: application/atom+xml");
  29. $o = get_feed_for($a, '', $a->argv[1],$last_update);
  30. echo $o;
  31. killme();
  32. }
  33. if(($type === 'profile') && (! strlen($sec))) {
  34. $sql_extra = '';
  35. switch($direction) {
  36. case (-1):
  37. $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
  38. $my_id = $dfrn_id;
  39. break;
  40. case 0:
  41. $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  42. $my_id = '1:' . $dfrn_id;
  43. break;
  44. case 1:
  45. $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  46. $my_id = '0:' . $dfrn_id;
  47. break;
  48. default:
  49. goaway($a->get_baseurl());
  50. break; // NOTREACHED
  51. }
  52. $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
  53. FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
  54. WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  55. AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
  56. dbesc($a->argv[1])
  57. );
  58. if(count($r)) {
  59. $s = fetch_url($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
  60. logger("dfrn_poll: old profile returns " . $s, LOGGER_DATA);
  61. if(strlen($s)) {
  62. $xml = parse_xml_string($s);
  63. if((int) $xml->status == 1) {
  64. $_SESSION['authenticated'] = 1;
  65. $_SESSION['visitor_id'] = $r[0]['id'];
  66. $_SESSION['visitor_home'] = $r[0]['url'];
  67. $_SESSION['visitor_visiting'] = $r[0]['uid'];
  68. info( sprintf(t('%s welcomes %s'), $r[0]['username'] , $r[0]['name']) . EOL);
  69. // Visitors get 1 day session.
  70. $session_id = session_id();
  71. $expire = time() + 86400;
  72. q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s' LIMIT 1",
  73. dbesc($expire),
  74. dbesc($session_id)
  75. );
  76. }
  77. }
  78. $profile = $r[0]['nickname'];
  79. goaway((strlen($destination_url)) ? $destination_url : $a->get_baseurl() . '/profile/' . $profile);
  80. }
  81. goaway($a->get_baseurl());
  82. }
  83. if($type === 'profile-check' && $dfrn_version < 2.2 ) {
  84. if((strlen($challenge)) && (strlen($sec))) {
  85. q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
  86. $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
  87. dbesc($sec)
  88. );
  89. if(! count($r)) {
  90. xml_status(3, 'No ticket');
  91. // NOTREACHED
  92. }
  93. $orig_id = $r[0]['dfrn_id'];
  94. if(strpos($orig_id, ':'))
  95. $orig_id = substr($orig_id,2);
  96. $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
  97. intval($r[0]['cid'])
  98. );
  99. if(! count($c)) {
  100. xml_status(3, 'No profile');
  101. }
  102. $contact = $c[0];
  103. $sent_dfrn_id = hex2bin($dfrn_id);
  104. $challenge = hex2bin($challenge);
  105. $final_dfrn_id = '';
  106. if(($contact['duplex']) && strlen($contact['prvkey'])) {
  107. openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
  108. openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
  109. }
  110. else {
  111. openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
  112. openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
  113. }
  114. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  115. if(strpos($final_dfrn_id,':') == 1)
  116. $final_dfrn_id = substr($final_dfrn_id,2);
  117. if($final_dfrn_id != $orig_id) {
  118. logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
  119. // did not decode properly - cannot trust this site
  120. xml_status(3, 'Bad decryption');
  121. }
  122. header("Content-type: text/xml");
  123. echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
  124. killme();
  125. // NOTREACHED
  126. }
  127. else {
  128. // old protocol
  129. switch($direction) {
  130. case 1:
  131. $dfrn_id = '0:' . $dfrn_id;
  132. break;
  133. case 0:
  134. $dfrn_id = '1:' . $dfrn_id;
  135. break;
  136. default:
  137. break;
  138. }
  139. q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
  140. $r = q("SELECT * FROM `profile_check` WHERE `dfrn_id` = '%s' ORDER BY `expire` DESC",
  141. dbesc($dfrn_id));
  142. if(count($r)) {
  143. xml_status(1);
  144. return; // NOTREACHED
  145. }
  146. xml_status(0);
  147. return; // NOTREACHED
  148. }
  149. }
  150. }
  151. function dfrn_poll_post(&$a) {
  152. $dfrn_id = ((x($_POST,'dfrn_id')) ? $_POST['dfrn_id'] : '');
  153. $challenge = ((x($_POST,'challenge')) ? $_POST['challenge'] : '');
  154. $url = ((x($_POST,'url')) ? $_POST['url'] : '');
  155. $sec = ((x($_POST,'sec')) ? $_POST['sec'] : '');
  156. $ptype = ((x($_POST,'type')) ? $_POST['type'] : '');
  157. $dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0);
  158. $perm = ((x($_POST,'perm')) ? $_POST['perm'] : 'r');
  159. if($ptype === 'profile-check') {
  160. if((strlen($challenge)) && (strlen($sec))) {
  161. logger('dfrn_poll: POST: profile-check');
  162. q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
  163. $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
  164. dbesc($sec)
  165. );
  166. if(! count($r)) {
  167. xml_status(3, 'No ticket');
  168. // NOTREACHED
  169. }
  170. $orig_id = $r[0]['dfrn_id'];
  171. if(strpos($orig_id, ':'))
  172. $orig_id = substr($orig_id,2);
  173. $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
  174. intval($r[0]['cid'])
  175. );
  176. if(! count($c)) {
  177. xml_status(3, 'No profile');
  178. }
  179. $contact = $c[0];
  180. $sent_dfrn_id = hex2bin($dfrn_id);
  181. $challenge = hex2bin($challenge);
  182. $final_dfrn_id = '';
  183. if(($contact['duplex']) && strlen($contact['prvkey'])) {
  184. openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
  185. openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
  186. }
  187. else {
  188. openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
  189. openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
  190. }
  191. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  192. if(strpos($final_dfrn_id,':') == 1)
  193. $final_dfrn_id = substr($final_dfrn_id,2);
  194. if($final_dfrn_id != $orig_id) {
  195. logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
  196. // did not decode properly - cannot trust this site
  197. xml_status(3, 'Bad decryption');
  198. }
  199. header("Content-type: text/xml");
  200. echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
  201. killme();
  202. // NOTREACHED
  203. }
  204. }
  205. $direction = (-1);
  206. if(strpos($dfrn_id,':') == 1) {
  207. $direction = intval(substr($dfrn_id,0,1));
  208. $dfrn_id = substr($dfrn_id,2);
  209. }
  210. $r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
  211. dbesc($dfrn_id),
  212. dbesc($challenge)
  213. );
  214. if(! count($r))
  215. killme();
  216. $type = $r[0]['type'];
  217. $last_update = $r[0]['last_update'];
  218. $r = q("DELETE FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
  219. dbesc($dfrn_id),
  220. dbesc($challenge)
  221. );
  222. $sql_extra = '';
  223. switch($direction) {
  224. case (-1):
  225. $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
  226. $my_id = $dfrn_id;
  227. break;
  228. case 0:
  229. $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  230. $my_id = '1:' . $dfrn_id;
  231. break;
  232. case 1:
  233. $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  234. $my_id = '0:' . $dfrn_id;
  235. break;
  236. default:
  237. goaway($a->get_baseurl());
  238. break; // NOTREACHED
  239. }
  240. $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1");
  241. if(! count($r))
  242. killme();
  243. $contact = $r[0];
  244. $owner_uid = $r[0]['uid'];
  245. $contact_id = $r[0]['id'];
  246. if($type === 'reputation' && strlen($url)) {
  247. $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
  248. dbesc($url),
  249. intval($owner_uid)
  250. );
  251. $reputation = 0;
  252. $text = '';
  253. if(count($r)) {
  254. $reputation = $r[0]['rating'];
  255. $text = $r[0]['reason'];
  256. if($r[0]['id'] == $contact_id) { // inquiring about own reputation not allowed
  257. $reputation = 0;
  258. $text = '';
  259. }
  260. }
  261. echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
  262. <reputation>
  263. <url>$url</url>
  264. <rating>$reputation</rating>
  265. <description>$text</description>
  266. </reputation>
  267. ";
  268. killme();
  269. // NOTREACHED
  270. }
  271. else {
  272. // Update the writable flag if it changed
  273. logger('dfrn_poll: post request feed: ' . print_r($_POST,true),LOGGER_DATA);
  274. if($dfrn_version >= 2.21) {
  275. if($perm === 'rw')
  276. $writable = 1;
  277. else
  278. $writable = 0;
  279. if($writable != $contact['writable']) {
  280. q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d LIMIT 1",
  281. intval($writable),
  282. intval($contact_id)
  283. );
  284. }
  285. }
  286. header("Content-type: application/atom+xml");
  287. $o = get_feed_for($a,$dfrn_id, $a->argv[1], $last_update, $direction);
  288. echo $o;
  289. killme();
  290. }
  291. }
  292. function dfrn_poll_content(&$a) {
  293. $dfrn_id = ((x($_GET,'dfrn_id')) ? $_GET['dfrn_id'] : '');
  294. $type = ((x($_GET,'type')) ? $_GET['type'] : 'data');
  295. $last_update = ((x($_GET,'last_update')) ? $_GET['last_update'] : '');
  296. $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url'] : '');
  297. $sec = ((x($_GET,'sec')) ? $_GET['sec'] : '');
  298. $dfrn_version = ((x($_GET,'dfrn_version')) ? (float) $_GET['dfrn_version'] : 2.0);
  299. $perm = ((x($_GET,'perm')) ? $_GET['perm'] : 'r');
  300. $direction = (-1);
  301. if(strpos($dfrn_id,':') == 1) {
  302. $direction = intval(substr($dfrn_id,0,1));
  303. $dfrn_id = substr($dfrn_id,2);
  304. }
  305. if($dfrn_id != '') {
  306. // initial communication from external contact
  307. $hash = random_string();
  308. $status = 0;
  309. $r = q("DELETE FROM `challenge` WHERE `expire` < " . intval(time()));
  310. if($type !== 'profile') {
  311. $r = q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` )
  312. VALUES( '%s', '%s', '%s', '%s', '%s' ) ",
  313. dbesc($hash),
  314. dbesc($dfrn_id),
  315. intval(time() + 60 ),
  316. dbesc($type),
  317. dbesc($last_update)
  318. );
  319. }
  320. $sql_extra = '';
  321. switch($direction) {
  322. case (-1):
  323. if($type === 'profile')
  324. $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
  325. else
  326. $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
  327. $my_id = $dfrn_id;
  328. break;
  329. case 0:
  330. $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  331. $my_id = '1:' . $dfrn_id;
  332. break;
  333. case 1:
  334. $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  335. $my_id = '0:' . $dfrn_id;
  336. break;
  337. default:
  338. goaway($a->get_baseurl());
  339. break; // NOTREACHED
  340. }
  341. $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
  342. FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
  343. WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  344. AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
  345. dbesc($a->argv[1])
  346. );
  347. if(count($r)) {
  348. $challenge = '';
  349. $encrypted_id = '';
  350. $id_str = $my_id . '.' . mt_rand(1000,9999);
  351. if($r[0]['duplex'] && strlen($r[0]['pubkey'])) {
  352. openssl_public_encrypt($hash,$challenge,$r[0]['pubkey']);
  353. openssl_public_encrypt($id_str,$encrypted_id,$r[0]['pubkey']);
  354. }
  355. else {
  356. openssl_private_encrypt($hash,$challenge,$r[0]['prvkey']);
  357. openssl_private_encrypt($id_str,$encrypted_id,$r[0]['prvkey']);
  358. }
  359. $challenge = bin2hex($challenge);
  360. $encrypted_id = bin2hex($encrypted_id);
  361. }
  362. else {
  363. $status = 1;
  364. $challenge = '';
  365. $encrypted_id = '';
  366. }
  367. if(($type === 'profile') && (strlen($sec))) {
  368. // URL reply
  369. if($dfrn_version < 2.2) {
  370. $s = fetch_url($r[0]['poll']
  371. . '?dfrn_id=' . $encrypted_id
  372. . '&type=profile-check'
  373. . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
  374. . '&challenge=' . $challenge
  375. . '&sec=' . $sec
  376. );
  377. }
  378. else {
  379. $s = post_url($r[0]['poll'], array(
  380. 'dfrn_id' => $encrypted_id,
  381. 'type' => 'profile-check',
  382. 'dfrn_version' => DFRN_PROTOCOL_VERSION,
  383. 'challenge' => $challenge,
  384. 'sec' => $sec
  385. ));
  386. }
  387. $profile = $r[0]['nickname'];
  388. switch($destination_url) {
  389. case 'profile':
  390. $dest = $a->get_baseurl() . '/profile/' . $profile . '?tab=profile';
  391. break;
  392. case 'photos':
  393. $dest = $a->get_baseurl() . '/photos/' . $profile;
  394. break;
  395. case 'status':
  396. case '':
  397. $dest = $a->get_baseurl() . '/profile/' . $profile;
  398. break;
  399. default:
  400. $dest = $destination_url;
  401. break;
  402. }
  403. logger("dfrn_poll: sec profile: " . $s, LOGGER_DATA);
  404. if(strlen($s) && strstr($s,'<?xml')) {
  405. $xml = parse_xml_string($s);
  406. logger('dfrn_poll: profile: parsed xml: ' . print_r($xml,true), LOGGER_DATA);
  407. logger('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash);
  408. logger('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec);
  409. if(((int) $xml->status == 0) && ($xml->challenge == $hash) && ($xml->sec == $sec)) {
  410. $_SESSION['authenticated'] = 1;
  411. $_SESSION['visitor_id'] = $r[0]['id'];
  412. $_SESSION['visitor_home'] = $r[0]['url'];
  413. $_SESSION['visitor_visiting'] = $r[0]['uid'];
  414. info( sprintf(t('%s welcomes %s'), $r[0]['username'] , $r[0]['name']) . EOL);
  415. // Visitors get 1 day session.
  416. $session_id = session_id();
  417. $expire = time() + 86400;
  418. q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s' LIMIT 1",
  419. dbesc($expire),
  420. dbesc($session_id)
  421. );
  422. }
  423. goaway($dest);
  424. }
  425. goaway($dest);
  426. // NOTREACHED
  427. }
  428. else {
  429. // XML reply
  430. header("Content-type: text/xml");
  431. echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
  432. . '<dfrn_poll>' . "\r\n"
  433. . "\t" . '<status>' . $status . '</status>' . "\r\n"
  434. . "\t" . '<dfrn_version>' . DFRN_PROTOCOL_VERSION . '</dfrn_version>' . "\r\n"
  435. . "\t" . '<dfrn_id>' . $encrypted_id . '</dfrn_id>' . "\r\n"
  436. . "\t" . '<challenge>' . $challenge . '</challenge>' . "\r\n"
  437. . '</dfrn_poll>' . "\r\n" ;
  438. killme();
  439. // NOTREACHED
  440. }
  441. }
  442. }