$output
));
$result = curl_exec($cSession);
curl_close($cSession);
return $result;
}
/* this function fetches all the data from a profile (bio, username, relationships, etc)
- $user is the id of the queried user
- returns the array conversion of the json response
*/
function user_info($user) {
global $user_settings;
$info = api_get("accounts/" . $user);
$rel = api_get("accounts/relationships?id=" . $user);
return array(
$info,
$rel
);
}
/* this function fetches the context (the previous posts and replies) of a specified post
- $post = ID of the post queried.
- returns an array conversion of the json response
*/
function context($post) {
global $srv;
global $token;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$post/context");
if (!is_null($token)) {
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $token
));
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
/* a function to fav or unfav a specified post
- $post = ID of the post queried.
- $mode can be true if is a fav or false if it's an unfav
- returns the number of favs of the post after the specified action or "error"
if there was an error performing the action
*/
function favourite($post, $mode) {
$result = api_post(($mode == true ? "statuses/$post/favourite" : "statuses/$post/unfavourite"),array());
if (isset($result['favourites_count'])) {
return $result['favourites_count'];
}
else {
return "error";
}
}
/* a function to reblog or unreblog a specified post
- $post = ID of the post queried.
- $mode can be true if is a reblog or false if it's an unreblog
- returns the number of reblogs of the post after the specified action or "error"
if there was an error performing the action
*/
function reblog($post, $mode) {
$result = api_post(($mode == true ? "statuses/$post/reblog" : "statuses/$post/unreblog"),array());
if (isset($result['reblog']['reblogs_count'])) {
return $result['reblog']['reblogs_count'];
}
elseif (isset($result['reblogs_count'])) {
return $result['reblogs_count'];
}
else {
return "error";
}
}
/* function to delete a post
- $id is the id of the post to delete
- returns 1 if the post was deleted succesfully or 0 if there was an error
*/
function delpost($id) {
global $srv;
global $token;
if (!is_null($token)) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$id");
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $token
));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
$result = json_decode($result, true);
if (empty($result)) {
return "1";
}
else {
return "0";
}
}
}
/* function to issue a vote to a poll
- $id is the id of the poll to vote on
- $choices is a comma separated string of the choices that will be voted.
*/
function vote($poll, $choices) {
global $srv;
global $token;
if (!is_null($token)) {
$cSession = curl_init();
curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/polls/$poll/votes");
curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cSession, CURLOPT_POST, 1);
curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $token
));
$query = "";
$choicelist = explode(",",$choices);
foreach($choicelist as $choice){
$query .= "choices[]=$choice&";
}
curl_setopt($cSession, CURLOPT_POSTFIELDS, $query);
$result = curl_exec($cSession);
curl_close($cSession);
return $result;
}
else {
return false;
}
}
/* function to send a new post to the logged in instance
- $text = the body of the message
- $media = array of uploaded media id's
- $reply = the id of a post being replied to, can be null.
- $markdown = specify if the post uses markdown (unused at this moment)
- $scope = the scope of the post (public, private, unlisted or direct)
- $sensitive = bool to specify if the post media is sensitive
- $spoiler = string of the title of the post
- returns the json of the api response
*/
function sendpost($text, $media, $reply = null, $markdown = false, $scope = "public", $sensitive = false, $spoiler = false) {
global $srv;
global $token;
if (!is_null($token)) {
$cSession = curl_init();
curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/statuses");
curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cSession, CURLOPT_POST, 1);
curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $token
));
$query = "";
$query .= "status=" . urlencode(html_entity_decode($text, ENT_QUOTES)) . "&visibility=" . $scope;;
if (!is_null($reply) && $reply != "null") {
$query .= "&in_reply_to_id=" . $reply;
}
if ($markdown == true) {
$query .= "&content_type=text/markdown";
}
if ($sensitive == 'true') {
$query .= "&sensitive=true";
}
if ($spoiler == true) {
$query .= "&spoiler_text=" . $spoiler;
}
if (!is_null($media)) {
foreach ($media as $mid) {
$query .= "&media_ids[]=" . $mid;
}
}
curl_setopt($cSession, CURLOPT_POSTFIELDS, $query);
$result = curl_exec($cSession);
curl_close($cSession);
return $result;
}
else {
return false;
}
}
/* uploads a file to the logged in instance.
- $file = path of the file to upload on local storage
- returns an array where:
0: the ID of the uploaded file
1: the url of the file (if the file isn't an image, returns a placeholder)
*/
function uploadpic($file) {
global $srv;
global $token;
if (!is_null($token)) {
$mime = get_mime($file);
$info = pathinfo($file);
$name = $info['basename'];
$output = new CURLFile($file, $mime, $name);
$cSession = curl_init();
curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/media");
curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cSession, CURLOPT_POST, 1);
curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $token
));
curl_setopt($cSession, CURLOPT_POSTFIELDS, array(
'file' => $output
));
$result = curl_exec($cSession);
curl_close($cSession);
echo $result;
$array = json_decode($result, true);
$ext = explode(".", $array['url']);
$ext = end($ext);
$ext = explode("?", $ext) [0];
if (in_array($ext, array('jpg','jpeg','gif','png','svg','webm'))) {
$file = $array['url'];
} elseif (in_array($ext, array('mp4','webp','ogv'))) {
$file = "img/vid.png";
} elseif (in_array($ext, array('mp3','ogg','oga','opus'))) {
$file = "img/aud.png";
} else {
$file = "img/doc.png";
}
return json_encode(array(
$array['id'],
$file
));
}
else {
return false;
}
}
/* this is used to register DashFE as an application on an instance
mostly used when logging in
- $instance = the url of the instance where to log in
- returns the array conversion of the json response
*/
function register_app($instance) {
global $setting;
$cSession = curl_init();
curl_setopt($cSession, CURLOPT_URL, "https://$instance/api/v1/apps");
curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cSession, CURLOPT_HEADER, false);
curl_setopt($cSession, CURLOPT_POST, 1);
curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query(array(
'client_name' => $setting['appname'],
'redirect_uris' => $setting['url'],
'scopes' => 'read write follow'
)));
$result = curl_exec($cSession);
curl_close($cSession);
return json_decode($result, true);
}
/* this function will get all the notes (reblogs and favs) that has an specified post
- $thread = the id of the post queried
- returns an array with all the notes
each note is another array where:
0 = type of note ("reb" reblog or "fab" favorite)
1 = array of the details of the user, converted from the json of the api response.
*/
function getnotes($thread) {
global $user_settings;
global $token;
global $srv;
global $setting;
global $logedin;
@$reb = array(
api_get("statuses/" . $thread . "/reblogged_by")
);
@$fab = array(
api_get("statuses/" . $thread . "/favourited_by")
);
$limit = (count($reb[0]) > count($fab[0]) ? count($reb[0]) - 1 : count($fab[0]) - 1);
$notes = array();
$index = 0;
for ($i = 0;$i <= $limit;$i++) {
if (isset($reb[0][$i])) {
$notes[$index][0] = "reb";
$notes[$index][1] = $reb[0][$i];
$index++;
}
if (isset($fab[0][$i])) {
$notes[$index][0] = "fav";
$notes[$index][1] = $fab[0][$i];
$index++;
}
}
return $notes;
}
/* this function will fetch replies of a post
- $thread = the id of the post from where to fetch replies
- $since = id of a post. If specified, the function will fetch the replies
only since the specified id.
- returns an array with all the replies
each element is another array where:
'mode' = type of the reply (ancestor or descendant)
'content' = array of the contents of the reply, converted from the json of the api response.
*/
function getreplies($thread, $since = false) {
global $user_settings;
global $token;
global $srv;
global $setting;
global $logedin;
$context = json_decode(context($thread) , true);
$array = array();
if (!empty($context['ancestors'])) {
if ($since == false) {
foreach ($context['ancestors'] as $elem) {
$elem['type'] = 'ancestor';
$array[] = $elem;
}
}
}
$flag = 0;
if (!empty($context['descendants'])) {
foreach ($context['descendants'] as $elem) {
if (($since != false && $flag == 1) || $since == false) {
$elem['type'] = 'descendant';
$array[] = $elem;
}
if ($since != false && $elem['id'] == $since) {
$flag = 1;
}
}
}
$replies = array();
foreach ($array as $item) {
$reply['mode'] = "";
if ($item['type'] == 'ancestor') {
$reply['mode'] = "ancestor";
}
$replies[] = array(
'mode' => $reply['mode'],
'content' => $item
);
}
return $replies;
}
/* this function takes some options from the init.php file and the user_settings cookie
and fetches all the posts of the appropiate timeline
- $query = an array with a set of elements generated by the file "include/init.php"
- returns an array of the posts list fetched converted from the json response of the api.
*/
function timeline($query) {
global $token;
global $srv;
$notes = "";
$hq = array();
$hq['limit'] = 10;
$hq['only_media'] = ($query['text'] == "off" ? 'true' : 'false');
if ($query['next']){
$hq['max_id'] = $query['next'];
$next = $query['next'];
} elseif ($query['since']) {
$hq['since_id'] = $query['since'];
}
switch ($query['mode']) {
case "home":
$array = api_get("timelines/home?".http_build_query($hq));
break;
case "federated":
$array = api_get("timelines/public?".http_build_query($hq));
break;
case "tag":
$array = api_get("timelines/tag/" . $query['tag'] . "?".http_build_query($hq));
break;
case "local":
$array = api_get("timelines/public?local=true&".http_build_query($hq));
break;
case "user":
$array = api_get("accounts/" . $query['user'] . "/statuses?".http_build_query($hq));
break;
case "thread":
$array = array(
api_get("statuses/" . $query['thread'])
);
break;
case "favourites":
$array = api_get("favourites?".http_build_query($hq));
break;
case "direct":
$array = api_get("timelines/direct?".http_build_query($hq));
break;
case "list":
$array = api_get("timelines/list/" . $query['list'] . "?".http_build_query($hq));
break;
case "bookmarks":
$array = api_get("bookmarks?".http_build_query($hq));
break;
case "search":
$array = api_getv2("search?limit=40&q=".$query['search']."{$next}")['statuses'];
break;
case "account":
$info = api_get("accounts/verify_credentials");
$array = api_get("accounts/" . $info['id'] . "/statuses?".http_build_query($hq));
break;
default:
$array = api_get("timelines/public?".http_build_query($hq));
break;
}
if (!is_array($array)) {
return false;
}
$next = end($array) ['id'];
$thread = array();
/*
foreach ($array as $elem) {
if ($query['replies'] == "on" || $query['mode'] == "thread") {
$thread[] = $elem;
}
else {
if ($elem['in_reply_to_id'] == null) {
$thread[] = $elem;
}
}
}*/
foreach ($array as $elem) {
if ($query['replies'] == "on" || $query['mode'] == "thread") {
$thread[] = $elem;
}
else {
if ($elem['in_reply_to_id'] == null || $elem['in_reply_to_account_id'] == $query['uid']) {
$thread[] = $elem;
} else {
$rel = api_get("accounts/relationships?id=" . $elem['in_reply_to_account_id']);
if ($rel[0]['following']){
$thread[] = $elem;
}
}
}
}
return $thread;
}
// FUNCTIONS THAT HAVE TO DO WITH RENDERING STUFF FOR THE PAGE
/* this function is used to generate the html code of a poll */
function renderPoll($elem) {
global $logedin;
$output = "";
$output .= "
";
$votes = $elem['poll']['votes_count'];
if ($elem['poll']['voted'] || $elem['poll']['expired']) {
$output.= "Votes: $votes
";
foreach ($elem['poll']['options'] as $option){
$percentage = ($option['votes_count'] / $votes ) * 100;
$output .= "
');
$result = str_replace('
', '
', $result);
foreach ($elem['mentions'] as $mention) {
if(contains($result,"@".$mention['username'])) {
$result = str_replace("@" . $mention['username'], "@" . $mention['username'] . "", $result);
} else {
$result = "@" . $mention['username'] . " ".$result;
}
}
$result = emojify($result, $elem['emojis']);
/* We convert hashtags to clickable links. The regex detects only strings that are not part of an url.
If the user is not logged in, it just shows the hashtag in bold.
Regex expression got it from here https://stackoverflow.com/questions/39414007/php-find-all-hashtags-but-no-in-link
*/
if ($logedin){
$result = preg_replace("!(?:f|ht)tps?://[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;/=]+(*SKIP)(*F)|#(\w+)!", "#$1", $result);
} else {
$result = preg_replace("!(?:f|ht)tps?://[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;/=]+(*SKIP)(*F)|#(\w+)!", "#$1", $result);
}
return $result;
}
// OTHER FUNCTIONS
/* the purpose of this function is to encode the auth token
it is not used for now */
function msc($string, $action = 'e') {
// you may change these values to your own
$secret_key = 'yAmfVhZwm0749FSY24dC';
$secret_iv = 'm37uvAeKjYLKdI1lPkcJ';
$output = false;
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', $secret_key);
$iv = substr(hash('sha256', $secret_iv) , 0, 16);
if ($action == 'e') {
$output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv));
}
else if ($action == 'd') {
$output = openssl_decrypt(base64_decode($string) , $encrypt_method, $key, 0, $iv);
}
return $output;
}
/* this function extracts the urls from a text and return them in an array */
function get_urls($input) {
$pattern = '$(https?://[a-z0-9_./?=&-~]+)(?![^<>]*>)$i';
if (preg_match_all($pattern, $input, $matches)) {
list($dummy, $links) = ($matches);
return $links;
}
return false;
}
/* general function to check if one strings starts with a given search */
function starts_with($string,$search){
if (substr(strtolower($string),0,strlen($search)) == strtolower($search)){
return true;
}
return false;
}
/* general function to check if one strings contains with a given search */
function contains($string,$search){
if (is_numeric(strpos(strtolower($string),strtolower($search)))){
return true;
}
return false;
}
/* same as avobe but check against all elements of an array */
function contains_any($where, $array)
{
$n = 1;
foreach ($array as $elem)
{
if (is_numeric(strpos($where, $elem)))
{
return $n;
}
$n++;
}
return false;
}
/* this function just reduces an image to a 1x1 pixel image to get the overall color.
*/
function averageColor($url) {
@$image = imagecreatefromstring(file_get_contents($url));
if (!$image) {
$mainColor = "CCCCCC";
}
else {
$thumb = imagecreatetruecolor(1, 1);
imagecopyresampled($thumb, $image, 0, 0, 0, 0, 1, 1, imagesx($image) , imagesy($image));
$mainColor = strtoupper(dechex(imagecolorat($thumb, 0, 0)));
}
return $mainColor;
}
/* function used in the process of uploading a file */
function get_mime($filename) {
$result = new finfo();
if (is_resource($result) === true) {
return $result->file($filename, FILEINFO_MIME_TYPE);
}
return false;
}
function sanitize($text){
return preg_replace("/[^a-zA-Z0-9.]+/", "", $text);
}
function themes($mode,$name = false){
global $user_settings;
switch ($mode){
case "list":
$themes = scandir("themes/");
$themelist = array();
foreach ($themes as $elem){
if ($elem != ".." && $elem != "." && $elem != "custom" && is_dir("themes/".$elem)){
$themelist[] = $elem;
}
}
return $themelist;
case "file":
$theme = sanitize($user_settings['theme']);
if (file_exists("themes/$theme/$name")){
return "themes/$theme/$name";
} else {
return "$name";
}
case "get":
$theme = sanitize($user_settings['theme']);
if (file_exists("themes/$theme/$name")){
return file_get_contents("themes/$theme/$name");
} else {
return file_get_contents("$name");
}
}
}
function time_elapsed_string($datetime, $full = false) {
$now = new DateTime;
$ago = new DateTime($datetime);
$diff = $now->diff($ago);
$diff->w = floor($diff->d / 7);
$diff->d -= $diff->w * 7;
$string = array(
'y' => 'year',
'm' => 'month',
'w' => 'week',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
);
foreach ($string as $k => &$v) {
if ($diff->$k) {
$v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
} else {
unset($string[$k]);
}
}
if (!$full) $string = array_slice($string, 0, 1);
return $string ? implode(', ', $string) . ' ago' : 'just now';
}
function getHeaders($respHeaders) {
$headers = array();
$headerText = substr($respHeaders, 0, strpos($respHeaders, "\r\n\r\n"));
foreach (explode("\r\n", $headerText) as $i => $line) {
if ($i === 0) {
$headers['http_code'] = $line;
} else {
list ($key, $value) = explode(': ', $line);
$headers[$key] = $value;
}
}
return $headerText;
}