X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fapi.php;h=0b925aee0f6df4345f46473a4a7a921efe5c8344;hb=57bba711dc64de7796730750873fb5bbc3302337;hp=bcf14d6a829603e38054331a762f81bb08a46655;hpb=199f72ee3cd3c2ef7edfafa1d5a36a365bad93ad;p=friendica.git diff --git a/include/api.php b/include/api.php index bcf14d6a82..0b925aee0f 100644 --- a/include/api.php +++ b/include/api.php @@ -1,6 +1,6 @@ user@server, ignoring server part * * @param App $a App + * @param bool $do_login try to log in when not logged in, otherwise quit silently * @throws ForbiddenException * @throws InternalServerErrorException * @throws UnauthorizedException @@ -186,8 +186,10 @@ function api_register_func($path, $func, $auth = false, $method = API_METHOD_ANY * 'authenticated' => return status, * 'user_record' => return authenticated user record */ -function api_login(App $a) +function api_login(App $a, bool $do_login = true) { + $_SESSION["allow_api"] = false; + // workaround for HTTP-auth in CGI mode if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)); @@ -217,6 +219,10 @@ function api_login(App $a) Logger::warning(API_LOG_PREFIX . 'OAuth error', ['module' => 'api', 'action' => 'login', 'exception' => $e->getMessage()]); } + if (!$do_login) { + return; + } + Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]); header('WWW-Authenticate: Basic realm="Friendica"'); throw new UnauthorizedException("This API requires login"); @@ -248,7 +254,7 @@ function api_login(App $a) */ Hook::callAll('authenticate', $addon_auth); - if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) { + if ($addon_auth['authenticated'] && !empty($addon_auth['user_record'])) { $record = $addon_auth['user_record']; } else { $user_id = User::authenticate(trim($user), trim($password), true); @@ -258,6 +264,9 @@ function api_login(App $a) } if (!DBA::isResult($record)) { + if (!$do_login) { + return; + } Logger::debug(API_LOG_PREFIX . 'failed', ['module' => 'api', 'action' => 'login', 'parameters' => $_SERVER]); header('WWW-Authenticate: Basic realm="Friendica"'); //header('HTTP/1.0 401 Unauthorized'); @@ -1022,7 +1031,7 @@ function api_statuses_mediap($type) $_REQUEST['profile_uid'] = api_user(); $_REQUEST['api_source'] = true; - $txt = requestdata('status'); + $txt = requestdata('status') ?? ''; /// @TODO old-lost code? //$txt = urldecode(requestdata('status')); @@ -1077,7 +1086,7 @@ function api_statuses_update($type) // convert $_POST array items to the form we use for web posts. if (requestdata('htmlstatus')) { - $txt = requestdata('htmlstatus'); + $txt = requestdata('htmlstatus') ?? ''; if ((strpos($txt, '<') !== false) || (strpos($txt, '>') !== false)) { $txt = HTML::toBBCodeVideo($txt); @@ -1119,8 +1128,8 @@ function api_statuses_update($type) if ($throttle_day > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_day = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_day = Post::count($condition); if ($posts_day > $throttle_day) { Logger::log('Daily posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1133,8 +1142,8 @@ function api_statuses_update($type) if ($throttle_week > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_week = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_week = Post::count($condition); if ($posts_week > $throttle_week) { Logger::log('Weekly posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1147,8 +1156,8 @@ function api_statuses_update($type) if ($throttle_month > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_month = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_month = Post::count($condition); if ($posts_month > $throttle_month) { Logger::log('Monthly posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1158,30 +1167,56 @@ function api_statuses_update($type) } } - if (!empty($_FILES['media'])) { + if (requestdata('media_ids')) { + $ids = explode(',', requestdata('media_ids') ?? ''); + } elseif (!empty($_FILES['media'])) { // upload the image if we have one $picture = wall_upload_post($a, false); if (is_array($picture)) { - $_REQUEST['body'] .= "\n\n" . '[url=' . $picture["albumpage"] . '][img]' . $picture["preview"] . "[/img][/url]"; + $ids[] = $picture['id']; } } - if (requestdata('media_ids')) { - $ids = explode(',', requestdata('media_ids')); + $attachments = []; + $ressources = []; + + if (!empty($ids)) { foreach ($ids as $id) { - $r = q( - "SELECT `resource-id`, `scale`, `nickname`, `type`, `desc` FROM `photo` INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = %d) AND `scale` > 0 AND `photo`.`uid` = %d ORDER BY `photo`.`width` DESC LIMIT 1", - intval($id), - api_user() - ); - if (DBA::isResult($r)) { + $media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `nickname`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo` + INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN + (SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ? + ORDER BY `photo`.`width` DESC LIMIT 2", $id, api_user())); + + if (!empty($media)) { + $ressources[] = $media[0]['resource-id']; $phototypes = Images::supportedTypes(); - $ext = $phototypes[$r[0]['type']]; - $description = $r[0]['desc'] ?? ''; - $_REQUEST['body'] .= "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $r[0]['nickname'] . '/image/' . $r[0]['resource-id'] . ']'; - $_REQUEST['body'] .= '[img=' . DI::baseUrl() . '/photo/' . $r[0]['resource-id'] . '-' . $r[0]['scale'] . '.' . $ext . ']' . $description . '[/img][/url]'; + $ext = $phototypes[$media[0]['type']]; + + $attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'], + 'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext, + 'size' => $media[0]['datasize'], + 'name' => $media[0]['filename'] ?: $media[0]['resource-id'], + 'description' => $media[0]['desc'] ?? '', + 'width' => $media[0]['width'], + 'height' => $media[0]['height']]; + + if (count($media) > 1) { + $attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext; + $attachment['preview-width'] = $media[1]['width']; + $attachment['preview-height'] = $media[1]['height']; + } + $attachments[] = $attachment; } } + + // We have to avoid that the post is rejected because of an empty body + if (empty($_REQUEST['body'])) { + $_REQUEST['body'] = '[hr]'; + } + } + + if (!empty($attachments)) { + $_REQUEST['attachments'] = $attachments; } // set this so that the item_post() function is quiet and doesn't redirect or emit json @@ -1195,6 +1230,13 @@ function api_statuses_update($type) // call out normal post function $item_id = item_post($a); + if (!empty($ressources) && !empty($item_id)) { + $item = Post::selectFirst(['uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], ['id' => $item_id]); + foreach ($ressources as $ressource) { + Photo::setPermissionForRessource($ressource, api_user(), $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']); + } + } + // output the post that we just posted. return api_status_show($type, $item_id); } @@ -1732,16 +1774,16 @@ function api_statuses_public_timeline($type) $start = max(0, ($page - 1) * $count); if ($exclude_replies && !$conversation_id) { - $condition = ["`gravity` IN (?, ?) AND `iid` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`", - GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; + $condition = ["`gravity` = ? AND `id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`", + GRAVITY_PARENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `iid` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Post::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Post::selectForUser(api_user(), [], $condition, $params); $r = Post::toArray($statuses); } else { @@ -1811,18 +1853,18 @@ function api_statuses_networkpublic_timeline($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `iid` > ? AND `private` = ?", + $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `id` > ? AND `private` = ?", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `iid` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Post::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Post::toArray(Post::selectForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params)); - $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); + $ret = api_format_items($statuses, $user_info, false, $type); bindComments($ret); @@ -2041,13 +2083,13 @@ function api_statuses_repeat($type) $fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink']; $item = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]); - + if (DBA::isResult($item) && !empty($item['body'])) { if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) { if (!Item::performActivity($id, 'announce', local_user())) { throw new InternalServerErrorException(); } - + $item_id = $id; } else { if (strpos($item['body'], "[/share]") !== false) { @@ -2170,24 +2212,21 @@ function api_statuses_mentions($type) $start = max(0, ($page - 1) * $count); - $query = "`gravity` IN (?, ?) AND `id` IN (SELECT `iid` FROM `user-item` - WHERE (`hidden` IS NULL OR NOT `hidden`) AND - `uid` = ? AND `notification-type` & ? != 0 - AND `iid` > ?"; + $query = "`gravity` IN (?, ?) AND `uri-id` IN + (SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`) + AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?"; $condition = [GRAVITY_PARENT, GRAVITY_COMMENT, api_user(), - UserItem::NOTIF_EXPLICIT_TAGGED | UserItem::NOTIF_IMPLICIT_TAGGED | - UserItem::NOTIF_THREAD_COMMENT | UserItem::NOTIF_DIRECT_COMMENT | - UserItem::NOTIF_DIRECT_THREAD_COMMENT, - $since_id]; + Post\UserNotification::NOTIF_EXPLICIT_TAGGED | Post\UserNotification::NOTIF_IMPLICIT_TAGGED | + Post\UserNotification::NOTIF_THREAD_COMMENT | Post\UserNotification::NOTIF_DIRECT_COMMENT | + Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, + api_user(), $since_id]; if ($max_id > 0) { - $query .= " AND `iid` <= ?"; + $query .= " AND `id` <= ?"; $condition[] = $max_id; } - $query .= ")"; - array_unshift($condition, $query); $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; @@ -2266,7 +2305,6 @@ function api_statuses_user_timeline($type) $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; $statuses = Post::selectForUser(api_user(), [], $condition, $params); @@ -2513,7 +2551,8 @@ function api_format_messages($item, $recipient, $sender) */ function api_convert_item($item) { - $body = $item['body']; + $body = api_add_attachments_to_body($item); + $entities = api_get_entitities($statustext, $body); // Add pictures to the attachment array and remove them from the body @@ -2538,7 +2577,7 @@ function api_convert_item($item) $statustext = mb_substr($statustext, 0, 1000) . "... \n" . ($item['plink'] ?? ''); } - $statushtml = BBCode::convert(BBCode::removeAttachment($body), false); + $statushtml = BBCode::convert(BBCode::removeAttachment($body), false, BBCode::API, true); // Workaround for clients with limited HTML parser functionality $search = ["
", "
", "
", @@ -2581,6 +2620,34 @@ function api_convert_item($item) ]; } +/** + * Add media attachments to the body + * + * @param array $item + * @return string body with added media + */ +function api_add_attachments_to_body(array $item) +{ + $body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); + + if (strpos($body, '[/img]') !== false) { + return $body; + } + + foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]) as $media) { + if (!empty($media['preview'])) { + $description = $media['description'] ?: $media['name']; + if (!empty($description)) { + $body .= "\n[img=" . $media['preview'] . ']' . $description .'[/img]'; + } else { + $body .= "\n[img]" . $media['preview'] .'[/img]'; + } + } + } + + return $body; +} + /** * * @param string $body @@ -2609,7 +2676,11 @@ function api_get_attachments(&$body) $imagedata = Images::getInfoFromURLCached($image); if ($imagedata) { - $attachments[] = ["url" => $image, "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; + if (DI::config()->get("system", "proxy_disabled")) { + $attachments[] = ["url" => $image, "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; + } else { + $attachments[] = ["url" => ProxyUtils::proxifyUrl($image, false), "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; + } } } @@ -2633,7 +2704,7 @@ function api_get_entitities(&$text, $bbcode) preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images); foreach ($images[1] as $image) { - $replace = ProxyUtils::proxifyUrl($image); + $replace = ProxyUtils::proxifyUrl($image, false); $text = str_replace($image, $replace, $text); } return []; @@ -2748,7 +2819,7 @@ function api_get_entitities(&$text, $bbcode) // If image cache is activated, then use the following sizes: // thumb (150), small (340), medium (600) and large (1024) if (!DI::config()->get("system", "proxy_disabled")) { - $media_url = ProxyUtils::proxifyUrl($url); + $media_url = ProxyUtils::proxifyUrl($url, false); $sizes = []; $scale = Images::getScalingDimensions($image[0], $image[1], 150); @@ -2935,6 +3006,10 @@ function api_format_items($items, $user_info, $filter_user = false, $type = "jso $ret = []; + if (empty($items)) { + return $ret; + } + foreach ((array)$items as $item) { list($status_user, $author_user, $owner_user) = api_item_get_user($a, $item); @@ -4825,7 +4900,7 @@ function prepare_photo_data($type, $scale, $photo_id) "SELECT %s `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`, `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`, MIN(`scale`) AS `minscale`, MAX(`scale`) AS `maxscale` - FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY + FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`, `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`", $data_sql, @@ -4873,7 +4948,7 @@ function prepare_photo_data($type, $scale, $photo_id) } // retrieve item element for getting activities (like, dislike etc.) related to photo - $condition = ['uid' => api_user(), 'resource-id' => $photo_id, 'type' => 'photo']; + $condition = ['uid' => api_user(), 'resource-id' => $photo_id]; $item = Post::selectFirst(['id', 'uid', 'uri', 'parent', 'allow_cid', 'deny_cid', 'allow_gid', 'deny_gid'], $condition); if (!DBA::isResult($item)) { throw new NotFoundException('Photo-related item not found.'); @@ -4882,7 +4957,7 @@ function prepare_photo_data($type, $scale, $photo_id) $data['photo']['friendica_activities'] = api_format_items_activities($item, $type); // retrieve comments on photo - $condition = ["`parent` = ? AND `uid` = ? AND (`gravity` IN (?, ?) OR `type`='photo')", + $condition = ["`parent` = ? AND `uid` = ? AND `gravity` IN (?, ?)", $item['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT]; $statuses = Post::selectForUser(api_user(), [], $condition); @@ -5995,12 +6070,12 @@ api_register_func('api/saved_searches/list', 'api_saved_searches_list', true); * * @return void */ -function bindComments(&$data) +function bindComments(&$data) { if (count($data) == 0) { return; } - + $ids = []; $comments = []; foreach ($data as $item) { @@ -6008,7 +6083,7 @@ function bindComments(&$data) } $idStr = DBA::escape(implode(', ', $ids)); - $sql = "SELECT `parent`, COUNT(*) as comments FROM `post-view` WHERE `parent` IN ($idStr) AND `deleted` = ? AND `gravity`= ? GROUP BY `parent`"; + $sql = "SELECT `parent`, COUNT(*) as comments FROM `post-user-view` WHERE `parent` IN ($idStr) AND `deleted` = ? AND `gravity`= ? GROUP BY `parent`"; $items = DBA::p($sql, 0, GRAVITY_COMMENT); $itemsData = DBA::toArray($items);