X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fapi.php;h=a450f867a574e45edce018014a10be5944b86cc7;hb=51716957b24d97c20f078d1f880a6ca112cae792;hp=4a3d60969362c91dcc695d33ab2473f3aa391b2f;hpb=18bd7f5eb7153726d616297981e04816798b5463;p=friendica.git diff --git a/include/api.php b/include/api.php index 4a3d609693..a450f867a5 100644 --- a/include/api.php +++ b/include/api.php @@ -25,6 +25,7 @@ require_once('include/like.php'); require_once('include/NotificationsManager.php'); require_once('include/plaintext.php'); + require_once('include/xml.php'); define('API_METHOD_ANY','*'); @@ -91,7 +92,7 @@ * * Register a function to be the endpont for defined API path. * - * @param string $path API URL path, relative to $a->get_baseurl() + * @param string $path API URL path, relative to App::get_baseurl() * @param string $func Function name to call on path request * @param bool $auth API need logged user * @param string $method @@ -132,7 +133,7 @@ * @hook 'logged_in' * array $user logged user record */ - function api_login(&$a){ + function api_login(App &$a){ // login with oauth try{ $oauth = new FKOAuth1(); @@ -201,13 +202,13 @@ else { // process normal login request - $r = q("SELECT * FROM `user` WHERE ( `email` = '%s' OR `nickname` = '%s' ) - AND `password` = '%s' AND `blocked` = 0 AND `account_expired` = 0 AND `account_removed` = 0 AND `verified` = 1 LIMIT 1", + $r = q("SELECT * FROM `user` WHERE (`email` = '%s' OR `nickname` = '%s') + AND `password` = '%s' AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified` LIMIT 1", dbesc(trim($user)), dbesc(trim($user)), dbesc($encrypted) ); - if(count($r)) + if (dbm::is_result($r)) $record = $r[0]; } @@ -219,7 +220,9 @@ throw new UnauthorizedException("This API requires login"); } - authenticate_success($record); $_SESSION["allow_api"] = true; + authenticate_success($record); + + $_SESSION["allow_api"] = true; call_hooks('logged_in', $a->user); @@ -248,15 +251,14 @@ * @param App $a * @return string API call result */ - function api_call(&$a){ - GLOBAL $API, $called_api; + function api_call(App &$a){ + global $API, $called_api; $type="json"; if (strpos($a->query_string, ".xml")>0) $type="xml"; if (strpos($a->query_string, ".json")>0) $type="json"; if (strpos($a->query_string, ".rss")>0) $type="rss"; if (strpos($a->query_string, ".atom")>0) $type="atom"; - if (strpos($a->query_string, ".as")>0) $type="as"; try { foreach ($API as $p=>$info){ if (strpos($a->query_string, $p)===0){ @@ -274,10 +276,48 @@ logger('API parameters: ' . print_r($_REQUEST,true)); $stamp = microtime(true); - $r = call_user_func($info['func'], $a, $type); + $r = call_user_func($info['func'], $type); $duration = (float)(microtime(true)-$stamp); logger("API call duration: ".round($duration, 2)."\t".$a->query_string, LOGGER_DEBUG); + if (get_config("system", "profiler")) { + $duration = microtime(true)-$a->performance["start"]; + + logger(parse_url($a->query_string, PHP_URL_PATH).": ".sprintf("Database: %s/%s, Network: %s, I/O: %s, Other: %s, Total: %s", + round($a->performance["database"] - $a->performance["database_write"], 3), + round($a->performance["database_write"], 3), + round($a->performance["network"], 2), + round($a->performance["file"], 2), + round($duration - ($a->performance["database"] + $a->performance["network"] + + $a->performance["file"]), 2), + round($duration, 2)), + LOGGER_DEBUG); + + if (get_config("rendertime", "callstack")) { + $o = "Database Read:\n"; + foreach ($a->callstack["database"] AS $func => $time) { + $time = round($time, 3); + if ($time > 0) + $o .= $func.": ".$time."\n"; + } + $o .= "\nDatabase Write:\n"; + foreach ($a->callstack["database_write"] AS $func => $time) { + $time = round($time, 3); + if ($time > 0) + $o .= $func.": ".$time."\n"; + } + + $o .= "\nNetwork:\n"; + foreach ($a->callstack["network"] AS $func => $time) { + $time = round($time, 3); + if ($time > 0) + $o .= $func.": ".$time."\n"; + } + logger($o, LOGGER_DEBUG); + } + } + + if ($r===false) { // api function returned false withour throw an // exception. This should not happend, throw a 500 @@ -286,9 +326,8 @@ switch($type){ case "xml": - $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r)); header ("Content-Type: text/xml"); - return ''."\n".$r; + return $r; break; case "json": header ("Content-Type: application/json"); @@ -306,12 +345,6 @@ header ("Content-Type: application/atom+xml"); return ''."\n".$r; break; - case "as": - //header ("Content-Type: application/json"); - //foreach($r as $rr) - // return json_encode($rr); - return json_encode($r); - break; } } @@ -319,42 +352,46 @@ throw new NotImplementedException(); } catch (HTTPException $e) { header("HTTP/1.1 {$e->httpcode} {$e->httpdesc}"); - return api_error($a, $type, $e); + return api_error($type, $e); } } /** * @brief Format API error string * - * @param Api $a * @param string $type Return type (xml, json, rss, as) * @param HTTPException $error Error object * @return strin error message formatted as $type */ - function api_error(&$a, $type, $e) { + function api_error($type, $e) { + + $a = get_app(); + $error = ($e->getMessage()!==""?$e->getMessage():$e->httpdesc); # TODO: https://dev.twitter.com/overview/api/response-codes - $xmlstr = "{$error}{$e->httpcode} {$e->httpdesc}{$a->query_string}"; + + $error = array("error" => $error, + "code" => $e->httpcode." ".$e->httpdesc, + "request" => $a->query_string); + + $ret = api_format_data('status', $type, array('status' => $error)); + switch($type){ case "xml": header ("Content-Type: text/xml"); - return ''."\n".$xmlstr; + return $ret; break; case "json": header ("Content-Type: application/json"); - return json_encode(array( - 'error' => $error, - 'request' => $a->query_string, - 'code' => $e->httpcode." ".$e->httpdesc - )); + return json_encode($ret); break; case "rss": header ("Content-Type: application/rss+xml"); - return ''."\n".$xmlstr; + return $ret; break; case "atom": header ("Content-Type: application/atom+xml"); - return ''."\n".$xmlstr; + return $ret; break; } } @@ -371,13 +408,13 @@ if (is_null($user_info)) $user_info = api_get_user($a); $arr['$user'] = $user_info; $arr['$rss'] = array( - 'alternate' => $user_info['url'], - 'self' => $a->get_baseurl(). "/". $a->query_string, - 'base' => $a->get_baseurl(), - 'updated' => api_date(null), + 'alternate' => $user_info['url'], + 'self' => App::get_baseurl(). "/". $a->query_string, + 'base' => App::get_baseurl(), + 'updated' => api_date(null), 'atom_updated' => datetime_convert('UTC','UTC','now',ATOM_TIME), - 'language' => $user_info['language'], - 'logo' => $a->get_baseurl()."/images/friendica-32.png", + 'language' => $user_info['language'], + 'logo' => App::get_baseurl()."/images/friendica-32.png", ); return $arr; @@ -392,7 +429,7 @@ * Contact url or False if contact id is unknown */ function api_unique_id_to_url($id){ - $r = q("SELECT `url` FROM `gcontact` WHERE `id`=%d LIMIT 1", + $r = q("SELECT `url` FROM `contact` WHERE `uid` = 0 AND `id` = %d LIMIT 1", intval($id)); if ($r) return ($r[0]["url"]); @@ -424,7 +461,7 @@ if (api_user()!==false) $extra_query .= "AND `contact`.`uid`=".intval(api_user()); } - // Searching for unique contact id + // Searching for contact id with uid = 0 if(!is_null($contact_id) AND (intval($contact_id) != 0)){ $user = dbesc(api_unique_id_to_url($contact_id)); @@ -481,7 +518,7 @@ return False; } else { $user = $_SESSION['uid']; - $extra_query = "AND `contact`.`uid` = %d AND `contact`.`self` = 1 "; + $extra_query = "AND `contact`.`uid` = %d AND `contact`.`self` "; } } @@ -497,14 +534,16 @@ // Selecting the id by priority, friendica first api_best_nickname($uinfo); - // if the contact wasn't found, fetch it from the unique contacts + // if the contact wasn't found, fetch it from the contacts with uid = 0 if (count($uinfo)==0) { $r = array(); if ($url != "") - $r = q("SELECT * FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($url))); + $r = q("SELECT * FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s' LIMIT 1", dbesc(normalise_link($url))); if ($r) { + $network_name = network_to_name($r[0]['network'], $r[0]['url']); + // If no nick where given, extract it from the address if (($r[0]['nick'] == "") OR ($r[0]['name'] == $r[0]['nick'])) $r[0]['nick'] = api_get_nick($r[0]["url"]); @@ -514,8 +553,10 @@ 'id_str' => (string) $r[0]["id"], 'name' => $r[0]["name"], 'screen_name' => (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']), - 'location' => $r[0]["location"], + 'location' => ($r[0]["location"] != "") ? $r[0]["location"] : $network_name, 'description' => $r[0]["about"], + 'profile_image_url' => $r[0]["micro"], + 'profile_image_url_https' => $r[0]["micro"], 'url' => $r[0]["url"], 'protected' => false, 'followers_count' => 0, @@ -532,16 +573,13 @@ 'contributors_enabled' => false, 'is_translator' => false, 'is_translation_enabled' => false, - 'profile_image_url' => $r[0]["photo"], - 'profile_image_url_https' => $r[0]["photo"], 'following' => false, 'follow_request_sent' => false, - 'notifications' => false, 'statusnet_blocking' => false, 'notifications' => false, 'statusnet_profile_url' => $r[0]["url"], 'uid' => 0, - 'cid' => 0, + 'cid' => get_contact($r[0]["url"], api_user(), true), 'self' => 0, 'network' => $r[0]["network"], ); @@ -553,6 +591,10 @@ } if($uinfo[0]['self']) { + + if ($uinfo[0]['network'] == "") + $uinfo[0]['network'] = NETWORK_DFRN; + $usr = q("select * from user where uid = %d limit 1", intval(api_user()) ); @@ -560,28 +602,28 @@ intval(api_user()) ); - //AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''", + // Counting is deactivated by now, due to performance issues // count public wall messages - $r = q("SELECT count(*) as `count` FROM `item` - WHERE `uid` = %d - AND `type`='wall'", - intval($uinfo[0]['uid']) - ); - $countitms = $r[0]['count']; - } - else { - //AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''", - $r = q("SELECT count(*) as `count` FROM `item` - WHERE `contact-id` = %d", - intval($uinfo[0]['id']) - ); - $countitms = $r[0]['count']; + //$r = q("SELECT COUNT(*) as `count` FROM `item` WHERE `uid` = %d AND `wall`", + // intval($uinfo[0]['uid']) + //); + //$countitms = $r[0]['count']; + $countitms = 0; + } else { + // Counting is deactivated by now, due to performance issues + //$r = q("SELECT count(*) as `count` FROM `item` + // WHERE `contact-id` = %d", + // intval($uinfo[0]['id']) + //); + //$countitms = $r[0]['count']; + $countitms = 0; } - +/* + // Counting is deactivated by now, due to performance issues // count friends $r = q("SELECT count(*) as `count` FROM `contact` WHERE `uid` = %d AND `rel` IN ( %d, %d ) - AND `self`=0 AND `blocked`=0 AND `pending`=0 AND `hidden`=0", + AND `self`=0 AND NOT `blocked` AND `hidden`=0", intval($uinfo[0]['uid']), intval(CONTACT_IS_SHARING), intval(CONTACT_IS_FRIEND) @@ -590,7 +632,7 @@ $r = q("SELECT count(*) as `count` FROM `contact` WHERE `uid` = %d AND `rel` IN ( %d, %d ) - AND `self`=0 AND `blocked`=0 AND `pending`=0 AND `hidden`=0", + AND `self`=0 AND NOT `blocked` AND `hidden`=0", intval($uinfo[0]['uid']), intval(CONTACT_IS_FOLLOWER), intval(CONTACT_IS_FRIEND) @@ -608,6 +650,10 @@ $countfollowers = 0; $starred = 0; } +*/ + $countfriends = 0; + $countfollowers = 0; + $starred = 0; // Add a nick if it isn't present there if (($uinfo[0]['nick'] == "") OR ($uinfo[0]['name'] == $uinfo[0]['nick'])) { @@ -616,12 +662,11 @@ $network_name = network_to_name($uinfo[0]['network'], $uinfo[0]['url']); - $gcontact_id = get_gcontact_id(array("url" => $uinfo[0]['url'], "network" => $uinfo[0]['network'], - "photo" => $uinfo[0]['micro'], "name" => $uinfo[0]['name'])); + $pcontact_id = get_contact($uinfo[0]['url'], 0, true); $ret = Array( - 'id' => intval($gcontact_id), - 'id_str' => (string) intval($gcontact_id), + 'id' => intval($pcontact_id), + 'id_str' => (string) intval($pcontact_id), 'name' => (($uinfo[0]['name']) ? $uinfo[0]['name'] : $uinfo[0]['nick']), 'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']), 'location' => ($usr) ? $usr[0]['default-location'] : $network_name, @@ -632,16 +677,23 @@ 'protected' => false, 'followers_count' => intval($countfollowers), 'friends_count' => intval($countfriends), + 'listed_count' => 0, 'created_at' => api_date($uinfo[0]['created']), 'favourites_count' => intval($starred), 'utc_offset' => "0", 'time_zone' => 'UTC', + 'geo_enabled' => false, + 'verified' => true, 'statuses_count' => intval($countitms), + 'lang' => '', + 'contributors_enabled' => false, + 'is_translator' => false, + 'is_translation_enabled' => false, 'following' => (($uinfo[0]['rel'] == CONTACT_IS_FOLLOWER) OR ($uinfo[0]['rel'] == CONTACT_IS_FRIEND)), - 'verified' => true, + 'follow_request_sent' => false, 'statusnet_blocking' => false, 'notifications' => false, - //'statusnet_profile_url' => $a->get_baseurl()."/contacts/".$uinfo[0]['cid'], + //'statusnet_profile_url' => App::get_baseurl()."/contacts/".$uinfo[0]['cid'], 'statusnet_profile_url' => $uinfo[0]['url'], 'uid' => intval($uinfo[0]['uid']), 'cid' => intval($uinfo[0]['cid']), @@ -653,67 +705,127 @@ } + /** + * @brief return api-formatted array for item's author and owner + * + * @param App $a + * @param array $item : item from db + * @return array(array:author, array:owner) + */ function api_item_get_user(&$a, $item) { - // Make sure that there is an entry in the global contacts for author and owner - get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'], - "photo" => $item['author-avatar'], "name" => $item['author-name'])); - - get_gcontact_id(array("url" => $item['owner-link'], "network" => $item['network'], - "photo" => $item['owner-avatar'], "name" => $item['owner-name'])); + $status_user = api_get_user($a, $item["author-link"]); - // Comments in threads may appear as wall-to-wall postings. - // So only take the owner at the top posting. - #if ($item["id"] == $item["parent"]) - # $status_user = api_get_user($a,$item["owner-link"]); - #else - - $status_user = api_get_user($a,$item["author-link"]); $status_user["protected"] = (($item["allow_cid"] != "") OR ($item["allow_gid"] != "") OR ($item["deny_cid"] != "") OR ($item["deny_gid"] != "") OR $item["private"]); - $owner_user = api_get_user($a,$item["owner-link"]); + $owner_user = api_get_user($a, $item["owner-link"]); return (array($status_user, $owner_user)); } - /** - * @brief transform $data array in xml without a template + * @brief walks recursively through an array with the possibility to change value and key + * + * @param array $array The array to walk through + * @param string $callback The callback function * - * @param array $data - * @return string xml string + * @return array the transformed array */ - function api_array_to_xml($data, $ename="") { - $attrs=""; - $childs=""; - if (count($data)==1 && !is_array($data[array_keys($data)[0]])) { - $ename = array_keys($data)[0]; - $ename = trim($ename,'$'); - $v = $data[$ename]; - return "<$ename>$v"; - } - foreach($data as $k=>$v) { - $k=trim($k,'$'); - if (!is_array($v)) { - $attrs .= sprintf('%s="%s" ', $k, $v); + function api_walk_recursive(array &$array, callable $callback) { + + $new_array = array(); + + foreach ($array as $k => $v) { + if (is_array($v)) { + if ($callback($v, $k)) + $new_array[$k] = api_walk_recursive($v, $callback); } else { - if (is_numeric($k)) $k=trim($ename,'s'); - $childs.=api_array_to_xml($v, $k); + if ($callback($v, $k)) + $new_array[$k] = $v; } } - $res = $childs; - if ($ename!="") $res = "<$ename $attrs>$res"; - return $res; + $array = $new_array; + + return $array; + } + + /** + * @brief Callback function to transform the array in an array that can be transformed in a XML file + * + * @param variant $item Array item value + * @param string $key Array key + * + * @return boolean Should the array item be deleted? + */ + function api_reformat_xml(&$item, &$key) { + if (is_bool($item)) + $item = ($item ? "true" : "false"); + + if (substr($key, 0, 10) == "statusnet_") + $key = "statusnet:".substr($key, 10); + elseif (substr($key, 0, 10) == "friendica_") + $key = "friendica:".substr($key, 10); + //else + // $key = "default:".$key; + + return true; + } + + /** + * @brief Creates the XML from a JSON style array + * + * @param array $data JSON style array + * @param string $root_element Name of the root element + * + * @return string The XML data + */ + function api_create_xml($data, $root_element) { + $childname = key($data); + $data2 = array_pop($data); + $key = key($data2); + + $namespaces = array("" => "http://api.twitter.com", + "statusnet" => "http://status.net/schema/api/1/", + "friendica" => "http://friendi.ca/schema/api/1/", + "georss" => "http://www.georss.org/georss"); + + /// @todo Auto detection of needed namespaces + if (in_array($root_element, array("ok", "hash", "config", "version", "ids", "notes", "photos"))) + $namespaces = array(); + + if (is_array($data2)) + api_walk_recursive($data2, "api_reformat_xml"); + + if ($key == "0") { + $data4 = array(); + $i = 1; + + foreach ($data2 AS $item) + $data4[$i++.":".$childname] = $item; + + $data2 = $data4; + } + + $data3 = array($root_element => $data2); + + $ret = xml::from_array($data3, $xml, false, $namespaces); + return $ret; } /** - * load api $templatename for $type and replace $data array + * @brief Formats the data according to the data type + * + * @param string $root_element Name of the root element + * @param string $type Return type (atom, rss, xml, json) + * @param array $data JSON style array + * + * @return (string|object) XML data or JSON data */ - function api_apply_template($templatename, $type, $data){ + function api_format_data($root_element, $type, $data){ $a = get_app(); @@ -721,18 +833,7 @@ case "atom": case "rss": case "xml": - $data = array_xmlify($data); - if ($templatename==="") { - $ret = api_array_to_xml($data); - } else { - $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); - if(! $tpl) { - header ("Content-Type: text/xml"); - echo ''."\n".'not implemented'; - killme(); - } - $ret = replace_macros($tpl, $data); - } + $ret = api_create_xml($data, $root_element); break; case "json": $ret = $data; @@ -751,7 +852,10 @@ * returns a 401 status code and an error message if not. * http://developer.twitter.com/doc/get/account/verify_credentials */ - function api_account_verify_credentials(&$a, $type){ + function api_account_verify_credentials($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); unset($_REQUEST["user_id"]); @@ -769,7 +873,7 @@ // - Adding last status if (!$skip_status) { - $user_info["status"] = api_status_show($a,"raw"); + $user_info["status"] = api_status_show("raw"); if (!count($user_info["status"])) unset($user_info["status"]); else @@ -780,7 +884,7 @@ unset($user_info["uid"]); unset($user_info["self"]); - return api_apply_template("user", $type, array('$user' => $user_info)); + return api_format_data("user", $type, array('user' => $user_info)); } api_register_func('api/account/verify_credentials','api_account_verify_credentials', true); @@ -800,7 +904,10 @@ } /*Waitman Gobble Mod*/ - function api_statuses_mediap(&$a, $type) { + function api_statuses_mediap($type) { + + $a = get_app(); + if (api_user()===false) { logger('api_statuses_update: no user'); throw new ForbiddenException(); @@ -833,13 +940,16 @@ item_post($a); // this should output the last post (the one we just posted). - return api_status_show($a,$type); + return api_status_show($type); } api_register_func('api/statuses/mediap','api_statuses_mediap', true, API_METHOD_POST); /*Waitman Gobble Mod*/ - function api_statuses_update(&$a, $type) { + function api_statuses_update($type) { + + $a = get_app(); + if (api_user()===false) { logger('api_statuses_update: no user'); throw new ForbiddenException(); @@ -904,7 +1014,7 @@ if ($posts_day > $throttle_day) { logger('Daily posting limit reached for user '.api_user(), LOGGER_DEBUG); - #die(api_error($a, $type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day))); + #die(api_error($type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day))); throw new TooManyRequestsException(sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day)); } } @@ -924,7 +1034,7 @@ if ($posts_week > $throttle_week) { logger('Weekly posting limit reached for user '.api_user(), LOGGER_DEBUG); - #die(api_error($a, $type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week))); + #die(api_error($type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week))); throw new TooManyRequestsException(sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week)); } @@ -945,7 +1055,7 @@ if ($posts_month > $throttle_month) { logger('Monthly posting limit reached for user '.api_user(), LOGGER_DEBUG); - #die(api_error($a, $type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month))); + #die(api_error($type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month))); throw new TooManyRequestsException(sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month)); } } @@ -968,8 +1078,8 @@ if ($r) { $phototypes = Photo::supportedTypes(); $ext = $phototypes[$r[0]['type']]; - $_REQUEST['body'] .= "\n\n".'[url='.$a->get_baseurl().'/photos/'.$r[0]['nickname'].'/image/'.$r[0]['resource-id'].']'; - $_REQUEST['body'] .= '[img]'.$a->get_baseurl()."/photo/".$r[0]['resource-id']."-".$r[0]['scale'].".".$ext."[/img][/url]"; + $_REQUEST['body'] .= "\n\n".'[url='.App::get_baseurl().'/photos/'.$r[0]['nickname'].'/image/'.$r[0]['resource-id'].']'; + $_REQUEST['body'] .= '[img]'.App::get_baseurl()."/photo/".$r[0]['resource-id']."-".$r[0]['scale'].".".$ext."[/img][/url]"; } } @@ -985,13 +1095,16 @@ item_post($a); // this should output the last post (the one we just posted). - return api_status_show($a,$type); + return api_status_show($type); } api_register_func('api/statuses/update','api_statuses_update', true, API_METHOD_POST); api_register_func('api/statuses/update_with_media','api_statuses_update', true, API_METHOD_POST); - function api_media_upload(&$a, $type) { + function api_media_upload($type) { + + $a = get_app(); + if (api_user()===false) { logger('no user'); throw new ForbiddenException(); @@ -1024,7 +1137,10 @@ } api_register_func('api/media/upload','api_media_upload', true, API_METHOD_POST); - function api_status_show(&$a, $type){ + function api_status_show($type){ + + $a = get_app(); + $user_info = api_get_user($a); logger('api_status_show: user_info: '.print_r($user_info, true), LOGGER_DEBUG); @@ -1035,13 +1151,12 @@ $privacy_sql = ""; // get last public wall message - $lastwall = q("SELECT `item`.*, `i`.`contact-id` as `reply_uid`, `i`.`author-link` AS `item-author` - FROM `item`, `item` as `i` + $lastwall = q("SELECT `item`.* + FROM `item` WHERE `item`.`contact-id` = %d AND `item`.`uid` = %d AND ((`item`.`author-link` IN ('%s', '%s')) OR (`item`.`owner-link` IN ('%s', '%s'))) - AND `i`.`id` = `item`.`parent` - AND `item`.`type`!='activity' $privacy_sql - ORDER BY `item`.`created` DESC + AND `item`.`type` != 'activity' $privacy_sql + ORDER BY `item`.`id` DESC LIMIT 1", intval($user_info['cid']), intval(api_user()), @@ -1054,40 +1169,15 @@ if (count($lastwall)>0){ $lastwall = $lastwall[0]; - $in_reply_to_status_id = NULL; - $in_reply_to_user_id = NULL; - $in_reply_to_status_id_str = NULL; - $in_reply_to_user_id_str = NULL; - $in_reply_to_screen_name = NULL; - if (intval($lastwall['parent']) != intval($lastwall['id'])) { - $in_reply_to_status_id= intval($lastwall['parent']); - $in_reply_to_status_id_str = (string) intval($lastwall['parent']); - - $r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($lastwall['item-author']))); - if ($r) { - if ($r[0]['nick'] == "") - $r[0]['nick'] = api_get_nick($r[0]["url"]); - - $in_reply_to_screen_name = (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']); - $in_reply_to_user_id = intval($r[0]['id']); - $in_reply_to_user_id_str = (string) intval($r[0]['id']); - } - } - - // There seems to be situation, where both fields are identical: - // https://github.com/friendica/friendica/issues/1010 - // This is a bugfix for that. - if (intval($in_reply_to_status_id) == intval($lastwall['id'])) { - logger('api_status_show: this message should never appear: id: '.$lastwall['id'].' similar to reply-to: '.$in_reply_to_status_id, LOGGER_DEBUG); - $in_reply_to_status_id = NULL; - $in_reply_to_user_id = NULL; - $in_reply_to_status_id_str = NULL; - $in_reply_to_user_id_str = NULL; - $in_reply_to_screen_name = NULL; - } + $in_reply_to = api_in_reply_to($lastwall); $converted = api_convert_item($lastwall); + if ($type == "xml") + $geo = "georss:point"; + else + $geo = "geo"; + $status_info = array( 'created_at' => api_date($lastwall['created']), 'id' => intval($lastwall['id']), @@ -1095,13 +1185,13 @@ 'text' => $converted["text"], 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), 'truncated' => false, - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'in_reply_to_status_id_str' => $in_reply_to_status_id_str, - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_user_id_str' => $in_reply_to_user_id_str, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, + 'in_reply_to_status_id' => $in_reply_to['status_id'], + 'in_reply_to_status_id_str' => $in_reply_to['status_id_str'], + 'in_reply_to_user_id' => $in_reply_to['user_id'], + 'in_reply_to_user_id_str' => $in_reply_to['user_id_str'], + 'in_reply_to_screen_name' => $in_reply_to['screen_name'], 'user' => $user_info, - 'geo' => NULL, + $geo => NULL, 'coordinates' => "", 'place' => "", 'contributors' => "", @@ -1137,7 +1227,7 @@ if ($type == "raw") return($status_info); - return api_apply_template("status", $type, array('$status' => $status_info)); + return api_format_data("statuses", $type, array('status' => $status_info)); } @@ -1150,17 +1240,19 @@ * The author's most recent status will be returned inline. * http://developer.twitter.com/doc/get/users/show */ - function api_users_show(&$a, $type){ - $user_info = api_get_user($a); + function api_users_show($type){ + $a = get_app(); + + $user_info = api_get_user($a); $lastwall = q("SELECT `item`.* - FROM `item`, `contact` + FROM `item` + INNER JOIN `contact` ON `contact`.`id`=`item`.`contact-id` AND `contact`.`uid` = `item`.`uid` WHERE `item`.`uid` = %d AND `verb` = '%s' AND `item`.`contact-id` = %d AND ((`item`.`author-link` IN ('%s', '%s')) OR (`item`.`owner-link` IN ('%s', '%s'))) - AND `contact`.`id`=`item`.`contact-id` AND `type`!='activity' AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`='' - ORDER BY `created` DESC + ORDER BY `id` DESC LIMIT 1", intval(api_user()), dbesc(ACTIVITY_POST), @@ -1170,48 +1262,32 @@ dbesc($user_info['url']), dbesc(normalise_link($user_info['url'])) ); + if (count($lastwall)>0){ $lastwall = $lastwall[0]; - $in_reply_to_status_id = NULL; - $in_reply_to_user_id = NULL; - $in_reply_to_status_id_str = NULL; - $in_reply_to_user_id_str = NULL; - $in_reply_to_screen_name = NULL; - if ($lastwall['parent']!=$lastwall['id']) { - $reply = q("SELECT `item`.`id`, `item`.`contact-id` as `reply_uid`, `contact`.`nick` as `reply_author`, `item`.`author-link` AS `item-author` - FROM `item`,`contact` WHERE `contact`.`id`=`item`.`contact-id` AND `item`.`id` = %d", intval($lastwall['parent'])); - if (count($reply)>0) { - $in_reply_to_status_id = intval($lastwall['parent']); - $in_reply_to_status_id_str = (string) intval($lastwall['parent']); - - $r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($reply[0]['item-author']))); - if ($r) { - if ($r[0]['nick'] == "") - $r[0]['nick'] = api_get_nick($r[0]["url"]); - - $in_reply_to_screen_name = (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']); - $in_reply_to_user_id = intval($r[0]['id']); - $in_reply_to_user_id_str = (string) intval($r[0]['id']); - } - } - } + $in_reply_to = api_in_reply_to($lastwall); $converted = api_convert_item($lastwall); + if ($type == "xml") + $geo = "georss:point"; + else + $geo = "geo"; + $user_info['status'] = array( 'text' => $converted["text"], 'truncated' => false, 'created_at' => api_date($lastwall['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'in_reply_to_status_id_str' => $in_reply_to_status_id_str, + 'in_reply_to_status_id' => $in_reply_to['status_id'], + 'in_reply_to_status_id_str' => $in_reply_to['status_id_str'], 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), 'id' => intval($lastwall['contact-id']), 'id_str' => (string) $lastwall['contact-id'], - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_user_id_str' => $in_reply_to_user_id_str, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => NULL, + 'in_reply_to_user_id' => $in_reply_to['user_id'], + 'in_reply_to_user_id_str' => $in_reply_to['user_id_str'], + 'in_reply_to_screen_name' => $in_reply_to['screen_name'], + $geo => NULL, 'favorited' => $lastwall['starred'] ? true : false, 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $lastwall['parent'], @@ -1234,28 +1310,34 @@ unset($user_info["uid"]); unset($user_info["self"]); - return api_apply_template("user", $type, array('$user' => $user_info)); + return api_format_data("user", $type, array('user' => $user_info)); } api_register_func('api/users/show','api_users_show'); - function api_users_search(&$a, $type) { + function api_users_search($type) { + + $a = get_app(); + $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); $userlist = array(); if (isset($_GET["q"])) { - $r = q("SELECT id FROM `gcontact` WHERE `name`='%s'", dbesc($_GET["q"])); - if (!count($r)) - $r = q("SELECT `id` FROM `gcontact` WHERE `nick`='%s'", dbesc($_GET["q"])); + $r = q("SELECT id FROM `contact` WHERE `uid` = 0 AND `name` = '%s'", dbesc($_GET["q"])); + if (!dbm::is_result($r)) + $r = q("SELECT `id` FROM `contact` WHERE `uid` = 0 AND `nick` = '%s'", dbesc($_GET["q"])); - if (count($r)) { + if (dbm::is_result($r)) { + $k = 0; foreach ($r AS $user) { - $user_info = api_get_user($a, $user["id"]); - //echo print_r($user_info, true)."\n"; - $userdata = api_apply_template("user", $type, array('user' => $user_info)); - $userlist[] = $userdata["user"]; + $user_info = api_get_user($a, $user["id"], "json"); + + if ($type == "xml") + $userlist[$k++.":user"] = $user_info; + else + $userlist[] = $user_info; } $userlist = array("users" => $userlist); } else { @@ -1264,7 +1346,7 @@ } else { throw new BadRequestException("User not found."); } - return ($userlist); + return api_format_data("users", $type, $userlist); } api_register_func('api/users/search','api_users_search'); @@ -1276,7 +1358,10 @@ * TODO: Optional parameters * TODO: Add reply info */ - function api_statuses_home_timeline(&$a, $type){ + function api_statuses_home_timeline($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); unset($_REQUEST["user_id"]); @@ -1288,7 +1373,6 @@ $user_info = api_get_user($a); // get last newtork messages - // params $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); @@ -1309,15 +1393,15 @@ if ($conversation_id > 0) $sql_extra .= ' AND `item`.`parent` = '.intval($conversation_id); - $r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, + $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` - FROM `item`, `contact` + FROM `item` + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`uid` = %d AND `verb` = '%s' - AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` $sql_extra AND `item`.`id`>%d ORDER BY `item`.`id` DESC LIMIT %d ,%d ", @@ -1327,7 +1411,7 @@ intval($start), intval($count) ); - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); // Set all posts from the query above to seen $idarray = array(); @@ -1343,26 +1427,23 @@ $r = q("UPDATE `item` SET `unseen` = 0 WHERE `unseen` AND `id` IN (%s)", $idlist); } - $data = array('$statuses' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); break; - case "as": - $as = api_format_as($a, $ret, $user_info); - $as['title'] = $a->config['sitename']." Home Timeline"; - $as['link']['url'] = $a->get_baseurl()."/".$user_info["screen_name"]."/all"; - return($as); - break; } - return api_apply_template("timeline", $type, $data); + return api_format_data("statuses", $type, $data); } api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); - function api_statuses_public_timeline(&$a, $type){ + function api_statuses_public_timeline($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1393,13 +1474,15 @@ `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `user`.`nickname`, `user`.`hidewall` - FROM `item` STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + FROM `item` + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) STRAIGHT_JOIN `user` ON `user`.`uid` = `item`.`uid` - WHERE `verb` = '%s' AND `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + AND NOT `user`.`hidewall` + WHERE `verb` = '%s' AND `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`private` = 0 AND `item`.`wall` = 1 AND `user`.`hidewall` = 0 - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + AND NOT `item`.`private` AND `item`.`wall` $sql_extra AND `item`.`id`>%d ORDER BY `item`.`id` DESC LIMIT %d, %d ", @@ -1408,31 +1491,28 @@ intval($start), intval($count)); - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); - $data = array('$statuses' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); break; - case "as": - $as = api_format_as($a, $ret, $user_info); - $as['title'] = $a->config['sitename']." Public Timeline"; - $as['link']['url'] = $a->get_baseurl()."/"; - return($as); - break; } - return api_apply_template("timeline", $type, $data); + return api_format_data("statuses", $type, $data); } api_register_func('api/statuses/public_timeline','api_statuses_public_timeline', true); /** * */ - function api_statuses_show(&$a, $type){ + function api_statuses_show($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1453,7 +1533,7 @@ $sql_extra = ''; if ($conversation) - $sql_extra .= " AND `item`.`parent` = %d ORDER BY `received` ASC "; + $sql_extra .= " AND `item`.`parent` = %d ORDER BY `id` ASC "; else $sql_extra .= " AND `item`.`id` = %d"; @@ -1461,10 +1541,11 @@ `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` - FROM `item`, `contact` - WHERE `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `contact`.`id` = `item`.`contact-id` AND `item`.`uid` = %d AND `item`.`verb` = '%s' - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) + WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` + AND `item`.`uid` = %d AND `item`.`verb` = '%s' $sql_extra", intval(api_user()), dbesc(ACTIVITY_POST), @@ -1475,19 +1556,14 @@ throw new BadRequestException("There is no status with this id."); } - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); if ($conversation) { - $data = array('$statuses' => $ret); - return api_apply_template("timeline", $type, $data); + $data = array('status' => $ret); + return api_format_data("statuses", $type, $data); } else { - $data = array('$status' => $ret[0]); - /*switch($type){ - case "atom": - case "rss": - $data = api_rss_extra($a, $data, $user_info); - }*/ - return api_apply_template("status", $type, $data); + $data = array('status' => $ret[0]); + return api_format_data("status", $type, $data); } } api_register_func('api/statuses/show','api_statuses_show', true); @@ -1496,7 +1572,10 @@ /** * */ - function api_conversation_show(&$a, $type){ + function api_conversation_show($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1539,11 +1618,11 @@ `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` FROM `item` - INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`parent` = %d AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` AND `item`.`uid` = %d AND `item`.`verb` = '%s' - AND NOT `contact`.`blocked` AND NOT `contact`.`pending` AND `item`.`id`>%d $sql_extra ORDER BY `item`.`id` DESC LIMIT %d ,%d", intval($id), intval(api_user()), @@ -1555,10 +1634,10 @@ if (!$r) throw new BadRequestException("There is no conversation with this id."); - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); - $data = array('$statuses' => $ret); - return api_apply_template("timeline", $type, $data); + $data = array('status' => $ret); + return api_format_data("statuses", $type, $data); } api_register_func('api/conversation/show','api_conversation_show', true); api_register_func('api/statusnet/conversation','api_conversation_show', true); @@ -1567,9 +1646,11 @@ /** * */ - function api_statuses_repeat(&$a, $type){ + function api_statuses_repeat($type){ global $called_api; + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1590,10 +1671,10 @@ `contact`.`name`, `contact`.`photo` as `reply_photo`, `contact`.`url` as `reply_url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` - FROM `item`, `contact` - WHERE `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) + WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` AND NOT `item`.`private` AND `item`.`allow_cid` = '' AND `item`.`allow`.`gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' $sql_extra @@ -1629,14 +1710,17 @@ // this should output the last post (the one we just posted). $called_api = null; - return(api_status_show($a,$type)); + return(api_status_show($type)); } api_register_func('api/statuses/retweet','api_statuses_repeat', true, API_METHOD_POST); /** * */ - function api_statuses_destroy(&$a, $type){ + function api_statuses_destroy($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1653,7 +1737,7 @@ logger('API: api_statuses_destroy: '.$id); - $ret = api_statuses_show($a, $type); + $ret = api_statuses_show($type); drop_item($id, false); @@ -1666,7 +1750,10 @@ * http://developer.twitter.com/doc/get/statuses/mentions * */ - function api_statuses_mentions(&$a, $type){ + function api_statuses_mentions($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); unset($_REQUEST["user_id"]); @@ -1690,7 +1777,7 @@ $start = $page*$count; // Ugly code - should be changed - $myurl = $a->get_baseurl() . '/profile/'. $a->user['nickname']; + $myurl = App::get_baseurl() . '/profile/'. $a->user['nickname']; $myurl = substr($myurl,strpos($myurl,'://')+3); //$myurl = str_replace(array('www.','.'),array('','\\.'),$myurl); $myurl = str_replace('www.','',$myurl); @@ -1703,12 +1790,12 @@ `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` - FROM `item` FORCE INDEX (`uid_id`), `contact` + FROM `item` FORCE INDEX (`uid_id`) + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`uid` = %d AND `verb` = '%s' AND NOT (`item`.`author-link` IN ('https://%s', 'http://%s')) AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `contact`.`id` = `item`.`contact-id` - AND NOT `contact`.`blocked` AND NOT `contact`.`pending` AND `item`.`parent` IN (SELECT `iid` FROM `thread` WHERE `uid` = %d AND `mention` AND !`ignored`) $sql_extra AND `item`.`id`>%d @@ -1722,30 +1809,27 @@ intval($start), intval($count) ); - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); - $data = array('$statuses' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); break; - case "as": - $as = api_format_as($a, $ret, $user_info); - $as["title"] = $a->config['sitename']." Mentions"; - $as['link']['url'] = $a->get_baseurl()."/"; - return($as); - break; } - return api_apply_template("timeline", $type, $data); + return api_format_data("statuses", $type, $data); } api_register_func('api/statuses/mentions','api_statuses_mentions', true); api_register_func('api/statuses/replies','api_statuses_mentions', true); - function api_statuses_user_timeline(&$a, $type){ + function api_statuses_user_timeline($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $user_info = api_get_user($a); @@ -1780,12 +1864,12 @@ `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid` - FROM `item`, `contact` + FROM `item` FORCE INDEX (`uid_contactid_id`) + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`uid` = %d AND `verb` = '%s' AND `item`.`contact-id` = %d - AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` $sql_extra AND `item`.`id`>%d ORDER BY `item`.`id` DESC LIMIT %d ,%d ", @@ -1796,16 +1880,16 @@ intval($start), intval($count) ); - $ret = api_format_items($r,$user_info, true); + $ret = api_format_items($r,$user_info, true, $type); - $data = array('$statuses' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); } - return api_apply_template("timeline", $type, $data); + return api_format_data("statuses", $type, $data); } api_register_func('api/statuses/user_timeline','api_statuses_user_timeline', true); @@ -1816,7 +1900,10 @@ * * api v1 : https://web.archive.org/web/20131019055350/https://dev.twitter.com/docs/api/1/post/favorites/create/%3Aid */ - function api_favorites_create_destroy(&$a, $type){ + function api_favorites_create_destroy($type){ + + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); // for versioned api. @@ -1859,24 +1946,26 @@ $user_info = api_get_user($a); - $rets = api_format_items($item,$user_info); + $rets = api_format_items($item, $user_info, false, $type); $ret = $rets[0]; - $data = array('$status' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); } - return api_apply_template("status", $type, $data); + return api_format_data("status", $type, $data); } api_register_func('api/favorites/create', 'api_favorites_create_destroy', true, API_METHOD_POST); api_register_func('api/favorites/destroy', 'api_favorites_create_destroy', true, API_METHOD_DELETE); - function api_favorites(&$a, $type){ + function api_favorites($type){ global $called_api; + $a = get_app(); + if (api_user()===false) throw new ForbiddenException(); $called_api= array(); @@ -1913,7 +2002,7 @@ AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 AND `item`.`starred` = 1 AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + AND (NOT `contact`.`blocked` OR `contact`.`pending`) $sql_extra AND `item`.`id`>%d ORDER BY `item`.`id` DESC LIMIT %d ,%d ", @@ -1922,88 +2011,21 @@ intval($start), intval($count) ); - $ret = api_format_items($r,$user_info); + $ret = api_format_items($r,$user_info, false, $type); } - $data = array('$statuses' => $ret); + $data = array('status' => $ret); switch($type){ case "atom": case "rss": $data = api_rss_extra($a, $data, $user_info); } - return api_apply_template("timeline", $type, $data); + return api_format_data("statuses", $type, $data); } api_register_func('api/favorites','api_favorites', true); - - - - function api_format_as($a, $ret, $user_info) { - $as = array(); - $as['title'] = $a->config['sitename']." Public Timeline"; - $items = array(); - foreach ($ret as $item) { - $singleitem["actor"]["displayName"] = $item["user"]["name"]; - $singleitem["actor"]["id"] = $item["user"]["contact_url"]; - $avatar[0]["url"] = $item["user"]["profile_image_url"]; - $avatar[0]["rel"] = "avatar"; - $avatar[0]["type"] = ""; - $avatar[0]["width"] = 96; - $avatar[0]["height"] = 96; - $avatar[1]["url"] = $item["user"]["profile_image_url"]; - $avatar[1]["rel"] = "avatar"; - $avatar[1]["type"] = ""; - $avatar[1]["width"] = 48; - $avatar[1]["height"] = 48; - $avatar[2]["url"] = $item["user"]["profile_image_url"]; - $avatar[2]["rel"] = "avatar"; - $avatar[2]["type"] = ""; - $avatar[2]["width"] = 24; - $avatar[2]["height"] = 24; - $singleitem["actor"]["avatarLinks"] = $avatar; - - $singleitem["actor"]["image"]["url"] = $item["user"]["profile_image_url"]; - $singleitem["actor"]["image"]["rel"] = "avatar"; - $singleitem["actor"]["image"]["type"] = ""; - $singleitem["actor"]["image"]["width"] = 96; - $singleitem["actor"]["image"]["height"] = 96; - $singleitem["actor"]["type"] = "person"; - $singleitem["actor"]["url"] = $item["person"]["contact_url"]; - $singleitem["actor"]["statusnet:profile_info"]["local_id"] = $item["user"]["id"]; - $singleitem["actor"]["statusnet:profile_info"]["following"] = $item["user"]["following"] ? "true" : "false"; - $singleitem["actor"]["statusnet:profile_info"]["blocking"] = "false"; - $singleitem["actor"]["contact"]["preferredUsername"] = $item["user"]["screen_name"]; - $singleitem["actor"]["contact"]["displayName"] = $item["user"]["name"]; - $singleitem["actor"]["contact"]["addresses"] = ""; - - $singleitem["body"] = $item["text"]; - $singleitem["object"]["displayName"] = $item["text"]; - $singleitem["object"]["id"] = $item["url"]; - $singleitem["object"]["type"] = "note"; - $singleitem["object"]["url"] = $item["url"]; - //$singleitem["context"] =; - $singleitem["postedTime"] = date("c", strtotime($item["published"])); - $singleitem["provider"]["objectType"] = "service"; - $singleitem["provider"]["displayName"] = "Test"; - $singleitem["provider"]["url"] = "http://test.tld"; - $singleitem["title"] = $item["text"]; - $singleitem["verb"] = "post"; - $singleitem["statusnet:notice_info"]["local_id"] = $item["id"]; - $singleitem["statusnet:notice_info"]["source"] = $item["source"]; - $singleitem["statusnet:notice_info"]["favorite"] = "false"; - $singleitem["statusnet:notice_info"]["repeated"] = "false"; - //$singleitem["original"] = $item; - $items[] = $singleitem; - } - $as['items'] = $items; - $as['link']['url'] = $a->get_baseurl()."/".$user_info["screen_name"]."/all"; - $as['link']['rel'] = "alternate"; - $as['link']['type'] = "text/html"; - return($as); - } - function api_format_messages($item, $recipient, $sender) { // standard meta information $ret=Array( @@ -2016,6 +2038,9 @@ 'recipient_screen_name' => $recipient['screen_name'], 'sender' => $sender, 'recipient' => $recipient, + 'title' => "", + 'friendica_seen' => $item['seen'], + 'friendica_parent_uri' => $item['parent-uri'], ); // "uid" and "self" are only needed for some internal stuff, so remove it from here @@ -2274,11 +2299,10 @@ return($entities); } function api_format_items_embeded_images(&$item, $text){ - $a = get_app(); $text = preg_replace_callback( "|data:image/([^;]+)[^=]+=*|m", - function($match) use ($a, $item) { - return $a->get_baseurl()."/display/".$item['guid']; + function($match) use ($item) { + return App::get_baseurl()."/display/".$item['guid']; }, $text); return $text; @@ -2319,7 +2343,7 @@ * likes => int count * dislikes => int count */ - function api_format_items_activities(&$item) { + function api_format_items_activities(&$item, $type = "json") { $activities = array( 'like' => array(), 'dislike' => array(), @@ -2327,21 +2351,107 @@ 'attendno' => array(), 'attendmaybe' => array() ); + $items = q('SELECT * FROM item WHERE uid=%d AND `thr-parent`="%s" AND visible AND NOT deleted', intval($item['uid']), dbesc($item['uri'])); + foreach ($items as $i){ - builtin_activity_puller($i, $activities); + // not used as result should be structured like other user data + //builtin_activity_puller($i, $activities); + + // get user data and add it to the array of the activity + $user = api_get_user($a, $i['author-link']); + switch($i['verb']) { + case ACTIVITY_LIKE: + $activities['like'][] = $user; + break; + case ACTIVITY_DISLIKE: + $activities['dislike'][] = $user; + break; + case ACTIVITY_ATTEND: + $activities['attendyes'][] = $user; + break; + case ACTIVITY_ATTENDNO: + $activities['attendno'][] = $user; + break; + case ACTIVITY_ATTENDMAYBE: + $activities['attendmaybe'][] = $user; + break; + default: + break; + } } - $res = array(); - $uri = $item['uri']."-l"; - foreach($activities as $k => $v) { - $res[$k] = ( x($v,$uri) ? array_map("api_contactlink_to_array", $v[$uri]) : array() ); + if ($type == "xml") { + $xml_activities = array(); + foreach ($activities as $k => $v) { + // change xml element from "like" to "friendica:like" + $xml_activities["friendica:".$k] = $v; + // add user data into xml output + $k_user = 0; + foreach ($v as $user) + $xml_activities["friendica:".$k][$k_user++.":user"] = $user; + } + $activities = $xml_activities; } - return $res; + return $activities; + + } + + + /** + * @brief return data from profiles + * + * @param array $profile array containing data from db table 'profile' + * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' + * @return array + */ + function api_format_items_profiles(&$profile = null, $type = "json") { + if ($profile != null) { + $profile = array('profile_id' => $profile['id'], + 'profile_name' => $profile['profile-name'], + 'is_default' => $profile['is-default'] ? true : false, + 'hide_friends'=> $profile['hide-friends'] ? true : false, + 'profile_photo' => $profile['photo'], + 'profile_thumb' => $profile['thumb'], + 'publish' => $profile['publish'] ? true : false, + 'net_publish' => $profile['net-publish'] ? true : false, + 'description' => $profile['pdesc'], + 'date_of_birth' => $profile['dob'], + 'address' => $profile['address'], + 'city' => $profile['locality'], + 'region' => $profile['region'], + 'postal_code' => $profile['postal-code'], + 'country' => $profile['country-name'], + 'hometown' => $profile['hometown'], + 'gender' => $profile['gender'], + 'marital' => $profile['marital'], + 'marital_with' => $profile['with'], + 'marital_since' => $profile['howlong'], + 'sexual' => $profile['sexual'], + 'politic' => $profile['politic'], + 'religion' => $profile['religion'], + 'public_keywords' => $profile['pub_keywords'], + 'private_keywords' => $profile['prv_keywords'], + 'likes' => bbcode(api_clean_plain_items($profile['likes']), false, false, 2, false), + 'dislikes' => bbcode(api_clean_plain_items($profile['dislikes']), false, false, 2, false), + 'about' => bbcode(api_clean_plain_items($profile['about']), false, false, 2, false), + 'music' => bbcode(api_clean_plain_items($profile['music']), false, false, 2, false), + 'book' => bbcode(api_clean_plain_items($profile['book']), false, false, 2, false), + 'tv' => bbcode(api_clean_plain_items($profile['tv']), false, false, 2, false), + 'film' => bbcode(api_clean_plain_items($profile['film']), false, false, 2, false), + 'interest' => bbcode(api_clean_plain_items($profile['interest']), false, false, 2, false), + 'romance' => bbcode(api_clean_plain_items($profile['romance']), false, false, 2, false), + 'work' => bbcode(api_clean_plain_items($profile['work']), false, false, 2, false), + 'education' => bbcode(api_clean_plain_items($profile['education']), false, false, 2, false), + 'social_networks' => bbcode(api_clean_plain_items($profile['contact']), false, false, 2, false), + 'homepage' => $profile['homepage'], + 'users' => null); + return $profile; + } } /** @@ -2351,13 +2461,13 @@ * @param array $user_info * @param bool $filter_user filter items by $user_info */ - function api_format_items($r,$user_info, $filter_user = false) { + function api_format_items($r,$user_info, $filter_user = false, $type = "json") { $a = get_app(); + $ret = Array(); foreach($r as $item) { - api_share_as_retweet($item); localize_item($item); list($status_user, $owner_user) = api_item_get_user($a,$item); @@ -2366,66 +2476,35 @@ if ($filter_user AND ($status_user["id"] != $user_info["id"])) continue; - if ($item['thr-parent'] != $item['uri']) { - $r = q("SELECT id FROM item WHERE uid=%d AND uri='%s' LIMIT 1", - intval(api_user()), - dbesc($item['thr-parent'])); - if ($r) - $in_reply_to_status_id = intval($r[0]['id']); - else - $in_reply_to_status_id = intval($item['parent']); - - $in_reply_to_status_id_str = (string) intval($item['parent']); - - $in_reply_to_screen_name = NULL; - $in_reply_to_user_id = NULL; - $in_reply_to_user_id_str = NULL; - - $r = q("SELECT `author-link` FROM item WHERE uid=%d AND id=%d LIMIT 1", - intval(api_user()), - intval($in_reply_to_status_id)); - if ($r) { - $r = q("SELECT * FROM `gcontact` WHERE `url` = '%s'", dbesc(normalise_link($r[0]['author-link']))); - - if ($r) { - if ($r[0]['nick'] == "") - $r[0]['nick'] = api_get_nick($r[0]["url"]); - - $in_reply_to_screen_name = (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']); - $in_reply_to_user_id = intval($r[0]['id']); - $in_reply_to_user_id_str = (string) intval($r[0]['id']); - } - } - } else { - $in_reply_to_screen_name = NULL; - $in_reply_to_user_id = NULL; - $in_reply_to_status_id = NULL; - $in_reply_to_user_id_str = NULL; - $in_reply_to_status_id_str = NULL; - } + $in_reply_to = api_in_reply_to($item); $converted = api_convert_item($item); + if ($type == "xml") + $geo = "georss:point"; + else + $geo = "geo"; + $status = array( 'text' => $converted["text"], 'truncated' => False, 'created_at'=> api_date($item['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'in_reply_to_status_id_str' => $in_reply_to_status_id_str, + 'in_reply_to_status_id' => $in_reply_to['status_id'], + 'in_reply_to_status_id_str' => $in_reply_to['status_id_str'], 'source' => (($item['app']) ? $item['app'] : 'web'), 'id' => intval($item['id']), 'id_str' => (string) intval($item['id']), - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_user_id_str' => $in_reply_to_user_id_str, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => NULL, + 'in_reply_to_user_id' => $in_reply_to['user_id'], + 'in_reply_to_user_id_str' => $in_reply_to['user_id_str'], + 'in_reply_to_screen_name' => $in_reply_to['screen_name'], + $geo => NULL, 'favorited' => $item['starred'] ? true : false, 'user' => $status_user , 'friendica_owner' => $owner_user, //'entities' => NULL, 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $item['parent'], - 'friendica_activities' => api_format_items_activities($item), + 'friendica_activities' => api_format_items_activities($item, $type), ); if (count($converted["attachments"]) > 0) @@ -2447,24 +2526,26 @@ # $IsRetweet = (($item['owner-name'] != $item['author-name']) OR ($item['owner-avatar'] != $item['author-avatar'])); - if ($item['is_retweet'] AND ($item["id"] == $item["parent"])) { - $retweeted_status = $status; - try { - $retweeted_status["user"] = api_get_user($a,$item["retweet-author-link"]); - } catch( BadRequestException $e ) { - // user not found. should be found? - // TODO: check if the user should be found... - $retweeted_status["user"] = array(); - } + if ($item["id"] == $item["parent"]) { + $retweeted_item = api_share_as_retweet($item); + if ($retweeted_item !== false) { + $retweeted_status = $status; + try { + $retweeted_status["user"] = api_get_user($a,$retweeted_item["author-link"]); + } catch( BadRequestException $e ) { + // user not found. should be found? + /// @todo check if the user should be always found + $retweeted_status["user"] = array(); + } - $status["retweeted_status"] = $retweeted_status; - $status["retweeted_status"]["body"] = $item["retweet-body"]; - $status["retweeted_status"]["author-name"] = $item["retweet-author-name"]; - $status["retweeted_status"]["author-link"] = $item["retweet-author-link"]; - $status["retweeted_status"]["author-avatar"] = $item["retweet-author-avatar"]; - $status["retweeted_status"]["plink"] = $item["retweet-plink"]; + $rt_converted = api_convert_item($retweeted_item); - //echo "
"; var_dump($status); killme();
+					$retweeted_status['text'] = $rt_converted["text"];
+					$retweeted_status['statusnet_html'] = $rt_converted["html"];
+					$retweeted_status['friendica_activities'] = api_format_items_activities($retweeted_item, $type);
+					$retweeted_status['created_at'] =  api_date($retweeted_item['created']);
+					$status['retweeted_status'] = $retweeted_status;
+				}
 			}
 
 			// "uid" and "self" are only needed for some internal stuff, so remove it from here
@@ -2474,51 +2555,64 @@
 			if ($item["coord"] != "") {
 				$coords = explode(' ',$item["coord"]);
 				if (count($coords) == 2) {
-					$status["geo"] = array('type' => 'Point',
-							'coordinates' => array((float) $coords[0],
-										(float) $coords[1]));
+					if ($type == "json")
+						$status["geo"] = array('type' => 'Point',
+								'coordinates' => array((float) $coords[0],
+											(float) $coords[1]));
+					else // Not sure if this is the official format - if someone founds a documentation we can check
+						$status["georss:point"] = $item["coord"];
 				}
 			}
-
 			$ret[] = $status;
 		};
 		return $ret;
 	}
 
 
-	function api_account_rate_limit_status(&$a,$type) {
-		$hash = array(
-			  'reset_time_in_seconds' => strtotime('now + 1 hour'),
-			  'remaining_hits' => (string) 150,
-			  'hourly_limit' => (string) 150,
-			  'reset_time' => api_date(datetime_convert('UTC','UTC','now + 1 hour',ATOM_TIME)),
-		);
+	function api_account_rate_limit_status($type) {
+
 		if ($type == "xml")
-			$hash['resettime_in_seconds'] = $hash['reset_time_in_seconds'];
+			$hash = array(
+					'remaining-hits' => (string) 150,
+					'@attributes' => array("type" => "integer"),
+					'hourly-limit' => (string) 150,
+					'@attributes2' => array("type" => "integer"),
+					'reset-time' => datetime_convert('UTC','UTC','now + 1 hour',ATOM_TIME),
+					'@attributes3' => array("type" => "datetime"),
+					'reset_time_in_seconds' => strtotime('now + 1 hour'),
+					'@attributes4' => array("type" => "integer"),
+				);
+		else
+			$hash = array(
+					'reset_time_in_seconds' => strtotime('now + 1 hour'),
+					'remaining_hits' => (string) 150,
+					'hourly_limit' => (string) 150,
+					'reset_time' => api_date(datetime_convert('UTC','UTC','now + 1 hour',ATOM_TIME)),
+				);
 
-		return api_apply_template('ratelimit', $type, array('$hash' => $hash));
+		return api_format_data('hash', $type, array('hash' => $hash));
 	}
 	api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true);
 
-	function api_help_test(&$a,$type) {
+	function api_help_test($type) {
 		if ($type == 'xml')
 			$ok = "true";
 		else
 			$ok = "ok";
 
-		return api_apply_template('test', $type, array("$ok" => $ok));
+		return api_format_data('ok', $type, array("ok" => $ok));
 	}
 	api_register_func('api/help/test','api_help_test',false);
 
-	function api_lists(&$a,$type) {
+	function api_lists($type) {
 		$ret = array();
-		return array($ret);
+		return api_format_data('lists', $type, array("lists_list" => $ret));
 	}
 	api_register_func('api/lists','api_lists',true);
 
-	function api_lists_list(&$a,$type) {
+	function api_lists_list($type) {
 		$ret = array();
-		return array($ret);
+		return api_format_data('lists', $type, array("lists_list" => $ret));
 	}
 	api_register_func('api/lists/list','api_lists_list',true);
 
@@ -2527,7 +2621,10 @@
 	 *  This function is deprecated by Twitter
 	 *  returns: json, xml
 	 **/
-	function api_statuses_f(&$a, $type, $qtype) {
+	function api_statuses_f($type, $qtype) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 		$user_info = api_get_user($a);
 
@@ -2551,7 +2648,7 @@
 		if ($user_info['self'] == 0)
 			$sql_extra = " AND false ";
 
-		$r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 $sql_extra",
+		$r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND NOT `self` AND (NOT `blocked` OR `pending`) $sql_extra",
 			intval(api_user())
 		);
 
@@ -2566,18 +2663,18 @@
 				$ret[] = $user;
 		}
 
-		return array('$users' => $ret);
+		return array('user' => $ret);
 
 	}
-	function api_statuses_friends(&$a, $type){
-		$data =  api_statuses_f($a,$type,"friends");
+	function api_statuses_friends($type){
+		$data =  api_statuses_f($type, "friends");
 		if ($data===false) return false;
-		return  api_apply_template("friends", $type, $data);
+		return  api_format_data("users", $type, $data);
 	}
-	function api_statuses_followers(&$a, $type){
-		$data = api_statuses_f($a,$type,"followers");
+	function api_statuses_followers($type){
+		$data = api_statuses_f($type, "followers");
 		if ($data===false) return false;
-		return  api_apply_template("friends", $type, $data);
+		return  api_format_data("users", $type, $data);
 	}
 	api_register_func('api/statuses/friends','api_statuses_friends',true);
 	api_register_func('api/statuses/followers','api_statuses_followers',true);
@@ -2587,10 +2684,13 @@
 
 
 
-	function api_statusnet_config(&$a,$type) {
+	function api_statusnet_config($type) {
+
+		$a = get_app();
+
 		$name = $a->config['sitename'];
 		$server = $a->get_hostname();
-		$logo = $a->get_baseurl() . '/images/friendica-64.png';
+		$logo = App::get_baseurl() . '/images/friendica-64.png';
 		$email = $a->config['admin_email'];
 		$closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false');
 		$private = (($a->config['system']['block_public']) ? 'true' : 'false');
@@ -2598,7 +2698,7 @@
 		if($a->config['api_import_size'])
 			$texlimit = string($a->config['api_import_size']);
 		$ssl = (($a->config['system']['have_ssl']) ? 'true' : 'false');
-		$sslserver = (($ssl === 'true') ? str_replace('http:','https:',$a->get_baseurl()) : '');
+		$sslserver = (($ssl === 'true') ? str_replace('http:','https:',App::get_baseurl()) : '');
 
 		$config = array(
 			'site' => array('name' => $name,'server' => $server, 'theme' => 'default', 'path' => '',
@@ -2615,32 +2715,26 @@
 			),
 		);
 
-		return api_apply_template('config', $type, array('$config' => $config));
+		return api_format_data('config', $type, array('config' => $config));
 
 	}
 	api_register_func('api/statusnet/config','api_statusnet_config',false);
 
-	function api_statusnet_version(&$a,$type) {
+	function api_statusnet_version($type) {
 		// liar
 		$fake_statusnet_version = "0.9.7";
 
-		if($type === 'xml') {
-			header("Content-type: application/xml");
-			echo '' . "\r\n" . ''.$fake_statusnet_version.'' . "\r\n";
-			killme();
-		}
-		elseif($type === 'json') {
-			header("Content-type: application/json");
-			echo '"'.$fake_statusnet_version.'"';
-			killme();
-		}
+		return api_format_data('version', $type, array('version' => $fake_statusnet_version));
 	}
 	api_register_func('api/statusnet/version','api_statusnet_version',false);
 
 	/**
-	 * @todo use api_apply_template() to return data
+	 * @todo use api_format_data() to return data
 	 */
-	function api_ff_ids(&$a,$type,$qtype) {
+	function api_ff_ids($type,$qtype) {
+
+		$a = get_app();
+
 		if(! api_user()) throw new ForbiddenException();
 
 		$user_info = api_get_user($a);
@@ -2655,46 +2749,39 @@
 
 		$stringify_ids = (x($_REQUEST,'stringify_ids')?$_REQUEST['stringify_ids']:false);
 
-		$r = q("SELECT `gcontact`.`id` FROM `contact`, `gcontact` WHERE `contact`.`nurl` = `gcontact`.`nurl` AND `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` $sql_extra",
+		$r = q("SELECT `pcontact`.`id` FROM `contact`
+				INNER JOIN `contact` AS `pcontact` ON `contact`.`nurl` = `pcontact`.`nurl` AND `pcontact`.`uid` = 0
+				WHERE `contact`.`uid` = %s AND NOT `contact`.`self`",
 			intval(api_user())
 		);
 
-		if(is_array($r)) {
+		if (!dbm::is_result($r))
+			return;
 
-			if($type === 'xml') {
-				header("Content-type: application/xml");
-				echo '' . "\r\n" . '' . "\r\n";
-				foreach($r as $rr)
-					echo '' . $rr['id'] . '' . "\r\n";
-				echo '' . "\r\n";
-				killme();
-			}
-			elseif($type === 'json') {
-				$ret = array();
-				header("Content-type: application/json");
-				foreach($r as $rr)
-					if ($stringify_ids)
-						$ret[] = $rr['id'];
-					else
-						$ret[] = intval($rr['id']);
+		$ids = array();
+		foreach($r as $rr)
+			if ($stringify_ids)
+				$ids[] = $rr['id'];
+			else
+				$ids[] = intval($rr['id']);
 
-				echo json_encode($ret);
-				killme();
-			}
-		}
+		return api_format_data("ids", $type, array('id' => $ids));
 	}
 
-	function api_friends_ids(&$a,$type) {
-		api_ff_ids($a,$type,'friends');
+	function api_friends_ids($type) {
+		return api_ff_ids($type,'friends');
 	}
-	function api_followers_ids(&$a,$type) {
-		api_ff_ids($a,$type,'followers');
+	function api_followers_ids($type) {
+		return api_ff_ids($type,'followers');
 	}
 	api_register_func('api/friends/ids','api_friends_ids',true);
 	api_register_func('api/followers/ids','api_followers_ids',true);
 
 
-	function api_direct_messages_new(&$a, $type) {
+	function api_direct_messages_new($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		if (!x($_POST, "text") OR (!x($_POST,"screen_name") AND !x($_POST,"user_id"))) return;
@@ -2741,7 +2828,7 @@
 			$ret = array("error"=>$id);
 		}
 
-		$data = Array('$messages'=>$ret);
+		$data = Array('direct_message'=>$ret);
 
 		switch($type){
 			case "atom":
@@ -2749,12 +2836,88 @@
 				$data = api_rss_extra($a, $data, $user_info);
 		}
 
-		return  api_apply_template("direct_messages", $type, $data);
+		return  api_format_data("direct-messages", $type, $data);
 
 	}
 	api_register_func('api/direct_messages/new','api_direct_messages_new',true, API_METHOD_POST);
 
-	function api_direct_messages_box(&$a, $type, $box) {
+
+	/**
+	 * @brief delete a direct_message from mail table through api
+	 *
+	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+	 * @return string
+	 */
+	function api_direct_messages_destroy($type){
+		$a = get_app();
+
+		if (api_user()===false) throw new ForbiddenException();
+
+		// params
+		$user_info = api_get_user($a);
+		//required
+		$id = (x($_REQUEST,'id') ? $_REQUEST['id'] : 0);
+		// optional
+		$parenturi = (x($_REQUEST, 'friendica_parenturi') ? $_REQUEST['friendica_parenturi'] : "");
+		$verbose = (x($_GET,'friendica_verbose')?strtolower($_GET['friendica_verbose']):"false");
+		/// @todo optional parameter 'include_entities' from Twitter API not yet implemented
+
+		$uid = $user_info['uid'];
+		// error if no id or parenturi specified (for clients posting parent-uri as well)
+		if ($verbose == "true") {
+			if ($id == 0 || $parenturi == "") {
+				$answer = array('result' => 'error', 'message' => 'message id or parenturi not specified');
+				return api_format_data("direct_messages_delete", $type, array('$result' => $answer));
+			}
+		}
+
+		// BadRequestException if no id specified (for clients using Twitter API)
+		if ($id == 0) throw new BadRequestException('Message id not specified');
+
+		// add parent-uri to sql command if specified by calling app		
+		$sql_extra = ($parenturi != "" ? " AND `parent-uri` = '" . dbesc($parenturi) . "'" : "");
+
+		// get data of the specified message id
+		$r = q("SELECT `id` FROM `mail` WHERE `uid` = %d AND `id` = %d" . $sql_extra,
+			intval($uid), 
+			intval($id));
+	
+		// error message if specified id is not in database
+		if (!dbm::is_result($r)) {
+			if ($verbose == "true") {
+				$answer = array('result' => 'error', 'message' => 'message id not in database');
+				return api_format_data("direct_messages_delete", $type, array('$result' => $answer));
+			}
+			/// @todo BadRequestException ok for Twitter API clients?
+			throw new BadRequestException('message id not in database');
+		}
+
+		// delete message
+		$result = q("DELETE FROM `mail` WHERE `uid` = %d AND `id` = %d" . $sql_extra, 
+			intval($uid), 
+			intval($id));
+
+		if ($verbose == "true") {
+			if ($result) {
+				// return success
+				$answer = array('result' => 'ok', 'message' => 'message deleted');
+				return api_format_data("direct_message_delete", $type, array('$result' => $answer));
+			}
+			else {
+				$answer = array('result' => 'error', 'message' => 'unknown error');
+				return api_format_data("direct_messages_delete", $type, array('$result' => $answer));
+			}
+		}
+		/// @todo return JSON data like Twitter API not yet implemented
+
+	}
+	api_register_func('api/direct_messages/destroy', 'api_direct_messages_destroy', true, API_METHOD_DELETE);
+
+
+	function api_direct_messages_box($type, $box, $verbose) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		// params
@@ -2776,7 +2939,6 @@
 		unset($_GET["screen_name"]);
 
 		$user_info = api_get_user($a);
-		//$profile_url = $a->get_baseurl() . '/profile/' . $a->user['nickname'];
 		$profile_url = $user_info["url"];
 
 
@@ -2812,7 +2974,13 @@
 				intval($since_id),
 				intval($start),	intval($count)
 		);
-
+		if ($verbose == "true") {
+			// stop execution and return error message if no mails available
+			if($r == null) {
+				$answer = array('result' => 'error', 'message' => 'no mails available');
+				return api_format_data("direct_messages_all", $type, array('$result' => $answer));
+			}
+		}
 
 		$ret = Array();
 		foreach($r as $item) {
@@ -2829,28 +2997,32 @@
 		}
 
 
-		$data = array('$messages' => $ret);
+		$data = array('direct_message' => $ret);
 		switch($type){
 			case "atom":
 			case "rss":
 				$data = api_rss_extra($a, $data, $user_info);
 		}
 
-		return  api_apply_template("direct_messages", $type, $data);
+		return  api_format_data("direct-messages", $type, $data);
 
 	}
 
-	function api_direct_messages_sentbox(&$a, $type){
-		return api_direct_messages_box($a, $type, "sentbox");
+	function api_direct_messages_sentbox($type){
+		$verbose = (x($_GET,'friendica_verbose')?strtolower($_GET['friendica_verbose']):"false");
+		return api_direct_messages_box($type, "sentbox", $verbose);
 	}
-	function api_direct_messages_inbox(&$a, $type){
-		return api_direct_messages_box($a, $type, "inbox");
+	function api_direct_messages_inbox($type){
+		$verbose = (x($_GET,'friendica_verbose')?strtolower($_GET['friendica_verbose']):"false");
+		return api_direct_messages_box($type, "inbox", $verbose);
 	}
-	function api_direct_messages_all(&$a, $type){
-		return api_direct_messages_box($a, $type, "all");
+	function api_direct_messages_all($type){
+		$verbose = (x($_GET,'friendica_verbose')?strtolower($_GET['friendica_verbose']):"false");
+		return api_direct_messages_box($type, "all", $verbose);
 	}
-	function api_direct_messages_conversation(&$a, $type){
-		return api_direct_messages_box($a, $type, "conversation");
+	function api_direct_messages_conversation($type){
+		$verbose = (x($_GET,'friendica_verbose')?strtolower($_GET['friendica_verbose']):"false");
+		return api_direct_messages_box($type, "conversation", $verbose);
 	}
 	api_register_func('api/direct_messages/conversation','api_direct_messages_conversation',true);
 	api_register_func('api/direct_messages/all','api_direct_messages_all',true);
@@ -2859,7 +3031,7 @@
 
 
 
-	function api_oauth_request_token(&$a, $type){
+	function api_oauth_request_token($type){
 		try{
 			$oauth = new FKOAuth1();
 			$r = $oauth->fetch_request_token(OAuthRequest::from_request());
@@ -2869,7 +3041,7 @@
 		echo $r;
 		killme();
 	}
-	function api_oauth_access_token(&$a, $type){
+	function api_oauth_access_token($type){
 		try{
 			$oauth = new FKOAuth1();
 			$r = $oauth->fetch_access_token(OAuthRequest::from_request());
@@ -2884,7 +3056,7 @@
 	api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);
 
 
-	function api_fr_photos_list(&$a,$type) {
+	function api_fr_photos_list($type) {
 		if (api_user()===false) throw new ForbiddenException();
 		$r = q("select `resource-id`, max(scale) as scale, album, filename, type from photo
 				where uid = %d and album != 'Contact Photos' group by `resource-id`",
@@ -2895,7 +3067,7 @@
 		'image/png' => 'png',
 		'image/gif' => 'gif'
 		);
-		$data = array('photos'=>array());
+		$data = array('photo'=>array());
 		if($r) {
 			foreach($r as $rr) {
 				$photo = array();
@@ -2903,14 +3075,20 @@
 				$photo['album'] = $rr['album'];
 				$photo['filename'] = $rr['filename'];
 				$photo['type'] = $rr['type'];
-				$photo['thumb'] = $a->get_baseurl()."/photo/".$rr['resource-id']."-".$rr['scale'].".".$typetoext[$rr['type']];
-				$data['photos'][] = $photo;
+				$thumb = App::get_baseurl()."/photo/".$rr['resource-id']."-".$rr['scale'].".".$typetoext[$rr['type']];
+
+				if ($type == "xml")
+					$data['photo'][] = array("@attributes" => $photo, "1" => $thumb);
+				else {
+					$photo['thumb'] = $thumb;
+					$data['photo'][] = $photo;
+				}
 			}
 		}
-		return  api_apply_template("photos_list", $type, $data);
+		return  api_format_data("photos", $type, $data);
 	}
 
-	function api_fr_photo_detail(&$a,$type) {
+	function api_fr_photo_detail($type) {
 		if (api_user()===false) throw new ForbiddenException();
 		if(!x($_REQUEST,'photo_id')) throw new BadRequestException("No photo id.");
 
@@ -2935,16 +3113,24 @@
 
 		if ($r) {
 			$data = array('photo' => $r[0]);
+			$data['photo']['id'] = $data['photo']['resource-id'];
 			if ($scale !== false) {
 				$data['photo']['data'] = base64_encode($data['photo']['data']);
 			} else {
 				unset($data['photo']['datasize']); //needed only with scale param
 			}
-			$data['photo']['link'] = array();
-			for($k=intval($data['photo']['minscale']); $k<=intval($data['photo']['maxscale']); $k++) {
-				$data['photo']['link'][$k] = $a->get_baseurl()."/photo/".$data['photo']['resource-id']."-".$k.".".$typetoext[$data['photo']['type']];
+			if ($type == "xml") {
+				$data['photo']['links'] = array();
+				for ($k=intval($data['photo']['minscale']); $k<=intval($data['photo']['maxscale']); $k++)
+					$data['photo']['links'][$k.":link"]["@attributes"] = array("type" => $data['photo']['type'],
+											"scale" => $k,
+											"href" => App::get_baseurl()."/photo/".$data['photo']['resource-id']."-".$k.".".$typetoext[$data['photo']['type']]);
+			} else {
+				$data['photo']['link'] = array();
+				for ($k=intval($data['photo']['minscale']); $k<=intval($data['photo']['maxscale']); $k++) {
+					$data['photo']['link'][$k] = App::get_baseurl()."/photo/".$data['photo']['resource-id']."-".$k.".".$typetoext[$data['photo']['type']];
+				}
 			}
-			$data['photo']['id'] = $data['photo']['resource-id'];
 			unset($data['photo']['resource-id']);
 			unset($data['photo']['minscale']);
 			unset($data['photo']['maxscale']);
@@ -2953,7 +3139,7 @@
 			throw new NotFoundException();
 		}
 
-		return api_apply_template("photo_detail", $type, $data);
+		return api_format_data("photo_detail", $type, $data);
 	}
 
 	api_register_func('api/friendica/photos/list', 'api_fr_photos_list', true);
@@ -2972,7 +3158,7 @@
 	 * 		c_url: url of remote contact to auth to
 	 * 		url: string, url to redirect after auth
 	 */
-	function api_friendica_remoteauth(&$a) {
+	function api_friendica_remoteauth() {
 		$url = ((x($_GET,'url')) ? $_GET['url'] : '');
 		$c_url = ((x($_GET,'c_url')) ? $_GET['c_url'] : '');
 
@@ -2988,7 +3174,7 @@
 			intval(api_user())
 		);
 
-		if ((! count($r)) || ($r[0]['network'] !== NETWORK_DFRN))
+		if ((! dbm::is_result($r)) || ($r[0]['network'] !== NETWORK_DFRN))
 			throw new BadRequestException("Unknown contact");
 
 		$cid = $r[0]['id'];
@@ -3023,23 +3209,29 @@
 	}
 	api_register_func('api/friendica/remoteauth', 'api_friendica_remoteauth', true);
 
-
+	/**
+	 * @brief Return the item shared, if the item contains only the [share] tag
+	 *
+	 * @param array $item Sharer item
+	 * @return array Shared item or false if not a reshare
+	 */
 	function api_share_as_retweet(&$item) {
 		$body = trim($item["body"]);
 
-		// Skip if it isn't a pure repeated messages
-		// Does it start with a share?
-		if (strpos($body, "[share") > 0)
-			return(false);
-
-		// Does it end with a share?
-		if (strlen($body) > (strrpos($body, "[/share]") + 8))
-			return(false);
+		if (diaspora::is_reshare($body, false)===false) {
+			return false;
+		}
 
 		$attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
 		// Skip if there is no shared message in there
+		// we already checked this in diaspora::is_reshare()
+		// but better one more than one less...
 		if ($body == $attributes)
-			return(false);
+			return false;
+
+
+		// build the fake reshared item
+		$reshared_item = $item;
 
 		$author = "";
 		preg_match("/author='(.*?)'/ism", $attributes, $matches);
@@ -3077,18 +3269,31 @@
 		if ($matches[1] != "")
 			$link = $matches[1];
 
+		$posted = "";
+		preg_match("/posted='(.*?)'/ism", $attributes, $matches);
+		if ($matches[1] != "")
+			$posted= $matches[1];
+
+		preg_match('/posted="(.*?)"/ism', $attributes, $matches);
+		if ($matches[1] != "")
+			$posted = $matches[1];
+
 		$shared_body = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$2",$body);
 
-		if (($shared_body == "") OR ($profile == "") OR ($author == "") OR ($avatar == ""))
-			return(false);
+		if (($shared_body == "") || ($profile == "") || ($author == "") || ($avatar == "") || ($posted == ""))
+			return false;
+
+
 
-		$item["retweet-body"] = $shared_body;
-		$item["retweet-author-name"] = $author;
-		$item["retweet-author-link"] = $profile;
-		$item["retweet-author-avatar"] = $avatar;
-		$item["retweet-plink"] = $link;
-		$item["is_retweet"] = true;
-		return(true);
+		$reshared_item["body"] = $shared_body;
+		$reshared_item["author-name"] = $author;
+		$reshared_item["author-link"] = $profile;
+		$reshared_item["author-avatar"] = $avatar;
+		$reshared_item["plink"] = $link;
+		$reshared_item["created"] = $posted;
+		$reshared_item["edited"] = $posted;
+
+		return $reshared_item;
 
 	}
 
@@ -3100,7 +3305,7 @@
 
 		$nick = "";
 
-		$r = q("SELECT `nick` FROM `gcontact` WHERE `nurl` = '%s'",
+		$r = q("SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'",
 			dbesc(normalise_link($profile)));
 		if ($r)
 			$nick = $r[0]["nick"];
@@ -3159,11 +3364,64 @@
 		return(false);
 	}
 
+	function api_in_reply_to($item) {
+		$in_reply_to = array();
+
+		$in_reply_to['status_id'] = NULL;
+		$in_reply_to['user_id'] = NULL;
+		$in_reply_to['status_id_str'] = NULL;
+		$in_reply_to['user_id_str'] = NULL;
+		$in_reply_to['screen_name'] = NULL;
+
+		if (($item['thr-parent'] != $item['uri']) AND (intval($item['parent']) != intval($item['id']))) {
+			$r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' LIMIT 1",
+				intval($item['uid']),
+				dbesc($item['thr-parent']));
+
+			if (dbm::is_result($r)) {
+				$in_reply_to['status_id'] = intval($r[0]['id']);
+			} else {
+				$in_reply_to['status_id'] = intval($item['parent']);
+			}
+
+			$in_reply_to['status_id_str'] = (string) intval($in_reply_to['status_id']);
+
+			$r = q("SELECT `contact`.`nick`, `contact`.`name`, `contact`.`id`, `contact`.`url` FROM item
+				STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`author-id`
+				WHERE `item`.`id` = %d LIMIT 1",
+				intval($in_reply_to['status_id'])
+			);
+
+			if (dbm::is_result($r)) {
+				if ($r[0]['nick'] == "") {
+					$r[0]['nick'] = api_get_nick($r[0]["url"]);
+				}
+
+				$in_reply_to['screen_name'] = (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']);
+				$in_reply_to['user_id'] = intval($r[0]['id']);
+				$in_reply_to['user_id_str'] = (string) intval($r[0]['id']);
+			}
+
+			// There seems to be situation, where both fields are identical:
+			// https://github.com/friendica/friendica/issues/1010
+			// This is a bugfix for that.
+			if (intval($in_reply_to['status_id']) == intval($item['id'])) {
+				logger('this message should never appear: id: '.$item['id'].' similar to reply-to: '.$in_reply_to['status_id'], LOGGER_DEBUG);
+				$in_reply_to['status_id'] = NULL;
+				$in_reply_to['user_id'] = NULL;
+				$in_reply_to['status_id_str'] = NULL;
+				$in_reply_to['user_id_str'] = NULL;
+				$in_reply_to['screen_name'] = NULL;
+			}
+		}
+
+		return $in_reply_to;
+	}
+
 	function api_clean_plain_items($Text) {
 		$include_entities = strtolower(x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:"false");
 
 		$Text = bb_CleanPictureLinks($Text);
-
 		$URLSearchString = "^\[\]";
 
 		$Text = preg_replace("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",'$1$3',$Text);
@@ -3202,6 +3460,8 @@
 		if (isset($data["url"]))
 			$body .= "\n".$data["url"];
 
+		$body .= $data["after"];
+
 		return $body;
 	}
 
@@ -3249,7 +3509,10 @@
 	}
 
 	// return all or a specified group of the user with the containing contacts
-	function api_friendica_group_show(&$a, $type) {
+	function api_friendica_group_show($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		// params
@@ -3263,7 +3526,7 @@
 				intval($uid),
 				intval($gid));
 			// error message if specified gid is not in database
-			if (count($r) == 0)
+			if (!dbm::is_result($r))
 				throw new BadRequestException("gid not available");
 		}
 		else
@@ -3274,19 +3537,33 @@
 		foreach ($r as $rr) {
 			$members = group_get_members($rr['id']);
 			$users = array();
-			foreach ($members as $member) {
-				$user = api_get_user($a, $member['nurl']);
-				$users[] = $user;
+
+			if ($type == "xml") {
+				$user_element = "users";
+				$k = 0;
+				foreach ($members as $member) {
+					$user = api_get_user($a, $member['nurl']);
+					$users[$k++.":user"] = $user;
+				}
+			} else {
+				$user_element = "user";
+				foreach ($members as $member) {
+					$user = api_get_user($a, $member['nurl']);
+					$users[] = $user;
+				}
 			}
-			$grps[] = array('name' => $rr['name'], 'gid' => $rr['id'], 'user' => $users);
+			$grps[] = array('name' => $rr['name'], 'gid' => $rr['id'], $user_element => $users);
 		}
-		return api_apply_template("group_show", $type, array('$groups' => $grps));
+		return api_format_data("groups", $type, array('group' => $grps));
 	}
 	api_register_func('api/friendica/group_show', 'api_friendica_group_show', true);
 
 
 	// delete the specified group of the user
-	function api_friendica_group_delete(&$a, $type) {
+	function api_friendica_group_delete($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		// params
@@ -3304,7 +3581,7 @@
 			intval($uid),
 			intval($gid));
 		// error message if specified gid is not in database
-		if (count($r) == 0)
+		if (!dbm::is_result($r))
 			throw new BadRequestException('gid not available');
 
 		// get data of the specified group id and group name
@@ -3313,7 +3590,7 @@
 			intval($gid),
 			dbesc($name));
 		// error message if specified gid is not in database
-		if (count($rname) == 0)
+		if (!dbm::is_result($rname))
 			throw new BadRequestException('wrong group name');
 
 		// delete group
@@ -3321,7 +3598,7 @@
 		if ($ret) {
 			// return success
 			$success = array('success' => $ret, 'gid' => $gid, 'name' => $name, 'status' => 'deleted', 'wrong users' => array());
-			return api_apply_template("group_delete", $type, array('$result' => $success));
+			return api_format_data("group_delete", $type, array('result' => $success));
 		}
 		else
 			throw new BadRequestException('other API error');
@@ -3330,7 +3607,10 @@
 
 
 	// create the specified group with the posted array of contacts
-	function api_friendica_group_create(&$a, $type) {
+	function api_friendica_group_create($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		// params
@@ -3349,7 +3629,7 @@
 			intval($uid),
 			dbesc($name));
 		// error message if specified group name already exists
-		if (count($rname) != 0)
+		if (dbm::is_result($rname))
 			throw new BadRequestException('group name already exists');
 
 		// check if specified group name is a deleted group
@@ -3357,7 +3637,7 @@
 			intval($uid),
 			dbesc($name));
 		// error message if specified group name already exists
-		if (count($rname) != 0)
+		if (dbm::is_result($rname))
 			$reactivate_group = true;
 
 		// create group
@@ -3387,13 +3667,16 @@
 		// return success message incl. missing users in array
 		$status = ($erroraddinguser ? "missing user" : ($reactivate_group ? "reactivated" : "ok"));
 		$success = array('success' => true, 'gid' => $gid, 'name' => $name, 'status' => $status, 'wrong users' => $errorusers);
-		return api_apply_template("group_create", $type, array('result' => $success));
+		return api_format_data("group_create", $type, array('result' => $success));
 	}
 	api_register_func('api/friendica/group_create', 'api_friendica_group_create', true, API_METHOD_POST);
 
 
 	// update the specified group with the posted array of contacts
-	function api_friendica_group_update(&$a, $type) {
+	function api_friendica_group_update($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 
 		// params
@@ -3444,12 +3727,15 @@
 		// return success message incl. missing users in array
 		$status = ($erroraddinguser ? "missing user" : "ok");
 		$success = array('success' => true, 'gid' => $gid, 'name' => $name, 'status' => $status, 'wrong users' => $errorusers);
-		return api_apply_template("group_update", $type, array('result' => $success));
+		return api_format_data("group_update", $type, array('result' => $success));
 	}
 	api_register_func('api/friendica/group_update', 'api_friendica_group_update', true, API_METHOD_POST);
 
 
-	function api_friendica_activity(&$a, $type) {
+	function api_friendica_activity($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 		$verb = strtolower($a->argv[3]);
 		$verb = preg_replace("|\..*$|", "", $verb);
@@ -3459,11 +3745,11 @@
 		$res = do_like($id, $verb);
 
 		if ($res) {
-			if ($type == 'xml')
+			if ($type == "xml")
 				$ok = "true";
 			else
 				$ok = "ok";
-			return api_apply_template('test', $type, array('ok' => $ok));
+			return api_format_data('ok', $type, array('ok' => $ok));
 		} else {
 			throw new BadRequestException('Error adding activity');
 		}
@@ -3483,17 +3769,28 @@
 	/**
 	 * @brief Returns notifications
 	 *
-	 * @param App $a
 	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
 	 * @return string
 	*/
-	function api_friendica_notification(&$a, $type) {
+	function api_friendica_notification($type) {
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 		if ($a->argc!==3) throw new BadRequestException("Invalid argument count");
 		$nm = new NotificationsManager();
 
 		$notes = $nm->getAll(array(), "+seen -date", 50);
-		return api_apply_template("", $type, array('$notes' => $notes));
+
+		if ($type == "xml") {
+			$xmlnotes = array();
+			foreach ($notes AS $note)
+				$xmlnotes[] = array("@attributes" => $note);
+
+			$notes = $xmlnotes;
+		}
+
+		return api_format_data("notes", $type, array('note' => $notes));
 	}
 
 	/**
@@ -3501,11 +3798,13 @@
 	 *
 	 * POST request with 'id' param as notification id
 	 *
-	 * @param App $a
 	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
 	 * @return string
 	 */
-	function api_friendica_notification_seen(&$a, $type){
+	function api_friendica_notification_seen($type){
+
+		$a = get_app();
+
 		if (api_user()===false) throw new ForbiddenException();
 		if ($a->argc!==4) throw new BadRequestException("Invalid argument count");
 
@@ -3525,19 +3824,190 @@
 			if ($r!==false) {
 				// we found the item, return it to the user
 				$user_info = api_get_user($a);
-				$ret = api_format_items($r,$user_info);
-				$data = array('$statuses' => $ret);
-				return api_apply_template("timeline", $type, $data);
+				$ret = api_format_items($r,$user_info, false, $type);
+				$data = array('status' => $ret);
+				return api_format_data("status", $type, $data);
 			}
 			// the item can't be found, but we set the note as seen, so we count this as a success
 		}
-		return api_apply_template('', $type, array('status' => "success"));
+		return api_format_data('result', $type, array('result' => "success"));
 	}
 
 	api_register_func('api/friendica/notification/seen', 'api_friendica_notification_seen', true, API_METHOD_POST);
 	api_register_func('api/friendica/notification', 'api_friendica_notification', true, API_METHOD_GET);
 
 
+	/**
+	 * @brief update a direct_message to seen state
+	 *
+	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+	 * @return string (success result=ok, error result=error with error message)
+	 */
+	function api_friendica_direct_messages_setseen($type){
+		$a = get_app();
+		if (api_user()===false) throw new ForbiddenException();
+
+		// params
+		$user_info = api_get_user($a);
+		$uid = $user_info['uid'];
+		$id = (x($_REQUEST, 'id') ? $_REQUEST['id'] : 0);
+
+		// return error if id is zero
+		if ($id == "") {
+			$answer = array('result' => 'error', 'message' => 'message id not specified');
+			return api_format_data("direct_messages_setseen", $type, array('$result' => $answer));
+		}
+
+		// get data of the specified message id
+		$r = q("SELECT `id` FROM `mail` WHERE `id` = %d AND `uid` = %d",
+			intval($id), 
+			intval($uid));
+		// error message if specified id is not in database
+		if (!dbm::is_result($r)) {
+			$answer = array('result' => 'error', 'message' => 'message id not in database');
+			return api_format_data("direct_messages_setseen", $type, array('$result' => $answer));
+		}
+
+		// update seen indicator
+		$result = q("UPDATE `mail` SET `seen` = 1 WHERE `id` = %d AND `uid` = %d", 
+			intval($id), 
+			intval($uid));
+
+		if ($result) {
+			// return success
+			$answer = array('result' => 'ok', 'message' => 'message set to seen');
+			return api_format_data("direct_message_setseen", $type, array('$result' => $answer));
+		} else {
+			$answer = array('result' => 'error', 'message' => 'unknown error');
+			return api_format_data("direct_messages_setseen", $type, array('$result' => $answer));
+		}
+	}
+	api_register_func('api/friendica/direct_messages_setseen', 'api_friendica_direct_messages_setseen', true);
+
+
+
+
+	/**
+	 * @brief search for direct_messages containing a searchstring through api
+	 *
+	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+	 * @return string (success: success=true if found and search_result contains found messages
+	 *                          success=false if nothing was found, search_result='nothing found',
+	 * 		   error: result=error with error message)
+	 */
+	function api_friendica_direct_messages_search($type){
+		$a = get_app();
+
+		if (api_user()===false) throw new ForbiddenException();
+
+		// params
+		$user_info = api_get_user($a);
+		$searchstring = (x($_REQUEST,'searchstring') ? $_REQUEST['searchstring'] : "");
+		$uid = $user_info['uid'];
+
+		// error if no searchstring specified
+		if ($searchstring == "") {
+			$answer = array('result' => 'error', 'message' => 'searchstring not specified');
+			return api_format_data("direct_messages_search", $type, array('$result' => $answer));
+		}
+
+		// get data for the specified searchstring
+		$r = q("SELECT `mail`.*, `contact`.`nurl` AS `contact-url` FROM `mail`,`contact` WHERE `mail`.`contact-id` = `contact`.`id` AND `mail`.`uid`=%d AND `body` LIKE '%s' ORDER BY `mail`.`id` DESC",
+			intval($uid),
+			dbesc('%'.$searchstring.'%')
+		);
+
+		$profile_url = $user_info["url"];
+		// message if nothing was found
+		if (!dbm::is_result($r))
+			$success = array('success' => false, 'search_results' => 'problem with query');
+		else if (count($r) == 0) 
+			$success = array('success' => false, 'search_results' => 'nothing found');
+		else {
+			$ret = Array();
+			foreach($r as $item) {
+				if ($box == "inbox" || $item['from-url'] != $profile_url){
+					$recipient = $user_info;
+					$sender = api_get_user($a,normalise_link($item['contact-url']));
+				}
+				elseif ($box == "sentbox" || $item['from-url'] == $profile_url){
+					$recipient = api_get_user($a,normalise_link($item['contact-url']));
+					$sender = $user_info;
+				}
+				$ret[]=api_format_messages($item, $recipient, $sender);
+			}
+			$success = array('success' => true, 'search_results' => $ret);
+		}
+
+		return api_format_data("direct_message_search", $type, array('$result' => $success));
+	}
+	api_register_func('api/friendica/direct_messages_search', 'api_friendica_direct_messages_search', true);
+
+	/**
+	 * @brief return data of all the profiles a user has to the client
+	 *
+	 * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+	 * @return string
+	 */
+	function api_friendica_profile_show($type){
+		$a = get_app();
+
+		if (api_user()===false) throw new ForbiddenException();
+
+		// input params
+		$profileid = (x($_REQUEST,'profile_id') ? $_REQUEST['profile_id'] : 0);
+
+		// retrieve general information about profiles for user
+		$multi_profiles = feature_enabled(api_user(),'multi_profiles');
+		$directory = get_config('system', 'directory');
+
+// get data of the specified profile id or all profiles of the user if not specified
+		if ($profileid != 0) {
+			$r = q("SELECT * FROM `profile` WHERE `uid` = %d AND `id` = %d",
+				intval(api_user()),
+				intval($profileid));
+			// error message if specified gid is not in database
+			if (!dbm::is_result($r))
+				throw new BadRequestException("profile_id not available");
+		}
+		else
+			$r = q("SELECT * FROM `profile` WHERE `uid` = %d",
+				intval(api_user()));
+
+		// loop through all returned profiles and retrieve data and users
+		$k = 0;
+		foreach ($r as $rr) {
+			$profile = api_format_items_profiles($rr, $type);
+
+			// select all users from contact table, loop and prepare standard return for user data
+			$users = array();
+			$r = q("SELECT `id`, `nurl` FROM `contact` WHERE `uid`= %d AND `profile-id` = %d",
+				intval(api_user()),
+				intval($rr['profile_id']));
+
+			foreach ($r as $rr) {
+				$user = api_get_user($a, $rr['nurl']);
+				($type == "xml") ? $users[$k++.":user"] = $user : $users[] = $user;
+			}
+			$profile['users'] = $users;
+
+			// add prepared profile data to array for final return
+			if ($type == "xml") {
+				$profiles[$k++.":profile"] = $profile;
+			} else {
+				$profiles[] = $profile;
+			}
+		}
+
+		// return settings, authenticated user and profiles data
+		$result = array('multi_profiles' => $multi_profiles ? true : false,
+						'global_dir' => $directory,
+						'friendica_owner' => api_get_user($a, intval(api_user())),
+						'profiles' => $profiles);
+		return api_format_data("friendica_profiles", $type, array('$result' => $result));
+	}
+	api_register_func('api/friendica/profile/show', 'api_friendica_profile_show', true, API_METHOD_GET);
+
 /*
 To.Do:
     [pagename] => api/1.1/statuses/lookup.json
@@ -3563,6 +4033,9 @@ account/update_profile_background_image
 account/update_profile_image
 blocks/create
 blocks/destroy
+friendica/profile/update
+friendica/profile/create
+friendica/profile/delete
 
 Not implemented in status.net:
 statuses/retweeted_to_me