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.

1165 lines
34 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
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
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
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 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
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
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
  1. <?php
  2. require_once("bbcode.php");
  3. require_once("datetime.php");
  4. require_once("conversation.php");
  5. require_once("oauth.php");
  6. /*
  7. * Twitter-Like API
  8. *
  9. */
  10. $API = Array();
  11. $called_api = Null;
  12. function api_date($str){
  13. //Wed May 23 06:01:13 +0000 2007
  14. return datetime_convert('UTC', 'UTC', $str, "D M d H:i:s +0000 Y" );
  15. }
  16. function api_register_func($path, $func, $auth=false){
  17. global $API;
  18. $API[$path] = array('func'=>$func,
  19. 'auth'=>$auth);
  20. }
  21. /**
  22. * Simple HTTP Login
  23. */
  24. function api_login(&$a){
  25. // workaround for HTTP-auth in CGI mode
  26. if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
  27. $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
  28. if(strlen($userpass)) {
  29. list($name, $password) = explode(':', $userpass);
  30. $_SERVER['PHP_AUTH_USER'] = $name;
  31. $_SERVER['PHP_AUTH_PW'] = $password;
  32. }
  33. }
  34. if (!isset($_SERVER['PHP_AUTH_USER'])) {
  35. logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
  36. header('WWW-Authenticate: Basic realm="Friendika"');
  37. header('HTTP/1.0 401 Unauthorized');
  38. die('This api requires login');
  39. }
  40. $user = $_SERVER['PHP_AUTH_USER'];
  41. $encrypted = hash('whirlpool',trim($_SERVER['PHP_AUTH_PW']));
  42. /**
  43. * next code from mod/auth.php. needs better solution
  44. */
  45. // process normal login request
  46. $r = q("SELECT * FROM `user` WHERE ( `email` = '%s' OR `nickname` = '%s' )
  47. AND `password` = '%s' AND `blocked` = 0 AND `account_expired` = 0 AND `verified` = 1 LIMIT 1",
  48. dbesc(trim($user)),
  49. dbesc(trim($user)),
  50. dbesc($encrypted)
  51. );
  52. if(count($r)){
  53. $record = $r[0];
  54. } else {
  55. logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
  56. header('WWW-Authenticate: Basic realm="Friendika"');
  57. header('HTTP/1.0 401 Unauthorized');
  58. die('This api requires login');
  59. }
  60. $_SESSION['uid'] = $record['uid'];
  61. $_SESSION['theme'] = $record['theme'];
  62. $_SESSION['authenticated'] = 1;
  63. $_SESSION['page_flags'] = $record['page-flags'];
  64. $_SESSION['my_url'] = $a->get_baseurl() . '/profile/' . $record['nickname'];
  65. $_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
  66. //notice( t("Welcome back ") . $record['username'] . EOL);
  67. $a->user = $record;
  68. if(strlen($a->user['timezone'])) {
  69. date_default_timezone_set($a->user['timezone']);
  70. $a->timezone = $a->user['timezone'];
  71. }
  72. $r = q("SELECT * FROM `contact` WHERE `uid` = %s AND `self` = 1 LIMIT 1",
  73. intval($_SESSION['uid']));
  74. if(count($r)) {
  75. $a->contact = $r[0];
  76. $a->cid = $r[0]['id'];
  77. $_SESSION['cid'] = $a->cid;
  78. }
  79. q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1",
  80. dbesc(datetime_convert()),
  81. intval($_SESSION['uid'])
  82. );
  83. call_hooks('logged_in', $a->user);
  84. header('X-Account-Management-Status: active; name="' . $a->user['username'] . '"; id="' . $a->user['nickname'] .'"');
  85. }
  86. /**************************
  87. * MAIN API ENTRY POINT *
  88. **************************/
  89. function api_call(&$a){
  90. GLOBAL $API, $called_api;
  91. foreach ($API as $p=>$info){
  92. if (strpos($a->query_string, $p)===0){
  93. $called_api= explode("/",$p);
  94. #unset($_SERVER['PHP_AUTH_USER']);
  95. if ($info['auth']===true && local_user()===false) {
  96. api_login($a);
  97. }
  98. load_contact_links(local_user());
  99. logger('API call for ' . $a->user['username'] . ': ' . $a->query_string);
  100. logger('API parameters: ' . print_r($_REQUEST,true));
  101. $type="json";
  102. if (strpos($a->query_string, ".xml")>0) $type="xml";
  103. if (strpos($a->query_string, ".json")>0) $type="json";
  104. if (strpos($a->query_string, ".rss")>0) $type="rss";
  105. if (strpos($a->query_string, ".atom")>0) $type="atom";
  106. $r = call_user_func($info['func'], $a, $type);
  107. if ($r===false) return;
  108. switch($type){
  109. case "xml":
  110. $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r));
  111. header ("Content-Type: text/xml");
  112. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  113. break;
  114. case "json":
  115. //header ("Content-Type: application/json");
  116. foreach($r as $rr)
  117. return json_encode($rr);
  118. break;
  119. case "rss":
  120. header ("Content-Type: application/rss+xml");
  121. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  122. break;
  123. case "atom":
  124. header ("Content-Type: application/atom+xml");
  125. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  126. break;
  127. }
  128. //echo "<pre>"; var_dump($r); die();
  129. }
  130. }
  131. $r = '<status><error>not implemented</error></status>';
  132. switch($type){
  133. case "xml":
  134. header ("Content-Type: text/xml");
  135. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  136. break;
  137. case "json":
  138. header ("Content-Type: application/json");
  139. return json_encode(array('error' => 'not implemented'));
  140. break;
  141. case "rss":
  142. header ("Content-Type: application/rss+xml");
  143. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  144. break;
  145. case "atom":
  146. header ("Content-Type: application/atom+xml");
  147. return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r;
  148. break;
  149. }
  150. }
  151. /**
  152. * RSS extra info
  153. */
  154. function api_rss_extra(&$a, $arr, $user_info){
  155. if (is_null($user_info)) $user_info = api_get_user($a);
  156. $arr['$user'] = $user_info;
  157. $arr['$rss'] = array(
  158. 'alternate' => $user_info['url'],
  159. 'self' => $a->get_baseurl(). "/". $a->query_string,
  160. 'base' => $a->get_baseurl(),
  161. 'updated' => api_date(null),
  162. 'atom_updated' => datetime_convert('UTC','UTC','now',ATOM_TIME),
  163. 'language' => $user_info['language'],
  164. 'logo' => $a->get_baseurl()."/images/friendika-32.png",
  165. );
  166. return $arr;
  167. }
  168. /**
  169. * Returns user info array.
  170. */
  171. function api_get_user(&$a, $contact_id = Null){
  172. global $called_api;
  173. $user = null;
  174. $extra_query = "";
  175. if(!is_null($contact_id)){
  176. $user=$contact_id;
  177. $extra_query = "AND `contact`.`id` = %d ";
  178. }
  179. if(is_null($user) && x($_GET, 'user_id')) {
  180. $user = intval($_GET['user_id']);
  181. $extra_query = "AND `contact`.`id` = %d ";
  182. }
  183. if(is_null($user) && x($_GET, 'screen_name')) {
  184. $user = dbesc($_GET['screen_name']);
  185. $extra_query = "AND `contact`.`nick` = '%s' ";
  186. if (local_user()!==false) $extra_query .= "AND `contact`.`uid`=".intval(local_user());
  187. }
  188. if (is_null($user) && $a->argc > (count($called_api)-1)){
  189. $argid = count($called_api);
  190. list($user, $null) = explode(".",$a->argv[$argid]);
  191. if(is_numeric($user)){
  192. $user = intval($user);
  193. $extra_query = "AND `contact`.`id` = %d ";
  194. } else {
  195. $user = dbesc($user);
  196. $extra_query = "AND `contact`.`nick` = '%s' ";
  197. if (local_user()!==false) $extra_query .= "AND `contact`.`uid`=".intval(local_user());
  198. }
  199. }
  200. if (! $user) {
  201. if (local_user()===false) {
  202. api_login($a); return False;
  203. } else {
  204. $user = $_SESSION['uid'];
  205. $extra_query = "AND `contact`.`uid` = %d AND `contact`.`self` = 1 ";
  206. }
  207. }
  208. logger('api_user: ' . $extra_query . ' ' , $user);
  209. // user info
  210. $uinfo = q("SELECT *, `contact`.`id` as `cid` FROM `contact`
  211. WHERE 1
  212. $extra_query",
  213. $user
  214. );
  215. if (count($uinfo)==0) {
  216. return False;
  217. }
  218. if($uinfo[0]['self']) {
  219. $usr = q("select * from user where uid = %d limit 1",
  220. intval(local_user())
  221. );
  222. $profile = q("select * from profile where uid = %d and `is-default` = 1 limit 1",
  223. intval(local_user())
  224. );
  225. // count public wall messages
  226. $r = q("SELECT COUNT(`id`) as `count` FROM `item`
  227. WHERE `uid` = %d
  228. AND `type`='wall'
  229. AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
  230. intval($uinfo[0]['uid'])
  231. );
  232. $countitms = $r[0]['count'];
  233. }
  234. else {
  235. $r = q("SELECT COUNT(`id`) as `count` FROM `item`
  236. WHERE `contact-id` = %d
  237. AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
  238. intval($uinfo[0]['id'])
  239. );
  240. $countitms = $r[0]['count'];
  241. }
  242. // count friends
  243. $r = q("SELECT COUNT(`id`) as `count` FROM `contact`
  244. WHERE `uid` = %d AND `rel` IN ( %d, %d )
  245. AND `self`=0 AND `blocked`=0",
  246. intval($uinfo[0]['uid']),
  247. intval(CONTACT_IS_SHARING),
  248. intval(CONTACT_IS_FRIEND)
  249. );
  250. $countfriends = $r[0]['count'];
  251. $r = q("SELECT COUNT(`id`) as `count` FROM `contact`
  252. WHERE `uid` = %d AND `rel` IN ( %d, %d )
  253. AND `self`=0 AND `blocked`=0",
  254. intval($uinfo[0]['uid']),
  255. intval(CONTACT_IS_FOLLOWER),
  256. intval(CONTACT_IS_FRIEND)
  257. );
  258. $countfollowers = $r[0]['count'];
  259. $r = q("SELECT count(`id`) as `count` FROM item where starred = 1 and uid = %d and deleted = 0",
  260. intval($uinfo[0]['uid'])
  261. );
  262. $starred = $r[0]['count'];
  263. if(! $uinfo[0]['self']) {
  264. $countfriends = 0;
  265. $countfollowers = 0;
  266. $starred = 0;
  267. }
  268. $ret = Array(
  269. 'self' => intval($uinfo[0]['self']),
  270. 'uid' => intval($uinfo[0]['uid']),
  271. 'id' => intval($uinfo[0]['cid']),
  272. 'name' => $uinfo[0]['name'],
  273. 'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']),
  274. 'location' => ($usr) ? $usr[0]['default-location'] : '',
  275. 'profile_image_url' => $uinfo[0]['micro'],
  276. 'url' => $uinfo[0]['url'],
  277. 'contact_url' => $a->get_baseurl()."/contacts/".$uinfo[0]['cid'],
  278. 'protected' => false,
  279. 'friends_count' => intval($countfriends),
  280. 'created_at' => api_date($uinfo[0]['name-date']),
  281. 'utc_offset' => "+00:00",
  282. 'time_zone' => 'UTC', //$uinfo[0]['timezone'],
  283. 'geo_enabled' => false,
  284. 'statuses_count' => intval($countitms), #XXX: fix me
  285. 'lang' => 'en', #XXX: fix me
  286. 'description' => (($profile) ? $profile[0]['pdesc'] : ''),
  287. 'followers_count' => intval($countfollowers),
  288. 'favourites_count' => intval($starred),
  289. 'contributors_enabled' => false,
  290. 'follow_request_sent' => true,
  291. 'profile_background_color' => 'cfe8f6',
  292. 'profile_text_color' => '000000',
  293. 'profile_link_color' => 'FF8500',
  294. 'profile_sidebar_fill_color' =>'AD0066',
  295. 'profile_sidebar_border_color' => 'AD0066',
  296. 'profile_background_image_url' => '',
  297. 'profile_background_tile' => false,
  298. 'profile_use_background_image' => false,
  299. 'notifications' => false,
  300. 'following' => '', #XXX: fix me
  301. 'verified' => true, #XXX: fix me
  302. 'status' => array()
  303. );
  304. return $ret;
  305. }
  306. function api_item_get_user(&$a, $item) {
  307. // The author is our direct contact, in a conversation with us.
  308. if(link_compare($item['url'],$item['author-link'])) {
  309. return api_get_user($a,$item['cid']);
  310. }
  311. else {
  312. // The author may be a contact of ours, but is replying to somebody else.
  313. // Figure out if we know him/her.
  314. $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']);
  315. if(($normalised != 'mailbox') && (x($a->contacts[$normalised])))
  316. return api_get_user($a,$a->contacts[$normalised]['id']);
  317. }
  318. // We don't know this person directly.
  319. list($nick, $name) = array_map("trim",explode("(",$item['author-name']));
  320. $name=str_replace(")","",$name);
  321. $ret = array(
  322. 'uid' => 0,
  323. 'id' => 0,
  324. 'name' => $name,
  325. 'screen_name' => $nick,
  326. 'location' => '', //$uinfo[0]['default-location'],
  327. 'profile_image_url' => $item['author-avatar'],
  328. 'url' => $item['author-link'],
  329. 'contact_url' => 0,
  330. 'protected' => false, #
  331. 'friends_count' => 0,
  332. 'created_at' => '',
  333. 'utc_offset' => 0, #XXX: fix me
  334. 'time_zone' => '', //$uinfo[0]['timezone'],
  335. 'geo_enabled' => false,
  336. 'statuses_count' => 0,
  337. 'lang' => 'en', #XXX: fix me
  338. 'description' => '',
  339. 'followers_count' => 0,
  340. 'favourites_count' => 0,
  341. 'contributors_enabled' => false,
  342. 'follow_request_sent' => false,
  343. 'profile_background_color' => 'cfe8f6',
  344. 'profile_text_color' => '000000',
  345. 'profile_link_color' => 'FF8500',
  346. 'profile_sidebar_fill_color' =>'AD0066',
  347. 'profile_sidebar_border_color' => 'AD0066',
  348. 'profile_background_image_url' => '',
  349. 'profile_background_tile' => false,
  350. 'profile_use_background_image' => false,
  351. 'notifications' => false,
  352. 'verified' => true, #XXX: fix me
  353. 'followers' => '', #XXX: fix me
  354. 'status' => array()
  355. );
  356. return $ret;
  357. }
  358. /**
  359. * apply xmlify() to all values of array $val, recursively
  360. */
  361. function api_xmlify($val){
  362. if (is_bool($val)) return $val?"true":"false";
  363. if (is_array($val)) return array_map('api_xmlify', $val);
  364. return xmlify((string) $val);
  365. }
  366. /**
  367. * load api $templatename for $type and replace $data array
  368. */
  369. function api_apply_template($templatename, $type, $data){
  370. $a = get_app();
  371. switch($type){
  372. case "atom":
  373. case "rss":
  374. case "xml":
  375. $data = api_xmlify($data);
  376. $tpl = get_markup_template("api_".$templatename."_".$type.".tpl");
  377. $ret = replace_macros($tpl, $data);
  378. break;
  379. case "json":
  380. $ret = $data;
  381. break;
  382. }
  383. return $ret;
  384. }
  385. /**
  386. ** TWITTER API
  387. */
  388. /**
  389. * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful;
  390. * returns a 401 status code and an error message if not.
  391. * http://developer.twitter.com/doc/get/account/verify_credentials
  392. */
  393. function api_account_verify_credentials(&$a, $type){
  394. if (local_user()===false) return false;
  395. $user_info = api_get_user($a);
  396. return api_apply_template("user", $type, array('$user' => $user_info));
  397. }
  398. api_register_func('api/account/verify_credentials','api_account_verify_credentials', true);
  399. /**
  400. * get data from $_POST or $_GET
  401. */
  402. function requestdata($k){
  403. if (isset($_POST[$k])){
  404. return $_POST[$k];
  405. }
  406. if (isset($_GET[$k])){
  407. return $_GET[$k];
  408. }
  409. return null;
  410. }
  411. // TODO - media uploads
  412. function api_statuses_update(&$a, $type) {
  413. if (local_user()===false) return false;
  414. $user_info = api_get_user($a);
  415. // convert $_POST array items to the form we use for web posts.
  416. // logger('api_post: ' . print_r($_POST,true));
  417. if(requestdata('htmlstatus')) {
  418. require_once('library/HTMLPurifier.auto.php');
  419. require_once('include/html2bbcode.php');
  420. $txt = requestdata('htmlstatus');
  421. if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) {
  422. $txt = html2bb_video($txt);
  423. $config = HTMLPurifier_Config::createDefault();
  424. $config->set('Cache.DefinitionImpl', null);
  425. $purifier = new HTMLPurifier($config);
  426. $txt = $purifier->purify($txt);
  427. $_POST['body'] = html2bbcode($txt);
  428. }
  429. }
  430. else
  431. $_POST['body'] = urldecode(requestdata('status'));
  432. $parent = requestdata('in_reply_to_status_id');
  433. if(ctype_digit($parent))
  434. $_POST['parent'] = $parent;
  435. else
  436. $_POST['parent_uri'] = $parent;
  437. if(requestdata('lat') && requestdata('long'))
  438. $_POST['coord'] = sprintf("%s %s",requestdata('lat'),requestdata('long'));
  439. $_POST['profile_uid'] = local_user();
  440. if(requestdata('parent'))
  441. $_POST['type'] = 'net-comment';
  442. else
  443. $_POST['type'] = 'wall';
  444. // set this so that the item_post() function is quiet and doesn't redirect or emit json
  445. $_POST['api_source'] = true;
  446. // call out normal post function
  447. require_once('mod/item.php');
  448. item_post($a);
  449. // this should output the last post (the one we just posted).
  450. return api_status_show($a,$type);
  451. }
  452. api_register_func('api/statuses/update','api_statuses_update', true);
  453. function api_status_show(&$a, $type){
  454. $user_info = api_get_user($a);
  455. // get last public wall message
  456. $lastwall = q("SELECT `item`.*, `i`.`contact-id` as `reply_uid`, `i`.`nick` as `reply_author`
  457. FROM `item`, `contact`,
  458. (SELECT `item`.`id`, `item`.`contact-id`, `contact`.`nick` FROM `item`,`contact` WHERE `contact`.`id`=`item`.`contact-id`) as `i`
  459. WHERE `item`.`contact-id` = %d
  460. AND `i`.`id` = `item`.`parent`
  461. AND `contact`.`id`=`item`.`contact-id` AND `contact`.`self`=1
  462. AND `type`!='activity'
  463. AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`=''
  464. ORDER BY `created` DESC
  465. LIMIT 1",
  466. intval($user_info['id'])
  467. );
  468. if (count($lastwall)>0){
  469. $lastwall = $lastwall[0];
  470. $in_reply_to_status_id = '';
  471. $in_reply_to_user_id = '';
  472. $in_reply_to_screen_name = '';
  473. if ($lastwall['parent']!=$lastwall['id']) {
  474. $in_reply_to_status_id=$lastwall['parent'];
  475. $in_reply_to_user_id = $lastwall['reply_uid'];
  476. $in_reply_to_screen_name = $lastwall['reply_author'];
  477. }
  478. $status_info = array(
  479. 'created_at' => api_date($lastwall['created']),
  480. 'id' => $lastwall['contact-id'],
  481. 'text' => strip_tags(bbcode($lastwall['body'])),
  482. 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'),
  483. 'truncated' => false,
  484. 'in_reply_to_status_id' => $in_reply_to_status_id,
  485. 'in_reply_to_user_id' => $in_reply_to_user_id,
  486. 'favorited' => false,
  487. 'in_reply_to_screen_name' => $in_reply_to_screen_name,
  488. 'geo' => '',
  489. 'coordinates' => $lastwall['coord'],
  490. 'place' => $lastwall['location'],
  491. 'contributors' => ''
  492. );
  493. $status_info['user'] = $user_info;
  494. }
  495. return api_apply_template("status", $type, array('$status' => $status_info));
  496. }
  497. /**
  498. * Returns extended information of a given user, specified by ID or screen name as per the required id parameter.
  499. * The author's most recent status will be returned inline.
  500. * http://developer.twitter.com/doc/get/users/show
  501. */
  502. function api_users_show(&$a, $type){
  503. $user_info = api_get_user($a);
  504. // get last public wall message
  505. $lastwall = q("SELECT `item`.*, `i`.`contact-id` as `reply_uid`, `i`.`nick` as `reply_author`
  506. FROM `item`, `contact`,
  507. (SELECT `item`.`id`, `item`.`contact-id`, `contact`.`nick` FROM `item`,`contact` WHERE `contact`.`id`=`item`.`contact-id`) as `i`
  508. WHERE `item`.`contact-id` = %d
  509. AND `i`.`id` = `item`.`parent`
  510. AND `contact`.`id`=`item`.`contact-id` AND `contact`.`self`=1
  511. AND `type`!='activity'
  512. AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`=''
  513. ORDER BY `created` DESC
  514. LIMIT 1",
  515. intval($user_info['id'])
  516. );
  517. if (count($lastwall)>0){
  518. $lastwall = $lastwall[0];
  519. $in_reply_to_status_id = '';
  520. $in_reply_to_user_id = '';
  521. $in_reply_to_screen_name = '';
  522. if ($lastwall['parent']!=$lastwall['id']) {
  523. $in_reply_to_status_id=$lastwall['parent'];
  524. $in_reply_to_user_id = $lastwall['reply_uid'];
  525. $in_reply_to_screen_name = $lastwall['reply_author'];
  526. }
  527. $user_info['status'] = array(
  528. 'created_at' => api_date($lastwall['created']),
  529. 'id' => $lastwall['contact-id'],
  530. 'text' => strip_tags(bbcode($lastwall['body'])),
  531. 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'),
  532. 'truncated' => false,
  533. 'in_reply_to_status_id' => $in_reply_to_status_id,
  534. 'in_reply_to_user_id' => $in_reply_to_user_id,
  535. 'favorited' => false,
  536. 'in_reply_to_screen_name' => $in_reply_to_screen_name,
  537. 'geo' => '',
  538. 'coordinates' => $lastwall['coord'],
  539. 'place' => $lastwall['location'],
  540. 'contributors' => ''
  541. );
  542. }
  543. return api_apply_template("user", $type, array('$user' => $user_info));
  544. }
  545. api_register_func('api/users/show','api_users_show');
  546. /**
  547. *
  548. * http://developer.twitter.com/doc/get/statuses/home_timeline
  549. *
  550. * TODO: Optional parameters
  551. * TODO: Add reply info
  552. */
  553. function api_statuses_home_timeline(&$a, $type){
  554. if (local_user()===false) return false;
  555. $user_info = api_get_user($a);
  556. // get last newtork messages
  557. // params
  558. $count = (x($_REQUEST,'count')?$_REQUEST['count']:20);
  559. $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
  560. if ($page<0) $page=0;
  561. $since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
  562. $start = $page*$count;
  563. $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
  564. `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
  565. `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
  566. `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
  567. FROM `item`, `contact`
  568. WHERE `item`.`uid` = %d
  569. AND `item`.`visible` = 1 AND `item`.`deleted` = 0
  570. AND `contact`.`id` = `item`.`contact-id`
  571. AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  572. $sql_extra
  573. AND `item`.`id`>%d
  574. ORDER BY `item`.`received` DESC LIMIT %d ,%d ",
  575. intval($user_info['uid']),
  576. intval($since_id),
  577. intval($start), intval($count)
  578. );
  579. $ret = api_format_items($r,$user_info);
  580. $data = array('$statuses' => $ret);
  581. switch($type){
  582. case "atom":
  583. case "rss":
  584. $data = api_rss_extra($a, $data, $user_info);
  585. }
  586. return api_apply_template("timeline", $type, $data);
  587. }
  588. api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true);
  589. api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true);
  590. function api_statuses_user_timeline(&$a, $type){
  591. if (local_user()===false) return false;
  592. $user_info = api_get_user($a);
  593. // get last newtork messages
  594. logger("api_statuses_user_timeline: local_user: ". local_user() .
  595. "\nuser_info: ".print_r($user_info, true) .
  596. "\n_REQUEST: ".print_r($_REQUEST, true),
  597. LOGGER_DEBUG);
  598. // params
  599. $count = (x($_REQUEST,'count')?$_REQUEST['count']:20);
  600. $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
  601. if ($page<0) $page=0;
  602. $since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
  603. $start = $page*$count;
  604. if ($user_info['self']==1) $sql_extra = "AND `item`.`wall` = 1 ";
  605. $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
  606. `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
  607. `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
  608. `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
  609. FROM `item`, `contact`
  610. WHERE `item`.`uid` = %d
  611. AND `item`.`contact-id` = %d
  612. AND `item`.`visible` = 1 AND `item`.`deleted` = 0
  613. AND `contact`.`id` = `item`.`contact-id`
  614. AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  615. $sql_extra
  616. AND `item`.`id`>%d
  617. ORDER BY `item`.`received` DESC LIMIT %d ,%d ",
  618. intval(local_user()),
  619. intval($user_info['id']),
  620. intval($since_id),
  621. intval($start), intval($count)
  622. );
  623. $ret = api_format_items($r,$user_info);
  624. $data = array('$statuses' => $ret);
  625. switch($type){
  626. case "atom":
  627. case "rss":
  628. $data = api_rss_extra($a, $data, $user_info);
  629. }
  630. return api_apply_template("timeline", $type, $data);
  631. }
  632. api_register_func('api/statuses/user_timeline','api_statuses_user_timeline', true);
  633. function api_favorites(&$a, $type){
  634. if (local_user()===false) return false;
  635. $user_info = api_get_user($a);
  636. // in friendika starred item are private
  637. // return favorites only for self
  638. logger('api_favorites: self:' . $user_info['self']);
  639. if ($user_info['self']==0) {
  640. $ret = array();
  641. } else {
  642. // params
  643. $count = (x($_GET,'count')?$_GET['count']:20);
  644. $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
  645. if ($page<0) $page=0;
  646. $start = $page*$count;
  647. $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
  648. `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
  649. `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
  650. `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
  651. FROM `item`, `contact`
  652. WHERE `item`.`uid` = %d
  653. AND `item`.`visible` = 1 AND `item`.`deleted` = 0
  654. AND `item`.`starred` = 1
  655. AND `contact`.`id` = `item`.`contact-id`
  656. AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  657. $sql_extra
  658. ORDER BY `item`.`received` DESC LIMIT %d ,%d ",
  659. intval($user_info['uid']),
  660. intval($start), intval($count)
  661. );
  662. $ret = api_format_items($r,$user_info);
  663. }
  664. $data = array('$statuses' => $ret);
  665. switch($type){
  666. case "atom":
  667. case "rss":
  668. $data = api_rss_extra($a, $data, $user_info);
  669. }
  670. return api_apply_template("timeline", $type, $data);
  671. }
  672. api_register_func('api/favorites','api_favorites', true);
  673. function api_format_items($r,$user_info) {
  674. //logger('api_format_items: ' . print_r($r,true));
  675. //logger('api_format_items: ' . print_r($user_info,true));
  676. $a = get_app();
  677. $ret = Array();
  678. foreach($r as $item) {
  679. localize_item($item);
  680. $status_user = (($item['cid']==$user_info['id'])?$user_info: api_item_get_user($a,$item));
  681. $status = array(
  682. 'created_at'=> api_date($item['created']),
  683. 'published' => api_date($item['created']),
  684. 'updated' => api_date($item['edited']),
  685. 'id' => intval($item['id']),
  686. 'message_id' => $item['uri'],
  687. 'text' => strip_tags(bbcode($item['body'])),
  688. 'statusnet_html' => bbcode($item['body']),
  689. 'source' => (($item['app']) ? $item['app'] : 'web'),
  690. 'url' => ($item['plink']!=''?$item['plink']:$item['author-link']),
  691. 'truncated' => False,
  692. 'in_reply_to_status_id' => ($item['parent']!=$item['id']? intval($item['parent']):''),
  693. 'in_reply_to_user_id' => '',
  694. 'favorited' => $item['starred'] ? true : false,
  695. 'in_reply_to_screen_name' => '',
  696. 'geo' => '',
  697. 'coordinates' => $item['coord'],
  698. 'place' => $item['location'],
  699. 'contributors' => '',
  700. 'annotations' => '',
  701. 'entities' => '',
  702. 'user' => $status_user ,
  703. 'objecttype' => (($item['object-type']) ? $item['object-type'] : ACTIVITY_OBJ_NOTE),
  704. 'verb' => (($item['verb']) ? $item['verb'] : ACTIVITY_POST),
  705. 'self' => $a->get_baseurl()."/api/statuses/show/".$item['id'].".".$type,
  706. 'edit' => $a->get_baseurl()."/api/statuses/show/".$item['id'].".".$type,
  707. );
  708. $ret[]=$status;
  709. };
  710. return $ret;
  711. }
  712. function api_account_rate_limit_status(&$a,$type) {
  713. $hash = array(
  714. 'remaining_hits' => (string) 150,
  715. 'hourly_limit' => (string) 150,
  716. 'reset_time' => datetime_convert('UTC','UTC','now + 1 hour',ATOM_TIME),
  717. 'reset_time_in_seconds' => strtotime('now + 1 hour')
  718. );
  719. return api_apply_template('ratelimit', $type, array('$hash' => $hash));
  720. }
  721. api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true);
  722. /**
  723. * https://dev.twitter.com/docs/api/1/get/statuses/friends
  724. * This function is deprecated by Twitter
  725. * returns: json, xml
  726. **/
  727. function api_statuses_f(&$a, $type, $qtype) {
  728. if (local_user()===false) return false;
  729. $user_info = api_get_user($a);
  730. // friends and followers only for self
  731. if ($user_info['self']==0){
  732. return false;
  733. }
  734. if (x($_GET,'cursor') && $_GET['cursor']=='undefined'){
  735. /* this is to stop Hotot to load friends multiple times
  736. * I'm not sure if I'm missing return something or
  737. * is a bug in hotot. Workaround, meantime
  738. */
  739. /*$ret=Array();
  740. return array('$users' => $ret);*/
  741. return false;
  742. }
  743. if($qtype == 'friends')
  744. $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_SHARING), intval(CONTACT_IS_FRIEND));
  745. if($qtype == 'followers')
  746. $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_FOLLOWER), intval(CONTACT_IS_FRIEND));
  747. $r = q("SELECT id FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 $sql_extra",
  748. intval(local_user())
  749. );
  750. $ret = array();
  751. foreach($r as $cid){
  752. $ret[] = api_get_user($a, $cid['id']);
  753. }
  754. return array('$users' => $ret);
  755. }
  756. function api_statuses_friends(&$a, $type){
  757. $data = api_statuses_f($a,$type,"friends");
  758. if ($data===false) return false;
  759. return api_apply_template("friends", $type, $data);
  760. }
  761. function api_statuses_followers(&$a, $type){
  762. $data = api_statuses_f($a,$type,"followers");
  763. if ($data===false) return false;
  764. return api_apply_template("friends", $type, $data);
  765. }
  766. api_register_func('api/statuses/friends','api_statuses_friends',true);
  767. api_register_func('api/statuses/followers','api_statuses_followers',true);
  768. function api_statusnet_config(&$a,$type) {
  769. $name = $a->config['sitename'];
  770. $server = $a->get_hostname();
  771. $logo = $a->get_baseurl() . '/images/friendika-64.png';
  772. $email = $a->config['admin_email'];
  773. $closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false');
  774. $private = (($a->config['system']['block_public']) ? 'true' : 'false');
  775. $textlimit = (string) (($a->config['max_import_size']) ? $a->config['max_import_size'] : 200000);
  776. if($a->config['api_import_size'])
  777. $texlimit = string($a->config['api_import_size']);
  778. $ssl = (($a->config['system']['have_ssl']) ? 'true' : 'false');
  779. $sslserver = (($ssl === 'true') ? str_replace('http:','https:',$a->get_baseurl()) : '');
  780. $config = array(
  781. 'site' => array('name' => $name,'server' => $server, 'theme' => 'default', 'path' => '',
  782. 'logo' => $logo, 'fancy' => 'true', 'language' => 'en', 'email' => $email, 'broughtby' => '',
  783. 'broughtbyurl' => '', 'timezone' => 'UTC', 'closed' => $closed, 'inviteonly' => 'false',
  784. 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl,
  785. 'shorturllength' => '30'
  786. ),
  787. );
  788. return api_apply_template('config', $type, array('$config' => $config));
  789. }
  790. api_register_func('api/statusnet/config','api_statusnet_config',false);
  791. function api_statusnet_version(&$a,$type) {
  792. // liar
  793. if($type === 'xml') {
  794. header("Content-type: application/xml");
  795. echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>0.9.7</version>' . "\r\n";
  796. killme();
  797. }
  798. elseif($type === 'json') {
  799. header("Content-type: application/json");
  800. echo '"0.9.7"';
  801. killme();
  802. }
  803. }
  804. api_register_func('api/statusnet/version','api_statusnet_version',false);
  805. function api_ff_ids(&$a,$type,$qtype) {
  806. if(! local_user())
  807. return false;
  808. if($qtype == 'friends')
  809. $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_SHARING), intval(CONTACT_IS_FRIEND));
  810. if($qtype == 'followers')
  811. $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_FOLLOWER), intval(CONTACT_IS_FRIEND));
  812. $r = q("SELECT id FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 $sql_extra",
  813. intval(local_user())
  814. );
  815. if(is_array($r)) {
  816. if($type === 'xml') {
  817. header("Content-type: application/xml");
  818. echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<ids>' . "\r\n";
  819. foreach($r as $rr)
  820. echo '<id>' . $rr['id'] . '</id>' . "\r\n";
  821. echo '</ids>' . "\r\n";
  822. killme();
  823. }
  824. elseif($type === 'json') {
  825. $ret = array();
  826. header("Content-type: application/json");
  827. foreach($r as $rr) $ret[] = $rr['id'];
  828. echo json_encode($ret);
  829. killme();
  830. }
  831. }
  832. }
  833. function api_friends_ids(&$a,$type) {
  834. api_ff_ids($a,$type,'friends');
  835. }
  836. function api_followers_ids(&$a,$type) {
  837. api_ff_ids($a,$type,'followers');
  838. }
  839. api_register_func('api/friends/ids','api_friends_ids',true);
  840. api_register_func('api/followers/ids','api_followers_ids',true);
  841. function api_direct_messages_new(&$a, $type) {
  842. if (local_user()===false) return false;
  843. if (!x($_POST, "text") || !x($_POST,"screen_name")) return;
  844. $sender = api_get_user($a);
  845. $r = q("SELECT `id` FROM `contact` WHERE `uid`=%d AND `nick`='%s'",
  846. intval(local_user()),
  847. dbesc($_POST['screen_name']));
  848. $recipient = api_get_user($a, $r[0]['id']);
  849. require_once("include/message.php");
  850. $sub = ( (strlen($_POST['text'])>10)?substr($_POST['text'],0,10)."...":$_POST['text']);
  851. $id = send_message($recipient['id'], $_POST['text'], $sub);
  852. if ($id>-1) {
  853. $r = q("SELECT * FROM `mail` WHERE id=%d", intval($id));
  854. $item = $r[0];
  855. $ret=Array(
  856. 'id' => $item['id'],
  857. 'created_at'=> api_date($item['created']),
  858. 'sender_id'=> $sender['id'] ,
  859. 'sender_screen_name'=> $sender['screen_name'],
  860. 'sender'=> $sender,
  861. 'recipient_id'=> $recipient['id'],
  862. 'recipient_screen_name'=> $recipient['screen_name'],
  863. 'recipient'=> $recipient,
  864. 'text'=> $item['title']."\n".strip_tags(bbcode($item['body'])) ,
  865. );
  866. } else {
  867. $ret = array("error"=>$id);
  868. }
  869. $data = Array('$messages'=>$ret);
  870. switch($type){
  871. case "atom":
  872. case "rss":
  873. $data = api_rss_extra($a, $data, $user_info);
  874. }
  875. return api_apply_template("direct_messages", $type, $data);
  876. }
  877. api_register_func('api/direct_messages/new','api_direct_messages_new',true);
  878. function api_direct_messages_box(&$a, $type, $box) {
  879. if (local_user()===false) return false;
  880. $user_info = api_get_user($a);
  881. // params
  882. $count = (x($_GET,'count')?$_GET['count']:20);
  883. $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
  884. if ($page<0) $page=0;
  885. $start = $page*$count;
  886. if ($box=="sentbox") {
  887. $sql_extra = "`from-url`='%s'";
  888. } else {
  889. $sql_extra = "`from-url`!='%s'";
  890. }
  891. $r = q("SELECT * FROM `mail` WHERE uid=%d AND $sql_extra ORDER BY created DESC LIMIT %d,%d",
  892. intval(local_user()),
  893. dbesc( $a->get_baseurl() . '/profile/' . $a->user['nickname'] ),
  894. intval($start), intval($count)
  895. );
  896. $ret = Array();
  897. foreach($r as $item){
  898. switch ($box){
  899. case "inbox":
  900. $recipient = $user_info;
  901. $sender = api_get_user($a,$item['contact-id']);
  902. break;
  903. case "sentbox":
  904. $recipient = api_get_user($a,$item['contact-id']);
  905. $sender = $user_info;
  906. break;
  907. }
  908. $ret[]=Array(
  909. 'id' => $item['id'],
  910. 'created_at'=> api_date($item['created']),
  911. 'sender_id'=> $sender['id'] ,
  912. 'sender_screen_name'=> $sender['screen_name'],
  913. 'sender'=> $sender,
  914. 'recipient_id'=> $recipient['id'],
  915. 'recipient_screen_name'=> $recipient['screen_name'],
  916. 'recipient'=> $recipient,
  917. 'text'=> $item['title']."\n".strip_tags(bbcode($item['body'])) ,
  918. );
  919. }
  920. $data = array('$messages' => $ret);
  921. switch($type){
  922. case "atom":
  923. case "rss":
  924. $data = api_rss_extra($a, $data, $user_info);
  925. }
  926. return api_apply_template("direct_messages", $type, $data);
  927. }
  928. function api_direct_messages_sentbox(&$a, $type){
  929. return api_direct_messages_box($a, $type, "sentbox");
  930. }
  931. function api_direct_messages_inbox(&$a, $type){
  932. return api_direct_messages_box($a, $type, "inbox");
  933. }
  934. api_register_func('api/direct_messages/sent','api_direct_messages_sentbox',true);
  935. api_register_func('api/direct_messages','api_direct_messages_inbox',true);
  936. function api_oauth_request_token(&$a, $type){
  937. try{
  938. $oauth = new FKOAuth1();
  939. $r = $oauth->fetch_request_token(OAuthRequest::from_request());
  940. }catch(Exception $e){
  941. echo "error=". OAuthUtil::urlencode_rfc3986($e->getMessage()); killme();
  942. }
  943. echo "oauth_token=".$r->key."&oauth_secret=".$r->secret;
  944. killme();
  945. }
  946. function api_oauth_access_token(&$a, $type){
  947. try{
  948. $oauth = new FKOAuth1();
  949. $r = $oauth->fetch_access_token(OAuthRequest::from_request());
  950. }catch(Exception $e){
  951. echo "error=". OAuthUtil::urlencode_rfc3986($e->getMessage()); killme();
  952. }
  953. echo "oauth_token=".$r->key."&oauth_secret=".$r->secret;
  954. killme();
  955. }
  956. api_register_func('api/oauth/request_token', 'api_oauth_request_token', false);
  957. api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);