diff --git a/config/dbstructure.php b/config/dbstructure.php
index 96f90fb82..99e3de9d0 100644
--- a/config/dbstructure.php
+++ b/config/dbstructure.php
@@ -34,7 +34,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-	define('DB_UPDATE_VERSION', 1289);
+	define('DB_UPDATE_VERSION', 1290);
 }
 
 return [
@@ -1368,7 +1368,10 @@ return [
 			"pid" => ["pid"],
 			"parameter" => ["parameter(64)"],
 			"priority_created_next_try" => ["priority", "created", "next_try"],
-			"done_executed_next_try" => ["done", "executed", "next_try"]
+			"done_priority_executed_next_try" => ["done", "priority", "executed", "next_try"],
+			"done_executed_next_try" => ["done", "executed", "next_try"],
+			"done_priority_next_try" => ["done", "priority", "next_try"],
+			"done_next_try" => ["done", "next_try"]
 		]
 	]
 ];
diff --git a/include/api.php b/include/api.php
index 0f7fa85bd..5bfb2d65a 100644
--- a/include/api.php
+++ b/include/api.php
@@ -43,6 +43,7 @@ use Friendica\Protocol\Diaspora;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/conversation.php';
@@ -526,7 +527,7 @@ function api_get_user(App $a, $contact_id = null)
 
 	// Searching for contact URL
 	if (!is_null($contact_id) && (intval($contact_id) == 0)) {
-		$user = DBA::escape(normalise_link($contact_id));
+		$user = DBA::escape(Strings::normaliseLink($contact_id));
 		$url = $user;
 		$extra_query = "AND `contact`.`nurl` = '%s' ";
 		if (api_user() !== false) {
@@ -571,7 +572,7 @@ function api_get_user(App $a, $contact_id = null)
 	}
 
 	if (is_null($user) && x($_GET, 'profileurl')) {
-		$user = DBA::escape(normalise_link($_GET['profileurl']));
+		$user = DBA::escape(Strings::normaliseLink($_GET['profileurl']));
 		$extra_query = "AND `contact`.`nurl` = '%s' ";
 		if (api_user() !== false) {
 			$extra_query .= "AND `contact`.`uid`=".intval(api_user());
@@ -639,7 +640,7 @@ function api_get_user(App $a, $contact_id = null)
 			throw new BadRequestException("User not found.");
 		}
 
-		$contact = DBA::selectFirst('contact', [], ['uid' => 0, 'nurl' => normalise_link($url)]);
+		$contact = DBA::selectFirst('contact', [], ['uid' => 0, 'nurl' => Strings::normaliseLink($url)]);
 
 		if (DBA::isResult($contact)) {
 			$network_name = ContactSelector::networkToName($contact['network'], $contact['url']);
@@ -2662,7 +2663,7 @@ function api_get_entitities(&$text, $bbcode)
 							"id" => $start+1,
 							"id_str" => (string)$start+1,
 							"indices" => [$start, $start+strlen($url)],
-							"media_url" => normalise_link($media_url),
+							"media_url" => Strings::normaliseLink($media_url),
 							"media_url_https" => $media_url,
 							"url" => $url,
 							"display_url" => $display_url,
@@ -3665,8 +3666,8 @@ function api_friendships_destroy($type)
 	$url = $contact["url"];
 
 	$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
-			$uid, Contact::SHARING, Contact::FRIEND, normalise_link($url),
-			normalise_link($url), $url];
+			$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
+			Strings::normaliseLink($url), $url];
 	$contact = DBA::selectFirst('contact', [], $condition);
 
 	if (!DBA::isResult($contact)) {
@@ -3790,9 +3791,9 @@ function api_direct_messages_box($type, $box, $verbose)
 	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']));
+			$sender = api_get_user($a, Strings::normaliseLink($item['contact-url']));
 		} elseif ($box == "sentbox" || $item['from-url'] == $profile_url) {
-			$recipient = api_get_user($a, normalise_link($item['contact-url']));
+			$recipient = api_get_user($a, Strings::normaliseLink($item['contact-url']));
 			$sender = $user_info;
 		}
 
@@ -4499,7 +4500,7 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $
 	// check against max upload size within Friendica instance
 	$maximagesize = Config::get('system', 'maximagesize');
 	if ($maximagesize && ($filesize > $maximagesize)) {
-		$formattedBytes = formatBytes($maximagesize);
+		$formattedBytes = Strings::formatBytes($maximagesize);
 		throw new InternalServerErrorException("image size exceeds Friendica config setting (uploaded size: $formattedBytes)");
 	}
 
@@ -4779,7 +4780,7 @@ function api_friendica_remoteauth()
 		throw new BadRequestException("Wrong parameters.");
 	}
 
-	$c_url = normalise_link($c_url);
+	$c_url = Strings::normaliseLink($c_url);
 
 	// traditional DFRN
 
@@ -4802,7 +4803,7 @@ function api_friendica_remoteauth()
 		$dfrn_id = '0:' . $orig_id;
 	}
 
-	$sec = random_string();
+	$sec = Strings::getRandomHex();
 
 	$fields = ['uid' => api_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
 		'sec' => $sec, 'expire' => time() + 45];
@@ -4943,7 +4944,7 @@ function api_get_nick($profile)
 
 	$r = q(
 		"SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'",
-		DBA::escape(normalise_link($profile))
+		DBA::escape(Strings::normaliseLink($profile))
 	);
 
 	if (DBA::isResult($r)) {
@@ -4953,7 +4954,7 @@ function api_get_nick($profile)
 	if (!$nick == "") {
 		$r = q(
 			"SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'",
-			DBA::escape(normalise_link($profile))
+			DBA::escape(Strings::normaliseLink($profile))
 		);
 
 		if (DBA::isResult($r)) {
@@ -5836,9 +5837,9 @@ function api_friendica_direct_messages_search($type, $box = "")
 		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']));
+				$sender = api_get_user($a, Strings::normaliseLink($item['contact-url']));
 			} elseif ($box == "sentbox" || $item['from-url'] == $profile_url) {
-				$recipient = api_get_user($a, normalise_link($item['contact-url']));
+				$recipient = api_get_user($a, Strings::normaliseLink($item['contact-url']));
 				$sender = $user_info;
 			}
 
diff --git a/include/conversation.php b/include/conversation.php
index cd635521e..ee286ca98 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -26,6 +26,7 @@ use Friendica\Object\Thread;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
 use Friendica\Util\Temporal;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use Friendica\Util\Crypto;
 
@@ -482,7 +483,7 @@ function conversation(App $a, array $items, Pager $pager, $mode, $update, $previ
 		if (!$update) {
 			$tab = 'posts';
 			if (x($_GET, 'tab')) {
-				$tab = notags(trim($_GET['tab']));
+				$tab = Strings::escapeTags(trim($_GET['tab']));
 			}
 			if ($tab === 'posts') {
 				/*
@@ -638,7 +639,7 @@ function conversation(App $a, array $items, Pager $pager, $mode, $update, $previ
 				$lock = false;
 				$likebuttons = false;
 
-				$body = prepare_body($item, true, $preview);
+				$body = Item::prepareBody($item, true, $preview);
 
 				list($categories, $folders) = get_cats_and_terms($item);
 
@@ -689,7 +690,7 @@ function conversation(App $a, array $items, Pager $pager, $mode, $update, $previ
 					'owner_name' => $owner_name_e,
 					'owner_url' => $owner_url,
 					'owner_photo' => System::removedBaseUrl(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)),
-					'plink' => get_plink($item),
+					'plink' => Item::getPlink($item),
 					'edpost' => false,
 					'isstarred' => $isstarred,
 					'star' => $star,
@@ -842,7 +843,7 @@ function item_photo_menu($item) {
 	$cid = 0;
 	$network = '';
 	$rel = 0;
-	$condition = ['uid' => local_user(), 'nurl' => normalise_link($item['author-link'])];
+	$condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])];
 	$contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition);
 	if (DBA::isResult($contact)) {
 		$cid = $contact['id'];
diff --git a/include/enotify.php b/include/enotify.php
index 339e31a3c..a4b0fd14b 100644
--- a/include/enotify.php
+++ b/include/enotify.php
@@ -15,6 +15,7 @@ use Friendica\Model\Contact;
 use Friendica\Model\Item;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Emailer;
+use Friendica\Util\Strings;
 
 /**
  * @brief Creates a notification entry and possibly sends a mail
@@ -157,7 +158,7 @@ function notification($params)
 			$item = Item::selectFirstForUser($params['uid'], Item::ITEM_FIELDLIST, ['id' => $parent_id]);
 		}
 
-		$item_post_type = item_post_type($item);
+		$item_post_type = Item::postType($item);
 		$itemlink = $item['plink'];
 
 		// "a post"
@@ -457,7 +458,7 @@ function notification($params)
 		Logger::log("adding notification entry", Logger::DEBUG);
 		do {
 			$dups = false;
-			$hash = random_string();
+			$hash = Strings::getRandomHex();
 			if (DBA::exists('notify', ['hash' => $hash])) {
 				$dups = true;
 			}
@@ -703,11 +704,11 @@ function check_item_notification($itemid, $uid, $defaulttype = "") {
 		// Check for invalid profile urls. 13 should be the shortest possible profile length:
 		// http://a.bc/d
 		// Additionally check for invalid urls that would return the normalised value "http:"
-		if ((strlen($profile) >= 13) && (normalise_link($profile) != "http:")) {
+		if ((strlen($profile) >= 13) && (Strings::normaliseLink($profile) != "http:")) {
 			if (!in_array($profile, $profiles2))
 				$profiles2[] = $profile;
 
-			$profile = normalise_link($profile);
+			$profile = Strings::normaliseLink($profile);
 			if (!in_array($profile, $profiles2))
 				$profiles2[] = $profile;
 
@@ -761,7 +762,7 @@ function check_item_notification($itemid, $uid, $defaulttype = "") {
 
 			if (DBA::isResult($tags)) {
 				foreach ($tags AS $tag) {
-					$condition = ['nurl' => normalise_link($tag["url"]), 'uid' => $uid, 'notify_new_posts' => true];
+					$condition = ['nurl' => Strings::normaliseLink($tag["url"]), 'uid' => $uid, 'notify_new_posts' => true];
 					$r = DBA::exists('contact', $condition);
 					if ($r) {
 						$send_notification = true;
diff --git a/include/items.php b/include/items.php
index 9929f535f..adc5bf013 100644
--- a/include/items.php
+++ b/include/items.php
@@ -21,6 +21,7 @@ use Friendica\Protocol\OStatus;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\ParseUrl;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/text.php';
@@ -308,7 +309,7 @@ function subscribe_to_hub($url, array $importer, array $contact, $hubmode = 'sub
 	$push_url = System::baseUrl() . '/pubsub/' . $user['nickname'] . '/' . $contact['id'];
 
 	// Use a single verify token, even if multiple hubs
-	$verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string());
+	$verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : Strings::getRandomHex());
 
 	$params= 'hub.mode=' . $hubmode . '&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token;
 
diff --git a/include/text.php b/include/text.php
index d2e3d0c9f..ee83345c0 100644
--- a/include/text.php
+++ b/include/text.php
@@ -26,144 +26,12 @@ use Friendica\Util\Proxy as ProxyUtils;
 use Friendica\Core\Logger;
 use Friendica\Core\Renderer;
 use Friendica\Model\FileTag;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use Friendica\Content\Text\HTML;
 
 require_once "include/conversation.php";
 
-/**
- * @brief Generates a pseudo-random string of hexadecimal characters
- *
- * @param int $size
- * @return string
- */
-function random_string($size = 64)
-{
-	$byte_size = ceil($size / 2);
-
-	$bytes = random_bytes($byte_size);
-
-	$return = substr(bin2hex($bytes), 0, $size);
-
-	return $return;
-}
-
-/**
- * This is our primary input filter.
- *
- * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
- * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
- * after cleansing, and angle chars with the high bit set could get through as markup.
- *
- * This is now disabled because it was interfering with some legitimate unicode sequences
- * and hopefully there aren't a lot of those browsers left.
- *
- * Use this on any text input where angle chars are not valid or permitted
- * They will be replaced with safer brackets. This may be filtered further
- * if these are not allowed either.
- *
- * @param string $string Input string
- * @return string Filtered string
- */
-function notags($string) {
-	return str_replace(["<", ">"], ['[', ']'], $string);
-
-//  High-bit filter no longer used
-//	return str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string);
-}
-
-
-/**
- * use this on "body" or "content" input where angle chars shouldn't be removed,
- * and allow them to be safely displayed.
- * @param string $string
- * @return string
- */
-function escape_tags($string) {
-	return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
-}
-
-
-/**
- * generate a string that's random, but usually pronounceable.
- * used to generate initial passwords
- * @param int $len
- * @return string
- */
-function autoname($len) {
-
-	if ($len <= 0) {
-		return '';
-	}
-
-	$vowels = ['a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'];
-	if (mt_rand(0, 5) == 4) {
-		$vowels[] = 'y';
-	}
-
-	$cons = [
-			'b','bl','br',
-			'c','ch','cl','cr',
-			'd','dr',
-			'f','fl','fr',
-			'g','gh','gl','gr',
-			'h',
-			'j',
-			'k','kh','kl','kr',
-			'l',
-			'm',
-			'n',
-			'p','ph','pl','pr',
-			'qu',
-			'r','rh',
-			's','sc','sh','sm','sp','st',
-			't','th','tr',
-			'v',
-			'w','wh',
-			'x',
-			'z','zh'
-			];
-
-	$midcons = ['ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
-				'nd','ng','nk','nt','rn','rp','rt'];
-
-	$noend = ['bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
-				'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh','q'];
-
-	$start = mt_rand(0,2);
-	if ($start == 0) {
-		$table = $vowels;
-	} else {
-		$table = $cons;
-	}
-
-	$word = '';
-
-	for ($x = 0; $x < $len; $x ++) {
-		$r = mt_rand(0,count($table) - 1);
-		$word .= $table[$r];
-
-		if ($table == $vowels) {
-			$table = array_merge($cons,$midcons);
-		} else {
-			$table = $vowels;
-		}
-
-	}
-
-	$word = substr($word,0,$len);
-
-	foreach ($noend as $noe) {
-		$noelen = strlen($noe);
-		if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) {
-			$word = autoname($len);
-			break;
-		}
-	}
-
-	return $word;
-}
-
 /**
  * Turn user/group ACLs stored as angle bracketed text into arrays
  *
@@ -194,7 +62,7 @@ function expand_acl($s) {
  */
 function sanitise_acl(&$item) {
 	if (intval($item)) {
-		$item = '<' . intval(notags(trim($item))) . '>';
+		$item = '<' . intval(Strings::escapeTags(trim($item))) . '>';
 	} else {
 		unset($item);
 	}
@@ -255,78 +123,6 @@ function activity_match($haystack,$needle) {
 	return (($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle, NAMESPACE_ACTIVITY_SCHEMA)));
 }
 
-
-/**
- * @brief Pull out all #hashtags and @person tags from $string.
- *
- * We also get @person@domain.com - which would make
- * the regex quite complicated as tags can also
- * end a sentence. So we'll run through our results
- * and strip the period from any tags which end with one.
- * Returns array of tags found, or empty array.
- *
- * @param string $string Post content
- * @return array List of tag and person names
- */
-function get_tags($string) {
-	$ret = [];
-
-	// Convert hashtag links to hashtags
-	$string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
-
-	// ignore anything in a code block
-	$string = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $string);
-
-	// Force line feeds at bbtags
-	$string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
-
-	// ignore anything in a bbtag
-	$string = preg_replace('/\[(.*?)\]/sm', '', $string);
-
-	// Match full names against @tags including the space between first and last
-	// We will look these up afterward to see if they are full names or not recognisable.
-
-	if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
-		foreach ($matches[1] as $match) {
-			if (strstr($match, ']')) {
-				// we might be inside a bbcode color tag - leave it alone
-				continue;
-			}
-			if (substr($match, -1, 1) === '.') {
-				$ret[] = substr($match, 0, -1);
-			} else {
-				$ret[] = $match;
-			}
-		}
-	}
-
-	// Otherwise pull out single word tags. These can be @nickname, @first_last
-	// and #hash tags.
-
-	if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
-		foreach ($matches[1] as $match) {
-			if (strstr($match, ']')) {
-				// we might be inside a bbcode color tag - leave it alone
-				continue;
-			}
-			if (substr($match, -1, 1) === '.') {
-				$match = substr($match,0,-1);
-			}
-			// ignore strictly numeric tags like #1
-			if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
-				continue;
-			}
-			// try not to catch url fragments
-			if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
-				continue;
-			}
-			$ret[] = $match;
-		}
-	}
-	return $ret;
-}
-
-
 /**
  * quick and dirty quoted_printable encoding
  *
@@ -337,45 +133,6 @@ function qp($s) {
 	return str_replace("%", "=", rawurlencode($s));
 }
 
-/**
- * @brief Check for a valid email string
- *
- * @param string $email_address
- * @return boolean
- */
-function valid_email($email_address)
-{
-	return preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/', $email_address);
-}
-
-/**
- * Normalize url
- *
- * @param string $url
- * @return string
- */
-function normalise_link($url) {
-	$ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
-	return rtrim($ret,'/');
-}
-
-
-/**
- * Compare two URLs to see if they are the same, but ignore
- * slight but hopefully insignificant differences such as if one
- * is https and the other isn't, or if one is www.something and
- * the other isn't - and also ignore case differences.
- *
- * @param string $a first url
- * @param string $b second url
- * @return boolean True if the URLs match, otherwise False
- *
- */
-function link_compare($a, $b) {
-	return (strcasecmp(normalise_link($a), normalise_link($b)) === 0);
-}
-
-
 /**
  * @brief Find any non-embedded images in private items and add redir links to them
  *
@@ -400,236 +157,6 @@ function redir_private_images($a, &$item)
 	}
 }
 
-/**
- * Sets the "rendered-html" field of the provided item
- *
- * Body is preserved to avoid side-effects as we modify it just-in-time for spoilers and private image links
- *
- * @param array $item
- * @param bool  $update
- *
- * @todo Remove reference, simply return "rendered-html" and "rendered-hash"
- */
-function put_item_in_cache(&$item, $update = false)
-{
-	$body = $item["body"];
-
-	$rendered_hash = defaults($item, 'rendered-hash', '');
-	$rendered_html = defaults($item, 'rendered-html', '');
-
-	if ($rendered_hash == ''
-		|| $rendered_html == ""
-		|| $rendered_hash != hash("md5", $item["body"])
-		|| Config::get("system", "ignore_cache")
-	) {
-		$a = get_app();
-		redir_private_images($a, $item);
-
-		$item["rendered-html"] = prepare_text($item["body"]);
-		$item["rendered-hash"] = hash("md5", $item["body"]);
-
-		$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
-		Addon::callHooks('put_item_in_cache', $hook_data);
-		$item['rendered-html'] = $hook_data['rendered-html'];
-		$item['rendered-hash'] = $hook_data['rendered-hash'];
-		unset($hook_data);
-
-		// Force an update if the generated values differ from the existing ones
-		if ($rendered_hash != $item["rendered-hash"]) {
-			$update = true;
-		}
-
-		// Only compare the HTML when we forcefully ignore the cache
-		if (Config::get("system", "ignore_cache") && ($rendered_html != $item["rendered-html"])) {
-			$update = true;
-		}
-
-		if ($update && !empty($item["id"])) {
-			Item::update(['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]],
-					['id' => $item["id"]]);
-		}
-	}
-
-	$item["body"] = $body;
-}
-
-/**
- * @brief Given an item array, convert the body element from bbcode to html and add smilie icons.
- * If attach is true, also add icons for item attachments.
- *
- * @param array   $item
- * @param boolean $attach
- * @param boolean $is_preview
- * @return string item body html
- * @hook prepare_body_init item array before any work
- * @hook prepare_body_content_filter ('item'=>item array, 'filter_reasons'=>string array) before first bbcode to html
- * @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
- * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
- */
-function prepare_body(array &$item, $attach = false, $is_preview = false)
-{
-	$a = get_app();
-	Addon::callHooks('prepare_body_init', $item);
-
-	// In order to provide theme developers more possibilities, event items
-	// are treated differently.
-	if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) {
-		$ev = Event::getItemHTML($item);
-		return $ev;
-	}
-
-	$tags = \Friendica\Model\Term::populateTagsFromItem($item);
-
-	$item['tags'] = $tags['tags'];
-	$item['hashtags'] = $tags['hashtags'];
-	$item['mentions'] = $tags['mentions'];
-
-	// Compile eventual content filter reasons
-	$filter_reasons = [];
-	if (!$is_preview && public_contact() != $item['author-id']) {
-		if (!empty($item['content-warning']) && (!local_user() || !PConfig::get(local_user(), 'system', 'disable_cw', false))) {
-			$filter_reasons[] = L10n::t('Content warning: %s', $item['content-warning']);
-		}
-
-		$hook_data = [
-			'item' => $item,
-			'filter_reasons' => $filter_reasons
-		];
-		Addon::callHooks('prepare_body_content_filter', $hook_data);
-		$filter_reasons = $hook_data['filter_reasons'];
-		unset($hook_data);
-	}
-
-	// Update the cached values if there is no "zrl=..." on the links.
-	$update = (!local_user() && !remote_user() && ($item["uid"] == 0));
-
-	// Or update it if the current viewer is the intented viewer.
-	if (($item["uid"] == local_user()) && ($item["uid"] != 0)) {
-		$update = true;
-	}
-
-	put_item_in_cache($item, $update);
-	$s = $item["rendered-html"];
-
-	$hook_data = [
-		'item' => $item,
-		'html' => $s,
-		'preview' => $is_preview,
-		'filter_reasons' => $filter_reasons
-	];
-	Addon::callHooks('prepare_body', $hook_data);
-	$s = $hook_data['html'];
-	unset($hook_data);
-
-	if (!$attach) {
-		// Replace the blockquotes with quotes that are used in mails.
-		$mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
-		$s = str_replace(['<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'], [$mailquote, $mailquote, $mailquote], $s);
-		return $s;
-	}
-
-	$as = '';
-	$vhead = false;
-	$matches = [];
-	preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\"(?: title=\"(.*?)\")?|', $item['attach'], $matches, PREG_SET_ORDER);
-	foreach ($matches as $mtch) {
-		$mime = $mtch[3];
-
-		$the_url = Contact::magicLinkById($item['author-id'], $mtch[1]);
-
-		if (strpos($mime, 'video') !== false) {
-			if (!$vhead) {
-				$vhead = true;
-				$a->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'), [
-					'$baseurl' => System::baseUrl(),
-				]);
-			}
-
-			$url_parts = explode('/', $the_url);
-			$id = end($url_parts);
-			$as .= Renderer::replaceMacros(Renderer::getMarkupTemplate('video_top.tpl'), [
-				'$video' => [
-					'id'     => $id,
-					'title'  => L10n::t('View Video'),
-					'src'    => $the_url,
-					'mime'   => $mime,
-				],
-			]);
-		}
-
-		$filetype = strtolower(substr($mime, 0, strpos($mime, '/')));
-		if ($filetype) {
-			$filesubtype = strtolower(substr($mime, strpos($mime, '/') + 1));
-			$filesubtype = str_replace('.', '-', $filesubtype);
-		} else {
-			$filetype = 'unkn';
-			$filesubtype = 'unkn';
-		}
-
-		$title = escape_tags(trim(!empty($mtch[4]) ? $mtch[4] : $mtch[1]));
-		$title .= ' ' . $mtch[2] . ' ' . L10n::t('bytes');
-
-		$icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
-		$as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
-	}
-
-	if ($as != '') {
-		$s .= '<div class="body-attach">'.$as.'<div class="clear"></div></div>';
-	}
-
-	// Map.
-	if (strpos($s, '<div class="map">') !== false && x($item, 'coord')) {
-		$x = Map::byCoordinates(trim($item['coord']));
-		if ($x) {
-			$s = preg_replace('/\<div class\=\"map\"\>/', '$0' . $x, $s);
-		}
-	}
-
-
-	// Look for spoiler.
-	$spoilersearch = '<blockquote class="spoiler">';
-
-	// Remove line breaks before the spoiler.
-	while ((strpos($s, "\n" . $spoilersearch) !== false)) {
-		$s = str_replace("\n" . $spoilersearch, $spoilersearch, $s);
-	}
-	while ((strpos($s, "<br />" . $spoilersearch) !== false)) {
-		$s = str_replace("<br />" . $spoilersearch, $spoilersearch, $s);
-	}
-
-	while ((strpos($s, $spoilersearch) !== false)) {
-		$pos = strpos($s, $spoilersearch);
-		$rnd = random_string(8);
-		$spoilerreplace = '<br /> <span id="spoiler-wrap-' . $rnd . '" class="spoiler-wrap fakelink" onclick="openClose(\'spoiler-' . $rnd . '\');">' . L10n::t('Click to open/close') . '</span>'.
-					'<blockquote class="spoiler" id="spoiler-' . $rnd . '" style="display: none;">';
-		$s = substr($s, 0, $pos) . $spoilerreplace . substr($s, $pos + strlen($spoilersearch));
-	}
-
-	// Look for quote with author.
-	$authorsearch = '<blockquote class="author">';
-
-	while ((strpos($s, $authorsearch) !== false)) {
-		$pos = strpos($s, $authorsearch);
-		$rnd = random_string(8);
-		$authorreplace = '<br /> <span id="author-wrap-' . $rnd . '" class="author-wrap fakelink" onclick="openClose(\'author-' . $rnd . '\');">' . L10n::t('Click to open/close') . '</span>'.
-					'<blockquote class="author" id="author-' . $rnd . '" style="display: block;">';
-		$s = substr($s, 0, $pos) . $authorreplace . substr($s, $pos + strlen($authorsearch));
-	}
-
-	// Replace friendica image url size with theme preference.
-	if (x($a->theme_info, 'item_image_size')){
-		$ps = $a->theme_info['item_image_size'];
-		$s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s);
-	}
-
-	$s = HTML::applyContentFilter($s, $filter_reasons);
-
-	$hook_data = ['item' => $item, 'html' => $s];
-	Addon::callHooks('prepare_body_final', $hook_data);
-
-	return $hook_data['html'];
-}
-
 /**
  * @brief Given a text string, convert from bbcode to html and add smilie icons.
  *
@@ -723,42 +250,6 @@ function get_cats_and_terms($item)
 	return [$categories, $folders];
 }
 
-
-/**
- * get private link for item
- * @param array $item
- * @return boolean|array False if item has not plink, otherwise array('href'=>plink url, 'title'=>translated title)
- */
-function get_plink($item) {
-	$a = get_app();
-
-	if ($a->user['nickname'] != "") {
-		$ret = [
-				//'href' => "display/" . $a->user['nickname'] . "/" . $item['id'],
-				'href' => "display/" . $item['guid'],
-				'orig' => "display/" . $item['guid'],
-				'title' => L10n::t('View on separate page'),
-				'orig_title' => L10n::t('view on separate page'),
-			];
-
-		if (x($item, 'plink')) {
-			$ret["href"] = $a->removeBaseURL($item['plink']);
-			$ret["title"] = L10n::t('link to source');
-		}
-
-	} elseif (x($item, 'plink') && ($item['private'] != 1)) {
-		$ret = [
-				'href' => $item['plink'],
-				'orig' => $item['plink'],
-				'title' => L10n::t('link to source'),
-			];
-	} else {
-		$ret = [];
-	}
-
-	return $ret;
-}
-
 /**
  * return number of bytes in size (K, M, G)
  * @param string $size_str
@@ -773,53 +264,6 @@ function return_bytes($size_str) {
 	}
 }
 
-/**
- * @param string $s
- * @param boolean $strip_padding
- * @return string
- */
-function base64url_encode($s, $strip_padding = false) {
-
-	$s = strtr(base64_encode($s), '+/', '-_');
-
-	if ($strip_padding) {
-		$s = str_replace('=','',$s);
-	}
-
-	return $s;
-}
-
-/**
- * @param string $s
- * @return string
- */
-function base64url_decode($s) {
-
-	if (is_array($s)) {
-		Logger::log('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
-		return $s;
-	}
-
-/*
- *  // Placeholder for new rev of salmon which strips base64 padding.
- *  // PHP base64_decode handles the un-padded input without requiring this step
- *  // Uncomment if you find you need it.
- *
- *	$l = strlen($s);
- *	if (!strpos($s,'=')) {
- *		$m = $l % 4;
- *		if ($m == 2)
- *			$s .= '==';
- *		if ($m == 3)
- *			$s .= '=';
- *	}
- *
- */
-
-	return base64_decode(strtr($s,'-_','+/'));
-}
-
-
 function bb_translate_video($s) {
 
 	$matches = null;
@@ -836,31 +280,6 @@ function bb_translate_video($s) {
 	return $s;
 }
 
-/**
- * get translated item type
- *
- * @param array $itme
- * @return string
- */
-function item_post_type($item) {
-	if (!empty($item['event-id'])) {
-		return L10n::t('event');
-	} elseif (!empty($item['resource-id'])) {
-		return L10n::t('photo');
-	} elseif (!empty($item['verb']) && $item['verb'] !== ACTIVITY_POST) {
-		return L10n::t('activity');
-	} elseif ($item['id'] != $item['parent']) {
-		return L10n::t('comment');
-	}
-
-	return L10n::t('post');
-}
-
-function normalise_openid($s) {
-	return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
-}
-
-
 function undo_post_tagging($s) {
 	$matches = null;
 	$cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism', $s, $matches, PREG_SET_ORDER);
@@ -876,10 +295,6 @@ function undo_post_tagging($s) {
 	return $s;
 }
 
-function protect_sprintf($s) {
-	return str_replace('%', '%%', $s);
-}
-
 /// @TODO Rewrite this
 function is_a_date_arg($s) {
 	$i = intval($s);
@@ -898,59 +313,3 @@ function is_a_date_arg($s) {
 
 	return false;
 }
-
-/**
- * remove intentation from a text
- */
-function deindent($text, $chr = "[\t ]", $count = NULL) {
-	$lines = explode("\n", $text);
-
-	if (is_null($count)) {
-		$m = [];
-		$k = 0;
-		while ($k < count($lines) && strlen($lines[$k]) == 0) {
-			$k++;
-		}
-		preg_match("|^" . $chr . "*|", $lines[$k], $m);
-		$count = strlen($m[0]);
-	}
-
-	for ($k = 0; $k < count($lines); $k++) {
-		$lines[$k] = preg_replace("|^" . $chr . "{" . $count . "}|", "", $lines[$k]);
-	}
-
-	return implode("\n", $lines);
-}
-
-function formatBytes($bytes, $precision = 2) {
-	$units = ['B', 'KB', 'MB', 'GB', 'TB'];
-
-	$bytes = max($bytes, 0);
-	$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
-	$pow = min($pow, count($units) - 1);
-
-	$bytes /= pow(1024, $pow);
-
-	return round($bytes, $precision) . ' ' . $units[$pow];
-}
-
-/**
- * @brief translate and format the networkname of a contact
- *
- * @param string $network
- *	Networkname of the contact (e.g. dfrn, rss and so on)
- * @param sting $url
- *	The contact url
- * @return string
- */
-function format_network_name($network, $url = 0) {
-	if ($network != "") {
-		if ($url != "") {
-			$network_name = '<a href="'.$url.'">'.ContactSelector::networkToName($network, $url)."</a>";
-		} else {
-			$network_name = ContactSelector::networkToName($network);
-		}
-
-		return $network_name;
-	}
-}
diff --git a/mod/acl.php b/mod/acl.php
index a63cd83ae..cb378dc27 100644
--- a/mod/acl.php
+++ b/mod/acl.php
@@ -12,6 +12,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Item;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 
@@ -188,7 +189,7 @@ function acl_content(App $a)
 		);
 	} elseif ($type == 'x') {
 		// autocomplete for global contact search (e.g. navbar search)
-		$search = notags(trim($_REQUEST['search']));
+		$search = Strings::escapeTags(trim($_REQUEST['search']));
 		$mode = $_REQUEST['smode'];
 
 		$r = ACL::contactAutocomplete($search, $mode);
diff --git a/mod/admin.php b/mod/admin.php
index 272b97a88..5adc8e9c4 100644
--- a/mod/admin.php
+++ b/mod/admin.php
@@ -30,6 +30,7 @@ use Friendica\Module\Tos;
 use Friendica\Util\Arrays;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/enotify.php';
@@ -416,8 +417,8 @@ function admin_page_blocklist_post(App $a)
 		//  Add new item to blocklist
 		$blocklist = Config::get('system', 'blocklist');
 		$blocklist[] = [
-			'domain' => notags(trim($_POST['newentry_domain'])),
-			'reason' => notags(trim($_POST['newentry_reason']))
+			'domain' => Strings::escapeTags(trim($_POST['newentry_domain'])),
+			'reason' => Strings::escapeTags(trim($_POST['newentry_reason']))
 		];
 		Config::set('system', 'blocklist', $blocklist);
 		info(L10n::t('Server added to blocklist.') . EOL);
@@ -426,8 +427,8 @@ function admin_page_blocklist_post(App $a)
 		$blocklist = [];
 		foreach ($_POST['domain'] as $id => $domain) {
 			// Trimming whitespaces as well as any lingering slashes
-			$domain = notags(trim($domain, "\x00..\x1F/"));
-			$reason = notags(trim($_POST['reason'][$id]));
+			$domain = Strings::escapeTags(trim($domain, "\x00..\x1F/"));
+			$reason = Strings::escapeTags(trim($_POST['reason'][$id]));
 			if (!x($_POST['delete'][$id])) {
 				$blocklist[] = [
 					'domain' => $domain,
@@ -565,7 +566,7 @@ function admin_page_deleteitem_post(App $a)
 	BaseModule::checkFormSecurityTokenRedirectOnError('/admin/deleteitem/', 'admin_deleteitem');
 
 	if (x($_POST['page_deleteitem_submit'])) {
-		$guid = trim(notags($_POST['deleteitemguid']));
+		$guid = trim(Strings::escapeTags($_POST['deleteitemguid']));
 		// The GUID should not include a "/", so if there is one, we got an URL
 		// and the last part of it is most likely the GUID.
 		if (strpos($guid, '/')) {
@@ -996,8 +997,8 @@ function admin_page_site_post(App $a)
 		$old_url = $a->getBaseURL(true);
 
 		// Generate host names for relocation the addresses in the format user@address.tld
-		$new_host = str_replace("http://", "@", normalise_link($new_url));
-		$old_host = str_replace("http://", "@", normalise_link($old_url));
+		$new_host = str_replace("http://", "@", Strings::normaliseLink($new_url));
+		$old_host = str_replace("http://", "@", Strings::normaliseLink($old_url));
 
 		function update_table(App $a, $table_name, $fields, $old_url, $new_url)
 		{
@@ -1048,16 +1049,16 @@ function admin_page_site_post(App $a)
 	}
 	// end relocate
 
-	$sitename 		=	((x($_POST,'sitename'))			? notags(trim($_POST['sitename']))		: '');
-	$hostname 		=	((x($_POST,'hostname'))			? notags(trim($_POST['hostname']))		: '');
-	$sender_email		=	((x($_POST,'sender_email'))		? notags(trim($_POST['sender_email']))		: '');
+	$sitename 		=	((x($_POST,'sitename'))			? Strings::escapeTags(trim($_POST['sitename']))		: '');
+	$hostname 		=	((x($_POST,'hostname'))			? Strings::escapeTags(trim($_POST['hostname']))		: '');
+	$sender_email		=	((x($_POST,'sender_email'))		? Strings::escapeTags(trim($_POST['sender_email']))		: '');
 	$banner			=	((x($_POST,'banner'))			? trim($_POST['banner'])			: false);
-	$shortcut_icon 		=	((x($_POST,'shortcut_icon'))		? notags(trim($_POST['shortcut_icon']))		: '');
-	$touch_icon 		=	((x($_POST,'touch_icon'))		? notags(trim($_POST['touch_icon']))		: '');
+	$shortcut_icon 		=	((x($_POST,'shortcut_icon'))		? Strings::escapeTags(trim($_POST['shortcut_icon']))		: '');
+	$touch_icon 		=	((x($_POST,'touch_icon'))		? Strings::escapeTags(trim($_POST['touch_icon']))		: '');
 	$info			=	((x($_POST,'info'))			? trim($_POST['info'])				: false);
-	$language		=	((x($_POST,'language'))			? notags(trim($_POST['language']))		: '');
-	$theme			=	((x($_POST,'theme'))			? notags(trim($_POST['theme']))			: '');
-	$theme_mobile		=	((x($_POST,'theme_mobile'))		? notags(trim($_POST['theme_mobile']))		: '');
+	$language		=	((x($_POST,'language'))			? Strings::escapeTags(trim($_POST['language']))		: '');
+	$theme			=	((x($_POST,'theme'))			? Strings::escapeTags(trim($_POST['theme']))			: '');
+	$theme_mobile		=	((x($_POST,'theme_mobile'))		? Strings::escapeTags(trim($_POST['theme_mobile']))		: '');
 	$maximagesize		=	((x($_POST,'maximagesize'))		? intval(trim($_POST['maximagesize']))		:  0);
 	$maximagelength		=	((x($_POST,'maximagelength'))		? intval(trim($_POST['maximagelength']))	:  MAX_IMAGE_LENGTH);
 	$jpegimagequality	=	((x($_POST,'jpegimagequality'))		? intval(trim($_POST['jpegimagequality']))	:  JPEG_QUALITY);
@@ -1069,14 +1070,14 @@ function admin_page_site_post(App $a)
 
 	$register_text		=	((x($_POST,'register_text'))		? strip_tags(trim($_POST['register_text']))		: '');
 
-	$allowed_sites		=	((x($_POST,'allowed_sites'))		? notags(trim($_POST['allowed_sites']))		: '');
-	$allowed_email		=	((x($_POST,'allowed_email'))		? notags(trim($_POST['allowed_email']))		: '');
-	$forbidden_nicknames	=	((x($_POST,'forbidden_nicknames'))	? strtolower(notags(trim($_POST['forbidden_nicknames'])))		: '');
+	$allowed_sites		=	((x($_POST,'allowed_sites'))		? Strings::escapeTags(trim($_POST['allowed_sites']))		: '');
+	$allowed_email		=	((x($_POST,'allowed_email'))		? Strings::escapeTags(trim($_POST['allowed_email']))		: '');
+	$forbidden_nicknames	=	((x($_POST,'forbidden_nicknames'))	? strtolower(Strings::escapeTags(trim($_POST['forbidden_nicknames'])))		: '');
 	$no_oembed_rich_content = x($_POST,'no_oembed_rich_content');
-	$allowed_oembed		=	((x($_POST,'allowed_oembed'))		? notags(trim($_POST['allowed_oembed']))		: '');
+	$allowed_oembed		=	((x($_POST,'allowed_oembed'))		? Strings::escapeTags(trim($_POST['allowed_oembed']))		: '');
 	$block_public		=	((x($_POST,'block_public'))		? True						: False);
 	$force_publish		=	((x($_POST,'publish_all'))		? True						: False);
-	$global_directory	=	((x($_POST,'directory'))		? notags(trim($_POST['directory']))		: '');
+	$global_directory	=	((x($_POST,'directory'))		? Strings::escapeTags(trim($_POST['directory']))		: '');
 	$newuser_private		=	((x($_POST,'newuser_private'))		? True					: False);
 	$enotify_no_content		=	((x($_POST,'enotify_no_content'))	? True					: False);
 	$private_addons			=	((x($_POST,'private_addons'))		? True					: False);
@@ -1091,8 +1092,8 @@ function admin_page_site_post(App $a)
 	$max_author_posts_community_page	=	((x($_POST,'max_author_posts_community_page'))	? intval(trim($_POST['max_author_posts_community_page']))	: 0);
 
 	$verifyssl		=	((x($_POST,'verifyssl'))		? True						: False);
-	$proxyuser		=	((x($_POST,'proxyuser'))		? notags(trim($_POST['proxyuser']))		: '');
-	$proxy			=	((x($_POST,'proxy'))			? notags(trim($_POST['proxy']))			: '');
+	$proxyuser		=	((x($_POST,'proxyuser'))		? Strings::escapeTags(trim($_POST['proxyuser']))		: '');
+	$proxy			=	((x($_POST,'proxy'))			? Strings::escapeTags(trim($_POST['proxy']))			: '');
 	$timeout		=	((x($_POST,'timeout'))			? intval(trim($_POST['timeout']))		: 60);
 	$maxloadavg		=	((x($_POST,'maxloadavg'))		? intval(trim($_POST['maxloadavg']))		: 50);
 	$maxloadavg_frontend	=	((x($_POST,'maxloadavg_frontend'))	? intval(trim($_POST['maxloadavg_frontend']))	: 50);
@@ -1116,16 +1117,16 @@ function admin_page_site_post(App $a)
 	$dbclean_expire_days	=	((x($_POST,'dbclean_expire_days'))	? intval($_POST['dbclean_expire_days'])		: 0);
 	$dbclean_unclaimed	=	((x($_POST,'dbclean_unclaimed'))	? intval($_POST['dbclean_unclaimed'])		: 0);
 	$suppress_tags		=	((x($_POST,'suppress_tags'))		? True   					: False);
-	$itemcache		=	((x($_POST,'itemcache'))		? notags(trim($_POST['itemcache']))		: '');
+	$itemcache		=	((x($_POST,'itemcache'))		? Strings::escapeTags(trim($_POST['itemcache']))		: '');
 	$itemcache_duration	=	((x($_POST,'itemcache_duration'))	? intval($_POST['itemcache_duration'])		: 0);
 	$max_comments		=	((x($_POST,'max_comments'))		? intval($_POST['max_comments'])		: 0);
-	$temppath		=	((x($_POST,'temppath'))			? notags(trim($_POST['temppath']))		: '');
-	$basepath		=	((x($_POST,'basepath'))			? notags(trim($_POST['basepath']))		: '');
-	$singleuser		=	((x($_POST,'singleuser'))		? notags(trim($_POST['singleuser']))		: '');
+	$temppath		=	((x($_POST,'temppath'))			? Strings::escapeTags(trim($_POST['temppath']))		: '');
+	$basepath		=	((x($_POST,'basepath'))			? Strings::escapeTags(trim($_POST['basepath']))		: '');
+	$singleuser		=	((x($_POST,'singleuser'))		? Strings::escapeTags(trim($_POST['singleuser']))		: '');
 	$proxy_disabled		=	((x($_POST,'proxy_disabled'))		? True						: False);
 	$only_tag_search	=	((x($_POST,'only_tag_search'))		? True						: False);
 	$rino			=	((x($_POST,'rino'))			? intval($_POST['rino'])			: 0);
-	$check_new_version_url	=	((x($_POST, 'check_new_version_url'))	?	notags(trim($_POST['check_new_version_url']))	: 'none');
+	$check_new_version_url	=	((x($_POST, 'check_new_version_url'))	?	Strings::escapeTags(trim($_POST['check_new_version_url']))	: 'none');
 
 	$worker_queues		=	((x($_POST,'worker_queues'))		? intval($_POST['worker_queues'])		: 10);
 	$worker_dont_fork	=	((x($_POST,'worker_dont_fork'))		? True						: False);
@@ -1133,10 +1134,10 @@ function admin_page_site_post(App $a)
 	$worker_frontend	=	((x($_POST,'worker_frontend'))		? True						: False);
 
 	$relay_directly		=	((x($_POST,'relay_directly'))		? True						: False);
-	$relay_server		=	((x($_POST,'relay_server'))		? notags(trim($_POST['relay_server']))		: '');
+	$relay_server		=	((x($_POST,'relay_server'))		? Strings::escapeTags(trim($_POST['relay_server']))		: '');
 	$relay_subscribe	=	((x($_POST,'relay_subscribe'))		? True						: False);
-	$relay_scope		=	((x($_POST,'relay_scope'))		? notags(trim($_POST['relay_scope']))		: '');
-	$relay_server_tags	=	((x($_POST,'relay_server_tags'))	? notags(trim($_POST['relay_server_tags']))	: '');
+	$relay_scope		=	((x($_POST,'relay_scope'))		? Strings::escapeTags(trim($_POST['relay_scope']))		: '');
+	$relay_server_tags	=	((x($_POST,'relay_server_tags'))	? Strings::escapeTags(trim($_POST['relay_server_tags']))	: '');
 	$relay_user_tags	=	((x($_POST,'relay_user_tags'))		? True						: False);
 
 	// Has the directory url changed? If yes, then resubmit the existing profiles there
@@ -1695,10 +1696,10 @@ function admin_page_users_post(App $a)
 		}
 
 		$user = $result['user'];
-		$preamble = deindent(L10n::t('
+		$preamble = Strings::deindent(L10n::t('
 			Dear %1$s,
 				the administrator of %2$s has set up an account for you.'));
-		$body = deindent(L10n::t('
+		$body = Strings::deindent(L10n::t('
 			The login details are as follows:
 
 			Site Location:	%1$s
@@ -2370,7 +2371,7 @@ function admin_page_logs_post(App $a)
 	if (x($_POST, "page_logs")) {
 		BaseModule::checkFormSecurityTokenRedirectOnError('/admin/logs', 'admin_logs');
 
-		$logfile   = ((x($_POST,'logfile'))   ? notags(trim($_POST['logfile']))  : '');
+		$logfile   = ((x($_POST,'logfile'))   ? Strings::escapeTags(trim($_POST['logfile']))  : '');
 		$debugging = ((x($_POST,'debugging')) ? true                             : false);
 		$loglevel  = ((x($_POST,'loglevel'))  ? intval(trim($_POST['loglevel'])) : 0);
 
@@ -2477,9 +2478,9 @@ function admin_page_viewlogs(App $a)
 				}
 				$seek = fseek($fp, 0 - $size, SEEK_END);
 				if ($seek === 0) {
-					$data = escape_tags(fread($fp, $size));
+					$data = Strings::escapeHtml(fread($fp, $size));
 					while (!feof($fp)) {
-						$data .= escape_tags(fread($fp, 4096));
+						$data .= Strings::escapeHtml(fread($fp, 4096));
 					}
 				}
 			}
diff --git a/mod/bookmarklet.php b/mod/bookmarklet.php
index e1ae9aa64..d9c2f52f8 100644
--- a/mod/bookmarklet.php
+++ b/mod/bookmarklet.php
@@ -9,6 +9,7 @@ use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\System;
 use Friendica\Module\Login;
+use Friendica\Util\Strings;
 
 require_once 'include/conversation.php';
 require_once 'include/items.php';
@@ -26,8 +27,8 @@ function bookmarklet_content(App $a)
 		return $o;
 	}
 
-	$referer = normalise_link(defaults($_SERVER, 'HTTP_REFERER', ''));
-	$page = normalise_link(System::baseUrl() . "/bookmarklet");
+	$referer = Strings::normaliseLink(defaults($_SERVER, 'HTTP_REFERER', ''));
+	$page = Strings::normaliseLink(System::baseUrl() . "/bookmarklet");
 
 	if (!strstr($referer, $page)) {
 		if (empty($_REQUEST["url"])) {
diff --git a/mod/common.php b/mod/common.php
index 0f9bc096a..b335e296d 100644
--- a/mod/common.php
+++ b/mod/common.php
@@ -12,7 +12,7 @@ use Friendica\Database\DBA;
 use Friendica\Model;
 use Friendica\Module;
 use Friendica\Util\Proxy as ProxyUtils;
-
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 
@@ -67,11 +67,11 @@ function common_content(App $a)
 	}
 
 	if (!$cid && Model\Profile::getMyURL()) {
-		$contact = DBA::selectFirst('contact', ['id'], ['nurl' => normalise_link(Model\Profile::getMyURL()), 'uid' => $uid]);
+		$contact = DBA::selectFirst('contact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL()), 'uid' => $uid]);
 		if (DBA::isResult($contact)) {
 			$cid = $contact['id'];
 		} else {
-			$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => normalise_link(Model\Profile::getMyURL())]);
+			$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL())]);
 			if (DBA::isResult($gcontact)) {
 				$zcid = $gcontact['id'];
 			}
diff --git a/mod/crepair.php b/mod/crepair.php
index 330831559..f9ba281d1 100644
--- a/mod/crepair.php
+++ b/mod/crepair.php
@@ -12,6 +12,7 @@ use Friendica\Core\Renderer;
 use Friendica\Database\DBA;
 use Friendica\Model;
 use Friendica\Module;
+use Friendica\Util\Strings;
 
 function crepair_init(App $a)
 {
@@ -61,7 +62,7 @@ function crepair_post(App $a)
 	$attag       = defaults($_POST, 'attag'      , '');
 	$photo       = defaults($_POST, 'photo'      , '');
 	$remote_self = defaults($_POST, 'remote_self', false);
-	$nurl        = normalise_link($url);
+	$nurl        = Strings::normaliseLink($url);
 
 	$r = q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `url` = '%s', `nurl` = '%s', `request` = '%s', `confirm` = '%s', `notify` = '%s', `poll` = '%s', `attag` = '%s' , `remote_self` = %d
 		WHERE `id` = %d AND `uid` = %d",
diff --git a/mod/delegate.php b/mod/delegate.php
index 116245812..c8987ab57 100644
--- a/mod/delegate.php
+++ b/mod/delegate.php
@@ -12,6 +12,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\User;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 
 require_once 'mod/settings.php';
 
@@ -72,7 +73,7 @@ function delegate_content(App $a)
 		if (DBA::isResult($user)) {
 			$condition = [
 				'uid' => local_user(),
-				'nurl' => normalise_link(System::baseUrl() . '/profile/' . $user['nickname'])
+				'nurl' => Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname'])
 			];
 			if (DBA::exists('contact', $condition)) {
 				DBA::insert('manage', ['uid' => $user_id, 'mid' => local_user()]);
@@ -114,7 +115,7 @@ function delegate_content(App $a)
 		AND SUBSTRING_INDEX(`nurl`, '/', 3) = '%s'
 		AND `uid` = %d
 		AND `network` = '%s' ",
-		DBA::escape(normalise_link(System::baseUrl())),
+		DBA::escape(Strings::normaliseLink(System::baseUrl())),
 		intval(local_user()),
 		DBA::escape(Protocol::DFRN)
 	);
diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php
index 0f001b11a..48c8e32e5 100644
--- a/mod/dfrn_confirm.php
+++ b/mod/dfrn_confirm.php
@@ -33,6 +33,7 @@ use Friendica\Protocol\ActivityPub;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/enotify.php';
@@ -84,7 +85,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
 			$cid      = 0;
 			$hidden   = intval(defaults($handsfree, 'hidden'  , 0));
 		} else {
-			$dfrn_id  = notags(trim(defaults($_POST, 'dfrn_id'   , '')));
+			$dfrn_id  = Strings::escapeTags(trim(defaults($_POST, 'dfrn_id'   , '')));
 			$intro_id =      intval(defaults($_POST, 'intro_id'  , 0));
 			$duplex   =      intval(defaults($_POST, 'duplex'    , 0));
 			$cid      =      intval(defaults($_POST, 'contact_id', 0));
@@ -263,7 +264,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
 					break;
 				case 1:
 					// birthday paradox - generate new dfrn-id and fall through.
-					$new_dfrn_id = random_string();
+					$new_dfrn_id = Strings::getRandomHex();
 					q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d",
 						DBA::escape($new_dfrn_id),
 						intval($contact_id),
diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php
index b14c71fb8..63f53e060 100644
--- a/mod/dfrn_notify.php
+++ b/mod/dfrn_notify.php
@@ -14,6 +14,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Protocol\DFRN;
 use Friendica\Protocol\Diaspora;
+use Friendica\Util\Strings;
 
 require_once 'include/items.php';
 
@@ -38,15 +39,15 @@ function dfrn_notify_post(App $a) {
 		}
 	}
 
-	$dfrn_id      = ((x($_POST,'dfrn_id'))      ? notags(trim($_POST['dfrn_id']))   : '');
+	$dfrn_id      = ((x($_POST,'dfrn_id'))      ? Strings::escapeTags(trim($_POST['dfrn_id']))   : '');
 	$dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version']    : 2.0);
-	$challenge    = ((x($_POST,'challenge'))    ? notags(trim($_POST['challenge'])) : '');
+	$challenge    = ((x($_POST,'challenge'))    ? Strings::escapeTags(trim($_POST['challenge'])) : '');
 	$data         = ((x($_POST,'data'))         ? $_POST['data']                    : '');
 	$key          = ((x($_POST,'key'))          ? $_POST['key']                     : '');
 	$rino_remote  = ((x($_POST,'rino'))         ? intval($_POST['rino'])            :  0);
 	$dissolve     = ((x($_POST,'dissolve'))     ? intval($_POST['dissolve'])        :  0);
-	$perm         = ((x($_POST,'perm'))         ? notags(trim($_POST['perm']))      : 'r');
-	$ssl_policy   = ((x($_POST,'ssl_policy'))   ? notags(trim($_POST['ssl_policy'])): 'none');
+	$perm         = ((x($_POST,'perm'))         ? Strings::escapeTags(trim($_POST['perm']))      : 'r');
+	$ssl_policy   = ((x($_POST,'ssl_policy'))   ? Strings::escapeTags(trim($_POST['ssl_policy'])): 'none');
 	$page         = ((x($_POST,'page'))         ? intval($_POST['page'])            :  0);
 
 	$forum = (($page == 1) ? 1 : 0);
@@ -253,7 +254,7 @@ function dfrn_notify_content(App $a) {
 		 * If this is a duplex communication, ours will be the opposite.
 		 */
 
-		$dfrn_id = notags(trim($_GET['dfrn_id']));
+		$dfrn_id = Strings::escapeTags(trim($_GET['dfrn_id']));
 		$dfrn_version = (float) $_GET['dfrn_version'];
 		$rino_remote = ((x($_GET,'rino')) ? intval($_GET['rino']) : 0);
 		$type = "";
@@ -267,7 +268,7 @@ function dfrn_notify_content(App $a) {
 			$dfrn_id = substr($dfrn_id,2);
 		}
 
-		$hash = random_string();
+		$hash = Strings::getRandomHex();
 
 		$status = 0;
 
diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php
index a961506d1..9cd110917 100644
--- a/mod/dfrn_poll.php
+++ b/mod/dfrn_poll.php
@@ -14,6 +14,7 @@ use Friendica\Module\Login;
 use Friendica\Protocol\DFRN;
 use Friendica\Protocol\OStatus;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/items.php';
@@ -415,7 +416,7 @@ function dfrn_poll_content(App $a)
 
 	if ($dfrn_id != '') {
 		// initial communication from external contact
-		$hash = random_string();
+		$hash = Strings::getRandomHex();
 
 		$status = 0;
 
diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php
index fdb1a42ee..26d7efda5 100644
--- a/mod/dfrn_request.php
+++ b/mod/dfrn_request.php
@@ -28,6 +28,7 @@ use Friendica\Module\Login;
 use Friendica\Network\Probe;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 require_once 'include/enotify.php';
 
@@ -75,7 +76,7 @@ function dfrn_request_post(App $a)
 	if ((x($_POST, 'localconfirm')) && ($_POST['localconfirm'] == 1)) {
 		// Ensure this is a valid request
 		if (local_user() && ($a->user['nickname'] == $a->argv[1]) && (x($_POST, 'dfrn_url'))) {
-			$dfrn_url = notags(trim($_POST['dfrn_url']));
+			$dfrn_url = Strings::escapeTags(trim($_POST['dfrn_url']));
 			$aes_allow = (((x($_POST, 'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0);
 			$confirm_key = ((x($_POST, 'confirm_key')) ? $_POST['confirm_key'] : "");
 			$hidden = ((x($_POST, 'hidden-contact')) ? intval($_POST['hidden-contact']) : 0);
@@ -87,7 +88,7 @@ function dfrn_request_post(App $a)
 				// Lookup the contact based on their URL (which is the only unique thing we have at the moment)
 				$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND NOT `self` LIMIT 1",
 					intval(local_user()),
-					DBA::escape(normalise_link($dfrn_url))
+					DBA::escape(Strings::normaliseLink($dfrn_url))
 				);
 
 				if (DBA::isResult($r)) {
@@ -141,7 +142,7 @@ function dfrn_request_post(App $a)
 						intval(local_user()),
 						DateTimeFormat::utcNow(),
 						DBA::escape($dfrn_url),
-						DBA::escape(normalise_link($dfrn_url)),
+						DBA::escape(Strings::normaliseLink($dfrn_url)),
 						$parms['addr'],
 						$parms['fn'],
 						$parms['nick'],
@@ -269,7 +270,7 @@ function dfrn_request_post(App $a)
 			}
 		}
 
-		$real_name = x($_POST, 'realname') ? notags(trim($_POST['realname'])) : '';
+		$real_name = x($_POST, 'realname') ? Strings::escapeTags(trim($_POST['realname'])) : '';
 
 		$url = trim($_POST['dfrn_url']);
 		if (!strlen($url)) {
@@ -320,7 +321,7 @@ function dfrn_request_post(App $a)
 				}
 			}
 
-			$issued_id = random_string();
+			$issued_id = Strings::getRandomHex();
 
 			if (is_array($contact_record)) {
 				// There is a contact record but no issued-id, so this
@@ -380,7 +381,7 @@ function dfrn_request_post(App $a)
 					intval($uid),
 					DBA::escape(DateTimeFormat::utcNow()),
 					$parms['url'],
-					DBA::escape(normalise_link($url)),
+					DBA::escape(Strings::normaliseLink($url)),
 					$parms['addr'],
 					$parms['fn'],
 					$parms['nick'],
@@ -415,7 +416,7 @@ function dfrn_request_post(App $a)
 				return;
 			}
 
-			$hash = random_string() . (string) time();   // Generate a confirm_key
+			$hash = Strings::getRandomHex() . (string) time();   // Generate a confirm_key
 
 			if (is_array($contact_record)) {
 				$ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
@@ -423,7 +424,7 @@ function dfrn_request_post(App $a)
 					intval($uid),
 					intval($contact_record['id']),
 					((x($_POST,'knowyou') && ($_POST['knowyou'] == 1)) ? 1 : 0),
-					DBA::escape(notags(trim(defaults($_POST, 'dfrn-request-message', '')))),
+					DBA::escape(Strings::escapeTags(trim(defaults($_POST, 'dfrn-request-message', '')))),
 					DBA::escape($hash),
 					DBA::escape(DateTimeFormat::utcNow())
 				);
@@ -497,12 +498,12 @@ function dfrn_request_content(App $a)
 			return Login::form();
 		}
 
-		$dfrn_url = notags(trim(hex2bin($_GET['dfrn_url'])));
+		$dfrn_url = Strings::escapeTags(trim(hex2bin($_GET['dfrn_url'])));
 		$aes_allow = x($_GET, 'aes_allow') && $_GET['aes_allow'] == 1 ? 1 : 0;
 		$confirm_key = x($_GET, 'confirm_key') ? $_GET['confirm_key'] : "";
 
 		// Checking fastlane for validity
-		if (x($_SESSION, "fastlane") && (normalise_link($_SESSION["fastlane"]) == normalise_link($dfrn_url))) {
+		if (x($_SESSION, "fastlane") && (Strings::normaliseLink($_SESSION["fastlane"]) == Strings::normaliseLink($dfrn_url))) {
 			$_POST["dfrn_url"] = $dfrn_url;
 			$_POST["confirm_key"] = $confirm_key;
 			$_POST["localconfirm"] = 1;
diff --git a/mod/directory.php b/mod/directory.php
index 10eaa4492..e59cf7b82 100644
--- a/mod/directory.php
+++ b/mod/directory.php
@@ -15,6 +15,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 function directory_init(App $a)
 {
@@ -47,9 +48,9 @@ function directory_content(App $a)
 	Nav::setSelected('directory');
 
 	if (x($a->data, 'search')) {
-		$search = notags(trim($a->data['search']));
+		$search = Strings::escapeTags(trim($a->data['search']));
 	} else {
-		$search = ((x($_GET, 'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
+		$search = ((x($_GET, 'search')) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
 	}
 
 	$gdirpath = '';
diff --git a/mod/dirfind.php b/mod/dirfind.php
index 2451beb18..d3298b64d 100644
--- a/mod/dirfind.php
+++ b/mod/dirfind.php
@@ -20,6 +20,7 @@ use Friendica\Network\Probe;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 
 function dirfind_init(App $a) {
@@ -45,15 +46,15 @@ function dirfind_content(App $a, $prefix = "") {
 
 	$local = Config::get('system','poco_local_search');
 
-	$search = $prefix.notags(trim(defaults($_REQUEST, 'search', '')));
+	$search = $prefix.Strings::escapeTags(trim(defaults($_REQUEST, 'search', '')));
 
 	$header = '';
 
 	if (strpos($search,'@') === 0) {
 		$search = substr($search,1);
 		$header = L10n::t('People Search - %s', $search);
-		if ((valid_email($search) && Network::isEmailDomainValid($search)) ||
-			(substr(normalise_link($search), 0, 7) == "http://")) {
+		if ((filter_var($search, FILTER_VALIDATE_EMAIL) && Network::isEmailDomainValid($search)) ||
+			(substr(Strings::normaliseLink($search), 0, 7) == "http://")) {
 			$user_data = Probe::uri($search);
 			$discover_user = (in_array($user_data["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA]));
 		}
@@ -125,8 +126,8 @@ function dirfind_content(App $a, $prefix = "") {
 						(`url` LIKE '%s' OR `name` LIKE '%s' OR `location` LIKE '%s' OR
 						`addr` LIKE '%s' OR `about` LIKE '%s' OR `keywords` LIKE '%s') $extra_sql",
 					DBA::escape(Protocol::DFRN), DBA::escape($ostatus), DBA::escape($diaspora),
-					DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)),
-					DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)));
+					DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)),
+					DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)));
 
 			$results = q("SELECT `nurl`
 					FROM `gcontact`
@@ -137,8 +138,8 @@ function dirfind_content(App $a, $prefix = "") {
 						GROUP BY `nurl`
 						ORDER BY `updated` DESC LIMIT %d, %d",
 					DBA::escape(Protocol::DFRN), DBA::escape($ostatus), DBA::escape($diaspora),
-					DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)),
-					DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)), DBA::escape(escape_tags($search2)),
+					DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)),
+					DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)), DBA::escape(Strings::escapeHtml($search2)),
 					$pager->getStart(), $pager->getItemsPerPage());
 			$j = new stdClass();
 			$j->total = $count[0]["total"];
diff --git a/mod/display.php b/mod/display.php
index ac345d541..39661cb76 100644
--- a/mod/display.php
+++ b/mod/display.php
@@ -21,6 +21,7 @@ use Friendica\Model\Item;
 use Friendica\Model\Profile;
 use Friendica\Protocol\ActivityPub;
 use Friendica\Protocol\DFRN;
+use Friendica\Util\Strings;
 
 function display_init(App $a)
 {
@@ -90,8 +91,8 @@ function display_init(App $a)
 
 	$profiledata = display_fetchauthor($a, $item);
 
-	if (strstr(normalise_link($profiledata["url"]), normalise_link(System::baseUrl()))) {
-		$nickname = str_replace(normalise_link(System::baseUrl())."/profile/", "", normalise_link($profiledata["url"]));
+	if (strstr(Strings::normaliseLink($profiledata["url"]), Strings::normaliseLink(System::baseUrl()))) {
+		$nickname = str_replace(Strings::normaliseLink(System::baseUrl())."/profile/", "", Strings::normaliseLink($profiledata["url"]));
 
 		if (($nickname != $a->user["nickname"])) {
 			$profile = DBA::fetchFirst("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `user`.* FROM `profile`
diff --git a/mod/events.php b/mod/events.php
index c9461a48e..f147e0054 100644
--- a/mod/events.php
+++ b/mod/events.php
@@ -19,6 +19,7 @@ use Friendica\Model\Item;
 use Friendica\Model\Profile;
 use Friendica\Module\Login;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/items.php';
@@ -59,8 +60,8 @@ function events_post(App $a)
 	$cid = !empty($_POST['cid']) ? intval($_POST['cid']) : 0;
 	$uid = local_user();
 
-	$start_text  = escape_tags(defaults($_REQUEST, 'start_text', ''));
-	$finish_text = escape_tags(defaults($_REQUEST, 'finish_text', ''));
+	$start_text  = Strings::escapeHtml(defaults($_REQUEST, 'start_text', ''));
+	$finish_text = Strings::escapeHtml(defaults($_REQUEST, 'finish_text', ''));
 
 	$adjust   = intval(defaults($_POST, 'adjust', 0));
 	$nofinish = intval(defaults($_POST, 'nofinish', 0));
@@ -96,9 +97,9 @@ function events_post(App $a)
 	// and we'll waste a bunch of time responding to it. Time that
 	// could've been spent doing something else.
 
-	$summary  = escape_tags(trim(defaults($_POST, 'summary', '')));
-	$desc     = escape_tags(trim(defaults($_POST, 'desc', '')));
-	$location = escape_tags(trim(defaults($_POST, 'location', '')));
+	$summary  = Strings::escapeHtml(trim(defaults($_POST, 'summary', '')));
+	$desc     = Strings::escapeHtml(trim(defaults($_POST, 'desc', '')));
+	$location = Strings::escapeHtml(trim(defaults($_POST, 'location', '')));
 	$type     = 'event';
 
 	$action = ($event_id == '') ? 'new' : "event/" . $event_id;
diff --git a/mod/fetch.php b/mod/fetch.php
index 4e7d8c751..9336c1404 100644
--- a/mod/fetch.php
+++ b/mod/fetch.php
@@ -10,6 +10,7 @@ use Friendica\Core\System;
 use Friendica\Protocol\Diaspora;
 use Friendica\Model\Item;
 use Friendica\Model\User;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use Friendica\Database\DBA;
 
@@ -35,7 +36,7 @@ function fetch_init(App $a)
 			$parts = parse_url($item["author-link"]);
 			$host = $parts["scheme"]."://".$parts["host"];
 
-			if (normalise_link($host) != normalise_link(System::baseUrl())) {
+			if (Strings::normaliseLink($host) != Strings::normaliseLink(System::baseUrl())) {
 				$location = $host."/fetch/".$a->argv[1]."/".urlencode($guid);
 
 				header("HTTP/1.1 301 Moved Permanently");
diff --git a/mod/follow.php b/mod/follow.php
index 5c6c6d9d3..08f664d39 100644
--- a/mod/follow.php
+++ b/mod/follow.php
@@ -13,6 +13,7 @@ use Friendica\Model\Profile;
 use Friendica\Network\Probe;
 use Friendica\Database\DBA;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 function follow_post(App $a)
 {
@@ -25,7 +26,7 @@ function follow_post(App $a)
 	}
 
 	$uid = local_user();
-	$url = notags(trim($_REQUEST['url']));
+	$url = Strings::escapeTags(trim($_REQUEST['url']));
 	$return_path = 'contacts';
 
 	// Makes the connection request for friendica contacts easier
@@ -60,7 +61,7 @@ function follow_content(App $a)
 	}
 
 	$uid = local_user();
-	$url = notags(trim($_REQUEST['url']));
+	$url = Strings::escapeTags(trim($_REQUEST['url']));
 
 	$submit = L10n::t('Submit Request');
 
@@ -68,8 +69,8 @@ function follow_content(App $a)
 	$r = q("SELECT `pending` FROM `contact` WHERE `uid` = %d AND ((`rel` != %d) OR (`network` = '%s')) AND
 		(`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND
 		`network` != '%s' LIMIT 1",
-		intval(local_user()), DBA::escape(Contact::FOLLOWER), DBA::escape(Protocol::DFRN), DBA::escape(normalise_link($url)),
-		DBA::escape(normalise_link($url)), DBA::escape($url), DBA::escape(Protocol::STATUSNET));
+		intval(local_user()), DBA::escape(Contact::FOLLOWER), DBA::escape(Protocol::DFRN), DBA::escape(Strings::normaliseLink($url)),
+		DBA::escape(Strings::normaliseLink($url)), DBA::escape($url), DBA::escape(Protocol::STATUSNET));
 
 	if ($r) {
 		if ($r[0]['pending']) {
@@ -130,7 +131,7 @@ function follow_content(App $a)
 	$_SESSION['fastlane'] = $ret['url'];
 
 	$r = q("SELECT `id`, `location`, `about`, `keywords` FROM `gcontact` WHERE `nurl` = '%s'",
-		normalise_link($ret['url']));
+		Strings::normaliseLink($ret['url']));
 
 	if (!$r) {
 		$r = [['location' => '', 'about' => '', 'keywords' => '']];
diff --git a/mod/fsuggest.php b/mod/fsuggest.php
index 35710bb33..2195f455a 100644
--- a/mod/fsuggest.php
+++ b/mod/fsuggest.php
@@ -9,6 +9,7 @@ use Friendica\Core\L10n;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 
 function fsuggest_post(App $a)
 {
@@ -34,9 +35,9 @@ function fsuggest_post(App $a)
 
 	$new_contact = intval($_POST['suggest']);
 
-	$hash = random_string();
+	$hash = Strings::getRandomHex();
 
-	$note = escape_tags(trim(defaults($_POST, 'note', '')));
+	$note = Strings::escapeHtml(trim(defaults($_POST, 'note', '')));
 
 	if ($new_contact) {
 		$r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
diff --git a/mod/group.php b/mod/group.php
index 8b2ce9ca1..51fa2493e 100644
--- a/mod/group.php
+++ b/mod/group.php
@@ -16,6 +16,7 @@ use Friendica\Database\DBA;
 use Friendica\Model;
 use Friendica\Module;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 
 function group_init(App $a) {
 	if (local_user()) {
@@ -33,7 +34,7 @@ function group_post(App $a) {
 	if (($a->argc == 2) && ($a->argv[1] === 'new')) {
 		BaseModule::checkFormSecurityTokenRedirectOnError('/group/new', 'group_edit');
 
-		$name = notags(trim($_POST['groupname']));
+		$name = Strings::escapeTags(trim($_POST['groupname']));
 		$r = Model\Group::create(local_user(), $name);
 		if ($r) {
 			info(L10n::t('Group created.') . EOL);
@@ -61,7 +62,7 @@ function group_post(App $a) {
 			return; // NOTREACHED
 		}
 		$group = $r[0];
-		$groupname = notags(trim($_POST['groupname']));
+		$groupname = Strings::escapeTags(trim($_POST['groupname']));
 		if (strlen($groupname) && ($groupname != $group['name'])) {
 			$r = q("UPDATE `group` SET `name` = '%s' WHERE `uid` = %d AND `id` = %d",
 				DBA::escape($groupname),
diff --git a/mod/help.php b/mod/help.php
index 2c8f68ff3..3a21695b0 100644
--- a/mod/help.php
+++ b/mod/help.php
@@ -10,6 +10,7 @@ use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\Renderer;
 use Friendica\Core\System;
+use Friendica\Util\Strings;
 
 function load_doc_file($s)
 {
@@ -47,7 +48,7 @@ function help_content(App $a)
 		$title = basename($path);
 		$filename = $path;
 		$text = load_doc_file('doc/' . $path . '.md');
-		$a->page['title'] = L10n::t('Help:') . ' ' . str_replace('-', ' ', notags($title));
+		$a->page['title'] = L10n::t('Help:') . ' ' . str_replace('-', ' ', Strings::escapeTags($title));
 	}
 
 	$home = load_doc_file('doc/Home.md');
diff --git a/mod/hovercard.php b/mod/hovercard.php
index 1e2e14afb..f5ad3ef02 100644
--- a/mod/hovercard.php
+++ b/mod/hovercard.php
@@ -16,6 +16,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\GContact;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 function hovercard_init(App $a)
 {
@@ -55,7 +56,7 @@ function hovercard_content()
 
 	$contact = [];
 	// if it's the url containing https it should be converted to http
-	$nurl = normalise_link(GContact::cleanContactUrl($profileurl));
+	$nurl = Strings::normaliseLink(GContact::cleanContactUrl($profileurl));
 	if (!$nurl) {
 		return;
 	}
@@ -73,12 +74,12 @@ function hovercard_content()
 
 	// Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url
 	if (!count($contact) && local_user()) {
-		$nurl = normalise_link($profileurl);
+		$nurl = Strings::normaliseLink($profileurl);
 		$contact = Contact::getDetailsByURL($nurl, local_user());
 	}
 
 	if (!count($contact)) {
-		$nurl = normalise_link($profileurl);
+		$nurl = Strings::normaliseLink($profileurl);
 		$contact = Contact::getDetailsByURL($nurl);
 	}
 
@@ -104,7 +105,7 @@ function hovercard_content()
 		'location' => $contact['location'],
 		'gender'   => $contact['gender'],
 		'about'    => $contact['about'],
-		'network'  => format_network_name($contact['network'], $contact['url']),
+		'network'  => Strings::formatNetworkName($contact['network'], $contact['url']),
 		'tags'     => $contact['keywords'],
 		'bd'       => $contact['birthday'] <= '0001-01-01' ? '' : $contact['birthday'],
 		'account_type' => Contact::getAccountType($contact),
diff --git a/mod/invite.php b/mod/invite.php
index 1e02ae9ca..460a3461d 100644
--- a/mod/invite.php
+++ b/mod/invite.php
@@ -17,6 +17,7 @@ use Friendica\Database\DBA;
 use Friendica\Protocol\Email;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 
 function invite_post(App $a)
 {
@@ -40,7 +41,7 @@ function invite_post(App $a)
 
 
 	$recipients  = !empty($_POST['recipients']) ? explode("\n", $_POST['recipients']) : [];
-	$message     = !empty($_POST['message'])    ? notags(trim($_POST['message']))     : '';
+	$message     = !empty($_POST['message'])    ? Strings::escapeTags(trim($_POST['message']))     : '';
 
 	$total = 0;
 
@@ -55,7 +56,7 @@ function invite_post(App $a)
 	foreach ($recipients as $recipient) {
 		$recipient = trim($recipient);
 
-		if (! valid_email($recipient)) {
+		if (!filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
 			notice(L10n::t('%s : Not a valid email address.', $recipient) . EOL);
 			continue;
 		}
diff --git a/mod/item.php b/mod/item.php
index 739e09ab0..507185e7c 100644
--- a/mod/item.php
+++ b/mod/item.php
@@ -36,6 +36,7 @@ use Friendica\Protocol\Email;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Emailer;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 
 require_once 'include/enotify.php';
 require_once 'include/text.php';
@@ -203,8 +204,8 @@ function item_post(App $a) {
 		$objecttype        = $orig_post['object-type'];
 		$app               = $orig_post['app'];
 		$categories        = $orig_post['file'];
-		$title             = notags(trim($_REQUEST['title']));
-		$body              = escape_tags(trim($_REQUEST['body']));
+		$title             = Strings::escapeTags(trim($_REQUEST['title']));
+		$body              = Strings::escapeHtml(trim($_REQUEST['body']));
 		$private           = $orig_post['private'];
 		$pubmail_enabled   = $orig_post['pubmail'];
 		$network           = $orig_post['network'];
@@ -235,14 +236,14 @@ function item_post(App $a) {
 			$str_contact_deny  = perms2str(defaults($_REQUEST, 'contact_deny', ''));
 		}
 
-		$title             =      notags(trim(defaults($_REQUEST, 'title'   , '')));
-		$location          =      notags(trim(defaults($_REQUEST, 'location', '')));
-		$coord             =      notags(trim(defaults($_REQUEST, 'coord'   , '')));
-		$verb              =      notags(trim(defaults($_REQUEST, 'verb'    , '')));
-		$emailcc           =      notags(trim(defaults($_REQUEST, 'emailcc' , '')));
-		$body              = escape_tags(trim(defaults($_REQUEST, 'body'    , '')));
-		$network           =      notags(trim(defaults($_REQUEST, 'network' , Protocol::DFRN)));
-		$guid              =      System::createUUID();
+		$title             = Strings::escapeTags(trim(defaults($_REQUEST, 'title'   , '')));
+		$location          = Strings::escapeTags(trim(defaults($_REQUEST, 'location', '')));
+		$coord             = Strings::escapeTags(trim(defaults($_REQUEST, 'coord'   , '')));
+		$verb              = Strings::escapeTags(trim(defaults($_REQUEST, 'verb'    , '')));
+		$emailcc           = Strings::escapeTags(trim(defaults($_REQUEST, 'emailcc' , '')));
+		$body              = Strings::escapeHtml(trim(defaults($_REQUEST, 'body'    , '')));
+		$network           = Strings::escapeTags(trim(defaults($_REQUEST, 'network' , Protocol::DFRN)));
+		$guid              = System::createUUID();
 
 		$postopts = defaults($_REQUEST, 'postopts', '');
 
@@ -347,7 +348,7 @@ function item_post(App $a) {
 	$str_tags = '';
 	$inform   = '';
 
-	$tags = get_tags($body);
+	$tags = BBCode::getTags($body);
 
 	// Add a tag if the parent contact is from ActivityPub or OStatus (This will notify them)
 	if ($parent && in_array($thr_parent_contact['network'], [Protocol::OSTATUS, Protocol::ACTIVITYPUB])) {
@@ -697,11 +698,10 @@ function item_post(App $a) {
 		killme();
 	}
 
-	if ($orig_post) {
-
+	if ($orig_post)	{
 		// Fill the cache field
 		// This could be done in Item::update as well - but we have to check for the existance of some fields.
-		put_item_in_cache($datarray);
+		Item::putInCache($datarray);
 
 		$fields = [
 			'title' => $datarray['title'],
@@ -817,7 +817,7 @@ function item_post(App $a) {
 					$subject = Email::encodeHeader('[Friendica]' . ' ' . L10n::t('%s posted an update.', $a->user['username']), 'UTF-8');
 				}
 				$link = '<a href="' . System::baseUrl() . '/profile/' . $a->user['nickname'] . '"><img src="' . $author['thumb'] . '" alt="' . $a->user['username'] . '" /></a><br /><br />';
-				$html    = prepare_body($datarray);
+				$html    = Item::prepareBody($datarray);
 				$message = '<html><body>' . $link . $html . $disclaimer . '</body></html>';
 				$params =  [
 					'fromName' => $a->user['username'],
diff --git a/mod/like.php b/mod/like.php
index 296e563bf..97eaca163 100644
--- a/mod/like.php
+++ b/mod/like.php
@@ -3,6 +3,7 @@
 use Friendica\App;
 use Friendica\Core\System;
 use Friendica\Model\Item;
+use Friendica\Util\Strings;
 
 require_once 'include/items.php';
 
@@ -12,13 +13,13 @@ function like_content(App $a) {
 	}
 
 
-	$verb = notags(trim($_GET['verb']));
+	$verb = Strings::escapeTags(trim($_GET['verb']));
 
 	if (!$verb) {
 		$verb = 'like';
 	}
 
-	$item_id = (($a->argc > 1) ? notags(trim($a->argv[1])) : 0);
+	$item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0);
 
 	$r = Item::performLike($item_id, $verb);
 	if (!$r) {
diff --git a/mod/lostpass.php b/mod/lostpass.php
index ae94fbbbe..42a1764bf 100644
--- a/mod/lostpass.php
+++ b/mod/lostpass.php
@@ -11,6 +11,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\User;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 
 require_once 'boot.php';
 require_once 'include/enotify.php';
@@ -18,7 +19,7 @@ require_once 'include/text.php';
 
 function lostpass_post(App $a)
 {
-	$loginame = notags(trim($_POST['login-name']));
+	$loginame = Strings::escapeTags(trim($_POST['login-name']));
 	if (!$loginame) {
 		$a->internalRedirect();
 	}
@@ -30,7 +31,7 @@ function lostpass_post(App $a)
 		$a->internalRedirect();
 	}
 
-	$pwdreset_token = autoname(12) . mt_rand(1000, 9999);
+	$pwdreset_token = Strings::getRandomName(12) . mt_rand(1000, 9999);
 
 	$fields = [
 		'pwdreset' => $pwdreset_token,
@@ -44,7 +45,7 @@ function lostpass_post(App $a)
 	$sitename = Config::get('config', 'sitename');
 	$resetlink = System::baseUrl() . '/lostpass/' . $pwdreset_token;
 
-	$preamble = deindent(L10n::t('
+	$preamble = Strings::deindent(L10n::t('
 		Dear %1$s,
 			A request was recently received at "%2$s" to reset your account
 		password. In order to confirm this request, please select the verification link
@@ -55,7 +56,7 @@ function lostpass_post(App $a)
 
 		Your password will not be changed unless we can verify that you
 		issued this request.', $user['username'], $sitename));
-	$body = deindent(L10n::t('
+	$body = Strings::deindent(L10n::t('
 		Follow this link soon to verify your identity:
 
 		%1$s
@@ -150,13 +151,13 @@ function lostpass_generate_password($user)
 		info("Your password has been reset." . EOL);
 
 		$sitename = Config::get('config', 'sitename');
-		$preamble = deindent(L10n::t('
+		$preamble = Strings::deindent(L10n::t('
 			Dear %1$s,
 				Your password has been changed as requested. Please retain this
 			information for your records ' . "\x28" . 'or change your password immediately to
 			something that you will remember' . "\x29" . '.
 		', $user['username']));
-		$body = deindent(L10n::t('
+		$body = Strings::deindent(L10n::t('
 			Your login details are as follows:
 
 			Site Location:	%1$s
diff --git a/mod/maintenance.php b/mod/maintenance.php
index a1b032a61..8e0197b86 100644
--- a/mod/maintenance.php
+++ b/mod/maintenance.php
@@ -6,12 +6,13 @@ use Friendica\App;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\Renderer;
+use Friendica\Util\Strings;
 
 function maintenance_content(App $a)
 {
 	$reason = Config::get('system', 'maintenance_reason');
 
-	if (substr(normalise_link($reason), 0, 7) == 'http://') {
+	if (substr(Strings::normaliseLink($reason), 0, 7) == 'http://') {
 		header("HTTP/1.1 307 Temporary Redirect");
 		header("Location:".$reason);
 		return;
diff --git a/mod/match.php b/mod/match.php
index e924722aa..0ec753466 100644
--- a/mod/match.php
+++ b/mod/match.php
@@ -14,6 +14,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 require_once 'include/text.php';
 
@@ -76,7 +77,7 @@ function match_content(App $a)
 			$id = 0;
 
 			foreach ($j->results as $jj) {
-				$match_nurl = normalise_link($jj->url);
+				$match_nurl = Strings::normaliseLink($jj->url);
 				$match = q(
 					"SELECT `nurl` FROM `contact` WHERE `uid` = '%d' AND nurl='%s' LIMIT 1",
 					intval(local_user()),
diff --git a/mod/message.php b/mod/message.php
index 23c08f5a3..7491cd1bc 100644
--- a/mod/message.php
+++ b/mod/message.php
@@ -18,6 +18,7 @@ use Friendica\Model\Mail;
 use Friendica\Module\Login;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/conversation.php';
@@ -58,9 +59,9 @@ function message_post(App $a)
 		return;
 	}
 
-	$replyto   = x($_REQUEST, 'replyto')   ? notags(trim($_REQUEST['replyto']))   : '';
-	$subject   = x($_REQUEST, 'subject')   ? notags(trim($_REQUEST['subject']))   : '';
-	$body      = x($_REQUEST, 'body')      ? escape_tags(trim($_REQUEST['body'])) : '';
+	$replyto   = x($_REQUEST, 'replyto')   ? Strings::escapeTags(trim($_REQUEST['replyto']))   : '';
+	$subject   = x($_REQUEST, 'subject')   ? Strings::escapeTags(trim($_REQUEST['subject']))   : '';
+	$body      = x($_REQUEST, 'body')      ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
 	$recipient = x($_REQUEST, 'messageto') ? intval($_REQUEST['messageto'])       : 0;
 
 	$ret = Mail::send($recipient, $body, $subject, $replyto);
@@ -218,7 +219,7 @@ function message_content(App $a)
 			if (!DBA::isResult($r)) {
 				$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1",
 					intval(local_user()),
-					DBA::escape(normalise_link(base64_decode($a->argv[2])))
+					DBA::escape(Strings::normaliseLink(base64_decode($a->argv[2])))
 				);
 			}
 
@@ -253,7 +254,7 @@ function message_content(App $a)
 			'$preid' => $preid,
 			'$subject' => L10n::t('Subject:'),
 			'$subjtxt' => x($_REQUEST, 'subject') ? strip_tags($_REQUEST['subject']) : '',
-			'$text' => x($_REQUEST, 'body') ? escape_tags(htmlspecialchars($_REQUEST['body'])) : '',
+			'$text' => x($_REQUEST, 'body') ? Strings::escapeHtml(htmlspecialchars($_REQUEST['body'])) : '',
 			'$readonly' => '',
 			'$yourmessage' => L10n::t('Your message:'),
 			'$select' => $select,
@@ -462,7 +463,7 @@ function render_messages(array $msg, $t)
 	foreach ($msg as $rr) {
 		if ($rr['unknown']) {
 			$participants = L10n::t("Unknown sender - %s", $rr['from-name']);
-		} elseif (link_compare($rr['from-url'], $myprofile)) {
+		} elseif (Strings::compareLink($rr['from-url'], $myprofile)) {
 			$participants = L10n::t("You and %s", $rr['name']);
 		} else {
 			$participants = L10n::t("%s and You", $rr['from-name']);
diff --git a/mod/network.php b/mod/network.php
index 26458a7a0..81a2f2b2b 100644
--- a/mod/network.php
+++ b/mod/network.php
@@ -28,6 +28,7 @@ use Friendica\Model\Profile;
 use Friendica\Module\Login;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 require_once 'include/conversation.php';
 require_once 'include/items.php';
@@ -41,7 +42,7 @@ function network_init(App $a)
 
 	Hook::add('head', __FILE__, 'network_infinite_scroll_head');
 
-	$search = (x($_GET, 'search') ? escape_tags($_GET['search']) : '');
+	$search = (x($_GET, 'search') ? Strings::escapeHtml($_GET['search']) : '');
 
 	if (($search != '') && !empty($_GET['submit'])) {
 		$a->internalRedirect('search?search=' . urlencode($search));
@@ -518,9 +519,9 @@ function networkThreadedView(App $a, $update, $parent)
 		for ($x = 1; $x < $a->argc; $x ++) {
 			if (is_a_date_arg($a->argv[$x])) {
 				if ($datequery) {
-					$datequery2 = escape_tags($a->argv[$x]);
+					$datequery2 = Strings::escapeHtml($a->argv[$x]);
 				} else {
-					$datequery = escape_tags($a->argv[$x]);
+					$datequery = Strings::escapeHtml($a->argv[$x]);
 					$_GET['order'] = 'post';
 				}
 			} elseif (intval($a->argv[$x])) {
@@ -536,7 +537,7 @@ function networkThreadedView(App $a, $update, $parent)
 	$star  = intval(defaults($_GET, 'star' , 0));
 	$bmark = intval(defaults($_GET, 'bmark', 0));
 	$conv  = intval(defaults($_GET, 'conv' , 0));
-	$order = notags(defaults($_GET, 'order', 'comment'));
+	$order = Strings::escapeTags(defaults($_GET, 'order', 'comment'));
 	$nets  =        defaults($_GET, 'nets' , '');
 
 	if ($cid) {
@@ -649,7 +650,7 @@ function networkThreadedView(App $a, $update, $parent)
 
 			$sql_post_table .= " INNER JOIN `item` AS `temp1` ON `temp1`.`id` = " . $sql_table . "." . $sql_parent;
 			$sql_extra3 .= " AND (`thread`.`contact-id` IN ($contact_str) ";
-			$sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . protect_sprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))";
+			$sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . Strings::protectSprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))";
 		} else {
 			$sql_extra3 .= " AND false ";
 			info(L10n::t('Group is empty'));
@@ -697,11 +698,11 @@ function networkThreadedView(App $a, $update, $parent)
 	}
 
 	if ($datequery) {
-		$sql_extra3 .= protect_sprintf(sprintf(" AND $sql_table.created <= '%s' ",
+		$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.created <= '%s' ",
 				DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
 	}
 	if ($datequery2) {
-		$sql_extra3 .= protect_sprintf(sprintf(" AND $sql_table.created >= '%s' ",
+		$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.created >= '%s' ",
 				DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
 	}
 
@@ -882,7 +883,7 @@ function networkThreadedView(App $a, $update, $parent)
 			foreach ($data as $item) {
 				// Don't show hash tag posts from blocked or ignored contacts
 				$condition = ["`nurl` = ? AND `uid` = ? AND (`blocked` OR `readonly`)",
-					normalise_link($item['author-link']), local_user()];
+					Strings::normaliseLink($item['author-link']), local_user()];
 				if (!DBA::exists('contact', $condition)) {
 					$s[$item['uri']] = $item;
 				}
diff --git a/mod/oexchange.php b/mod/oexchange.php
index 296869aac..62d0bba07 100644
--- a/mod/oexchange.php
+++ b/mod/oexchange.php
@@ -8,6 +8,7 @@ use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Module\Login;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 function oexchange_init(App $a) {
 
@@ -33,13 +34,13 @@ function oexchange_content(App $a) {
 	}
 
 	$url = ((x($_REQUEST,'url') && strlen($_REQUEST['url']))
-		? urlencode(notags(trim($_REQUEST['url']))) : '');
+		? urlencode(Strings::escapeTags(trim($_REQUEST['url']))) : '');
 	$title = ((x($_REQUEST,'title') && strlen($_REQUEST['title']))
-		? '&title=' . urlencode(notags(trim($_REQUEST['title']))) : '');
+		? '&title=' . urlencode(Strings::escapeTags(trim($_REQUEST['title']))) : '');
 	$description = ((x($_REQUEST,'description') && strlen($_REQUEST['description']))
-		? '&description=' . urlencode(notags(trim($_REQUEST['description']))) : '');
+		? '&description=' . urlencode(Strings::escapeTags(trim($_REQUEST['description']))) : '');
 	$tags = ((x($_REQUEST,'tags') && strlen($_REQUEST['tags']))
-		? '&tags=' . urlencode(notags(trim($_REQUEST['tags']))) : '');
+		? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : '');
 
 	$s = Network::fetchUrl(System::baseUrl() . '/parse_url?f=&url=' . $url . $title . $description . $tags);
 
diff --git a/mod/openid.php b/mod/openid.php
index d1404ba80..209960ee5 100644
--- a/mod/openid.php
+++ b/mod/openid.php
@@ -10,6 +10,7 @@ use Friendica\Core\L10n;
 use Friendica\Core\Logger;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
+use Friendica\Util\Strings;
 
 function openid_content(App $a) {
 
@@ -43,7 +44,7 @@ function openid_content(App $a) {
 				AND `blocked` = 0 AND `account_expired` = 0
 				AND `account_removed` = 0 AND `verified` = 1
 				LIMIT 1",
-				DBA::escape($authid), DBA::escape(normalise_openid($authid))
+				DBA::escape($authid), DBA::escape(Strings::normaliseOpenID($authid))
 			);
 
 			if (DBA::isResult($r)) {
@@ -74,16 +75,16 @@ function openid_content(App $a) {
 			if (is_array($attr) && count($attr)) {
 				foreach ($attr as $k => $v) {
 					if ($k === 'namePerson/friendly') {
-						$nick = notags(trim($v));
+						$nick = Strings::escapeTags(trim($v));
 					}
 					if($k === 'namePerson/first') {
-						$first = notags(trim($v));
+						$first = Strings::escapeTags(trim($v));
 					}
 					if($k === 'namePerson') {
-						$args .= '&username=' . urlencode(notags(trim($v)));
+						$args .= '&username=' . urlencode(Strings::escapeTags(trim($v)));
 					}
 					if ($k === 'contact/email') {
-						$args .= '&email=' . urlencode(notags(trim($v)));
+						$args .= '&email=' . urlencode(Strings::escapeTags(trim($v)));
 					}
 					if ($k === 'media/image/aspect11') {
 						$photosq = bin2hex(trim($v));
@@ -107,7 +108,7 @@ function openid_content(App $a) {
 				$args .= '&photo=' . urlencode($photo);
 			}
 
-			$args .= '&openid_url=' . urlencode(notags(trim($authid)));
+			$args .= '&openid_url=' . urlencode(Strings::escapeTags(trim($authid)));
 
 			$a->internalRedirect('register?' . $args);
 
diff --git a/mod/photos.php b/mod/photos.php
index 7a49f061a..f07815c25 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -31,6 +31,7 @@ use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Map;
 use Friendica\Util\Security;
 use Friendica\Util\Temporal;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/items.php';
@@ -222,7 +223,7 @@ function photos_post(App $a)
 		}
 
 		// RENAME photo album
-		$newalbum = notags(trim($_POST['albumname']));
+		$newalbum = Strings::escapeTags(trim($_POST['albumname']));
 		if ($newalbum != $album) {
 			q("UPDATE `photo` SET `album` = '%s' WHERE `album` = '%s' AND `uid` = %d",
 				DBA::escape($newalbum),
@@ -365,11 +366,11 @@ function photos_post(App $a)
 	}
 
 	if ($a->argc > 2 && (!empty($_POST['desc']) || !empty($_POST['newtag']) || !empty($_POST['albname']) !== false)) {
-		$desc        = !empty($_POST['desc'])      ? notags(trim($_POST['desc']))      : '';
-		$rawtags     = !empty($_POST['newtag'])    ? notags(trim($_POST['newtag']))    : '';
+		$desc        = !empty($_POST['desc'])      ? Strings::escapeTags(trim($_POST['desc']))      : '';
+		$rawtags     = !empty($_POST['newtag'])    ? Strings::escapeTags(trim($_POST['newtag']))    : '';
 		$item_id     = !empty($_POST['item_id'])   ? intval($_POST['item_id'])         : 0;
-		$albname     = !empty($_POST['albname'])   ? notags(trim($_POST['albname']))   : '';
-		$origaname   = !empty($_POST['origaname']) ? notags(trim($_POST['origaname'])) : '';
+		$albname     = !empty($_POST['albname'])   ? Strings::escapeTags(trim($_POST['albname']))   : '';
+		$origaname   = !empty($_POST['origaname']) ? Strings::escapeTags(trim($_POST['origaname'])) : '';
 
 		$str_group_allow   = !empty($_POST['group_allow'])   ? perms2str($_POST['group_allow'])   : '';
 		$str_contact_allow = !empty($_POST['contact_allow']) ? perms2str($_POST['contact_allow']) : '';
@@ -524,7 +525,7 @@ function photos_post(App $a)
 			}
 
 			$taginfo = [];
-			$tags = get_tags($rawtags);
+			$tags = BBCode::getTags($rawtags);
 
 			if (count($tags)) {
 				foreach ($tags as $tag) {
@@ -707,8 +708,8 @@ function photos_post(App $a)
 	Addon::callHooks('photo_post_init', $_POST);
 
 	// Determine the album to use
-	$album    = !empty($_REQUEST['album'])    ? notags(trim($_REQUEST['album']))    : '';
-	$newalbum = !empty($_REQUEST['newalbum']) ? notags(trim($_REQUEST['newalbum'])) : '';
+	$album    = !empty($_REQUEST['album'])    ? Strings::escapeTags(trim($_REQUEST['album']))    : '';
+	$newalbum = !empty($_REQUEST['newalbum']) ? Strings::escapeTags(trim($_REQUEST['newalbum'])) : '';
 
 	Logger::log('mod/photos.php: photos_post(): album= ' . $album . ' newalbum= ' . $newalbum , Logger::DEBUG);
 
@@ -779,7 +780,7 @@ function photos_post(App $a)
 				notice(L10n::t('Image exceeds size limit of %s', ini_get('upload_max_filesize')) . EOL);
 				break;
 			case UPLOAD_ERR_FORM_SIZE:
-				notice(L10n::t('Image exceeds size limit of %s', formatBytes(defaults($_REQUEST, 'MAX_FILE_SIZE', 0))) . EOL);
+				notice(L10n::t('Image exceeds size limit of %s', Strings::formatBytes(defaults($_REQUEST, 'MAX_FILE_SIZE', 0))) . EOL);
 				break;
 			case UPLOAD_ERR_PARTIAL:
 				notice(L10n::t('Image upload didn\'t complete, please try again') . EOL);
@@ -808,7 +809,7 @@ function photos_post(App $a)
 	$maximagesize = Config::get('system', 'maximagesize');
 
 	if ($maximagesize && ($filesize > $maximagesize)) {
-		notice(L10n::t('Image exceeds size limit of %s', formatBytes($maximagesize)) . EOL);
+		notice(L10n::t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)) . EOL);
 		@unlink($src);
 		$foo = 0;
 		Addon::callHooks('photo_post_end', $foo);
diff --git a/mod/poco.php b/mod/poco.php
index 08677ef8d..cc2493c08 100644
--- a/mod/poco.php
+++ b/mod/poco.php
@@ -15,6 +15,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 function poco_init(App $a) {
@@ -25,7 +26,7 @@ function poco_init(App $a) {
 	}
 
 	if ($a->argc > 1) {
-		$user = notags(trim($a->argv[1]));
+		$user = Strings::escapeTags(trim($a->argv[1]));
 	}
 	if (empty($user)) {
 		$c = q("SELECT * FROM `pconfig` WHERE `cat` = 'system' AND `k` = 'suggestme' AND `v` = 1");
diff --git a/mod/poke.php b/mod/poke.php
index e8c43213a..6f09b348c 100644
--- a/mod/poke.php
+++ b/mod/poke.php
@@ -22,6 +22,7 @@ use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/items.php';
@@ -38,7 +39,7 @@ function poke_init(App $a)
 		return;
 	}
 
-	$verb = notags(trim($_GET['verb']));
+	$verb = Strings::escapeTags(trim($_GET['verb']));
 
 	$verbs = L10n::getPokeVerbs();
 
diff --git a/mod/profile.php b/mod/profile.php
index cfbe07dad..59f3bb5ae 100644
--- a/mod/profile.php
+++ b/mod/profile.php
@@ -24,6 +24,7 @@ use Friendica\Protocol\ActivityPub;
 use Friendica\Protocol\DFRN;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 function profile_init(App $a)
@@ -114,9 +115,9 @@ function profile_content(App $a, $update = 0)
 		for ($x = 2; $x < $a->argc; $x ++) {
 			if (is_a_date_arg($a->argv[$x])) {
 				if ($datequery) {
-					$datequery2 = escape_tags($a->argv[$x]);
+					$datequery2 = Strings::escapeHtml($a->argv[$x]);
 				} else {
-					$datequery = escape_tags($a->argv[$x]);
+					$datequery = Strings::escapeHtml($a->argv[$x]);
 				}
 			} else {
 				$category = $a->argv[$x];
@@ -193,7 +194,7 @@ function profile_content(App $a, $update = 0)
 	if (!$update) {
 		$tab = false;
 		if (!empty($_GET['tab'])) {
-			$tab = notags(trim($_GET['tab']));
+			$tab = Strings::escapeTags(trim($_GET['tab']));
 		}
 
 		$o .= Profile::getTabs($a, $is_owner, $a->profile['nickname']);
@@ -272,19 +273,19 @@ function profile_content(App $a, $update = 0)
 
 		if (!empty($category)) {
 			$sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ",
-				DBA::escape(protect_sprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['profile_uid']));
+				DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['profile_uid']));
 		}
 
 		if (!empty($hashtags)) {
 			$sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ",
-				DBA::escape(protect_sprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['profile_uid']));
+				DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['profile_uid']));
 		}
 
 		if (!empty($datequery)) {
-			$sql_extra2 .= protect_sprintf(sprintf(" AND `thread`.`created` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
+			$sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
 		}
 		if (!empty($datequery2)) {
-			$sql_extra2 .= protect_sprintf(sprintf(" AND `thread`.`created` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
+			$sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
 		}
 
 		// Does the profile page belong to a forum?
diff --git a/mod/profile_photo.php b/mod/profile_photo.php
index 3304e3cab..d8e236a73 100644
--- a/mod/profile_photo.php
+++ b/mod/profile_photo.php
@@ -16,6 +16,7 @@ use Friendica\Model\Photo;
 use Friendica\Model\Profile;
 use Friendica\Object\Image;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 
 function profile_photo_init(App $a)
 {
@@ -151,7 +152,7 @@ function profile_photo_post(App $a)
 	$maximagesize = Config::get('system', 'maximagesize');
 
 	if (($maximagesize) && ($filesize > $maximagesize)) {
-		notice(L10n::t('Image exceeds size limit of %s', formatBytes($maximagesize)) . EOL);
+		notice(L10n::t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)) . EOL);
 		@unlink($src);
 		return;
 	}
diff --git a/mod/profiles.php b/mod/profiles.php
index a535f2fc2..459a1c5e7 100644
--- a/mod/profiles.php
+++ b/mod/profiles.php
@@ -22,6 +22,7 @@ use Friendica\Model\Profile;
 use Friendica\Module\Login;
 use Friendica\Network\Probe;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 function profiles_init(App $a) {
@@ -201,13 +202,13 @@ function profiles_post(App $a) {
 
 		$is_default = (($orig[0]['is-default']) ? 1 : 0);
 
-		$profile_name = notags(trim($_POST['profile_name']));
+		$profile_name = Strings::escapeTags(trim($_POST['profile_name']));
 		if (! strlen($profile_name)) {
 			notice(L10n::t('Profile Name is required.') . EOL);
 			return;
 		}
 
-		$dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00';
+		$dob = $_POST['dob'] ? Strings::escapeHtml(trim($_POST['dob'])) : '0000-00-00';
 
 		$y = substr($dob, 0, 4);
 		if ((! ctype_digit($y)) || ($y < 1900)) {
@@ -228,7 +229,7 @@ function profiles_post(App $a) {
 			}
 		}
 
-		$name = notags(trim($_POST['name']));
+		$name = Strings::escapeTags(trim($_POST['name']));
 
 		if (! strlen($name)) {
 			$name = '[No Name]';
@@ -238,19 +239,19 @@ function profiles_post(App $a) {
 			$namechanged = true;
 		}
 
-		$pdesc = notags(trim($_POST['pdesc']));
-		$gender = notags(trim($_POST['gender']));
-		$address = notags(trim($_POST['address']));
-		$locality = notags(trim($_POST['locality']));
-		$region = notags(trim($_POST['region']));
-		$postal_code = notags(trim($_POST['postal_code']));
-		$country_name = notags(trim($_POST['country_name']));
-		$pub_keywords = profile_clean_keywords(notags(trim($_POST['pub_keywords'])));
-		$prv_keywords = profile_clean_keywords(notags(trim($_POST['prv_keywords'])));
-		$marital = notags(trim($_POST['marital']));
-		$howlong = notags(trim($_POST['howlong']));
+		$pdesc = Strings::escapeTags(trim($_POST['pdesc']));
+		$gender = Strings::escapeTags(trim($_POST['gender']));
+		$address = Strings::escapeTags(trim($_POST['address']));
+		$locality = Strings::escapeTags(trim($_POST['locality']));
+		$region = Strings::escapeTags(trim($_POST['region']));
+		$postal_code = Strings::escapeTags(trim($_POST['postal_code']));
+		$country_name = Strings::escapeTags(trim($_POST['country_name']));
+		$pub_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['pub_keywords'])));
+		$prv_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['prv_keywords'])));
+		$marital = Strings::escapeTags(trim($_POST['marital']));
+		$howlong = Strings::escapeTags(trim($_POST['howlong']));
 
-		$with = ((x($_POST,'with')) ? notags(trim($_POST['with'])) : '');
+		$with = ((x($_POST,'with')) ? Strings::escapeTags(trim($_POST['with'])) : '');
 
 		if (! strlen($howlong)) {
 			$howlong = DBA::NULL_DATETIME;
@@ -311,30 +312,30 @@ function profiles_post(App $a) {
 		}
 
 		/// @TODO Not flexible enough for later expansion, let's have more OOP here
-		$sexual = notags(trim($_POST['sexual']));
-		$xmpp = notags(trim($_POST['xmpp']));
-		$homepage = notags(trim($_POST['homepage']));
+		$sexual = Strings::escapeTags(trim($_POST['sexual']));
+		$xmpp = Strings::escapeTags(trim($_POST['xmpp']));
+		$homepage = Strings::escapeTags(trim($_POST['homepage']));
 		if ((strpos($homepage, 'http') !== 0) && (strlen($homepage))) {
 			// neither http nor https in URL, add them
 			$homepage = 'http://'.$homepage;
 		}
-		$hometown = notags(trim($_POST['hometown']));
-		$politic = notags(trim($_POST['politic']));
-		$religion = notags(trim($_POST['religion']));
+		$hometown = Strings::escapeTags(trim($_POST['hometown']));
+		$politic = Strings::escapeTags(trim($_POST['politic']));
+		$religion = Strings::escapeTags(trim($_POST['religion']));
 
-		$likes = escape_tags(trim($_POST['likes']));
-		$dislikes = escape_tags(trim($_POST['dislikes']));
+		$likes = Strings::escapeHtml(trim($_POST['likes']));
+		$dislikes = Strings::escapeHtml(trim($_POST['dislikes']));
 
-		$about = escape_tags(trim($_POST['about']));
-		$interest = escape_tags(trim($_POST['interest']));
-		$contact = escape_tags(trim($_POST['contact']));
-		$music = escape_tags(trim($_POST['music']));
-		$book = escape_tags(trim($_POST['book']));
-		$tv = escape_tags(trim($_POST['tv']));
-		$film = escape_tags(trim($_POST['film']));
-		$romance = escape_tags(trim($_POST['romance']));
-		$work = escape_tags(trim($_POST['work']));
-		$education = escape_tags(trim($_POST['education']));
+		$about = Strings::escapeHtml(trim($_POST['about']));
+		$interest = Strings::escapeHtml(trim($_POST['interest']));
+		$contact = Strings::escapeHtml(trim($_POST['contact']));
+		$music = Strings::escapeHtml(trim($_POST['music']));
+		$book = Strings::escapeHtml(trim($_POST['book']));
+		$tv = Strings::escapeHtml(trim($_POST['tv']));
+		$film = Strings::escapeHtml(trim($_POST['film']));
+		$romance = Strings::escapeHtml(trim($_POST['romance']));
+		$work = Strings::escapeHtml(trim($_POST['work']));
+		$education = Strings::escapeHtml(trim($_POST['education']));
 
 		$hide_friends = (($_POST['hide-friends'] == 1) ? 1: 0);
 
diff --git a/mod/pubsub.php b/mod/pubsub.php
index e14d50086..f0a8d463c 100644
--- a/mod/pubsub.php
+++ b/mod/pubsub.php
@@ -6,6 +6,7 @@ use Friendica\Core\Protocol;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Protocol\OStatus;
+use Friendica\Util\Strings;
 
 require_once 'include/items.php';
 
@@ -30,15 +31,15 @@ function hub_post_return()
 
 function pubsub_init(App $a)
 {
-	$nick       = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
+	$nick       = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : '');
 	$contact_id = (($a->argc > 2) ? intval($a->argv[2])       : 0 );
 
 	if ($_SERVER['REQUEST_METHOD'] === 'GET') {
-		$hub_mode      = notags(trim(defaults($_GET, 'hub_mode', '')));
-		$hub_topic     = notags(trim(defaults($_GET, 'hub_topic', '')));
-		$hub_challenge = notags(trim(defaults($_GET, 'hub_challenge', '')));
-		$hub_lease     = notags(trim(defaults($_GET, 'hub_lease_seconds', '')));
-		$hub_verify    = notags(trim(defaults($_GET, 'hub_verify_token', '')));
+		$hub_mode      = Strings::escapeTags(trim(defaults($_GET, 'hub_mode', '')));
+		$hub_topic     = Strings::escapeTags(trim(defaults($_GET, 'hub_topic', '')));
+		$hub_challenge = Strings::escapeTags(trim(defaults($_GET, 'hub_challenge', '')));
+		$hub_lease     = Strings::escapeTags(trim(defaults($_GET, 'hub_lease_seconds', '')));
+		$hub_verify    = Strings::escapeTags(trim(defaults($_GET, 'hub_verify_token', '')));
 
 		Logger::log('Subscription from ' . $_SERVER['REMOTE_ADDR'] . ' Mode: ' . $hub_mode . ' Nick: ' . $nick);
 		Logger::log('Data: ' . print_r($_GET,true), Logger::DATA);
@@ -63,7 +64,7 @@ function pubsub_init(App $a)
 			hub_return(false, '');
 		}
 
-		if (!empty($hub_topic) && !link_compare($hub_topic, $contact['poll'])) {
+		if (!empty($hub_topic) && !Strings::compareLink($hub_topic, $contact['poll'])) {
 			Logger::log('Hub topic ' . $hub_topic . ' != ' . $contact['poll']);
 			hub_return(false, '');
 		}
@@ -91,7 +92,7 @@ function pubsub_post(App $a)
 	Logger::log('Feed arrived from ' . $_SERVER['REMOTE_ADDR'] . ' for ' .  $a->cmd . ' with user-agent: ' . $_SERVER['HTTP_USER_AGENT']);
 	Logger::log('Data: ' . $xml, Logger::DATA);
 
-	$nick       = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
+	$nick       = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : '');
 	$contact_id = (($a->argc > 2) ? intval($a->argv[2])       : 0 );
 
 	$importer = DBA::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
diff --git a/mod/pubsubhubbub.php b/mod/pubsubhubbub.php
index ea27f0482..d38cbe227 100644
--- a/mod/pubsubhubbub.php
+++ b/mod/pubsubhubbub.php
@@ -7,9 +7,10 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\PushSubscriber;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 function post_var($name) {
-	return (x($_POST, $name)) ? notags(trim($_POST[$name])) : '';
+	return (x($_POST, $name)) ? Strings::escapeTags(trim($_POST[$name])) : '';
 }
 
 function pubsubhubbub_init(App $a) {
@@ -87,13 +88,13 @@ function pubsubhubbub_init(App $a) {
 
 		// sanity check that topic URLs are the same
 		$hub_topic2 = str_replace('/feed/', '/dfrn_poll/', $hub_topic);
-		if (!link_compare($hub_topic, $contact['poll']) && !link_compare($hub_topic2, $contact['poll'])) {
+		if (!Strings::compareLink($hub_topic, $contact['poll']) && !Strings::compareLink($hub_topic2, $contact['poll'])) {
 			Logger::log('Hub topic ' . $hub_topic . ' != ' . $contact['poll']);
 			System::httpExit(404);
 		}
 
 		// do subscriber verification according to the PuSH protocol
-		$hub_challenge = random_string(40);
+		$hub_challenge = Strings::getRandomHex(40);
 		$params = 'hub.mode=' .
 			($subscribe == 1 ? 'subscribe' : 'unsubscribe') .
 			'&hub.topic=' . urlencode($hub_topic) .
diff --git a/mod/redir.php b/mod/redir.php
index 088a5f55e..f22af545f 100644
--- a/mod/redir.php
+++ b/mod/redir.php
@@ -8,6 +8,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Profile;
+use Friendica\Util\Strings;
 
 function redir_init(App $a) {
 
@@ -93,7 +94,7 @@ function redir_init(App $a) {
 				$dfrn_id = '0:' . $orig_id;
 			}
 
-			$sec = random_string();
+			$sec = Strings::getRandomHex();
 
 			$fields = ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
 				'sec' => $sec, 'expire' => time() + 45];
@@ -115,7 +116,7 @@ function redir_init(App $a) {
 	if (!empty($url)) {
 		$my_profile = Profile::getMyURL();
 
-		if (!empty($my_profile) && !link_compare($my_profile, $url)) {
+		if (!empty($my_profile) && !Strings::compareLink($my_profile, $url)) {
 			$separator = strpos($url, '?') ? '&' : '?';
 
 			$url .= $separator . 'zrl=' . urlencode($my_profile);
diff --git a/mod/register.php b/mod/register.php
index d8231bd21..2b0d87ca9 100644
--- a/mod/register.php
+++ b/mod/register.php
@@ -16,6 +16,7 @@ use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Model;
 use Friendica\Module\Tos;
+use Friendica\Util\Strings;
 
 require_once 'include/enotify.php';
 
@@ -83,7 +84,7 @@ function register_post(App $a)
 
 	$using_invites = Config::get('system', 'invitation_only');
 	$num_invites   = Config::get('system', 'number_invites');
-	$invite_id = ((x($_POST, 'invite_id')) ? notags(trim($_POST['invite_id'])) : '');
+	$invite_id = ((x($_POST, 'invite_id')) ? Strings::escapeTags(trim($_POST['invite_id'])) : '');
 
 	if (intval(Config::get('config', 'register_policy')) === REGISTER_OPEN) {
 		if ($using_invites && $invite_id) {
diff --git a/mod/removeme.php b/mod/removeme.php
index c2ceb7d4c..44671ef07 100644
--- a/mod/removeme.php
+++ b/mod/removeme.php
@@ -68,7 +68,7 @@ function removeme_content(App $a)
 		$a->internalRedirect();
 	}
 
-	$hash = random_string();
+	$hash = Strings::getRandomHex();
 
 	require_once("mod/settings.php");
 	settings_init($a);
diff --git a/mod/salmon.php b/mod/salmon.php
index 23e4e8884..02339c777 100644
--- a/mod/salmon.php
+++ b/mod/salmon.php
@@ -12,6 +12,7 @@ use Friendica\Model\Contact;
 use Friendica\Protocol\OStatus;
 use Friendica\Protocol\Salmon;
 use Friendica\Util\Crypto;
+use Friendica\Util\Strings;
 
 require_once 'include/items.php';
 
@@ -23,7 +24,7 @@ function salmon_post(App $a, $xml = '') {
 
 	Logger::log('new salmon ' . $xml, Logger::DATA);
 
-	$nick       = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
+	$nick       = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : '');
 	$mentions   = (($a->argc > 2 && $a->argv[2] === 'mention') ? true : false);
 
 	$r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1",
@@ -57,7 +58,7 @@ function salmon_post(App $a, $xml = '') {
 	// Stash the signature away for now. We have to find their key or it won't be good for anything.
 
 
-	$signature = base64url_decode($base->sig);
+	$signature = Strings::base64UrlDecode($base->sig);
 
 	// unpack the  data
 
@@ -76,13 +77,13 @@ function salmon_post(App $a, $xml = '') {
 
 	$stnet_signed_data = $data;
 
-	$signed_data = $data  . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg);
+	$signed_data = $data  . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
 
 	$compliant_format = str_replace('=', '', $signed_data);
 
 
 	// decode the data
-	$data = base64url_decode($data);
+	$data = Strings::base64UrlDecode($data);
 
 	$author = OStatus::salmonAuthor($data, $importer);
 	$author_link = $author["author-link"];
@@ -105,8 +106,8 @@ function salmon_post(App $a, $xml = '') {
 
 	$key_info = explode('.',$key);
 
-	$m = base64url_decode($key_info[1]);
-	$e = base64url_decode($key_info[2]);
+	$m = Strings::base64UrlDecode($key_info[1]);
+	$e = Strings::base64UrlDecode($key_info[2]);
 
 	Logger::log('key details: ' . print_r($key_info,true), Logger::DEBUG);
 
@@ -149,9 +150,9 @@ function salmon_post(App $a, $xml = '') {
 						AND `uid` = %d LIMIT 1",
 		DBA::escape(Protocol::OSTATUS),
 		DBA::escape(Protocol::DFRN),
-		DBA::escape(normalise_link($author_link)),
+		DBA::escape(Strings::normaliseLink($author_link)),
 		DBA::escape($author_link),
-		DBA::escape(normalise_link($author_link)),
+		DBA::escape(Strings::normaliseLink($author_link)),
 		intval($importer['uid'])
 	);
 
diff --git a/mod/search.php b/mod/search.php
index 809a6f7e8..f552ad43c 100644
--- a/mod/search.php
+++ b/mod/search.php
@@ -16,6 +16,7 @@ use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
+use Friendica\Util\Strings;
 
 require_once 'include/conversation.php';
 require_once 'mod/dirfind.php';
@@ -23,7 +24,7 @@ require_once 'mod/dirfind.php';
 function search_saved_searches() {
 
 	$o = '';
-	$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
+	$search = ((x($_GET,'search')) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
 
 	if (!Feature::isEnabled(local_user(),'savedsearch'))
 		return $o;
@@ -62,7 +63,7 @@ function search_saved_searches() {
 
 function search_init(App $a) {
 
-	$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
+	$search = ((x($_GET,'search')) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
 
 	if (local_user()) {
 		if (x($_GET,'save') && $search) {
@@ -149,14 +150,14 @@ function search_content(App $a) {
 
 	$search = '';
 	if (x($a->data,'search'))
-		$search = notags(trim($a->data['search']));
+		$search = Strings::escapeTags(trim($a->data['search']));
 	else
-		$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
+		$search = ((x($_GET,'search')) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
 
 	$tag = false;
 	if (x($_GET,'tag')) {
 		$tag = true;
-		$search = (x($_GET,'tag') ? '#' . notags(trim(rawurldecode($_GET['tag']))) : '');
+		$search = (x($_GET,'tag') ? '#' . Strings::escapeTags(trim(rawurldecode($_GET['tag']))) : '');
 	}
 
 	// contruct a wrapper for the search header
diff --git a/mod/settings.php b/mod/settings.php
index d88628840..857012048 100644
--- a/mod/settings.php
+++ b/mod/settings.php
@@ -25,6 +25,7 @@ use Friendica\Model\User;
 use Friendica\Module\Login;
 use Friendica\Protocol\Email;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 function get_theme_config_file($theme)
@@ -314,8 +315,8 @@ function settings_post(App $a)
 	if (($a->argc > 1) && ($a->argv[1] === 'display')) {
 		BaseModule::checkFormSecurityTokenRedirectOnError('/settings/display', 'settings_display');
 
-		$theme             = x($_POST, 'theme')             ? notags(trim($_POST['theme']))        : $a->user['theme'];
-		$mobile_theme      = x($_POST, 'mobile_theme')      ? notags(trim($_POST['mobile_theme'])) : '';
+		$theme             = x($_POST, 'theme')             ? Strings::escapeTags(trim($_POST['theme']))        : $a->user['theme'];
+		$mobile_theme      = x($_POST, 'mobile_theme')      ? Strings::escapeTags(trim($_POST['mobile_theme'])) : '';
 		$nosmile           = x($_POST, 'nosmile')           ? intval($_POST['nosmile'])            : 0;
 		$first_day_of_week = x($_POST, 'first_day_of_week') ? intval($_POST['first_day_of_week'])  : 0;
 		$noinfo            = x($_POST, 'noinfo')            ? intval($_POST['noinfo'])             : 0;
@@ -422,13 +423,13 @@ function settings_post(App $a)
 		}
 	}
 
-	$username         = ((x($_POST, 'username'))   ? notags(trim($_POST['username']))     : '');
-	$email            = ((x($_POST, 'email'))      ? notags(trim($_POST['email']))        : '');
-	$timezone         = ((x($_POST, 'timezone'))   ? notags(trim($_POST['timezone']))     : '');
-	$language         = ((x($_POST, 'language'))   ? notags(trim($_POST['language']))     : '');
+	$username         = ((x($_POST, 'username'))   ? Strings::escapeTags(trim($_POST['username']))     : '');
+	$email            = ((x($_POST, 'email'))      ? Strings::escapeTags(trim($_POST['email']))        : '');
+	$timezone         = ((x($_POST, 'timezone'))   ? Strings::escapeTags(trim($_POST['timezone']))     : '');
+	$language         = ((x($_POST, 'language'))   ? Strings::escapeTags(trim($_POST['language']))     : '');
 
-	$defloc           = ((x($_POST, 'defloc'))     ? notags(trim($_POST['defloc']))       : '');
-	$openid           = ((x($_POST, 'openid_url')) ? notags(trim($_POST['openid_url']))   : '');
+	$defloc           = ((x($_POST, 'defloc'))     ? Strings::escapeTags(trim($_POST['defloc']))       : '');
+	$openid           = ((x($_POST, 'openid_url')) ? Strings::escapeTags(trim($_POST['openid_url']))   : '');
 	$maxreq           = ((x($_POST, 'maxreq'))     ? intval($_POST['maxreq'])             : 0);
 	$expire           = ((x($_POST, 'expire'))     ? intval($_POST['expire'])             : 0);
 	$def_gid          = ((x($_POST, 'group-selection')) ? intval($_POST['group-selection']) : 0);
@@ -516,7 +517,7 @@ function settings_post(App $a)
 			$email = $a->user['email'];
 		}
 		//  check the email is valid
-		if (!valid_email($email)) {
+		if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
 			$err .= L10n::t('Invalid email.');
 		}
 		//  ensure new email is not the admin mail
@@ -544,7 +545,7 @@ function settings_post(App $a)
 	$str_contact_deny  = !empty($_POST['contact_deny'])  ? perms2str($_POST['contact_deny'])  : '';
 
 	$openidserver = $a->user['openidserver'];
-	//$openid = normalise_openid($openid);
+	//$openid = Strings::normaliseOpenID($openid);
 
 	// If openid has changed or if there's an openid but no openidserver, try and discover it.
 	if ($openid != $a->user['openid'] || (strlen($openid) && (!strlen($openidserver)))) {
diff --git a/mod/subthread.php b/mod/subthread.php
index 425306b6f..b287957b2 100644
--- a/mod/subthread.php
+++ b/mod/subthread.php
@@ -10,6 +10,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/items.php';
@@ -22,7 +23,7 @@ function subthread_content(App $a) {
 
 	$activity = ACTIVITY_FOLLOW;
 
-	$item_id = (($a->argc > 1) ? notags(trim($a->argv[1])) : 0);
+	$item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0);
 
 	$condition = ["`parent` = ? OR `parent-uri` = ? AND `parent` = `id`", $item_id, $item_id];
 	$item = Item::selectFirst([], $condition);
diff --git a/mod/tagger.php b/mod/tagger.php
index dd859e61c..6c3c6157a 100644
--- a/mod/tagger.php
+++ b/mod/tagger.php
@@ -10,6 +10,7 @@ use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/items.php';
@@ -20,7 +21,7 @@ function tagger_content(App $a) {
 		return;
 	}
 
-	$term = notags(trim($_GET['term']));
+	$term = Strings::escapeTags(trim($_GET['term']));
 	// no commas allowed
 	$term = str_replace([',',' '],['','_'],$term);
 
@@ -28,7 +29,7 @@ function tagger_content(App $a) {
 		return;
 	}
 
-	$item_id = (($a->argc > 1) ? notags(trim($a->argv[1])) : 0);
+	$item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0);
 
 	Logger::log('tagger: tag ' . $term . ' item ' . $item_id);
 
diff --git a/mod/tagrm.php b/mod/tagrm.php
index 2678748de..3785d8750 100644
--- a/mod/tagrm.php
+++ b/mod/tagrm.php
@@ -9,6 +9,7 @@ use Friendica\Core\L10n;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
 use Friendica\Model\Term;
+use Friendica\Util\Strings;
 
 function tagrm_post(App $a)
 {
@@ -22,7 +23,7 @@ function tagrm_post(App $a)
 
 	$tags = [];
 	foreach (defaults($_POST, 'tag', []) as $tag) {
-		$tags[] = hex2bin(notags(trim($tag)));
+		$tags[] = hex2bin(Strings::escapeTags(trim($tag)));
 	}
 
 	$item_id = defaults($_POST,'item', 0);
@@ -73,7 +74,7 @@ function tagrm_content(App $a)
 	}
 
 	if ($a->argc == 3) {
-		update_tags($a->argv[1], [notags(trim(hex2bin($a->argv[2])))]);
+		update_tags($a->argv[1], [Strings::escapeTags(trim(hex2bin($a->argv[2])))]);
 		$a->internalRedirect($_SESSION['photo_return']);
 	}
 
diff --git a/mod/unfollow.php b/mod/unfollow.php
index 372364810..9b1049286 100644
--- a/mod/unfollow.php
+++ b/mod/unfollow.php
@@ -12,6 +12,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Model\User;
+use Friendica\Util\Strings;
 
 function unfollow_post(App $a)
 {
@@ -24,11 +25,11 @@ function unfollow_post(App $a)
 	}
 
 	$uid = local_user();
-	$url = notags(trim(defaults($_REQUEST, 'url', '')));
+	$url = Strings::escapeTags(trim(defaults($_REQUEST, 'url', '')));
 
 	$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
-		$uid, Contact::SHARING, Contact::FRIEND, normalise_link($url),
-		normalise_link($url), $url];
+		$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
+		Strings::normaliseLink($url), $url];
 	$contact = DBA::selectFirst('contact', [], $condition);
 
 	if (!DBA::isResult($contact)) {
@@ -79,11 +80,11 @@ function unfollow_content(App $a)
 	}
 
 	$uid = local_user();
-	$url = notags(trim($_REQUEST['url']));
+	$url = Strings::escapeTags(trim($_REQUEST['url']));
 
 	$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
-		local_user(), Contact::SHARING, Contact::FRIEND, normalise_link($url),
-		normalise_link($url), $url];
+		local_user(), Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
+		Strings::normaliseLink($url), $url];
 
 	$contact = DBA::selectFirst('contact', ['url', 'network', 'addr', 'name'], $condition);
 
diff --git a/mod/wall_attach.php b/mod/wall_attach.php
index 2a3038e8f..b4254ba64 100644
--- a/mod/wall_attach.php
+++ b/mod/wall_attach.php
@@ -11,6 +11,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Mimetype;
+use Friendica\Util\Strings;
 
 function wall_attach_post(App $a) {
 
@@ -115,7 +116,7 @@ function wall_attach_post(App $a) {
 	}
 
 	if ($maxfilesize && $filesize > $maxfilesize) {
-		$msg = L10n::t('File exceeds size limit of %s', formatBytes($maxfilesize));
+		$msg = L10n::t('File exceeds size limit of %s', Strings::formatBytes($maxfilesize));
 		if ($r_json) {
 			echo json_encode(['error' => $msg]);
 		} else {
diff --git a/mod/wall_upload.php b/mod/wall_upload.php
index 84e40d3b5..3358433da 100644
--- a/mod/wall_upload.php
+++ b/mod/wall_upload.php
@@ -17,13 +17,14 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Photo;
 use Friendica\Object\Image;
+use Friendica\Util\Strings;
 
 function wall_upload_post(App $a, $desktopmode = true)
 {
 	Logger::log("wall upload: starting new upload", Logger::DEBUG);
 
 	$r_json = (x($_GET, 'response') && $_GET['response'] == 'json');
-	$album = (x($_GET, 'album') ? notags(trim($_GET['album'])) : '');
+	$album = (x($_GET, 'album') ? Strings::escapeTags(trim($_GET['album'])) : '');
 
 	if ($a->argc > 1) {
 		if (!x($_FILES, 'media')) {
@@ -193,7 +194,7 @@ function wall_upload_post(App $a, $desktopmode = true)
 	$maximagesize = Config::get('system', 'maximagesize');
 
 	if (($maximagesize) && ($filesize > $maximagesize)) {
-		$msg = L10n::t('Image exceeds size limit of %s', formatBytes($maximagesize));
+		$msg = L10n::t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize));
 		if ($r_json) {
 			echo json_encode(['error' => $msg]);
 		} else {
diff --git a/mod/wallmessage.php b/mod/wallmessage.php
index 78cdd5a55..b7a62b3ad 100644
--- a/mod/wallmessage.php
+++ b/mod/wallmessage.php
@@ -10,6 +10,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Mail;
 use Friendica\Model\Profile;
+use Friendica\Util\Strings;
 
 function wallmessage_post(App $a) {
 
@@ -19,10 +20,10 @@ function wallmessage_post(App $a) {
 		return;
 	}
 
-	$subject   = ((x($_REQUEST,'subject'))   ? notags(trim($_REQUEST['subject']))   : '');
-	$body      = ((x($_REQUEST,'body'))      ? escape_tags(trim($_REQUEST['body'])) : '');
+	$subject   = ((x($_REQUEST,'subject'))   ? Strings::escapeTags(trim($_REQUEST['subject']))   : '');
+	$body      = ((x($_REQUEST,'body'))      ? Strings::escapeHtml(trim($_REQUEST['body'])) : '');
 
-	$recipient = (($a->argc > 1) ? notags($a->argv[1]) : '');
+	$recipient = (($a->argc > 1) ? Strings::escapeTags($a->argv[1]) : '');
 	if ((! $recipient) || (! $body)) {
 		return;
 	}
@@ -131,7 +132,7 @@ function wallmessage_content(App $a) {
 		'$recipname' => $user['username'],
 		'$nickname' => $user['nickname'],
 		'$subjtxt' => ((x($_REQUEST, 'subject')) ? strip_tags($_REQUEST['subject']) : ''),
-		'$text' => ((x($_REQUEST, 'body')) ? escape_tags(htmlspecialchars($_REQUEST['body'])) : ''),
+		'$text' => ((x($_REQUEST, 'body')) ? Strings::escapeHtml(htmlspecialchars($_REQUEST['body'])) : ''),
 		'$readonly' => '',
 		'$yourmessage' => L10n::t('Your message:'),
 		'$parent' => '',
diff --git a/mod/xrd.php b/mod/xrd.php
index 83f069d14..4b9b0ee8f 100644
--- a/mod/xrd.php
+++ b/mod/xrd.php
@@ -9,6 +9,7 @@ use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Protocol\Salmon;
+use Friendica\Util\Strings;
 
 function xrd_init(App $a)
 {
@@ -17,7 +18,7 @@ function xrd_init(App $a)
 			System::httpExit(404);
 		}
 
-		$uri = urldecode(notags(trim($_GET['uri'])));
+		$uri = urldecode(Strings::escapeTags(trim($_GET['uri'])));
 		if (defaults($_SERVER, 'HTTP_ACCEPT', '') == 'application/jrd+json') {
 			$mode = 'json';
 		} else {
@@ -28,7 +29,7 @@ function xrd_init(App $a)
 			System::httpExit(404);
 		}
 
-		$uri = urldecode(notags(trim($_GET['resource'])));
+		$uri = urldecode(Strings::escapeTags(trim($_GET['resource'])));
 		if (defaults($_SERVER, 'HTTP_ACCEPT', '') == 'application/xrd+xml') {
 			$mode = 'xml';
 		} else {
diff --git a/spec/dfrn2_contact_request.svg b/spec/dfrn2_contact_request.svg
index cc78be55c..34de340f3 100644
--- a/spec/dfrn2_contact_request.svg
+++ b/spec/dfrn2_contact_request.svg
@@ -38,7 +38,7 @@ text { font:12px Dialog; }
 <text x="904" y="1084" style="font:13px Open Sans">where self = 0 to look if this contact is already there (if </text>
 <text x="904" y="1107" style="font:13px Open Sans">issued-id or rel is already available return here because it </text>
 <text x="904" y="1130" style="font:13px Open Sans">seems that we are already connected)</text>
-<text x="904" y="1176" style="font:13px Open Sans">- create a issued-id with $issued_id = random_string();</text>
+<text x="904" y="1176" style="font:13px Open Sans">- create a issued-id with $issued_id = Strings::getRandomHex();</text>
 <text x="904" y="1222" style="font:13px Open Sans">- if we already found a contact record above update the </text>
 <text x="904" y="1245" style="font:13px Open Sans">issued-id with the one we have created</text>
 <text x="904" y="1291" style="font:13px Open Sans">- otherwise if Bob is not already in the contact table scrape </text>
diff --git a/spec/zot-2012.txt b/spec/zot-2012.txt
index 2e1f3c3c4..3d939bab7 100644
--- a/spec/zot-2012.txt
+++ b/spec/zot-2012.txt
@@ -11,12 +11,12 @@ First create a global unique userid
 Site userid:
 https://macgirvin.com/1
 
-$guuid = base64url_encode(hash('whirlpool','https://macgirvin.com/1.' . mt_rand(1000000,9999999),1);
+$guuid = Strings::base64UrlEncode(hash('whirlpool','https://macgirvin.com/1.' . mt_rand(1000000,9999999),1);
 
 
 Then create a hashed site destination.
 
-$gduid = base64url_encode(hash('whirlpool', $guuid . 'https://macgirvin.com',1);
+$gduid = Strings::base64UrlEncode(hash('whirlpool', $guuid . 'https://macgirvin.com',1);
 
 These two keys will identify you as a person+site pair in the future.
 You will also obtain a password upon introducing yourself to a site.
diff --git a/src/App.php b/src/App.php
index cf8fc7abe..12f302f06 100644
--- a/src/App.php
+++ b/src/App.php
@@ -816,12 +816,12 @@ class App
 	public function removeBaseURL($origURL)
 	{
 		// Remove the hostname from the url if it is an internal link
-		$nurl = normalise_link($origURL);
-		$base = normalise_link($this->getBaseURL());
+		$nurl = Util\Strings::normaliseLink($origURL);
+		$base = Util\Strings::normaliseLink($this->getBaseURL());
 		$url = str_replace($base . '/', '', $nurl);
 
 		// if it is an external link return the orignal value
-		if ($url == normalise_link($origURL)) {
+		if ($url == Util\Strings::normaliseLink($origURL)) {
 			return $origURL;
 		} else {
 			return $url;
@@ -1443,7 +1443,7 @@ class App
 		// and www.example.com vs example.com.
 		// We will only change the url to an ip address if there is no existing setting
 
-		if (empty($url) || (!link_compare($url, $this->getBaseURL())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $this->getHostName()))) {
+		if (empty($url) || (!Util\Strings::compareLink($url, $this->getBaseURL())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $this->getHostName()))) {
 			Core\Config::set('system', 'url', $this->getBaseURL());
 		}
 	}
diff --git a/src/Content/ContactSelector.php b/src/Content/ContactSelector.php
index 298f2512e..a23acecc5 100644
--- a/src/Content/ContactSelector.php
+++ b/src/Content/ContactSelector.php
@@ -10,6 +10,7 @@ use Friendica\Core\Protocol;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 /**
  * @brief ContactSelector class
@@ -106,12 +107,12 @@ class ContactSelector
 			// Create the server url out of the profile url
 			$parts = parse_url($profile);
 			unset($parts['path']);
-			$server_url = [normalise_link(Network::unparseURL($parts))];
+			$server_url = [Strings::normaliseLink(Network::unparseURL($parts))];
 
 			// Fetch the server url
-			$gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => normalise_link($profile)]);
+			$gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]);
 			if (!empty($gcontact) && !empty($gcontact['server_url'])) {
-				$server_url[] = normalise_link($gcontact['server_url']);
+				$server_url[] = Strings::normaliseLink($gcontact['server_url']);
 			}
 
 			// Now query the GServer for the platform name
diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php
index 943b91e1f..c77db3827 100644
--- a/src/Content/OEmbed.php
+++ b/src/Content/OEmbed.php
@@ -21,6 +21,7 @@ use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\ParseUrl;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 
@@ -61,7 +62,7 @@ class OEmbed
 
 		$cache_key = 'oembed:' . $a->videowidth . ':' . $embedurl;
 
-		$condition = ['url' => normalise_link($embedurl), 'maxwidth' => $a->videowidth];
+		$condition = ['url' => Strings::normaliseLink($embedurl), 'maxwidth' => $a->videowidth];
 		$oembed_record = DBA::selectFirst('oembed', ['content'], $condition);
 		if (DBA::isResult($oembed_record)) {
 			$json_string = $oembed_record['content'];
@@ -116,7 +117,7 @@ class OEmbed
 
 			if (!empty($oembed->type) && $oembed->type != 'error') {
 				DBA::insert('oembed', [
-					'url' => normalise_link($embedurl),
+					'url' => Strings::normaliseLink($embedurl),
 					'maxwidth' => $a->videowidth,
 					'content' => $json_string,
 					'created' => DateTimeFormat::utcNow()
@@ -373,7 +374,7 @@ class OEmbed
 		}
 		$width = '100%';
 
-		$src = System::baseUrl() . '/oembed/' . base64url_encode($src);
+		$src = System::baseUrl() . '/oembed/' . Strings::base64UrlEncode($src);
 		return '<iframe onload="resizeIframe(this);" class="embed_rich" height="' . $height . '" width="' . $width . '" src="' . $src . '" allowfullscreen scrolling="no" frameborder="no">' . L10n::t('Embedded content') . '</iframe>';
 	}
 
diff --git a/src/Content/Smilies.php b/src/Content/Smilies.php
index 39de3c20e..292841361 100644
--- a/src/Content/Smilies.php
+++ b/src/Content/Smilies.php
@@ -19,6 +19,7 @@ use Friendica\Core\Addon;
 use Friendica\Core\Config;
 use Friendica\Core\PConfig;
 use Friendica\Core\System;
+use Friendica\Util\Strings;
 
 /**
  * This class contains functions to handle smiles
@@ -241,7 +242,7 @@ class Smilies
 	 */
 	private static function encode($m)
 	{
-		return(str_replace($m[1], base64url_encode($m[1]), $m[0]));
+		return(str_replace($m[1], Strings::base64UrlEncode($m[1]), $m[0]));
 	}
 
 	/**
@@ -251,7 +252,7 @@ class Smilies
 	 */
 	private static function decode($m)
 	{
-		return(str_replace($m[1], base64url_decode($m[1]), $m[0]));
+		return(str_replace($m[1], Strings::base64UrlDecode($m[1]), $m[0]));
 	}
 
 
diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php
index 3592059d0..9c9adec0f 100644
--- a/src/Content/Text/BBCode.php
+++ b/src/Content/Text/BBCode.php
@@ -27,6 +27,7 @@ use Friendica\Util\Map;
 use Friendica\Util\Network;
 use Friendica\Util\ParseUrl;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 class BBCode extends BaseObject
 {
@@ -943,7 +944,7 @@ class BBCode extends BaseObject
 			case 3: // Diaspora
 				$headline = '<p><b>' . html_entity_decode('&#x2672; ', ENT_QUOTES, 'UTF-8') . $mention . ':</b></p>' . "\n";
 
-				if (stripos(normalise_link($attributes['link']), 'http://twitter.com/') === 0) {
+				if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0) {
 					$text = ($is_quote_share? '<hr />' : '') . '<p><a href="' . $attributes['link'] . '">' . $attributes['link'] . '</a></p>' . "\n";
 				} else {
 					$text = ($is_quote_share? '<hr />' : '') . $headline . '<blockquote>' . trim($content) . '</blockquote>' . "\n";
@@ -978,7 +979,7 @@ class BBCode extends BaseObject
 				break;
 			default:
 				// Transforms quoted tweets in rich attachments to avoid nested tweets
-				if (stripos(normalise_link($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
+				if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
 					try {
 						$text = ($is_quote_share? '<br />' : '') . OEmbed::getHTML($attributes['link']);
 					} catch (Exception $e) {
@@ -1363,7 +1364,7 @@ class BBCode extends BaseObject
 		$text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $text);
 
 		// leave open the posibility of [map=something]
-		// this is replaced in prepare_body() which has knowledge of the item location
+		// this is replaced in Item::prepareBody() which has knowledge of the item location
 
 		if (strpos($text, '[/map]') !== false) {
 			$text = preg_replace_callback(
@@ -1474,7 +1475,7 @@ class BBCode extends BaseObject
 
 		$text = str_replace('[hr]', '<hr />', $text);
 
-		// This is actually executed in prepare_body()
+		// This is actually executed in Item::prepareBody()
 
 		$text = str_replace('[nosmile]', '', $text);
 
@@ -1910,4 +1911,78 @@ class BBCode extends BaseObject
 
 		return $text;
 	}
+
+	/**
+     * @brief Pull out all #hashtags and @person tags from $string.
+     *
+     * We also get @person@domain.com - which would make
+     * the regex quite complicated as tags can also
+     * end a sentence. So we'll run through our results
+     * and strip the period from any tags which end with one.
+     * Returns array of tags found, or empty array.
+     *
+     * @param string $string Post content
+     * 
+     * @return array List of tag and person names
+     */
+    public static function getTags($string)
+    {
+        $ret = [];
+
+        // Convert hashtag links to hashtags
+        $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
+
+        // ignore anything in a code block
+        $string = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $string);
+
+        // Force line feeds at bbtags
+        $string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
+
+        // ignore anything in a bbtag
+        $string = preg_replace('/\[(.*?)\]/sm', '', $string);
+
+        // Match full names against @tags including the space between first and last
+        // We will look these up afterward to see if they are full names or not recognisable.
+
+        if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
+            foreach ($matches[1] as $match) {
+                if (strstr($match, ']')) {
+                    // we might be inside a bbcode color tag - leave it alone
+                    continue;
+                }
+
+                if (substr($match, -1, 1) === '.') {
+                    $ret[] = substr($match, 0, -1);
+                } else {
+                    $ret[] = $match;
+                }
+            }
+        }
+
+        // Otherwise pull out single word tags. These can be @nickname, @first_last
+        // and #hash tags.
+
+        if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
+            foreach ($matches[1] as $match) {
+                if (strstr($match, ']')) {
+                    // we might be inside a bbcode color tag - leave it alone
+                    continue;
+                }
+                if (substr($match, -1, 1) === '.') {
+                    $match = substr($match,0,-1);
+                }
+                // ignore strictly numeric tags like #1
+                if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
+                    continue;
+                }
+                // try not to catch url fragments
+                if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
+                    continue;
+                }
+                $ret[] = $match;
+            }
+        }
+
+        return $ret;
+    }
 }
diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php
index cdfd41c11..9b73da56d 100644
--- a/src/Content/Text/HTML.php
+++ b/src/Content/Text/HTML.php
@@ -7,6 +7,7 @@ namespace Friendica\Content\Text;
 
 use DOMDocument;
 use DOMXPath;
+use Friendica\Content\Feature;
 use Friendica\Core\Addon;
 use Friendica\Core\L10n;
 use Friendica\Core\Config;
@@ -17,9 +18,9 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use League\HTMLToMarkdown\HtmlConverter;
-use Friendica\Content\Feature;
 
 class HTML
 {
@@ -1011,7 +1012,7 @@ class HTML
 			$tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl');
 			$html = Renderer::replaceMacros($tpl, [
 				'$reasons'   => $reasons,
-				'$rnd'       => random_string(8),
+				'$rnd'       => Strings::getRandomHex(8),
 				'$openclose' => L10n::t('Click to open/close'),
 				'$html'      => $html
 			]);
diff --git a/src/Content/Widget.php b/src/Content/Widget.php
index 397a1863d..0ea539dec 100644
--- a/src/Content/Widget.php
+++ b/src/Content/Widget.php
@@ -18,6 +18,7 @@ use Friendica\Model\Contact;
 use Friendica\Model\FileTag;
 use Friendica\Model\GContact;
 use Friendica\Model\Profile;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'boot.php';
@@ -270,11 +271,11 @@ class Widget
 		if (!$cid) {
 			if (Profile::getMyURL()) {
 				$contact = DBA::selectFirst('contact', ['id'],
-						['nurl' => normalise_link(Profile::getMyURL()), 'uid' => $profile_uid]);
+						['nurl' => Strings::normaliseLink(Profile::getMyURL()), 'uid' => $profile_uid]);
 				if (DBA::isResult($contact)) {
 					$cid = $contact['id'];
 				} else {
-					$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => normalise_link(Profile::getMyURL())]);
+					$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Profile::getMyURL())]);
 					if (DBA::isResult($gcontact)) {
 						$zcid = $gcontact['id'];
 					}
diff --git a/src/Core/Authentication.php b/src/Core/Authentication.php
index 4d227c271..50825c525 100644
--- a/src/Core/Authentication.php
+++ b/src/Core/Authentication.php
@@ -12,6 +12,7 @@ use Friendica\Core\L10n;
 use Friendica\Core\Logger;
 use Friendica\Core\PConfig;
 use Friendica\Database\DBA;
+use Friendica\Model\User;
 use Friendica\Util\DateTimeFormat;
 
 /**
@@ -103,55 +104,16 @@ class Authentication extends BaseObject
 			$a->timezone = $a->user['timezone'];
 		}
 
-		$master_record = $a->user;
+		$masterUid = $user_record['uid'];
 
 		if ((x($_SESSION, 'submanage')) && intval($_SESSION['submanage'])) {
-			$user = DBA::selectFirst('user', [], ['uid' => $_SESSION['submanage']]);
+			$user = DBA::selectFirst('user', ['uid'], ['uid' => $_SESSION['submanage']]);
 			if (DBA::isResult($user)) {
-				$master_record = $user;
+				$masterUid = $user['uid'];
 			}
 		}
 
-		if ($master_record['parent-uid'] == 0) {
-			// First add our own entry
-			$a->identities = [['uid' => $master_record['uid'],
-					'username' => $master_record['username'],
-					'nickname' => $master_record['nickname']]];
-
-			// Then add all the children
-			$r = DBA::select('user', ['uid', 'username', 'nickname'],
-				['parent-uid' => $master_record['uid'], 'account_removed' => false]);
-			if (DBA::isResult($r)) {
-				$a->identities = array_merge($a->identities, DBA::toArray($r));
-			}
-		} else {
-			// Just ensure that the array is always defined
-			$a->identities = [];
-
-			// First entry is our parent
-			$r = DBA::select('user', ['uid', 'username', 'nickname'],
-				['uid' => $master_record['parent-uid'], 'account_removed' => false]);
-			if (DBA::isResult($r)) {
-				$a->identities = DBA::toArray($r);
-			}
-
-			// Then add all siblings
-			$r = DBA::select('user', ['uid', 'username', 'nickname'],
-				['parent-uid' => $master_record['parent-uid'], 'account_removed' => false]);
-			if (DBA::isResult($r)) {
-				$a->identities = array_merge($a->identities, DBA::toArray($r));
-			}
-		}
-
-		$r = DBA::p("SELECT `user`.`uid`, `user`.`username`, `user`.`nickname`
-			FROM `manage`
-			INNER JOIN `user` ON `manage`.`mid` = `user`.`uid`
-			WHERE `user`.`account_removed` = 0 AND `manage`.`uid` = ?",
-			$master_record['uid']
-		);
-		if (DBA::isResult($r)) {
-			$a->identities = array_merge($a->identities, DBA::toArray($r));
-		}
+		$a->identities = User::identities($masterUid);
 
 		if ($login_initial) {
 			Logger::log('auth_identities: ' . print_r($a->identities, true), Logger::DEBUG);
@@ -174,7 +136,7 @@ class Authentication extends BaseObject
 
 			// Set the login date for all identities of the user
 			DBA::update('user', ['login_date' => DateTimeFormat::utcNow()],
-				['parent-uid' => $master_record['uid'], 'account_removed' => false]);
+				['parent-uid' => $masterUid, 'account_removed' => false]);
 		}
 
 		if ($login_initial) {
diff --git a/src/Core/Console/ArchiveContact.php b/src/Core/Console/ArchiveContact.php
index 481037a5f..ffcd5a165 100644
--- a/src/Core/Console/ArchiveContact.php
+++ b/src/Core/Console/ArchiveContact.php
@@ -5,6 +5,7 @@ namespace Friendica\Core\Console;
 use Friendica\App;
 use Friendica\Core\L10n;
 use Friendica\Database\DBA;
+use Friendica\Util\Strings;
 use RuntimeException;
 
 /**
@@ -60,7 +61,7 @@ HELP;
 			throw new RuntimeException('Friendica isn\'t properly installed yet.');
 		}
 
-		$nurl = normalise_link($this->getArgument(0));
+		$nurl = Strings::normaliseLink($this->getArgument(0));
 		if (!DBA::exists('contact', ['nurl' => $nurl, 'archive' => false])) {
 			throw new RuntimeException(L10n::t('Could not find any unarchived contact entry for this URL (%s)', $nurl));
 		}
diff --git a/src/Core/Console/GlobalCommunitySilence.php b/src/Core/Console/GlobalCommunitySilence.php
index 3ea6b4155..01413cab1 100644
--- a/src/Core/Console/GlobalCommunitySilence.php
+++ b/src/Core/Console/GlobalCommunitySilence.php
@@ -5,6 +5,7 @@ namespace Friendica\Core\Console;
 use Friendica\Core\Protocol;
 use Friendica\Database\DBA;
 use Friendica\Network\Probe;
+use Friendica\Util\Strings;
 use RuntimeException;
 
 require_once 'include/text.php';
@@ -79,7 +80,7 @@ HELP;
 			throw new RuntimeException('This account seems not to exist.');
 		}
 
-		$nurl = normalise_link($net['url']);
+		$nurl = Strings::normaliseLink($net['url']);
 		$contact = DBA::selectFirst("contact", ["id"], ["nurl" => $nurl, "uid" => 0]);
 		if (DBA::isResult($contact)) {
 			DBA::update("contact", ["hidden" => true], ["id" => $contact["id"]]);
diff --git a/src/Core/Installer.php b/src/Core/Installer.php
index d2b5c4f05..f7190b2f8 100644
--- a/src/Core/Installer.php
+++ b/src/Core/Installer.php
@@ -11,6 +11,7 @@ use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
 use Friendica\Object\Image;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 /**
  * Contains methods for installation purpose of Friendica
@@ -264,7 +265,7 @@ class Installer
 		}
 
 		if ($passed2) {
-			$str = autoname(8);
+			$str = Strings::getRandomName(8);
 			$cmd = "$phppath util/testargs.php $str";
 			$result = trim(shell_exec($cmd));
 			$passed3 = $result == $str;
@@ -510,7 +511,7 @@ class Installer
 		if (function_exists('curl_init')) {
 			$fetchResult = Network::fetchUrlFull($baseurl . "/install/testrewrite");
 
-			$url = normalise_link($baseurl . "/install/testrewrite");
+			$url = Strings::normaliseLink($baseurl . "/install/testrewrite");
 			if ($fetchResult->getReturnCode() != 204) {
 				$fetchResult = Network::fetchUrlFull($url);
 			}
diff --git a/src/Core/Update.php b/src/Core/Update.php
index 139688335..252ea8ef3 100644
--- a/src/Core/Update.php
+++ b/src/Core/Update.php
@@ -4,6 +4,7 @@ namespace Friendica\Core;
 
 use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
+use Friendica\Util\Strings;
 
 class Update
 {
@@ -209,7 +210,7 @@ class Update
 			$lang = (($admin['language'])?$admin['language']:'en');
 			L10n::pushLang($lang);
 
-			$preamble = deindent(L10n::t("
+			$preamble = Strings::deindent(L10n::t("
 				The friendica developers released update %s recently,
 				but when I tried to install it, something went terribly wrong.
 				This needs to be fixed soon and I can't do it alone. Please contact a
@@ -244,7 +245,7 @@ class Update
 				$lang = (($admin['language']) ? $admin['language'] : 'en');
 				L10n::pushLang($lang);
 
-				$preamble = deindent(L10n::t("
+				$preamble = Strings::deindent(L10n::t("
 					The friendica database was successfully updated from %s to %s.",
 					$from_build, $to_build));
 
diff --git a/src/Core/UserImport.php b/src/Core/UserImport.php
index 7ccb6f80c..5488fe43f 100644
--- a/src/Core/UserImport.php
+++ b/src/Core/UserImport.php
@@ -10,6 +10,7 @@ use Friendica\Core\Protocol;
 use Friendica\Database\DBA;
 use Friendica\Model\Photo;
 use Friendica\Object\Image;
+use Friendica\Util\Strings;
 
 require_once "include/dba.php";
 
@@ -119,8 +120,8 @@ class UserImport
 		$oldbaseurl = $account['baseurl'];
 		$newbaseurl = System::baseUrl();
 
-		$oldaddr = str_replace('http://', '@', normalise_link($oldbaseurl));
-		$newaddr = str_replace('http://', '@', normalise_link($newbaseurl));
+		$oldaddr = str_replace('http://', '@', Strings::normaliseLink($oldbaseurl));
+		$newaddr = str_replace('http://', '@', Strings::normaliseLink($newbaseurl));
 
 		if (!empty($account['profile']['addr'])) {
 			$old_handle = $account['profile']['addr'];
diff --git a/src/Model/APContact.php b/src/Model/APContact.php
index 917e0895d..c306b3e00 100644
--- a/src/Model/APContact.php
+++ b/src/Model/APContact.php
@@ -7,13 +7,14 @@
 namespace Friendica\Model;
 
 use Friendica\BaseObject;
+use Friendica\Content\Text\HTML;
 use Friendica\Core\Logger;
 use Friendica\Database\DBA;
 use Friendica\Protocol\ActivityPub;
 use Friendica\Util\Network;
 use Friendica\Util\JsonLD;
 use Friendica\Util\DateTimeFormat;
-use Friendica\Content\Text\HTML;
+use Friendica\Util\Strings;
 
 require_once 'boot.php';
 
@@ -186,16 +187,16 @@ class APContact extends BaseObject
 
 		// Update some data in the contact table with various ways to catch them all
 		$contact_fields = ['name' => $apcontact['name'], 'about' => $apcontact['about']];
-		DBA::update('contact', $contact_fields, ['nurl' => normalise_link($url)]);
+		DBA::update('contact', $contact_fields, ['nurl' => Strings::normaliseLink($url)]);
 
-		$contacts = DBA::select('contact', ['uid', 'id'], ['nurl' => normalise_link($url)]);
+		$contacts = DBA::select('contact', ['uid', 'id'], ['nurl' => Strings::normaliseLink($url)]);
 		while ($contact = DBA::fetch($contacts)) {
 			Contact::updateAvatar($apcontact['photo'], $contact['uid'], $contact['id']);
 		}
 		DBA::close($contacts);
 
 		// Update the gcontact table
-		DBA::update('gcontact', $contact_fields, ['nurl' => normalise_link($url)]);
+		DBA::update('gcontact', $contact_fields, ['nurl' => Strings::normaliseLink($url)]);
 
 		Logger::log('Updated profile for ' . $url, Logger::DEBUG);
 
diff --git a/src/Model/Contact.php b/src/Model/Contact.php
index 5d571e30e..8e150b506 100644
--- a/src/Model/Contact.php
+++ b/src/Model/Contact.php
@@ -25,6 +25,7 @@ use Friendica\Protocol\PortableContact;
 use Friendica\Protocol\Salmon;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 require_once 'boot.php';
 require_once 'include/dba.php';
@@ -392,7 +393,7 @@ class Contact extends BaseObject
 			'blocked'     => 0,
 			'pending'     => 0,
 			'url'         => System::baseUrl() . '/profile/' . $user['nickname'],
-			'nurl'        => normalise_link(System::baseUrl() . '/profile/' . $user['nickname']),
+			'nurl'        => Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']),
 			'addr'        => $user['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3),
 			'request'     => System::baseUrl() . '/dfrn_request/' . $user['nickname'],
 			'notify'      => System::baseUrl() . '/dfrn_notify/'  . $user['nickname'],
@@ -477,7 +478,7 @@ class Contact extends BaseObject
 
 		// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
 		$fields['url'] = System::baseUrl() . '/profile/' . $user['nickname'];
-		$fields['nurl'] = normalise_link($fields['url']);
+		$fields['nurl'] = Strings::normaliseLink($fields['url']);
 		$fields['addr'] = $user['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3);
 		$fields['request'] = System::baseUrl() . '/dfrn_request/' . $user['nickname'];
 		$fields['notify'] = System::baseUrl() . '/dfrn_notify/'  . $user['nickname'];
@@ -597,7 +598,7 @@ class Contact extends BaseObject
 
 		if ($contact['term-date'] <= DBA::NULL_DATETIME) {
 			DBA::update('contact', ['term-date' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
-			DBA::update('contact', ['term-date' => DateTimeFormat::utcNow()], ['`nurl` = ? AND `term-date` <= ? AND NOT `self`', normalise_link($contact['url']), DBA::NULL_DATETIME]);
+			DBA::update('contact', ['term-date' => DateTimeFormat::utcNow()], ['`nurl` = ? AND `term-date` <= ? AND NOT `self`', Strings::normaliseLink($contact['url']), DBA::NULL_DATETIME]);
 		} else {
 			/* @todo
 			 * We really should send a notification to the owner after 2-3 weeks
@@ -615,7 +616,7 @@ class Contact extends BaseObject
 				 * the whole process over again.
 				 */
 				DBA::update('contact', ['archive' => 1], ['id' => $contact['id']]);
-				DBA::update('contact', ['archive' => 1], ['nurl' => normalise_link($contact['url']), 'self' => false]);
+				DBA::update('contact', ['archive' => 1], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
 			}
 		}
 	}
@@ -649,7 +650,7 @@ class Contact extends BaseObject
 		// It's a miracle. Our dead contact has inexplicably come back to life.
 		$fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false];
 		DBA::update('contact', $fields, ['id' => $contact['id']]);
-		DBA::update('contact', $fields, ['nurl' => normalise_link($contact['url'])]);
+		DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url'])]);
 
 		if (!empty($contact['batch'])) {
 			$condition = ['batch' => $contact['batch'], 'contact-type' => self::ACCOUNT_TYPE_RELAY];
@@ -690,14 +691,14 @@ class Contact extends BaseObject
 		// Fetch contact data from the contact table for the given user
 		$s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
 			`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
-		FROM `contact` WHERE `nurl` = ? AND `uid` = ?", normalise_link($url), $uid);
+		FROM `contact` WHERE `nurl` = ? AND `uid` = ?", Strings::normaliseLink($url), $uid);
 		$r = DBA::toArray($s);
 
 		// Fetch contact data from the contact table for the given user, checking with the alias
 		if (!DBA::isResult($r)) {
 			$s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
 				`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
-			FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", normalise_link($url), $url, $ssl_url, $uid);
+			FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", Strings::normaliseLink($url), $url, $ssl_url, $uid);
 			$r = DBA::toArray($s);
 		}
 
@@ -705,7 +706,7 @@ class Contact extends BaseObject
 		if (!DBA::isResult($r)) {
 			$s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
 			`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
-			FROM `contact` WHERE `nurl` = ? AND `uid` = 0", normalise_link($url));
+			FROM `contact` WHERE `nurl` = ? AND `uid` = 0", Strings::normaliseLink($url));
 			$r = DBA::toArray($s);
 		}
 
@@ -713,7 +714,7 @@ class Contact extends BaseObject
 		if (!DBA::isResult($r)) {
 			$s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
 			`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
-			FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", normalise_link($url), $url, $ssl_url);
+			FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", Strings::normaliseLink($url), $url, $ssl_url);
 			$r = DBA::toArray($s);
 		}
 
@@ -721,7 +722,7 @@ class Contact extends BaseObject
 		if (!DBA::isResult($r)) {
 			$s = DBA::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
 			`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
-			FROM `gcontact` WHERE `nurl` = ?", normalise_link($url));
+			FROM `gcontact` WHERE `nurl` = ?", Strings::normaliseLink($url));
 			$r = DBA::toArray($s);
 		}
 
@@ -1038,7 +1039,7 @@ class Contact extends BaseObject
 
 		/// @todo Verify if we can't use Contact::getDetailsByUrl instead of the following
 		// We first try the nurl (http://server.tld/nick), most common case
-		$contact = DBA::selectFirst('contact', ['id', 'avatar', 'avatar-date'], ['nurl' => normalise_link($url), 'uid' => $uid, 'deleted' => false]);
+		$contact = DBA::selectFirst('contact', ['id', 'avatar', 'avatar-date'], ['nurl' => Strings::normaliseLink($url), 'uid' => $uid, 'deleted' => false]);
 
 		// Then the addr (nick@server.tld)
 		if (!DBA::isResult($contact)) {
@@ -1049,7 +1050,7 @@ class Contact extends BaseObject
 		if (!DBA::isResult($contact)) {
 			// The link could be provided as http although we stored it as https
 			$ssl_url = str_replace('http://', 'https://', $url);
-			$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, normalise_link($url), $ssl_url, $uid];
+			$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, $uid];
 			$contact = DBA::selectFirst('contact', ['id', 'avatar', 'avatar-date'], $condition);
 		}
 
@@ -1076,7 +1077,7 @@ class Contact extends BaseObject
 			$fields = ['url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick',
 				'photo', 'keywords', 'location', 'about', 'network',
 				'priority', 'batch', 'request', 'confirm', 'poco'];
-			$data = DBA::selectFirst('contact', $fields, ['nurl' => normalise_link($url)]);
+			$data = DBA::selectFirst('contact', $fields, ['nurl' => Strings::normaliseLink($url)]);
 
 			if (DBA::isResult($data)) {
 				// For security reasons we don't fetch key data from our users
@@ -1103,9 +1104,9 @@ class Contact extends BaseObject
 
 			// Get data from the gcontact table
 			$fields = ['name', 'nick', 'url', 'photo', 'addr', 'alias', 'network'];
-			$contact = DBA::selectFirst('gcontact', $fields, ['nurl' => normalise_link($url)]);
+			$contact = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]);
 			if (!DBA::isResult($contact)) {
-				$contact = DBA::selectFirst('contact', $fields, ['nurl' => normalise_link($url)]);
+				$contact = DBA::selectFirst('contact', $fields, ['nurl' => Strings::normaliseLink($url)]);
 			}
 
 			if (!DBA::isResult($contact)) {
@@ -1118,14 +1119,14 @@ class Contact extends BaseObject
 			if (!DBA::isResult($contact)) {
 				// The link could be provided as http although we stored it as https
 				$ssl_url = str_replace('http://', 'https://', $url);
-				$condition = ['alias' => [$url, normalise_link($url), $ssl_url]];
+				$condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]];
 				$contact = DBA::selectFirst('contact', $fields, $condition);
 			}
 
 			if (!DBA::isResult($contact)) {
 				$fields = ['url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick',
 					'photo', 'network', 'priority', 'batch', 'request', 'confirm'];
-				$condition = ['url' => [$url, normalise_link($url), $ssl_url]];
+				$condition = ['url' => [$url, Strings::normaliseLink($url), $ssl_url]];
 				$contact = DBA::selectFirst('fcontact', $fields, $condition);
 			}
 
@@ -1150,7 +1151,7 @@ class Contact extends BaseObject
 				'uid'       => $uid,
 				'created'   => DateTimeFormat::utcNow(),
 				'url'       => $data["url"],
-				'nurl'      => normalise_link($data["url"]),
+				'nurl'      => Strings::normaliseLink($data["url"]),
 				'addr'      => $data["addr"],
 				'alias'     => $data["alias"],
 				'notify'    => $data["notify"],
@@ -1178,7 +1179,7 @@ class Contact extends BaseObject
 				'pending'   => 0]
 			);
 
-			$s = DBA::select('contact', ['id'], ['nurl' => normalise_link($data["url"]), 'uid' => $uid], ['order' => ['id'], 'limit' => 2]);
+			$s = DBA::select('contact', ['id'], ['nurl' => Strings::normaliseLink($data["url"]), 'uid' => $uid], ['order' => ['id'], 'limit' => 2]);
 			$contacts = DBA::toArray($s);
 			if (!DBA::isResult($contacts)) {
 				return 0;
@@ -1187,7 +1188,7 @@ class Contact extends BaseObject
 			$contact_id = $contacts[0]["id"];
 
 			// Update the newly created contact from data in the gcontact table
-			$gcontact = DBA::selectFirst('gcontact', ['location', 'about', 'keywords', 'gender'], ['nurl' => normalise_link($data["url"])]);
+			$gcontact = DBA::selectFirst('gcontact', ['location', 'about', 'keywords', 'gender'], ['nurl' => Strings::normaliseLink($data["url"])]);
 			if (DBA::isResult($gcontact)) {
 				// Only use the information when the probing hadn't fetched these values
 				if ($data['keywords'] != '') {
@@ -1204,7 +1205,7 @@ class Contact extends BaseObject
 
 			if (count($contacts) > 1 && $uid == 0 && $contact_id != 0 && $data["url"] != "") {
 				DBA::delete('contact', ["`nurl` = ? AND `uid` = 0 AND `id` != ? AND NOT `self`",
-					normalise_link($data["url"]), $contact_id]);
+					Strings::normaliseLink($data["url"]), $contact_id]);
 			}
 		}
 
@@ -1221,7 +1222,7 @@ class Contact extends BaseObject
 		$updated = ['addr' => $data['addr'],
 			'alias' => $data['alias'],
 			'url' => $data['url'],
-			'nurl' => normalise_link($data['url']),
+			'nurl' => Strings::normaliseLink($data['url']),
 			'name' => $data['name'],
 			'nick' => $data['nick']];
 
@@ -1543,7 +1544,7 @@ class Contact extends BaseObject
 		DBA::update(
 			'contact', [
 				'url'     => $ret['url'],
-				'nurl'    => normalise_link($ret['url']),
+				'nurl'    => Strings::normaliseLink($ret['url']),
 				'network' => $ret['network'],
 				'addr'    => $ret['addr'],
 				'alias'   => $ret['alias'],
@@ -1627,10 +1628,10 @@ class Contact extends BaseObject
 		// the poll url is more reliable than the profile url, as we may have
 		// indirect links or webfinger links
 
-		$condition = ['uid' => $uid, 'poll' => [$ret['poll'], normalise_link($ret['poll'])], 'network' => $ret['network'], 'pending' => false];
+		$condition = ['uid' => $uid, 'poll' => [$ret['poll'], Strings::normaliseLink($ret['poll'])], 'network' => $ret['network'], 'pending' => false];
 		$contact = DBA::selectFirst('contact', ['id', 'rel'], $condition);
 		if (!DBA::isResult($contact)) {
-			$condition = ['uid' => $uid, 'nurl' => normalise_link($url), 'network' => $ret['network'], 'pending' => false];
+			$condition = ['uid' => $uid, 'nurl' => Strings::normaliseLink($url), 'network' => $ret['network'], 'pending' => false];
 			$contact = DBA::selectFirst('contact', ['id', 'rel'], $condition);
 		}
 
@@ -1710,7 +1711,7 @@ class Contact extends BaseObject
 				'uid'     => $uid,
 				'created' => DateTimeFormat::utcNow(),
 				'url'     => $ret['url'],
-				'nurl'    => normalise_link($ret['url']),
+				'nurl'    => Strings::normaliseLink($ret['url']),
 				'addr'    => $ret['addr'],
 				'alias'   => $ret['alias'],
 				'batch'   => $ret['batch'],
@@ -1855,7 +1856,7 @@ class Contact extends BaseObject
 
 			// send email notification to owner?
 		} else {
-			if (DBA::exists('contact', ['nurl' => normalise_link($url), 'uid' => $importer['uid'], 'pending' => true])) {
+			if (DBA::exists('contact', ['nurl' => Strings::normaliseLink($url), 'uid' => $importer['uid'], 'pending' => true])) {
 				Logger::log('ignoring duplicated connection request from pending contact ' . $url);
 				return;
 			}
@@ -1866,7 +1867,7 @@ class Contact extends BaseObject
 				intval($importer['uid']),
 				DBA::escape(DateTimeFormat::utcNow()),
 				DBA::escape($url),
-				DBA::escape(normalise_link($url)),
+				DBA::escape(Strings::normaliseLink($url)),
 				DBA::escape($name),
 				DBA::escape($nick),
 				DBA::escape($photo),
@@ -1889,7 +1890,7 @@ class Contact extends BaseObject
 			$user = DBA::selectFirst('user', $fields, ['uid' => $importer['uid']]);
 			if (DBA::isResult($user) && !in_array($user['page-flags'], [self::PAGE_SOAPBOX, self::PAGE_FREELOVE, self::PAGE_COMMUNITY])) {
 				// create notification
-				$hash = random_string();
+				$hash = Strings::getRandomHex();
 
 				if (is_array($contact_record)) {
 					DBA::insert('intro', ['uid' => $importer['uid'], 'contact-id' => $contact_record['id'],
diff --git a/src/Model/GContact.php b/src/Model/GContact.php
index 398fc7758..6e2e520c8 100644
--- a/src/Model/GContact.php
+++ b/src/Model/GContact.php
@@ -17,6 +17,7 @@ use Friendica\Network\Probe;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 
@@ -146,13 +147,13 @@ class GContact
 		$alternate = PortableContact::alternateOStatusUrl($gcontact['url']);
 
 		// The global contacts should contain the original picture, not the cached one
-		if (($gcontact['generation'] != 1) && stristr(normalise_link($gcontact['photo']), normalise_link(System::baseUrl()."/photo/"))) {
+		if (($gcontact['generation'] != 1) && stristr(Strings::normaliseLink($gcontact['photo']), Strings::normaliseLink(System::baseUrl()."/photo/"))) {
 			$gcontact['photo'] = "";
 		}
 
 		if (!isset($gcontact['network'])) {
 			$condition = ["`uid` = 0 AND `nurl` = ? AND `network` != '' AND `network` != ?",
-				normalise_link($gcontact['url']), Protocol::STATUSNET];
+				Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET];
 			$contact = DBA::selectFirst('contact', ['network'], $condition);
 			if (DBA::isResult($contact)) {
 				$gcontact['network'] = $contact["network"];
@@ -160,7 +161,7 @@ class GContact
 
 			if (($gcontact['network'] == "") || ($gcontact['network'] == Protocol::OSTATUS)) {
 				$condition = ["`uid` = 0 AND `alias` IN (?, ?) AND `network` != '' AND `network` != ?",
-					$gcontact['url'], normalise_link($gcontact['url']), Protocol::STATUSNET];
+					$gcontact['url'], Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET];
 				$contact = DBA::selectFirst('contact', ['network'], $condition);
 				if (DBA::isResult($contact)) {
 					$gcontact['network'] = $contact["network"];
@@ -172,7 +173,7 @@ class GContact
 		$gcontact['network'] = '';
 
 		$fields = ['network', 'updated', 'server_url', 'url', 'addr'];
-		$gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => normalise_link($gcontact['url'])]);
+		$gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($gcontact['url'])]);
 		if (DBA::isResult($gcnt)) {
 			if (!isset($gcontact['network']) && ($gcnt["network"] != Protocol::STATUSNET)) {
 				$gcontact['network'] = $gcnt["network"];
@@ -180,7 +181,7 @@ class GContact
 			if ($gcontact['updated'] <= DBA::NULL_DATETIME) {
 				$gcontact['updated'] = $gcnt["updated"];
 			}
-			if (!isset($gcontact['server_url']) && (normalise_link($gcnt["server_url"]) != normalise_link($gcnt["url"]))) {
+			if (!isset($gcontact['server_url']) && (Strings::normaliseLink($gcnt["server_url"]) != Strings::normaliseLink($gcnt["url"]))) {
 				$gcontact['server_url'] = $gcnt["server_url"];
 			}
 			if (!isset($gcontact['addr'])) {
@@ -205,8 +206,8 @@ class GContact
 
 			if ($alternate && ($gcontact['network'] == Protocol::OSTATUS)) {
 				// Delete the old entry - if it exists
-				if (DBA::exists('gcontact', ['nurl' => normalise_link($orig_profile)])) {
-					DBA::delete('gcontact', ['nurl' => normalise_link($orig_profile)]);
+				if (DBA::exists('gcontact', ['nurl' => Strings::normaliseLink($orig_profile)])) {
+					DBA::delete('gcontact', ['nurl' => Strings::normaliseLink($orig_profile)]);
 				}
 			}
 		}
@@ -658,7 +659,7 @@ class GContact
 
 		DBA::lock('gcontact');
 		$fields = ['id', 'last_contact', 'last_failure', 'network'];
-		$gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => normalise_link($contact["url"])]);
+		$gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($contact["url"])]);
 		if (DBA::isResult($gcnt)) {
 			$gcontact_id = $gcnt["id"];
 
@@ -683,7 +684,7 @@ class GContact
 				DBA::escape($contact["addr"]),
 				DBA::escape($contact["network"]),
 				DBA::escape($contact["url"]),
-				DBA::escape(normalise_link($contact["url"])),
+				DBA::escape(Strings::normaliseLink($contact["url"])),
 				DBA::escape($contact["photo"]),
 				DBA::escape(DateTimeFormat::utcNow()),
 				DBA::escape(DateTimeFormat::utcNow()),
@@ -693,7 +694,7 @@ class GContact
 				intval($contact["generation"])
 			);
 
-			$condition = ['nurl' => normalise_link($contact["url"])];
+			$condition = ['nurl' => Strings::normaliseLink($contact["url"])];
 			$cnt = DBA::selectFirst('gcontact', ['id', 'network'], $condition, ['order' => ['id']]);
 			if (DBA::isResult($cnt)) {
 				$gcontact_id = $cnt["id"];
@@ -793,7 +794,7 @@ class GContact
 				$contact["server_url"] = $data['baseurl'];
 			}
 		} else {
-			$contact["server_url"] = normalise_link($contact["server_url"]);
+			$contact["server_url"] = Strings::normaliseLink($contact["server_url"]);
 		}
 
 		if (($contact["addr"] == "") && ($contact["server_url"] != "") && ($contact["nick"] != "")) {
@@ -822,7 +823,7 @@ class GContact
 		if ($update) {
 			Logger::log("Update gcontact for ".$contact["url"], Logger::DEBUG);
 			$condition = ['`nurl` = ? AND (`generation` = 0 OR `generation` >= ?)',
-					normalise_link($contact["url"]), $contact["generation"]];
+					Strings::normaliseLink($contact["url"]), $contact["generation"]];
 			$contact["updated"] = DateTimeFormat::utc($contact["updated"]);
 
 			$updated = ['photo' => $contact['photo'], 'name' => $contact['name'],
@@ -842,7 +843,7 @@ class GContact
 			// This is used for the shadow copies of public items.
 			/// @todo Check if we really should do this.
 			// The quality of the gcontact table is mostly lower than the public contact
-			$public_contact = DBA::selectFirst('contact', ['id'], ['nurl' => normalise_link($contact["url"]), 'uid' => 0]);
+			$public_contact = DBA::selectFirst('contact', ['id'], ['nurl' => Strings::normaliseLink($contact["url"]), 'uid' => 0]);
 			if (DBA::isResult($public_contact)) {
 				Logger::log("Update public contact ".$public_contact["id"], Logger::DEBUG);
 
diff --git a/src/Model/Item.php b/src/Model/Item.php
index 8e0a397e2..30c29e151 100644
--- a/src/Model/Item.php
+++ b/src/Model/Item.php
@@ -8,25 +8,32 @@ namespace Friendica\Model;
 
 use Friendica\BaseObject;
 use Friendica\Content\Text\BBCode;
+use Friendica\Content\Text\HTML;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
 use Friendica\Core\Lock;
 use Friendica\Core\Logger;
+use Friendica\Core\L10n;
 use Friendica\Core\PConfig;
 use Friendica\Core\Protocol;
+use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
+use Friendica\Model\Event;
 use Friendica\Model\FileTag;
 use Friendica\Model\PermissionSet;
+use Friendica\Model\Term;
 use Friendica\Model\ItemURI;
 use Friendica\Object\Image;
 use Friendica\Protocol\Diaspora;
 use Friendica\Protocol\OStatus;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Map;
 use Friendica\Util\XML;
 use Friendica\Util\Security;
+use Friendica\Util\Strings;
 use Text_LanguageDetect;
 
 require_once 'boot.php';
@@ -1143,7 +1150,7 @@ class Item extends BaseObject
 	private static function guid($item, $notify)
 	{
 		if (!empty($item['guid'])) {
-			return notags(trim($item['guid']));
+			return Strings::escapeTags(trim($item['guid']));
 		}
 
 		if ($notify) {
@@ -1258,7 +1265,7 @@ class Item extends BaseObject
 		}
 
 		$item['guid'] = self::guid($item, $notify);
-		$item['uri'] = notags(trim(defaults($item, 'uri', self::newURI($item['uid'], $item['guid']))));
+		$item['uri'] = Strings::escapeTags(trim(defaults($item, 'uri', self::newURI($item['uid'], $item['guid']))));
 
 		// Store URI data
 		$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
@@ -1528,7 +1535,7 @@ class Item extends BaseObject
 				Logger::log("Checking if parent ".$parent_id." has to be tagged as mention for user ".$item['uid'], Logger::DEBUG);
 				$user = DBA::selectFirst('user', ['nickname'], ['uid' => $item['uid']]);
 				if (DBA::isResult($user)) {
-					$self = normalise_link(System::baseUrl() . '/profile/' . $user['nickname']);
+					$self = Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']);
 					$self_id = Contact::getIdForURL($self, 0, true);
 					Logger::log("'myself' is ".$self_id." for parent ".$parent_id." checking against ".$item['author-id']." and ".$item['owner-id'], Logger::DEBUG);
 					if (($item['author-id'] == $self_id) || ($item['owner-id'] == $self_id)) {
@@ -1607,7 +1614,7 @@ class Item extends BaseObject
 		$item["deleted"] = $parent_deleted;
 
 		// Fill the cache field
-		put_item_in_cache($item);
+		self::putInCache($item);
 
 		if ($notify) {
 			$item['edit'] = false;
@@ -2396,7 +2403,7 @@ class Item extends BaseObject
 	public static function setHashtags(&$item)
 	{
 
-		$tags = get_tags($item["body"]);
+		$tags = BBCode::getTags($item["body"]);
 
 		// No hashtags?
 		if (!count($tags)) {
@@ -2538,18 +2545,18 @@ class Item extends BaseObject
 			return;
 		}
 
-		$link = normalise_link(System::baseUrl() . '/profile/' . $user['nickname']);
+		$link = Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']);
 
 		/*
 		 * Diaspora uses their own hardwired link URL in @-tags
 		 * instead of the one we supply with webfinger
 		 */
-		$dlink = normalise_link(System::baseUrl() . '/u/' . $user['nickname']);
+		$dlink = Strings::normaliseLink(System::baseUrl() . '/u/' . $user['nickname']);
 
 		$cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
 		if ($cnt) {
 			foreach ($matches as $mtch) {
-				if (link_compare($link, $mtch[1]) || link_compare($dlink, $mtch[1])) {
+				if (Strings::compareLink($link, $mtch[1]) || Strings::compareLink($dlink, $mtch[1])) {
 					$mention = true;
 					Logger::log('mention found: ' . $mtch[2]);
 				}
@@ -3247,4 +3254,295 @@ class Item extends BaseObject
 
 		return $sql;
 	}
+
+	/**
+	 * get translated item type
+	 *
+	 * @param array $itme
+	 * @return string
+	 */
+	public static function postType($item)
+	{
+		if (!empty($item['event-id'])) {
+			return L10n::t('event');
+		} elseif (!empty($item['resource-id'])) {
+			return L10n::t('photo');
+		} elseif (!empty($item['verb']) && $item['verb'] !== ACTIVITY_POST) {
+			return L10n::t('activity');
+		} elseif ($item['id'] != $item['parent']) {
+			return L10n::t('comment');
+		}
+
+		return L10n::t('post');
+	}
+
+	/**
+	 * Sets the "rendered-html" field of the provided item
+	 *
+	 * Body is preserved to avoid side-effects as we modify it just-in-time for spoilers and private image links
+	 *
+	 * @param array $item
+	 * @param bool  $update
+	 *
+	 * @todo Remove reference, simply return "rendered-html" and "rendered-hash"
+	 */
+	public static function putInCache(&$item, $update = false)
+	{
+		$body = $item["body"];
+
+		$rendered_hash = defaults($item, 'rendered-hash', '');
+		$rendered_html = defaults($item, 'rendered-html', '');
+
+		if ($rendered_hash == ''
+			|| $rendered_html == ""
+			|| $rendered_hash != hash("md5", $item["body"])
+			|| Config::get("system", "ignore_cache")
+		) {
+			$a = self::getApp();
+			redir_private_images($a, $item);
+
+			$item["rendered-html"] = prepare_text($item["body"]);
+			$item["rendered-hash"] = hash("md5", $item["body"]);
+
+			$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
+			Addon::callHooks('put_item_in_cache', $hook_data);
+			$item['rendered-html'] = $hook_data['rendered-html'];
+			$item['rendered-hash'] = $hook_data['rendered-hash'];
+			unset($hook_data);
+
+			// Force an update if the generated values differ from the existing ones
+			if ($rendered_hash != $item["rendered-hash"]) {
+				$update = true;
+			}
+
+			// Only compare the HTML when we forcefully ignore the cache
+			if (Config::get("system", "ignore_cache") && ($rendered_html != $item["rendered-html"])) {
+				$update = true;
+			}
+
+			if ($update && !empty($item["id"])) {
+				self::update(
+					[
+						'rendered-html' => $item["rendered-html"],
+						'rendered-hash' => $item["rendered-hash"]
+					],
+					['id' => $item["id"]]
+				);
+			}
+		}
+
+		$item["body"] = $body;
+	}
+
+	/**
+	 * @brief Given an item array, convert the body element from bbcode to html and add smilie icons.
+	 * If attach is true, also add icons for item attachments.
+	 *
+	 * @param array   $item
+	 * @param boolean $attach
+	 * @param boolean $is_preview
+	 * @return string item body html
+	 * @hook prepare_body_init item array before any work
+	 * @hook prepare_body_content_filter ('item'=>item array, 'filter_reasons'=>string array) before first bbcode to html
+	 * @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
+	 * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
+	 */
+	public static function prepareBody(array &$item, $attach = false, $is_preview = false)
+	{
+		$a = self::getApp();
+		Addon::callHooks('prepare_body_init', $item);
+
+		// In order to provide theme developers more possibilities, event items
+		// are treated differently.
+		if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) {
+			$ev = Event::getItemHTML($item);
+			return $ev;
+		}
+
+		$tags = Term::populateTagsFromItem($item);
+
+		$item['tags'] = $tags['tags'];
+		$item['hashtags'] = $tags['hashtags'];
+		$item['mentions'] = $tags['mentions'];
+
+		// Compile eventual content filter reasons
+		$filter_reasons = [];
+		if (!$is_preview && public_contact() != $item['author-id']) {
+			if (!empty($item['content-warning']) && (!local_user() || !PConfig::get(local_user(), 'system', 'disable_cw', false))) {
+				$filter_reasons[] = L10n::t('Content warning: %s', $item['content-warning']);
+			}
+
+			$hook_data = [
+				'item' => $item,
+				'filter_reasons' => $filter_reasons
+			];
+			Addon::callHooks('prepare_body_content_filter', $hook_data);
+			$filter_reasons = $hook_data['filter_reasons'];
+			unset($hook_data);
+		}
+
+		// Update the cached values if there is no "zrl=..." on the links.
+		$update = (!local_user() && !remote_user() && ($item["uid"] == 0));
+
+		// Or update it if the current viewer is the intented viewer.
+		if (($item["uid"] == local_user()) && ($item["uid"] != 0)) {
+			$update = true;
+		}
+
+		self::putInCache($item, $update);
+		$s = $item["rendered-html"];
+
+		$hook_data = [
+			'item' => $item,
+			'html' => $s,
+			'preview' => $is_preview,
+			'filter_reasons' => $filter_reasons
+		];
+		Addon::callHooks('prepare_body', $hook_data);
+		$s = $hook_data['html'];
+		unset($hook_data);
+
+		if (!$attach) {
+			// Replace the blockquotes with quotes that are used in mails.
+			$mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
+			$s = str_replace(['<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'], [$mailquote, $mailquote, $mailquote], $s);
+			return $s;
+		}
+
+		$as = '';
+		$vhead = false;
+		$matches = [];
+		preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\"(?: title=\"(.*?)\")?|', $item['attach'], $matches, PREG_SET_ORDER);
+		foreach ($matches as $mtch) {
+			$mime = $mtch[3];
+
+			$the_url = Contact::magicLinkById($item['author-id'], $mtch[1]);
+
+			if (strpos($mime, 'video') !== false) {
+				if (!$vhead) {
+					$vhead = true;
+					$a->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'), [
+						'$baseurl' => System::baseUrl(),
+					]);
+				}
+
+				$url_parts = explode('/', $the_url);
+				$id = end($url_parts);
+				$as .= Renderer::replaceMacros(Renderer::getMarkupTemplate('video_top.tpl'), [
+					'$video' => [
+						'id'     => $id,
+						'title'  => L10n::t('View Video'),
+						'src'    => $the_url,
+						'mime'   => $mime,
+					],
+				]);
+			}
+
+			$filetype = strtolower(substr($mime, 0, strpos($mime, '/')));
+			if ($filetype) {
+				$filesubtype = strtolower(substr($mime, strpos($mime, '/') + 1));
+				$filesubtype = str_replace('.', '-', $filesubtype);
+			} else {
+				$filetype = 'unkn';
+				$filesubtype = 'unkn';
+			}
+
+			$title = Strings::escapeHtml(trim(!empty($mtch[4]) ? $mtch[4] : $mtch[1]));
+			$title .= ' ' . $mtch[2] . ' ' . L10n::t('bytes');
+
+			$icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
+			$as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
+		}
+
+		if ($as != '') {
+			$s .= '<div class="body-attach">'.$as.'<div class="clear"></div></div>';
+		}
+
+		// Map.
+		if (strpos($s, '<div class="map">') !== false && x($item, 'coord')) {
+			$x = Map::byCoordinates(trim($item['coord']));
+			if ($x) {
+				$s = preg_replace('/\<div class\=\"map\"\>/', '$0' . $x, $s);
+			}
+		}
+
+
+		// Look for spoiler.
+		$spoilersearch = '<blockquote class="spoiler">';
+
+		// Remove line breaks before the spoiler.
+		while ((strpos($s, "\n" . $spoilersearch) !== false)) {
+			$s = str_replace("\n" . $spoilersearch, $spoilersearch, $s);
+		}
+		while ((strpos($s, "<br />" . $spoilersearch) !== false)) {
+			$s = str_replace("<br />" . $spoilersearch, $spoilersearch, $s);
+		}
+
+		while ((strpos($s, $spoilersearch) !== false)) {
+			$pos = strpos($s, $spoilersearch);
+			$rnd = Strings::getRandomHex(8);
+			$spoilerreplace = '<br /> <span id="spoiler-wrap-' . $rnd . '" class="spoiler-wrap fakelink" onclick="openClose(\'spoiler-' . $rnd . '\');">' . L10n::t('Click to open/close') . '</span>'.
+						'<blockquote class="spoiler" id="spoiler-' . $rnd . '" style="display: none;">';
+			$s = substr($s, 0, $pos) . $spoilerreplace . substr($s, $pos + strlen($spoilersearch));
+		}
+
+		// Look for quote with author.
+		$authorsearch = '<blockquote class="author">';
+
+		while ((strpos($s, $authorsearch) !== false)) {
+			$pos = strpos($s, $authorsearch);
+			$rnd = Strings::getRandomHex(8);
+			$authorreplace = '<br /> <span id="author-wrap-' . $rnd . '" class="author-wrap fakelink" onclick="openClose(\'author-' . $rnd . '\');">' . L10n::t('Click to open/close') . '</span>'.
+						'<blockquote class="author" id="author-' . $rnd . '" style="display: block;">';
+			$s = substr($s, 0, $pos) . $authorreplace . substr($s, $pos + strlen($authorsearch));
+		}
+
+		// Replace friendica image url size with theme preference.
+		if (!empty($a->theme_info['item_image_size'])) {
+			$ps = $a->theme_info['item_image_size'];
+			$s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s);
+		}
+
+		$s = HTML::applyContentFilter($s, $filter_reasons);
+
+		$hook_data = ['item' => $item, 'html' => $s];
+		Addon::callHooks('prepare_body_final', $hook_data);
+
+		return $hook_data['html'];
+	}
+
+	/**
+	 * get private link for item
+	 * @param array $item
+	 * @return boolean|array False if item has not plink, otherwise array('href'=>plink url, 'title'=>translated title)
+	 */
+	public static function getPlink($item)
+	{
+		$a = self::getApp();
+
+		if ($a->user['nickname'] != "") {
+			$ret = [
+				'href' => "display/" . $item['guid'],
+				'orig' => "display/" . $item['guid'],
+				'title' => L10n::t('View on separate page'),
+				'orig_title' => L10n::t('view on separate page'),
+			];
+
+			if (!empty($item['plink'])) {
+				$ret["href"] = $a->removeBaseURL($item['plink']);
+				$ret["title"] = L10n::t('link to source');
+			}
+
+		} elseif (!empty($item['plink']) && ($item['private'] != 1)) {
+			$ret = [
+				'href' => $item['plink'],
+				'orig' => $item['plink'],
+				'title' => L10n::t('link to source'),
+			];
+		} else {
+			$ret = [];
+		}
+
+		return $ret;
+	}
 }
diff --git a/src/Model/Profile.php b/src/Model/Profile.php
index 95a6bd35b..c45bcdb84 100644
--- a/src/Model/Profile.php
+++ b/src/Model/Profile.php
@@ -25,6 +25,7 @@ use Friendica\Protocol\Diaspora;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/dba.php';
@@ -296,7 +297,7 @@ class Profile
 		$profile['picdate'] = urlencode(defaults($profile, 'picdate', ''));
 
 		if (($profile['network'] != '') && ($profile['network'] != Protocol::DFRN)) {
-			$profile['network_name'] = format_network_name($profile['network'], $profile['url']);
+			$profile['network_name'] = Strings::formatNetworkName($profile['network'], $profile['url']);
 		} else {
 			$profile['network_name'] = '';
 		}
@@ -326,9 +327,9 @@ class Profile
 		// Is the local user already connected to that user?
 		if ($connect && local_user()) {
 			if (isset($profile['url'])) {
-				$profile_url = normalise_link($profile['url']);
+				$profile_url = Strings::normaliseLink($profile['url']);
 			} else {
-				$profile_url = normalise_link(System::baseUrl() . '/profile/' . $profile['nickname']);
+				$profile_url = Strings::normaliseLink(System::baseUrl() . '/profile/' . $profile['nickname']);
 			}
 
 			if (DBA::exists('contact', ['pending' => false, 'uid' => local_user(), 'nurl' => $profile_url])) {
@@ -370,7 +371,7 @@ class Profile
 				$r = q(
 					"SELECT `url` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `rel` = %d",
 					intval($profile['uid']),
-					DBA::escape(normalise_link(self::getMyURL())),
+					DBA::escape(Strings::normaliseLink(self::getMyURL())),
 					intval(Contact::FRIEND)
 				);
 			}
@@ -881,7 +882,7 @@ class Profile
 
 		$tab = false;
 		if (x($_GET, 'tab')) {
-			$tab = notags(trim($_GET['tab']));
+			$tab = Strings::escapeTags(trim($_GET['tab']));
 		}
 
 		$url = System::baseUrl() . '/profile/' . $nickname;
@@ -1140,7 +1141,7 @@ class Profile
 		}
 		$achar = strpos($s, '?') ? '&' : '?';
 		$mine = self::getMyURL();
-		if ($mine && !link_compare($mine, $s)) {
+		if ($mine && !Strings::compareLink($mine, $s)) {
 			return $s . $achar . 'zrl=' . urlencode($mine);
 		}
 		return $s;
diff --git a/src/Model/Register.php b/src/Model/Register.php
index e54db87a6..66f19133b 100644
--- a/src/Model/Register.php
+++ b/src/Model/Register.php
@@ -7,6 +7,7 @@ namespace Friendica\Model;
 
 use Friendica\Database\DBA;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 
 /**
  * Class interacting with the register database table
@@ -77,7 +78,7 @@ class Register
 	 */
 	public static function createForInvitation()
 	{
-		$code = autoname(8) . srand(1000, 9999);
+		$code = Strings::getRandomName(8) . srand(1000, 9999);
 
 		$fields = [
 			'hash' => $code,
@@ -100,7 +101,7 @@ class Register
 	 */
 	public static function createForApproval($uid, $language, $note = '')
 	{
-		$hash = random_string();
+		$hash = Strings::getRandomHex();
 
 		if (!User::exists($uid)) {
 			return false;
diff --git a/src/Model/User.php b/src/Model/User.php
index 43992cc5f..0f397aadc 100644
--- a/src/Model/User.php
+++ b/src/Model/User.php
@@ -20,6 +20,7 @@ use Friendica\Object\Image;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use LightOpenID;
 
 require_once 'boot.php';
@@ -60,7 +61,7 @@ class User
 	 */
 	public static function getIdForURL($url)
 	{
-		$self = DBA::selectFirst('contact', ['uid'], ['nurl' => normalise_link($url), 'self' => true]);
+		$self = DBA::selectFirst('contact', ['uid'], ['nurl' => Strings::normaliseLink($url), 'self' => true]);
 		if (!DBA::isResult($self)) {
 			return false;
 		} else {
@@ -269,7 +270,7 @@ class User
 	 */
 	public static function generateNewPassword()
 	{
-		return autoname(6) . mt_rand(100, 9999);
+		return Strings::getRandomName(6) . mt_rand(100, 9999);
 	}
 
 	/**
@@ -401,18 +402,18 @@ class User
 		$using_invites = Config::get('system', 'invitation_only');
 		$num_invites   = Config::get('system', 'number_invites');
 
-		$invite_id  = !empty($data['invite_id'])  ? notags(trim($data['invite_id']))  : '';
-		$username   = !empty($data['username'])   ? notags(trim($data['username']))   : '';
-		$nickname   = !empty($data['nickname'])   ? notags(trim($data['nickname']))   : '';
-		$email      = !empty($data['email'])      ? notags(trim($data['email']))      : '';
-		$openid_url = !empty($data['openid_url']) ? notags(trim($data['openid_url'])) : '';
-		$photo      = !empty($data['photo'])      ? notags(trim($data['photo']))      : '';
+		$invite_id  = !empty($data['invite_id'])  ? Strings::escapeTags(trim($data['invite_id']))  : '';
+		$username   = !empty($data['username'])   ? Strings::escapeTags(trim($data['username']))   : '';
+		$nickname   = !empty($data['nickname'])   ? Strings::escapeTags(trim($data['nickname']))   : '';
+		$email      = !empty($data['email'])      ? Strings::escapeTags(trim($data['email']))      : '';
+		$openid_url = !empty($data['openid_url']) ? Strings::escapeTags(trim($data['openid_url'])) : '';
+		$photo      = !empty($data['photo'])      ? Strings::escapeTags(trim($data['photo']))      : '';
 		$password   = !empty($data['password'])   ? trim($data['password'])           : '';
 		$password1  = !empty($data['password1'])  ? trim($data['password1'])          : '';
 		$confirm    = !empty($data['confirm'])    ? trim($data['confirm'])            : '';
 		$blocked    = !empty($data['blocked'])    ? intval($data['blocked'])          : 0;
 		$verified   = !empty($data['verified'])   ? intval($data['verified'])         : 0;
-		$language   = !empty($data['language'])   ? notags(trim($data['language']))   : 'en';
+		$language   = !empty($data['language'])   ? Strings::escapeTags(trim($data['language']))   : 'en';
 
 		$publish = !empty($data['profile_publish_reg']) && intval($data['profile_publish_reg']) ? 1 : 0;
 		$netpublish = strlen(Config::get('system', 'directory')) ? $publish : 0;
@@ -498,7 +499,7 @@ class User
 			throw new Exception(L10n::t('Your email domain is not among those allowed on this site.'));
 		}
 
-		if (!valid_email($email) || !Network::isEmailDomainValid($email)) {
+		if (!filter_var($email, FILTER_VALIDATE_EMAIL) || !Network::isEmailDomainValid($email)) {
 			throw new Exception(L10n::t('Not a valid email address.'));
 		}
 		if (self::isNicknameBlocked($nickname)) {
@@ -692,7 +693,7 @@ class User
 	 */
 	public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password)
 	{
-		$body = deindent(L10n::t('
+		$body = Strings::deindent(L10n::t('
 			Dear %1$s,
 				Thank you for registering at %2$s. Your account is pending for approval by the administrator.
 
@@ -727,13 +728,13 @@ class User
 	 */
 	public static function sendRegisterOpenEmail($user, $sitename, $siteurl, $password)
 	{
-		$preamble = deindent(L10n::t('
+		$preamble = Strings::deindent(L10n::t('
 			Dear %1$s,
 				Thank you for registering at %2$s. Your account has been created.
 		',
 			$preamble, $user['username'], $sitename
 		));
-		$body = deindent(L10n::t('
+		$body = Strings::deindent(L10n::t('
 			The login details are as follows:
 
 			Site Location:	%3$s
@@ -813,4 +814,74 @@ class User
 			$a->internalRedirect();
 		}
 	}
+
+	/**
+	 * Return all identities to a user
+	 *
+	 * @param int $uid The user id
+	 * @return array All identities for this user
+	 *
+	 * Example for a return:
+	 * 	[
+	 * 		[
+	 * 			'uid' => 1,
+	 * 			'username' => 'maxmuster',
+	 * 			'nickname' => 'Max Mustermann'
+	 * 		],
+	 * 		[
+	 * 			'uid' => 2,
+	 * 			'username' => 'johndoe',
+	 * 			'nickname' => 'John Doe'
+	 * 		]
+	 * 	]
+	 */
+	public static function identities($uid)
+	{
+		$identities = [];
+
+		$user = DBA::selectFirst('user', ['uid', 'nickname', 'username', 'parent-uid'], ['uid' => $uid]);
+		if (!DBA::isResult($user)) {
+			return $identities;
+		}
+
+		if ($user['parent-uid'] == 0) {
+			// First add our own entry
+			$identities = [['uid' => $user['uid'],
+				'username' => $user['username'],
+				'nickname' => $user['nickname']]];
+
+			// Then add all the children
+			$r = DBA::select('user', ['uid', 'username', 'nickname'],
+				['parent-uid' => $user['uid'], 'account_removed' => false]);
+			if (DBA::isResult($r)) {
+				$identities = array_merge($identities, DBA::toArray($r));
+			}
+		} else {
+			// First entry is our parent
+			$r = DBA::select('user', ['uid', 'username', 'nickname'],
+				['uid' => $user['parent-uid'], 'account_removed' => false]);
+			if (DBA::isResult($r)) {
+				$identities = DBA::toArray($r);
+			}
+
+			// Then add all siblings
+			$r = DBA::select('user', ['uid', 'username', 'nickname'],
+				['parent-uid' => $user['parent-uid'], 'account_removed' => false]);
+			if (DBA::isResult($r)) {
+				$identities = array_merge($identities, DBA::toArray($r));
+			}
+		}
+
+		$r = DBA::p("SELECT `user`.`uid`, `user`.`username`, `user`.`nickname`
+			FROM `manage`
+			INNER JOIN `user` ON `manage`.`mid` = `user`.`uid`
+			WHERE `user`.`account_removed` = 0 AND `manage`.`uid` = ?",
+			$user['uid']
+		);
+		if (DBA::isResult($r)) {
+			$identities = array_merge($identities, DBA::toArray($r));
+		}
+
+		return $identities;
+	}
 }
diff --git a/src/Module/Contact.php b/src/Module/Contact.php
index db3007598..b7e182dc1 100644
--- a/src/Module/Contact.php
+++ b/src/Module/Contact.php
@@ -22,6 +22,7 @@ use Friendica\Module\Login;
 use Friendica\Network\Probe;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 /**
  *  Manages and show Contacts and their content
@@ -77,7 +78,7 @@ class Contact extends BaseModule
 			$a->data['contact'] = $contact;
 
 			if (($contact['network'] != '') && ($contact['network'] != Protocol::DFRN)) {
-				$networkname = format_network_name($contact['network'], $contact['url']);
+				$networkname = Strings::formatNetworkName($contact['network'], $contact['url']);
 			} else {
 				$networkname = '';
 			}
@@ -213,14 +214,14 @@ class Contact extends BaseModule
 
 		$fetch_further_information = intval(defaults($_POST, 'fetch_further_information', 0));
 
-		$ffi_keyword_blacklist = escape_tags(trim(defaults($_POST, 'ffi_keyword_blacklist', '')));
+		$ffi_keyword_blacklist = Strings::escapeHtml(trim(defaults($_POST, 'ffi_keyword_blacklist', '')));
 
 		$priority = intval(defaults($_POST, 'poll', 0));
 		if ($priority > 5 || $priority < 0) {
 			$priority = 0;
 		}
 
-		$info = escape_tags(trim($_POST['info']));
+		$info = Strings::escapeHtml(trim($_POST['info']));
 
 		$r = DBA::update('contact', [
 			'profile-id' => $profile_id,
@@ -303,7 +304,7 @@ class Contact extends BaseModule
 			}
 		}
 
-		$fields['nurl'] = normalise_link($data['url']);
+		$fields['nurl'] = Strings::normaliseLink($data['url']);
 
 		if (!empty($data['priority'])) {
 			$fields['priority'] = intval($data['priority']);
@@ -601,7 +602,7 @@ class Contact extends BaseModule
 				'$lbl_vis2'       => L10n::t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']),
 				'$lbl_info1'      => $lbl_info1,
 				'$lbl_info2'      => L10n::t('Their personal note'),
-				'$reason'         => trim(notags($contact['reason'])),
+				'$reason'         => trim(Strings::escapeTags($contact['reason'])),
 				'$infedit'        => L10n::t('Edit contact notes'),
 				'$common_link'    => 'common/loc/' . local_user() . '/' . $contact['id'],
 				'$relation_text'  => $relation_text,
@@ -694,8 +695,8 @@ class Contact extends BaseModule
 
 		$sql_extra .= sprintf(" AND `network` != '%s' ", Protocol::PHANTOM);
 
-		$search = notags(trim(defaults($_GET, 'search', '')));
-		$nets   = notags(trim(defaults($_GET, 'nets'  , '')));
+		$search = Strings::escapeTags(trim(defaults($_GET, 'search', '')));
+		$nets   = Strings::escapeTags(trim(defaults($_GET, 'nets'  , '')));
 
 		$tabs = [
 			[
@@ -765,7 +766,7 @@ class Contact extends BaseModule
 		if ($search) {
 			$searching = true;
 			$search_hdr = $search;
-			$search_txt = DBA::escape(protect_sprintf(preg_quote($search)));
+			$search_txt = DBA::escape(Strings::protectSprintf(preg_quote($search)));
 			$sql_extra .= " AND (name REGEXP '$search_txt' OR url REGEXP '$search_txt'  OR nick REGEXP '$search_txt') ";
 		}
 
diff --git a/src/Module/Hashtag.php b/src/Module/Hashtag.php
index bfe1eee7d..4d0918352 100644
--- a/src/Module/Hashtag.php
+++ b/src/Module/Hashtag.php
@@ -7,6 +7,7 @@ namespace Friendica\Module;
 use Friendica\BaseModule;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 require_once 'include/text.php';
@@ -21,7 +22,7 @@ class Hashtag extends BaseModule
 	{
 		$result = [];
 
-		$t = escape_tags($_REQUEST['t']);
+		$t = Strings::escapeHtml($_REQUEST['t']);
 		if (empty($t)) {
 			System::jsonExit($result);
 		}
diff --git a/src/Module/Install.php b/src/Module/Install.php
index 2defe24ad..2e0f16c4e 100644
--- a/src/Module/Install.php
+++ b/src/Module/Install.php
@@ -9,6 +9,7 @@ use Friendica\Database\DBStructure;
 use Friendica\Core;
 use Friendica\Core\L10n;
 use Friendica\Core\Renderer;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 class Install extends BaseModule
@@ -70,10 +71,10 @@ class Install extends BaseModule
 				break;
 
 			case self::SITE_SETTINGS:
-				$dbhost  = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
-				$dbuser  = notags(trim(defaults($_POST, 'dbuser', '')));
-				$dbpass  = notags(trim(defaults($_POST, 'dbpass', '')));
-				$dbdata  = notags(trim(defaults($_POST, 'dbdata', '')));
+				$dbhost  = Strings::escapeTags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
+				$dbuser  = Strings::escapeTags(trim(defaults($_POST, 'dbuser', '')));
+				$dbpass  = Strings::escapeTags(trim(defaults($_POST, 'dbpass', '')));
+				$dbdata  = Strings::escapeTags(trim(defaults($_POST, 'dbdata', '')));
 
 				// If we cannot connect to the database, return to the previous step
 				if (!self::$installer->checkDB($dbhost, $dbuser, $dbpass, $dbdata)) {
@@ -84,13 +85,13 @@ class Install extends BaseModule
 
 			case self::FINISHED:
 				$urlpath   = $a->getURLPath();
-				$dbhost    = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
-				$dbuser    = notags(trim(defaults($_POST, 'dbuser', '')));
-				$dbpass    = notags(trim(defaults($_POST, 'dbpass', '')));
-				$dbdata    = notags(trim(defaults($_POST, 'dbdata', '')));
-				$timezone  = notags(trim(defaults($_POST, 'timezone', Core\Installer::DEFAULT_TZ)));
-				$language  = notags(trim(defaults($_POST, 'language', Core\Installer::DEFAULT_LANG)));
-				$adminmail = notags(trim(defaults($_POST, 'adminmail', '')));
+				$dbhost    = Strings::escapeTags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
+				$dbuser    = Strings::escapeTags(trim(defaults($_POST, 'dbuser', '')));
+				$dbpass    = Strings::escapeTags(trim(defaults($_POST, 'dbpass', '')));
+				$dbdata    = Strings::escapeTags(trim(defaults($_POST, 'dbdata', '')));
+				$timezone  = Strings::escapeTags(trim(defaults($_POST, 'timezone', Core\Installer::DEFAULT_TZ)));
+				$language  = Strings::escapeTags(trim(defaults($_POST, 'language', Core\Installer::DEFAULT_LANG)));
+				$adminmail = Strings::escapeTags(trim(defaults($_POST, 'adminmail', '')));
 
 				// If we cannot connect to the database, return to the Database config wizard
 				if (!self::$installer->checkDB($dbhost, $dbuser, $dbpass, $dbdata)) {
@@ -139,12 +140,12 @@ class Install extends BaseModule
 				break;
 
 			case self::DATABASE_CONFIG:
-				$dbhost    = notags(trim(defaults($_POST, 'dbhost'   , Core\Installer::DEFAULT_HOST)));
-				$dbuser    = notags(trim(defaults($_POST, 'dbuser'   , ''                          )));
-				$dbpass    = notags(trim(defaults($_POST, 'dbpass'   , ''                          )));
-				$dbdata    = notags(trim(defaults($_POST, 'dbdata'   , ''                          )));
-				$phpath    = notags(trim(defaults($_POST, 'phpath'   , ''                          )));
-				$adminmail = notags(trim(defaults($_POST, 'adminmail', ''                          )));
+				$dbhost    = Strings::escapeTags(trim(defaults($_POST, 'dbhost'   , Core\Installer::DEFAULT_HOST)));
+				$dbuser    = Strings::escapeTags(trim(defaults($_POST, 'dbuser'   , ''                          )));
+				$dbpass    = Strings::escapeTags(trim(defaults($_POST, 'dbpass'   , ''                          )));
+				$dbdata    = Strings::escapeTags(trim(defaults($_POST, 'dbdata'   , ''                          )));
+				$phpath    = Strings::escapeTags(trim(defaults($_POST, 'phpath'   , ''                          )));
+				$adminmail = Strings::escapeTags(trim(defaults($_POST, 'adminmail', ''                          )));
 
 				$tpl = Renderer::getMarkupTemplate('install_db.tpl');
 				$output .= Renderer::replaceMacros($tpl, [
@@ -190,13 +191,13 @@ class Install extends BaseModule
 				break;
 
 			case self::SITE_SETTINGS:
-				$dbhost = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
-				$dbuser = notags(trim(defaults($_POST, 'dbuser', ''                          )));
-				$dbpass = notags(trim(defaults($_POST, 'dbpass', ''                          )));
-				$dbdata = notags(trim(defaults($_POST, 'dbdata', ''                          )));
-				$phpath = notags(trim(defaults($_POST, 'phpath', ''                          )));
+				$dbhost = Strings::escapeTags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST)));
+				$dbuser = Strings::escapeTags(trim(defaults($_POST, 'dbuser', ''                          )));
+				$dbpass = Strings::escapeTags(trim(defaults($_POST, 'dbpass', ''                          )));
+				$dbdata = Strings::escapeTags(trim(defaults($_POST, 'dbdata', ''                          )));
+				$phpath = Strings::escapeTags(trim(defaults($_POST, 'phpath', ''                          )));
 
-				$adminmail = notags(trim(defaults($_POST, 'adminmail', '')));
+				$adminmail = Strings::escapeTags(trim(defaults($_POST, 'adminmail', '')));
 
 				$timezone = defaults($_POST, 'timezone', Core\Installer::DEFAULT_TZ);
 				/* Installed langs */
diff --git a/src/Module/Login.php b/src/Module/Login.php
index 751d4d4cc..0cc65c3c3 100644
--- a/src/Module/Login.php
+++ b/src/Module/Login.php
@@ -17,6 +17,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\User;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use LightOpenID;
 
 require_once 'boot.php';
@@ -148,7 +149,7 @@ class Login extends BaseModule
 				);
 			}
 		} catch (Exception $e) {
-			Logger::log('authenticate: failed login attempt: ' . notags($username) . ' from IP ' . $_SERVER['REMOTE_ADDR']);
+			Logger::log('authenticate: failed login attempt: ' . Strings::escapeTags($username) . ' from IP ' . $_SERVER['REMOTE_ADDR']);
 			info('Login failed. Please check your credentials.' . EOL);
 			$a->internalRedirect();
 		}
diff --git a/src/Module/Magic.php b/src/Module/Magic.php
index b8af1f961..ecfe18e59 100644
--- a/src/Module/Magic.php
+++ b/src/Module/Magic.php
@@ -11,6 +11,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Util\HTTPSignature;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 /**
  * Magic Auth (remote authentication) module.
@@ -49,7 +50,7 @@ class Magic extends BaseModule
 		$contact = DBA::selectFirst('contact', ['id', 'nurl', 'url'], ['id' => $cid]);
 
 		// Redirect if the contact is already authenticated on this site.
-		if (!empty($a->contact) && array_key_exists('id', $a->contact) && strpos($contact['nurl'], normalise_link(self::getApp()->getBaseURL())) !== false) {
+		if (!empty($a->contact) && array_key_exists('id', $a->contact) && strpos($contact['nurl'], Strings::normaliseLink(self::getApp()->getBaseURL())) !== false) {
 			if ($test) {
 				$ret['success'] = true;
 				$ret['message'] .= 'Local site - you are already authenticated.' . EOL;
@@ -74,7 +75,7 @@ class Magic extends BaseModule
 
 				$headers = [];
 				$headers['Accept'] = 'application/x-dfrn+json';
-				$headers['X-Open-Web-Auth'] = random_string();
+				$headers['X-Open-Web-Auth'] = Strings::getRandomHex();
 
 				// Create a header that is signed with the local users private key.
 				$headers = HTTPSignature::createSig(
@@ -94,7 +95,7 @@ class Magic extends BaseModule
 						if ($j['encrypted_token']) {
 							// The token is encrypted. If the local user is really the one the other instance
 							// thinks he/she is, the token can be decrypted with the local users public key.
-							openssl_private_decrypt(base64url_decode($j['encrypted_token']), $token, $user['prvkey']);
+							openssl_private_decrypt(Strings::base64UrlDecode($j['encrypted_token']), $token, $user['prvkey']);
 						} else {
 							$token = $j['token'];
 						}
diff --git a/src/Module/Oembed.php b/src/Module/Oembed.php
index 452162ff2..9ca4530d8 100644
--- a/src/Module/Oembed.php
+++ b/src/Module/Oembed.php
@@ -4,6 +4,7 @@ namespace Friendica\Module;
 
 use Friendica\BaseModule;
 use Friendica\Content;
+use Friendica\Util\Strings;
 
 /**
  * Oembed module
@@ -36,7 +37,7 @@ class Oembed extends BaseModule
 
 		if ($a->argc == 2) {
 			echo '<html><body>';
-			$url = base64url_decode($a->argv[1]);
+			$url = Strings::base64UrlDecode($a->argv[1]);
 			$j = Content\OEmbed::fetchURL($url);
 
 			// workaround for media.ccc.de (and any other endpoint that return size 0)
diff --git a/src/Module/Owa.php b/src/Module/Owa.php
index a1dbfa2f3..bfe108c84 100644
--- a/src/Module/Owa.php
+++ b/src/Module/Owa.php
@@ -11,6 +11,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\OpenWebAuthToken;
 use Friendica\Util\HTTPSignature;
+use Friendica\Util\Strings;
 
 /**
  * @brief OpenWebAuth verifier and token generator
@@ -62,7 +63,7 @@ class Owa extends BaseModule
 								Logger::log('OWA success: ' . $contact['addr'], Logger::DATA);
 
 								$ret['success'] = true;
-								$token = random_string(32);
+								$token = Strings::getRandomHex(32);
 
 								// Store the generated token in the databe.
 								OpenWebAuthToken::create('owt', 0, $token, $contact['addr']);
@@ -74,7 +75,7 @@ class Owa extends BaseModule
 								// At a later time, we will compare weather the token we're getting
 								// is really the same token we have stored in the database.
 								openssl_public_encrypt($token, $result, $contact['pubkey']);
-								$ret['encrypted_token'] = base64url_encode($result);
+								$ret['encrypted_token'] = Strings::base64UrlEncode($result);
 							} else {
 								Logger::log('OWA fail: ' . $contact['id'] . ' ' . $contact['addr'] . ' ' . $contact['url'], Logger::DEBUG);
 							}
diff --git a/src/Network/Probe.php b/src/Network/Probe.php
index fc93524bf..d870e06f9 100644
--- a/src/Network/Probe.php
+++ b/src/Network/Probe.php
@@ -24,6 +24,7 @@ use Friendica\Protocol\ActivityPub;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use DomXPath;
 
@@ -347,7 +348,7 @@ class Probe
 		}
 
 		if (x($data, "photo")) {
-			$data["baseurl"] = Network::getUrlMatch(normalise_link(defaults($data, "baseurl", "")), normalise_link($data["photo"]));
+			$data["baseurl"] = Network::getUrlMatch(Strings::normaliseLink(defaults($data, "baseurl", "")), Strings::normaliseLink($data["photo"]));
 		} else {
 			$data["photo"] = System::baseUrl().'/images/person-300.jpg';
 		}
@@ -426,7 +427,7 @@ class Probe
 
 				$fields['updated'] = DateTimeFormat::utcNow();
 
-				$condition = ['nurl' => normalise_link($data["url"])];
+				$condition = ['nurl' => Strings::normaliseLink($data["url"])];
 
 				$old_fields = DBA::selectFirst('gcontact', $fieldnames, $condition);
 
@@ -473,7 +474,7 @@ class Probe
 					}
 				}
 
-				$condition = ['nurl' => normalise_link($data["url"]), 'self' => false, 'uid' => 0];
+				$condition = ['nurl' => Strings::normaliseLink($data["url"]), 'self' => false, 'uid' => 0];
 
 				// "$old_fields" will return a "false" when the contact doesn't exist.
 				// This won't trigger an insert. This is intended, since we only need
@@ -1009,7 +1010,7 @@ class Probe
 			foreach ($webfinger["aliases"] as $alias) {
 				if (empty($data["url"]) && !strstr($alias, "@")) {
 					$data["url"] = $alias;
-				} elseif (!strstr($alias, "@") && normalise_link($alias) != normalise_link($data["url"])) {
+				} elseif (!strstr($alias, "@") && Strings::normaliseLink($alias) != Strings::normaliseLink($data["url"])) {
 					$data["alias"] = $alias;
 				} elseif (substr($alias, 0, 5) == 'acct:') {
 					$data["addr"] = substr($alias, 5);
@@ -1212,7 +1213,7 @@ class Probe
 
 		if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
 			foreach ($webfinger["aliases"] as $alias) {
-				if (normalise_link($alias) != normalise_link($data["url"]) && ! strstr($alias, "@")) {
+				if (Strings::normaliseLink($alias) != Strings::normaliseLink($data["url"]) && ! strstr($alias, "@")) {
 					$data["alias"] = $alias;
 				} elseif (substr($alias, 0, 5) == 'acct:') {
 					$data["addr"] = substr($alias, 5);
@@ -1268,14 +1269,14 @@ class Probe
 
 		if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
 			foreach ($webfinger["aliases"] as $alias) {
-				if (strstr($alias, "@") && !strstr(normalise_link($alias), "http://")) {
+				if (strstr($alias, "@") && !strstr(Strings::normaliseLink($alias), "http://")) {
 					$data["addr"] = str_replace('acct:', '', $alias);
 				}
 			}
 		}
 
 		if (!empty($webfinger["subject"]) && strstr($webfinger["subject"], "@")
-			&& !strstr(normalise_link($webfinger["subject"]), "http://")
+			&& !strstr(Strings::normaliseLink($webfinger["subject"]), "http://")
 		) {
 			$data["addr"] = str_replace('acct:', '', $webfinger["subject"]);
 		}
@@ -1301,7 +1302,7 @@ class Probe
 						} else {
 							$pubkey = substr($pubkey, 5);
 						}
-					} elseif (normalise_link($pubkey) == 'http://') {
+					} elseif (Strings::normaliseLink($pubkey) == 'http://') {
 						$curlResult = Network::curl($pubkey);
 						if ($curlResult->isTimeout()) {
 							return false;
@@ -1312,8 +1313,8 @@ class Probe
 					$key = explode(".", $pubkey);
 
 					if (sizeof($key) >= 3) {
-						$m = base64url_decode($key[1]);
-						$e = base64url_decode($key[2]);
+						$m = Strings::base64UrlDecode($key[1]);
+						$e = Strings::base64UrlDecode($key[2]);
 						$data["pubkey"] = Crypto::meToPem($m, $e);
 					}
 				}
@@ -1648,8 +1649,8 @@ class Probe
 		$data["nick"]    = $data["name"];
 		$data["photo"]   = Network::lookupAvatarByEmail($uri);
 		$data["url"]     = 'mailto:'.$uri;
-		$data["notify"]  = 'smtp '.random_string();
-		$data["poll"]    = 'email '.random_string();
+		$data["notify"]  = 'smtp ' . Strings::getRandomHex();
+		$data["poll"]    = 'email ' . Strings::getRandomHex();
 
 		$x = Email::messageMeta($mbox, $msgs[0]);
 		if (stristr($x[0]->from, $uri)) {
@@ -1673,7 +1674,7 @@ class Probe
 						}
 					}
 
-					$data["name"] = notags($data["name"]);
+					$data["name"] = Strings::escapeTags($data["name"]);
 				}
 			}
 		}
diff --git a/src/Object/Post.php b/src/Object/Post.php
index 644d53e25..220168343 100644
--- a/src/Object/Post.php
+++ b/src/Object/Post.php
@@ -21,6 +21,7 @@ use Friendica\Model\Term;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 
 require_once 'include/dba.php';
@@ -156,7 +157,7 @@ class Post extends BaseObject
 
 		$shareable = in_array($conv->getProfileOwner(), [0, local_user()]) && $item['private'] != 1;
 
-		if (local_user() && link_compare($a->contact['url'], $item['author-link'])) {
+		if (local_user() && Strings::compareLink($a->contact['url'], $item['author-link'])) {
 			if ($item["event-id"] != 0) {
 				$edpost = ["events/event/" . $item['event-id'], L10n::t("Edit")];
 			} else {
@@ -315,7 +316,7 @@ class Post extends BaseObject
 
 		localize_item($item);
 
-		$body = prepare_body($item, true);
+		$body = Item::prepareBody($item, true);
 
 		list($categories, $folders) = get_cats_and_terms($item);
 
@@ -392,7 +393,7 @@ class Post extends BaseObject
 			'owner_url'       => $this->getOwnerUrl(),
 			'owner_photo'     => $a->removeBaseURL(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)),
 			'owner_name'      => htmlentities($owner_name_e),
-			'plink'           => get_plink($item),
+			'plink'           => Item::getPlink($item),
 			'edpost'          => Feature::isEnabled($conv->getProfileOwner(), 'edit_posts') ? $edpost : '',
 			'isstarred'       => $isstarred,
 			'star'            => Feature::isEnabled($conv->getProfileOwner(), 'star_posts') ? $star : '',
@@ -854,8 +855,8 @@ class Post extends BaseObject
 					$this->owner_name = $a->page_contact['name'];
 					$this->wall_to_wall = true;
 				} elseif ($this->getDataValue('owner-link')) {
-					$owner_linkmatch = (($this->getDataValue('owner-link')) && link_compare($this->getDataValue('owner-link'), $this->getDataValue('author-link')));
-					$alias_linkmatch = (($this->getDataValue('alias')) && link_compare($this->getDataValue('alias'), $this->getDataValue('author-link')));
+					$owner_linkmatch = (($this->getDataValue('owner-link')) && Strings::compareLink($this->getDataValue('owner-link'), $this->getDataValue('author-link')));
+					$alias_linkmatch = (($this->getDataValue('alias')) && Strings::compareLink($this->getDataValue('alias'), $this->getDataValue('author-link')));
 					$owner_namematch = (($this->getDataValue('owner-name')) && $this->getDataValue('owner-name') == $this->getDataValue('author-name'));
 
 					if (!$owner_linkmatch && !$alias_linkmatch && !$owner_namematch) {
diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php
index 1e5001010..bff8767f3 100644
--- a/src/Protocol/ActivityPub/Processor.php
+++ b/src/Protocol/ActivityPub/Processor.php
@@ -5,6 +5,8 @@
 namespace Friendica\Protocol\ActivityPub;
 
 use Friendica\Database\DBA;
+use Friendica\Content\Text\HTML;
+use Friendica\Core\Config;
 use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
 use Friendica\Model\Conversation;
@@ -13,11 +15,10 @@ use Friendica\Model\APContact;
 use Friendica\Model\Item;
 use Friendica\Model\Event;
 use Friendica\Model\User;
-use Friendica\Content\Text\HTML;
-use Friendica\Util\JsonLD;
-use Friendica\Core\Config;
 use Friendica\Protocol\ActivityPub;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\JsonLD;
+use Friendica\Util\Strings;
 
 /**
  * ActivityPub Processor Protocol class
@@ -39,6 +40,23 @@ class Processor
 		return $body;
 	}
 
+	/**
+	 * Replaces emojis in the body
+	 *
+	 * @param array $emojis
+	 * @param string $body
+	 *
+	 * @return string with replaced emojis
+	 */
+	public static function replaceEmojis($emojis, $body)
+	{
+		foreach ($emojis as $emoji) {
+			$replace = '[class=emoji mastodon][img=' . $emoji['href'] . ']' . $emoji['name'] . '[/img][/class]';
+			$body = str_replace($emoji['name'], $replace, $body);
+		}
+		return $body;
+	}
+
 	/**
 	 * Constructs a string with tags for a given tag array
 	 *
@@ -115,7 +133,8 @@ class Processor
 		$item['edited'] = $activity['updated'];
 		$item['title'] = HTML::toBBCode($activity['name']);
 		$item['content-warning'] = HTML::toBBCode($activity['summary']);
-		$item['body'] = self::convertMentions(HTML::toBBCode($activity['content']));
+		$content = self::replaceEmojis($activity['emojis'], HTML::toBBCode($activity['content']));
+		$item['body'] = self::convertMentions($content);
 		$item['tag'] = self::constructTagList($activity['tags'], $activity['sensitive']);
 
 		Item::update($item, ['uri' => $activity['id']]);
@@ -250,7 +269,8 @@ class Processor
 		$item['guid'] = $activity['diaspora:guid'];
 		$item['title'] = HTML::toBBCode($activity['name']);
 		$item['content-warning'] = HTML::toBBCode($activity['summary']);
-		$item['body'] = self::convertMentions(HTML::toBBCode($activity['content']));
+		$content = self::replaceEmojis($activity['emojis'], HTML::toBBCode($activity['content']));
+		$item['body'] = self::convertMentions($content);
 
 		if (($activity['object_type'] == 'as:Video') && !empty($activity['alternate-url'])) {
 			$item['body'] .= "\n[video]" . $activity['alternate-url'] . '[/video]';
@@ -398,7 +418,7 @@ class Processor
 			return;
 		}
 
-		$contacts = DBA::select('contact', ['id'], ['nurl' => normalise_link($activity['object_id'])]);
+		$contacts = DBA::select('contact', ['id'], ['nurl' => Strings::normaliseLink($activity['object_id'])]);
 		while ($contact = DBA::fetch($contacts)) {
 			Contact::remove($contact['id']);
 		}
diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php
index 2cc165b23..686ac8be3 100644
--- a/src/Protocol/ActivityPub/Receiver.php
+++ b/src/Protocol/ActivityPub/Receiver.php
@@ -5,18 +5,19 @@
 namespace Friendica\Protocol\ActivityPub;
 
 use Friendica\Database\DBA;
-use Friendica\Util\HTTPSignature;
 use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
 use Friendica\Model\Contact;
 use Friendica\Model\APContact;
+use Friendica\Model\Conversation;
 use Friendica\Model\Item;
 use Friendica\Model\User;
+use Friendica\Protocol\ActivityPub;
+use Friendica\Util\DateTimeFormat;
+use Friendica\Util\HTTPSignature;
 use Friendica\Util\JsonLD;
 use Friendica\Util\LDSignature;
-use Friendica\Protocol\ActivityPub;
-use Friendica\Model\Conversation;
-use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 
 /**
  * @brief ActivityPub Receiver Protocol class
@@ -455,7 +456,7 @@ class Receiver
 
 				if (($receiver == self::PUBLIC_COLLECTION) && !empty($actor)) {
 					// This will most likely catch all OStatus connections to Mastodon
-					$condition = ['alias' => [$actor, normalise_link($actor)], 'rel' => [Contact::SHARING, Contact::FRIEND]
+					$condition = ['alias' => [$actor, Strings::normaliseLink($actor)], 'rel' => [Contact::SHARING, Contact::FRIEND]
 						, 'archive' => false, 'pending' => false];
 					$contacts = DBA::select('contact', ['uid'], $condition);
 					while ($contact = DBA::fetch($contacts)) {
@@ -472,7 +473,7 @@ class Receiver
 				}
 
 				// Fetching all directly addressed receivers
-				$condition = ['self' => true, 'nurl' => normalise_link($receiver)];
+				$condition = ['self' => true, 'nurl' => Strings::normaliseLink($receiver)];
 				$contact = DBA::selectFirst('contact', ['uid', 'contact-type'], $condition);
 				if (!DBA::isResult($contact)) {
 					continue;
@@ -482,7 +483,7 @@ class Receiver
 				// Exception: The receiver is targetted via "to" or this is a comment
 				if ((($element != 'as:to') && empty($replyto)) || ($contact['contact-type'] == Contact::ACCOUNT_TYPE_COMMUNITY)) {
 					$networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
-					$condition = ['nurl' => normalise_link($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
+					$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
 						'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']];
 
 					// Forum posts are only accepted from forum contacts
@@ -516,7 +517,7 @@ class Receiver
 	{
 		$receivers = [];
 		$networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
-		$condition = ['nurl' => normalise_link($actor), 'rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
+		$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
 			'network' => $networks, 'archive' => false, 'pending' => false];
 		$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
 		while ($contact = DBA::fetch($contacts)) {
@@ -589,7 +590,7 @@ class Receiver
 		unset($profile['photo']);
 		unset($profile['baseurl']);
 
-		$profile['nurl'] = normalise_link($profile['url']);
+		$profile['nurl'] = Strings::normaliseLink($profile['url']);
 		DBA::update('contact', $profile, ['id' => $cid]);
 
 		Contact::updateAvatar($photo, $uid, $cid);
@@ -614,12 +615,12 @@ class Receiver
 		}
 
 		foreach ($receivers as $receiver) {
-			$contact = DBA::selectFirst('contact', ['id'], ['uid' => $receiver, 'network' => Protocol::OSTATUS, 'nurl' => normalise_link($actor)]);
+			$contact = DBA::selectFirst('contact', ['id'], ['uid' => $receiver, 'network' => Protocol::OSTATUS, 'nurl' => Strings::normaliseLink($actor)]);
 			if (DBA::isResult($contact)) {
 				self::switchContact($contact['id'], $receiver, $actor);
 			}
 
-			$contact = DBA::selectFirst('contact', ['id'], ['uid' => $receiver, 'network' => Protocol::OSTATUS, 'alias' => [normalise_link($actor), $actor]]);
+			$contact = DBA::selectFirst('contact', ['id'], ['uid' => $receiver, 'network' => Protocol::OSTATUS, 'alias' => [Strings::normaliseLink($actor), $actor]]);
 			if (DBA::isResult($contact)) {
 				self::switchContact($contact['id'], $receiver, $actor);
 			}
@@ -727,13 +728,48 @@ class Receiver
 				continue;
 			}
 
-			$taglist[] = ['type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type')),
+			$element = ['type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type')),
 				'href' => JsonLD::fetchElement($tag, 'as:href'),
 				'name' => JsonLD::fetchElement($tag, 'as:name')];
+
+			if (empty($element['type'])) {
+				continue;
+			}
+
+			$taglist[] = $element;
 		}
 		return $taglist;
 	}
 
+	/**
+	 * Convert emojis from JSON-LD format into a simplified format
+	 *
+	 * @param array $tags Tags in JSON-LD format
+	 *
+	 * @return array with emojis in a simplified format
+	 */
+	private static function processEmojis($emojis)
+	{
+		$emojilist = [];
+
+		if (empty($emojis)) {
+			return [];
+		}
+
+		foreach ($emojis as $emoji) {
+			if (empty($emoji) || (JsonLD::fetchElement($emoji, '@type') != 'toot:Emoji') || empty($emoji['as:icon'])) {
+				continue;
+			}
+
+			$url = JsonLD::fetchElement($emoji['as:icon'], 'as:url');
+			$element = ['name' => JsonLD::fetchElement($emoji, 'as:name'),
+				'href' => $url];
+
+			$emojilist[] = $element;
+		}
+		return $emojilist;
+	}
+
 	/**
 	 * Convert attachments from JSON-LD format into a simplified format
 	 *
@@ -821,6 +857,7 @@ class Receiver
 		$object_data['longitude'] = JsonLD::fetchElement($object_data, 'longitude', '@value');
 		$object_data['attachments'] = self::processAttachments(JsonLD::fetchElementArray($object, 'as:attachment'));
 		$object_data['tags'] = self::processTags(JsonLD::fetchElementArray($object, 'as:tag'));
+		$object_data['emojis'] = self::processEmojis(JsonLD::fetchElementArray($object, 'as:tag', 'toot:Emoji'));
 		$object_data['generator'] = JsonLD::fetchElement($object, 'as:generator', 'as:name', '@type', 'as:Application');
 		$object_data['alternate-url'] = JsonLD::fetchElement($object, 'as:url');
 
diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php
index 8a2b21745..ff5081183 100644
--- a/src/Protocol/DFRN.php
+++ b/src/Protocol/DFRN.php
@@ -33,6 +33,7 @@ use Friendica\Object\Image;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use HTMLPurifier;
 use HTMLPurifier_Config;
@@ -240,7 +241,7 @@ class DFRN
 		if (isset($category)) {
 			$sql_post_table = sprintf(
 				"INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ",
-				DBA::escape(protect_sprintf($category)),
+				DBA::escape(Strings::protectSprintf($category)),
 				intval(TERM_OBJ_POST),
 				intval(TERM_CATEGORY),
 				intval($owner_id)
@@ -1001,7 +1002,7 @@ class DFRN
 		XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"] . "+00:00", DateTimeFormat::ATOM));
 
 		// "dfrn:env" is used to read the content
-		XML::addElement($doc, $entry, "dfrn:env", base64url_encode($body, true));
+		XML::addElement($doc, $entry, "dfrn:env", Strings::base64UrlEncode($body, true));
 
 		// The "content" field is not read by the receiver. We could remove it when the type is "text"
 		// We keep it at the moment, maybe there is some old version that doesn't read "dfrn:env"
@@ -1096,7 +1097,7 @@ class DFRN
 		}
 
 		foreach ($mentioned as $mention) {
-			$condition = ['uid' => $owner["uid"], 'nurl' => normalise_link($mention)];
+			$condition = ['uid' => $owner["uid"], 'nurl' => Strings::normaliseLink($mention)];
 			$contact = DBA::selectFirst('contact', ['forum', 'prv'], $condition);
 
 			if (DBA::isResult($contact) && ($contact["forum"] || $contact["prv"])) {
@@ -1568,7 +1569,7 @@ class DFRN
 		$fields = ['id', 'uid', 'url', 'network', 'avatar-date', 'avatar', 'name-date', 'uri-date', 'addr',
 			'name', 'nick', 'about', 'location', 'keywords', 'xmpp', 'bdyear', 'bd', 'hidden', 'contact-type'];
 		$condition = ["`uid` = ? AND `nurl` = ? AND `network` != ?",
-			$importer["importer_uid"], normalise_link($author["link"]), Protocol::STATUSNET];
+			$importer["importer_uid"], Strings::normaliseLink($author["link"]), Protocol::STATUSNET];
 		$contact_old = DBA::selectFirst('contact', $fields, $condition);
 
 		if (DBA::isResult($contact_old)) {
@@ -1959,7 +1960,7 @@ class DFRN
 		 *
 		 * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
 		 */
-		$condition = ['name' => $suggest["name"], 'nurl' => normalise_link($suggest["url"]),
+		$condition = ['name' => $suggest["name"], 'nurl' => Strings::normaliseLink($suggest["url"]),
 			'uid' => $suggest["uid"]];
 		if (DBA::exists('contact', $condition)) {
 			return false;
@@ -2009,7 +2010,7 @@ class DFRN
 
 		$fid = $r[0]["id"];
 
-		$hash = random_string();
+		$hash = Strings::getRandomHex();
 
 		$r = q(
 			"INSERT INTO `intro` (`uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked`)
@@ -2099,18 +2100,18 @@ class DFRN
 		$relocate["server_url"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $relocate["url"]);
 
 		$fields = ['name' => $relocate["name"], 'photo' => $relocate["avatar"],
-			'url' => $relocate["url"], 'nurl' => normalise_link($relocate["url"]),
+			'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]),
 			'addr' => $relocate["addr"], 'connect' => $relocate["addr"],
 			'notify' => $relocate["notify"], 'server_url' => $relocate["server_url"]];
-		DBA::update('gcontact', $fields, ['nurl' => normalise_link($old["url"])]);
+		DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($old["url"])]);
 
 		// Update the contact table. We try to find every entry.
 		$fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"],
-			'url' => $relocate["url"], 'nurl' => normalise_link($relocate["url"]),
+			'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]),
 			'addr' => $relocate["addr"], 'request' => $relocate["request"],
 			'confirm' => $relocate["confirm"], 'notify' => $relocate["notify"],
 			'poll' => $relocate["poll"], 'site-pubkey' => $relocate["sitepubkey"]];
-		$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer["id"], normalise_link($old["url"])];
+		$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer["id"], Strings::normaliseLink($old["url"])];
 
 		DBA::update('contact', $fields, $condition);
 
@@ -2255,7 +2256,7 @@ class DFRN
 				}
 			}
 
-			if ($Blink && link_compare($Blink, System::baseUrl() . "/profile/" . $importer["nickname"])) {
+			if ($Blink && Strings::compareLink($Blink, System::baseUrl() . "/profile/" . $importer["nickname"])) {
 				$author = DBA::selectFirst('contact', ['name', 'thumb', 'url'], ['id' => $item['author-id']]);
 
 				$item['id'] = $posted_id;
@@ -2493,7 +2494,7 @@ class DFRN
 		$item["body"] = XML::getFirstNodeValue($xpath, "dfrn:env/text()", $entry);
 		$item["body"] = str_replace([' ',"\t","\r","\n"], ['','','',''], $item["body"]);
 		// make sure nobody is trying to sneak some html tags by us
-		$item["body"] = notags(base64url_decode($item["body"]));
+		$item["body"] = Strings::escapeTags(Strings::base64UrlDecode($item["body"]));
 
 		$item["body"] = BBCode::limitBodySize($item["body"]);
 
@@ -2737,7 +2738,7 @@ class DFRN
 				Logger::log("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.", Logger::DEBUG);
 				return;
 			}
-			if (!link_compare($item["owner-link"], $importer["url"])) {
+			if (!Strings::compareLink($item["owner-link"], $importer["url"])) {
 				/*
 				 * The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
 				 * but otherwise there's a possible data mixup on the sender's system.
@@ -2985,7 +2986,7 @@ class DFRN
 				return;
 			}
 			$baseurl = substr($baseurl, $domain_st + 3);
-			$nurl = normalise_link($baseurl);
+			$nurl = Strings::normaliseLink($baseurl);
 
 			/// @todo Why is there a query for "url" *and* "nurl"? Especially this normalising is strange.
 			$r = q("SELECT `id` FROM `contact` WHERE `uid` = (SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1)
@@ -3030,7 +3031,7 @@ class DFRN
 				return;
 			}
 
-			$sec = random_string();
+			$sec = Strings::getRandomHex();
 
 			DBA::insert('profile_check', ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id, 'sec' => $sec, 'expire' => time() + 45]);
 
@@ -3078,18 +3079,18 @@ class DFRN
 		$community_page = ($user['page-flags'] == Contact::PAGE_COMMUNITY);
 		$prvgroup = ($user['page-flags'] == Contact::PAGE_PRVGROUP);
 
-		$link = normalise_link(System::baseUrl() . '/profile/' . $user['nickname']);
+		$link = Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']);
 
 		/*
 		 * Diaspora uses their own hardwired link URL in @-tags
 		 * instead of the one we supply with webfinger
 		 */
-		$dlink = normalise_link(System::baseUrl() . '/u/' . $user['nickname']);
+		$dlink = Strings::normaliseLink(System::baseUrl() . '/u/' . $user['nickname']);
 
 		$cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
 		if ($cnt) {
 			foreach ($matches as $mtch) {
-				if (link_compare($link, $mtch[1]) || link_compare($dlink, $mtch[1])) {
+				if (Strings::compareLink($link, $mtch[1]) || Strings::compareLink($dlink, $mtch[1])) {
 					$mention = true;
 					Logger::log('mention found: ' . $mtch[2]);
 				}
diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php
index 0b5c9c949..d59eb7a0a 100644
--- a/src/Protocol/Diaspora.php
+++ b/src/Protocol/Diaspora.php
@@ -34,6 +34,7 @@ use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Map;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use SimpleXMLElement;
 
@@ -112,7 +113,7 @@ class Diaspora
 		// Now we are collecting all relay contacts
 		foreach ($serverlist as $server_url) {
 			// We don't send messages to ourselves
-			if (link_compare($server_url, System::baseUrl())) {
+			if (Strings::compareLink($server_url, System::baseUrl())) {
 				continue;
 			}
 			$contact = self::getRelayContact($server_url);
@@ -146,7 +147,7 @@ class Diaspora
 		$fields = ['batch', 'id', 'name', 'network', 'archive', 'blocked'];
 
 		// Fetch the relay contact
-		$condition = ['uid' => 0, 'nurl' => normalise_link($server_url),
+		$condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($server_url),
 			'contact-type' => Contact::ACCOUNT_TYPE_RELAY];
 		$contact = DBA::selectFirst('contact', $fields, $condition);
 
@@ -185,7 +186,7 @@ class Diaspora
 
 		$fields = array_merge($fields, $network_fields);
 
-		$condition = ['uid' => 0, 'nurl' => normalise_link($server_url),
+		$condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($server_url),
 			'contact-type' => Contact::ACCOUNT_TYPE_RELAY];
 
 		if (DBA::exists('contact', $condition)) {
@@ -297,23 +298,23 @@ class Diaspora
 
 		$handle = "";
 
-		$data = base64url_decode($children->data);
+		$data = Strings::base64UrlDecode($children->data);
 		$type = $children->data->attributes()->type[0];
 
 		$encoding = $children->encoding;
 
 		$alg = $children->alg;
 
-		$sig = base64url_decode($children->sig);
+		$sig = Strings::base64UrlDecode($children->sig);
 		$key_id = $children->sig->attributes()->key_id[0];
 		if ($key_id != "") {
-			$handle = base64url_decode($key_id);
+			$handle = Strings::base64UrlDecode($key_id);
 		}
 
-		$b64url_data = base64url_encode($data);
+		$b64url_data = Strings::base64UrlEncode($data);
 		$msg = str_replace(["\n", "\r", " ", "\t"], ["", "", "", ""], $b64url_data);
 
-		$signable_data = $msg.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
+		$signable_data = $msg.".".Strings::base64UrlEncode($type).".".Strings::base64UrlEncode($encoding).".".Strings::base64UrlEncode($alg);
 
 		if ($handle == '') {
 			Logger::log('No author could be decoded. Discarding. Message: ' . $envelope);
@@ -425,10 +426,10 @@ class Diaspora
 		$type = $base->data[0]->attributes()->type[0];
 		$encoding = $base->encoding;
 		$alg = $base->alg;
-		$signed_data = $data.'.'.base64url_encode($type).'.'.base64url_encode($encoding).'.'.base64url_encode($alg);
+		$signed_data = $data.'.'.Strings::base64UrlEncode($type).'.'.Strings::base64UrlEncode($encoding).'.'.Strings::base64UrlEncode($alg);
 
 		// This is the signature
-		$signature = base64url_decode($base->sig);
+		$signature = Strings::base64UrlDecode($base->sig);
 
 		// Get the senders' public key
 		$key_id = $base->sig[0]->attributes()->key_id[0];
@@ -462,7 +463,7 @@ class Diaspora
 			}
 		}
 
-		return ['message' => (string)base64url_decode($base->data),
+		return ['message' => (string)Strings::base64UrlDecode($base->data),
 				'author' => XML::unescape($author_addr),
 				'key' => (string)$key];
 	}
@@ -546,7 +547,7 @@ class Diaspora
 
 
 		// Stash the signature away for now. We have to find their key or it won't be good for anything.
-		$signature = base64url_decode($base->sig);
+		$signature = Strings::base64UrlDecode($base->sig);
 
 		// unpack the  data
 
@@ -562,11 +563,11 @@ class Diaspora
 		$alg = $base->alg;
 
 
-		$signed_data = $data.'.'.base64url_encode($type).'.'.base64url_encode($encoding).'.'.base64url_encode($alg);
+		$signed_data = $data.'.'.Strings::base64UrlEncode($type).'.'.Strings::base64UrlEncode($encoding).'.'.Strings::base64UrlEncode($alg);
 
 
 		// decode the data
-		$data = base64url_decode($data);
+		$data = Strings::base64UrlDecode($data);
 
 
 		if ($public) {
@@ -1433,7 +1434,7 @@ class Diaspora
 	 */
 	private static function authorContactByUrl($def_contact, $person, $uid)
 	{
-		$condition = ['nurl' => normalise_link($person["url"]), 'uid' => $uid];
+		$condition = ['nurl' => Strings::normaliseLink($person["url"]), 'uid' => $uid];
 		$contact = DBA::selectFirst('contact', ['id', 'network'], $condition);
 		if (DBA::isResult($contact)) {
 			$cid = $contact["id"];
@@ -1505,9 +1506,9 @@ class Diaspora
 	 */
 	private static function receiveAccountMigration(array $importer, $data)
 	{
-		$old_handle = notags(XML::unescape($data->author));
-		$new_handle = notags(XML::unescape($data->profile->author));
-		$signature = notags(XML::unescape($data->signature));
+		$old_handle = Strings::escapeTags(XML::unescape($data->author));
+		$new_handle = Strings::escapeTags(XML::unescape($data->profile->author));
+		$signature = Strings::escapeTags(XML::unescape($data->signature));
 
 		$contact = self::contactByHandle($importer["uid"], $old_handle);
 		if (!$contact) {
@@ -1535,7 +1536,7 @@ class Diaspora
 			return false;
 		}
 
-		$fields = ['url' => $data['url'], 'nurl' => normalise_link($data['url']),
+		$fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']),
 				'name' => $data['name'], 'nick' => $data['nick'],
 				'addr' => $data['addr'], 'batch' => $data['batch'],
 				'notify' => $data['notify'], 'poll' => $data['poll'],
@@ -1543,7 +1544,7 @@ class Diaspora
 
 		DBA::update('contact', $fields, ['addr' => $old_handle]);
 
-		$fields = ['url' => $data['url'], 'nurl' => normalise_link($data['url']),
+		$fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']),
 				'name' => $data['name'], 'nick' => $data['nick'],
 				'addr' => $data['addr'], 'connect' => $data['addr'],
 				'notify' => $data['notify'], 'photo' => $data['photo'],
@@ -1565,7 +1566,7 @@ class Diaspora
 	 */
 	private static function receiveAccountDeletion($data)
 	{
-		$author = notags(XML::unescape($data->author));
+		$author = Strings::escapeTags(XML::unescape($data->author));
 
 		$contacts = DBA::select('contact', ['id'], ['addr' => $author]);
 		while ($contact = DBA::fetch($contacts)) {
@@ -1656,19 +1657,19 @@ class Diaspora
 	 */
 	private static function receiveComment(array $importer, $sender, $data, $xml)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$parent_guid = notags(XML::unescape($data->parent_guid));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$parent_guid = Strings::escapeTags(XML::unescape($data->parent_guid));
 		$text = XML::unescape($data->text);
 
 		if (isset($data->created_at)) {
-			$created_at = DateTimeFormat::utc(notags(XML::unescape($data->created_at)));
+			$created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($data->created_at)));
 		} else {
 			$created_at = DateTimeFormat::utcNow();
 		}
 
 		if (isset($data->thread_parent_guid)) {
-			$thread_parent_guid = notags(XML::unescape($data->thread_parent_guid));
+			$thread_parent_guid = Strings::escapeTags(XML::unescape($data->thread_parent_guid));
 			$thr_uri = self::getUriFromGuid("", $thread_parent_guid, true);
 		} else {
 			$thr_uri = "";
@@ -1773,24 +1774,24 @@ class Diaspora
 	 */
 	private static function receiveConversationMessage(array $importer, array $contact, $data, $msg, $mesg, $conversation)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$subject = notags(XML::unescape($data->subject));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$subject = Strings::escapeTags(XML::unescape($data->subject));
 
 		// "diaspora_handle" is the element name from the old version
 		// "author" is the element name from the new version
 		if ($mesg->author) {
-			$msg_author = notags(XML::unescape($mesg->author));
+			$msg_author = Strings::escapeTags(XML::unescape($mesg->author));
 		} elseif ($mesg->diaspora_handle) {
-			$msg_author = notags(XML::unescape($mesg->diaspora_handle));
+			$msg_author = Strings::escapeTags(XML::unescape($mesg->diaspora_handle));
 		} else {
 			return false;
 		}
 
-		$msg_guid = notags(XML::unescape($mesg->guid));
-		$msg_conversation_guid = notags(XML::unescape($mesg->conversation_guid));
+		$msg_guid = Strings::escapeTags(XML::unescape($mesg->guid));
+		$msg_conversation_guid = Strings::escapeTags(XML::unescape($mesg->conversation_guid));
 		$msg_text = XML::unescape($mesg->text);
-		$msg_created_at = DateTimeFormat::utc(notags(XML::unescape($mesg->created_at)));
+		$msg_created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($mesg->created_at)));
 
 		if ($msg_conversation_guid != $guid) {
 			Logger::log("message conversation guid does not belong to the current conversation.");
@@ -1861,11 +1862,11 @@ class Diaspora
 	 */
 	private static function receiveConversation(array $importer, $msg, $data)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$subject = notags(XML::unescape($data->subject));
-		$created_at = DateTimeFormat::utc(notags(XML::unescape($data->created_at)));
-		$participants = notags(XML::unescape($data->participants));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$subject = Strings::escapeTags(XML::unescape($data->subject));
+		$created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($data->created_at)));
+		$participants = Strings::escapeTags(XML::unescape($data->participants));
 
 		$messages = $data->message;
 
@@ -1919,11 +1920,11 @@ class Diaspora
 	 */
 	private static function receiveLike(array $importer, $sender, $data)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$parent_guid = notags(XML::unescape($data->parent_guid));
-		$parent_type = notags(XML::unescape($data->parent_type));
-		$positive = notags(XML::unescape($data->positive));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$parent_guid = Strings::escapeTags(XML::unescape($data->parent_guid));
+		$parent_type = Strings::escapeTags(XML::unescape($data->parent_type));
+		$positive = Strings::escapeTags(XML::unescape($data->positive));
 
 		// likes on comments aren't supported by Diaspora - only on posts
 		// But maybe this will be supported in the future, so we will accept it.
@@ -2028,11 +2029,11 @@ class Diaspora
 	 */
 	private static function receiveMessage(array $importer, $data)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$conversation_guid = notags(XML::unescape($data->conversation_guid));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$conversation_guid = Strings::escapeTags(XML::unescape($data->conversation_guid));
 		$text = XML::unescape($data->text);
-		$created_at = DateTimeFormat::utc(notags(XML::unescape($data->created_at)));
+		$created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($data->created_at)));
 
 		$contact = self::allowedContactByHandle($importer, $author, true);
 		if (!$contact) {
@@ -2103,8 +2104,8 @@ class Diaspora
 	 */
 	private static function receiveParticipation(array $importer, $data)
 	{
-		$author = strtolower(notags(XML::unescape($data->author)));
-		$parent_guid = notags(XML::unescape($data->parent_guid));
+		$author = strtolower(Strings::escapeTags(XML::unescape($data->author)));
+		$parent_guid = Strings::escapeTags(XML::unescape($data->parent_guid));
 
 		$contact_id = Contact::getIdForURL($author);
 		if (!$contact_id) {
@@ -2196,7 +2197,7 @@ class Diaspora
 	 */
 	private static function receiveProfile(array $importer, $data)
 	{
-		$author = strtolower(notags(XML::unescape($data->author)));
+		$author = strtolower(Strings::escapeTags(XML::unescape($data->author)));
 
 		$contact = self::contactByHandle($importer["uid"], $author);
 		if (!$contact) {
@@ -2391,7 +2392,7 @@ class Diaspora
 			DBA::escape($ret["addr"]),
 			DateTimeFormat::utcNow(),
 			DBA::escape($ret["url"]),
-			DBA::escape(normalise_link($ret["url"])),
+			DBA::escape(Strings::normaliseLink($ret["url"])),
 			DBA::escape($batch),
 			DBA::escape($ret["name"]),
 			DBA::escape($ret["nick"]),
@@ -2421,7 +2422,7 @@ class Diaspora
 		if (in_array($importer["page-flags"], [Contact::PAGE_NORMAL, Contact::PAGE_PRVGROUP])) {
 			Logger::log("Sending intra message for author ".$author.".", Logger::DEBUG);
 
-			$hash = random_string().(string)time();   // Generate a confirm_key
+			$hash = Strings::getRandomHex().(string)time();   // Generate a confirm_key
 
 			$ret = q(
 				"INSERT INTO `intro` (`uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
@@ -2573,13 +2574,13 @@ class Diaspora
 	 */
 	private static function receiveReshare(array $importer, $data, $xml)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$created_at = DateTimeFormat::utc(notags(XML::unescape($data->created_at)));
-		$root_author = notags(XML::unescape($data->root_author));
-		$root_guid = notags(XML::unescape($data->root_guid));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($data->created_at)));
+		$root_author = Strings::escapeTags(XML::unescape($data->root_author));
+		$root_guid = Strings::escapeTags(XML::unescape($data->root_guid));
 		/// @todo handle unprocessed property "provider_display_name"
-		$public = notags(XML::unescape($data->public));
+		$public = Strings::escapeTags(XML::unescape($data->public));
 
 		$contact = self::allowedContactByHandle($importer, $author, false);
 		if (!$contact) {
@@ -2665,9 +2666,9 @@ class Diaspora
 	 */
 	private static function itemRetraction(array $importer, array $contact, $data)
 	{
-		$author = notags(XML::unescape($data->author));
-		$target_guid = notags(XML::unescape($data->target_guid));
-		$target_type = notags(XML::unescape($data->target_type));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$target_guid = Strings::escapeTags(XML::unescape($data->target_guid));
+		$target_type = Strings::escapeTags(XML::unescape($data->target_type));
 
 		$person = self::personByHandle($author);
 		if (!is_array($person)) {
@@ -2705,7 +2706,7 @@ class Diaspora
 			$parent = Item::selectFirst(['author-link'], ['id' => $item["parent"]]);
 
 			// Only delete it if the parent author really fits
-			if (!link_compare($parent["author-link"], $contact["url"]) && !link_compare($item["author-link"], $contact["url"])) {
+			if (!Strings::compareLink($parent["author-link"], $contact["url"]) && !Strings::compareLink($item["author-link"], $contact["url"])) {
 				Logger::log("Thread author ".$parent["author-link"]." and item author ".$item["author-link"]." don't fit to expected contact ".$contact["url"], Logger::DEBUG);
 				continue;
 			}
@@ -2729,7 +2730,7 @@ class Diaspora
 	 */
 	private static function receiveRetraction(array $importer, $sender, $data)
 	{
-		$target_type = notags(XML::unescape($data->target_type));
+		$target_type = Strings::escapeTags(XML::unescape($data->target_type));
 
 		$contact = self::contactByHandle($importer["uid"], $sender);
 		if (!$contact && (in_array($target_type, ["Contact", "Person"]))) {
@@ -2774,12 +2775,12 @@ class Diaspora
 	 */
 	private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, $xml)
 	{
-		$author = notags(XML::unescape($data->author));
-		$guid = notags(XML::unescape($data->guid));
-		$created_at = DateTimeFormat::utc(notags(XML::unescape($data->created_at)));
-		$public = notags(XML::unescape($data->public));
+		$author = Strings::escapeTags(XML::unescape($data->author));
+		$guid = Strings::escapeTags(XML::unescape($data->guid));
+		$created_at = DateTimeFormat::utc(Strings::escapeTags(XML::unescape($data->created_at)));
+		$public = Strings::escapeTags(XML::unescape($data->public));
 		$text = XML::unescape($data->text);
-		$provider_display_name = notags(XML::unescape($data->provider_display_name));
+		$provider_display_name = Strings::escapeTags(XML::unescape($data->provider_display_name));
 
 		$contact = self::allowedContactByHandle($importer, $author, false);
 		if (!$contact) {
@@ -2794,7 +2795,7 @@ class Diaspora
 		$address = [];
 		if ($data->location) {
 			foreach ($data->location->children() as $fieldname => $data) {
-				$address[$fieldname] = notags(XML::unescape($data));
+				$address[$fieldname] = Strings::escapeTags(XML::unescape($data));
 			}
 		}
 
@@ -2961,14 +2962,14 @@ class Diaspora
 	 */
 	public static function buildMagicEnvelope($msg, array $user)
 	{
-		$b64url_data = base64url_encode($msg);
+		$b64url_data = Strings::base64UrlEncode($msg);
 		$data = str_replace(["\n", "\r", " ", "\t"], ["", "", "", ""], $b64url_data);
 
-		$key_id = base64url_encode(self::myHandle($user));
+		$key_id = Strings::base64UrlEncode(self::myHandle($user));
 		$type = "application/xml";
 		$encoding = "base64url";
 		$alg = "RSA-SHA256";
-		$signable_data = $data.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
+		$signable_data = $data.".".Strings::base64UrlEncode($type).".".Strings::base64UrlEncode($encoding).".".Strings::base64UrlEncode($alg);
 
 		// Fallback if the private key wasn't transmitted in the expected field
 		if ($user['uprvkey'] == "") {
@@ -2976,7 +2977,7 @@ class Diaspora
 		}
 
 		$signature = Crypto::rsaSign($signable_data, $user["uprvkey"]);
-		$sig = base64url_encode($signature);
+		$sig = Strings::base64UrlEncode($signature);
 
 		$xmldata = ["me:env" => ["me:data" => $data,
 							"@attributes" => ["type" => $type],
@@ -3055,7 +3056,7 @@ class Diaspora
 			return 200;
 		}
 
-		$logid = random_string(4);
+		$logid = Strings::getRandomHex(4);
 
 		$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
 
diff --git a/src/Protocol/Email.php b/src/Protocol/Email.php
index bb70972b0..ba8c31190 100644
--- a/src/Protocol/Email.php
+++ b/src/Protocol/Email.php
@@ -7,6 +7,7 @@ namespace Friendica\Protocol;
 use Friendica\Core\Logger;
 use Friendica\Content\Text\HTML;
 use Friendica\Core\Protocol;
+use Friendica\Model\Item;
 
 /**
  * @brief Email class
@@ -331,7 +332,7 @@ class Email
 
 		$part = uniqid("", true);
 
-		$html    = prepare_body($item);
+		$html    = Item::prepareBody($item);
 
 		$headers .= "Mime-Version: 1.0\n";
 		$headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n";
diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php
index a1857c5db..0c096c25a 100644
--- a/src/Protocol/OStatus.php
+++ b/src/Protocol/OStatus.php
@@ -26,6 +26,7 @@ use Friendica\Object\Image;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/dba.php';
@@ -98,7 +99,7 @@ class OStatus
 			}
 
 			$condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
-					$importer["uid"], normalise_link($author["author-link"]), normalise_link($aliaslink),
+					$importer["uid"], Strings::normaliseLink($author["author-link"]), Strings::normaliseLink($aliaslink),
 					Protocol::STATUSNET, Contact::SHARING, Contact::FRIEND];
 			$contact = DBA::selectFirst('contact', [], $condition);
 		}
@@ -164,7 +165,7 @@ class OStatus
 			//	$contact["poll"] = $value;
 
 			$contact['url'] = $author["author-link"];
-			$contact['nurl'] = normalise_link($contact['url']);
+			$contact['nurl'] = Strings::normaliseLink($contact['url']);
 
 			$value = XML::getFirstNodeValue($xpath, 'atom:author/atom:uri/text()', $context);
 			if ($value != "") {
@@ -209,7 +210,7 @@ class OStatus
 
 				// Update it with the current values
 				$fields = ['url' => $author["author-link"], 'name' => $contact["name"],
-						'nurl' => normalise_link($author["author-link"]),
+						'nurl' => Strings::normaliseLink($author["author-link"]),
 						'nick' => $contact["nick"], 'alias' => $contact["alias"],
 						'about' => $contact["about"], 'location' => $contact["location"],
 						'success_update' => DateTimeFormat::utcNow(), 'last-update' => DateTimeFormat::utcNow()];
@@ -1599,7 +1600,7 @@ class OStatus
 	{
 		$r = q(
 			"SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1",
-			DBA::escape(normalise_link($url)),
+			DBA::escape(Strings::normaliseLink($url)),
 			intval($owner["uid"])
 		);
 		if (DBA::isResult($r)) {
@@ -1608,7 +1609,7 @@ class OStatus
 		}
 
 		if (!DBA::isResult($r)) {
-			$gcontact = DBA::selectFirst('gcontact', [], ['nurl' => normalise_link($url)]);
+			$gcontact = DBA::selectFirst('gcontact', [], ['nurl' => Strings::normaliseLink($url)]);
 			if (DBA::isResult($r)) {
 				$contact = $gcontact;
 				$contact["uid"] = -1;
@@ -1651,7 +1652,7 @@ class OStatus
 	 */
 	private static function reshareEntry(DOMDocument $doc, array $item, array $owner, $repeated_guid, $toplevel)
 	{
-		if (($item["id"] != $item["parent"]) && (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+		if (($item["id"] != $item["parent"]) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
 			Logger::log("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", Logger::DEBUG);
 		}
 
@@ -1714,7 +1715,7 @@ class OStatus
 	 */
 	private static function likeEntry(DOMDocument $doc, array $item, array $owner, $toplevel)
 	{
-		if (($item["id"] != $item["parent"]) && (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+		if (($item["id"] != $item["parent"]) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
 			Logger::log("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", Logger::DEBUG);
 		}
 
@@ -1811,7 +1812,7 @@ class OStatus
 			$item['follow'] = $contact['alias'];
 		}
 
-		$condition = ['uid' => $owner['uid'], 'nurl' => normalise_link($contact["url"])];
+		$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($contact["url"])];
 		$user_contact = DBA::selectFirst('contact', ['id'], $condition);
 
 		if (DBA::isResult($user_contact)) {
@@ -1861,7 +1862,7 @@ class OStatus
 	 */
 	private static function noteEntry(DOMDocument $doc, array $item, array $owner, $toplevel)
 	{
-		if (($item["id"] != $item["parent"]) && (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+		if (($item["id"] != $item["parent"]) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
 			Logger::log("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", Logger::DEBUG);
 		}
 
@@ -2048,7 +2049,7 @@ class OStatus
 		$mentioned = $newmentions;
 
 		foreach ($mentioned as $mention) {
-			$condition = ['uid' => $owner['uid'], 'nurl' => normalise_link($mention)];
+			$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($mention)];
 			$contact = DBA::selectFirst('contact', ['forum', 'prv', 'self', 'contact-type'], $condition);
 			if ($contact["forum"] || $contact["prv"] || ($owner['contact-type'] == Contact::ACCOUNT_TYPE_COMMUNITY) ||
 				($contact['self'] && ($owner['account-type'] == Contact::ACCOUNT_TYPE_COMMUNITY))) {
diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php
index 9c22a2163..7243da523 100644
--- a/src/Protocol/PortableContact.php
+++ b/src/Protocol/PortableContact.php
@@ -23,6 +23,7 @@ use Friendica\Model\Profile;
 use Friendica\Network\Probe;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/dba.php';
@@ -284,7 +285,7 @@ class PortableContact
 
 		$r = q(
 			"SELECT `id` FROM `gserver` WHERE `nurl` = '%s' AND `last_contact` > `last_failure`",
-			DBA::escape(normalise_link($server_url))
+			DBA::escape(Strings::normaliseLink($server_url))
 		);
 
 		if (DBA::isResult($r)) {
@@ -309,7 +310,7 @@ class PortableContact
 	{
 		$gcontacts = q(
 			"SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
-			DBA::escape(normalise_link($profile))
+			DBA::escape(Strings::normaliseLink($profile))
 		);
 
 		if (!DBA::isResult($gcontacts)) {
@@ -324,7 +325,7 @@ class PortableContact
 
 		$server_url = '';
 		if ($force) {
-			$server_url = normalise_link(self::detectServer($profile));
+			$server_url = Strings::normaliseLink(self::detectServer($profile));
 		}
 
 		if (($server_url == '') && ($gcontacts[0]["server_url"] != "")) {
@@ -332,7 +333,7 @@ class PortableContact
 		}
 
 		if (!$force && (($server_url == '') || ($gcontacts[0]["server_url"] == $gcontacts[0]["nurl"]))) {
-			$server_url = normalise_link(self::detectServer($profile));
+			$server_url = Strings::normaliseLink(self::detectServer($profile));
 		}
 
 		if (!in_array($gcontacts[0]["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::FEED, Protocol::OSTATUS, ""])) {
@@ -344,7 +345,7 @@ class PortableContact
 			if (!self::checkServer($server_url, $gcontacts[0]["network"], $force)) {
 				if ($force) {
 					$fields = ['last_failure' => DateTimeFormat::utcNow()];
-					DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+					DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 				}
 
 				Logger::log("Profile ".$profile.": Server ".$server_url." wasn't reachable.", Logger::DEBUG);
@@ -356,7 +357,7 @@ class PortableContact
 		if (in_array($gcontacts[0]["network"], ["", Protocol::FEED])) {
 			$server = q(
 				"SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''",
-				DBA::escape(normalise_link($server_url))
+				DBA::escape(Strings::normaliseLink($server_url))
 			);
 
 			if ($server) {
@@ -369,7 +370,7 @@ class PortableContact
 		// noscrape is really fast so we don't cache the call.
 		if (($server_url != "") && ($gcontacts[0]["nick"] != "")) {
 			//  Use noscrape if possible
-			$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", DBA::escape(normalise_link($server_url)));
+			$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", DBA::escape(Strings::normaliseLink($server_url)));
 
 			if ($server) {
 				$curlResult = Network::curl($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
@@ -425,7 +426,7 @@ class PortableContact
 
 						if (!empty($noscrape["updated"])) {
 							$fields = ['last_contact' => DateTimeFormat::utcNow()];
-							DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+							DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 
 							Logger::log("Profile ".$profile." was last updated at ".$noscrape["updated"]." (noscrape)", Logger::DEBUG);
 
@@ -449,11 +450,11 @@ class PortableContact
 		// Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711)
 		// Then check the other link and delete this one
 		if (($data["network"] == Protocol::OSTATUS) && self::alternateOStatusUrl($profile)
-			&& (normalise_link($profile) == normalise_link($data["alias"]))
-			&& (normalise_link($profile) != normalise_link($data["url"]))
+			&& (Strings::normaliseLink($profile) == Strings::normaliseLink($data["alias"]))
+			&& (Strings::normaliseLink($profile) != Strings::normaliseLink($data["url"]))
 		) {
 			// Delete the old entry
-			DBA::delete('gcontact', ['nurl' => normalise_link($profile)]);
+			DBA::delete('gcontact', ['nurl' => Strings::normaliseLink($profile)]);
 
 			$gcontact = array_merge($gcontacts[0], $data);
 
@@ -474,7 +475,7 @@ class PortableContact
 
 		if (($data["poll"] == "") || (in_array($data["network"], [Protocol::FEED, Protocol::PHANTOM]))) {
 			$fields = ['last_failure' => DateTimeFormat::utcNow()];
-			DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+			DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 
 			Logger::log("Profile ".$profile." wasn't reachable (profile)", Logger::DEBUG);
 			return false;
@@ -490,7 +491,7 @@ class PortableContact
 
 		if (!$curlResult->isSuccess()) {
 			$fields = ['last_failure' => DateTimeFormat::utcNow()];
-			DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+			DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 
 			Logger::log("Profile ".$profile." wasn't reachable (no feed)", Logger::DEBUG);
 			return false;
@@ -533,11 +534,11 @@ class PortableContact
 			$fields['updated'] = $last_updated;
 		}
 
-		DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+		DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 
 		if (($gcontacts[0]["generation"] == 0)) {
 			$fields = ['generation' => 9];
-			DBA::update('gcontact', $fields, ['nurl' => normalise_link($profile)]);
+			DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($profile)]);
 		}
 
 		Logger::log("Profile ".$profile." was last updated at ".$last_updated, Logger::DEBUG);
@@ -930,11 +931,11 @@ class PortableContact
 			return false;
 		}
 
-		$gserver = DBA::selectFirst('gserver', [], ['nurl' => normalise_link($server_url)]);
+		$gserver = DBA::selectFirst('gserver', [], ['nurl' => Strings::normaliseLink($server_url)]);
 		if (DBA::isResult($gserver)) {
 			if ($gserver["created"] <= DBA::NULL_DATETIME) {
 				$fields = ['created' => DateTimeFormat::utcNow()];
-				$condition = ['nurl' => normalise_link($server_url)];
+				$condition = ['nurl' => Strings::normaliseLink($server_url)];
 				DBA::update('gserver', $fields, $condition);
 			}
 			$poco = $gserver["poco"];
@@ -990,7 +991,7 @@ class PortableContact
 		// Mastodon uses the "@" for user profiles.
 		// But this can be misunderstood.
 		if (parse_url($server_url, PHP_URL_USER) != '') {
-			DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => normalise_link($server_url)]);
+			DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($server_url)]);
 			return false;
 		}
 
@@ -1006,7 +1007,7 @@ class PortableContact
 		if (DBA::isResult($gserver) && ($orig_server_url == $server_url) &&
 			($curlResult->isTimeout())) {
 			Logger::log("Connection to server ".$server_url." timed out.", Logger::DEBUG);
-			DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => normalise_link($server_url)]);
+			DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($server_url)]);
 			return false;
 		}
 
@@ -1021,7 +1022,7 @@ class PortableContact
 			// Quit if there is a timeout
 			if ($curlResult->isTimeout()) {
 				Logger::log("Connection to server " . $server_url . " timed out.", Logger::DEBUG);
-				DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => normalise_link($server_url)]);
+				DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($server_url)]);
 				return false;
 			}
 
@@ -1048,7 +1049,7 @@ class PortableContact
 
 		if (!$failure) {
 			// This will be too low, but better than no value at all.
-			$registered_users = DBA::count('gcontact', ['server_url' => normalise_link($server_url)]);
+			$registered_users = DBA::count('gcontact', ['server_url' => Strings::normaliseLink($server_url)]);
 		}
 
 		// Look for poco
@@ -1410,7 +1411,7 @@ class PortableContact
 		}
 
 		// Check again if the server exists
-		$found = DBA::exists('gserver', ['nurl' => normalise_link($server_url)]);
+		$found = DBA::exists('gserver', ['nurl' => Strings::normaliseLink($server_url)]);
 
 		$version = strip_tags($version);
 		$site_name = strip_tags($site_name);
@@ -1424,9 +1425,9 @@ class PortableContact
 				'last_contact' => $last_contact, 'last_failure' => $last_failure];
 
 		if ($found) {
-			DBA::update('gserver', $fields, ['nurl' => normalise_link($server_url)]);
+			DBA::update('gserver', $fields, ['nurl' => Strings::normaliseLink($server_url)]);
 		} elseif (!$failure) {
-			$fields['nurl'] = normalise_link($server_url);
+			$fields['nurl'] = Strings::normaliseLink($server_url);
 			$fields['created'] = DateTimeFormat::utcNow();
 			DBA::insert('gserver', $fields);
 		}
@@ -1461,7 +1462,7 @@ class PortableContact
 			return;
 		}
 
-		$gserver = DBA::selectFirst('gserver', ['id', 'relay-subscribe', 'relay-scope'], ['nurl' => normalise_link($server_url)]);
+		$gserver = DBA::selectFirst('gserver', ['id', 'relay-subscribe', 'relay-scope'], ['nurl' => Strings::normaliseLink($server_url)]);
 
 		if (!DBA::isResult($gserver)) {
 			return;
@@ -1560,7 +1561,7 @@ class PortableContact
 		foreach ($serverlist as $server) {
 			$server_url = str_replace("/index.php", "", $server['url']);
 
-			$r = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", DBA::escape(normalise_link($server_url)));
+			$r = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", DBA::escape(Strings::normaliseLink($server_url)));
 
 			if (!DBA::isResult($r)) {
 				Logger::log("Call server check for server ".$server_url, Logger::DEBUG);
diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php
index 29abd77d0..47376890d 100644
--- a/src/Protocol/Salmon.php
+++ b/src/Protocol/Salmon.php
@@ -8,6 +8,7 @@ use Friendica\Core\Logger;
 use Friendica\Network\Probe;
 use Friendica\Util\Crypto;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 /**
@@ -51,7 +52,7 @@ class Salmon
 					} else {
 						$ret[$x] = substr($ret[$x], 5);
 					}
-				} elseif (normalise_link($ret[$x]) == 'http://') {
+				} elseif (Strings::normaliseLink($ret[$x]) == 'http://') {
 					$ret[$x] = Network::fetchUrl($ret[$x]);
 				}
 			}
@@ -70,7 +71,7 @@ class Salmon
 			return $ret[0];
 		} else {
 			foreach ($ret as $a) {
-				$hash = base64url_encode(hash('sha256', $a));
+				$hash = Strings::base64UrlEncode(hash('sha256', $a));
 				if ($hash == $keyhash) {
 					return $a;
 				}
@@ -104,22 +105,22 @@ class Salmon
 
 		// create a magic envelope
 
-		$data      = base64url_encode($slap);
+		$data      = Strings::base64UrlEncode($slap);
 		$data_type = 'application/atom+xml';
 		$encoding  = 'base64url';
 		$algorithm = 'RSA-SHA256';
-		$keyhash   = base64url_encode(hash('sha256', self::salmonKey($owner['spubkey'])), true);
+		$keyhash   = Strings::base64UrlEncode(hash('sha256', self::salmonKey($owner['spubkey'])), true);
 
-		$precomputed = '.' . base64url_encode($data_type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($algorithm);
+		$precomputed = '.' . Strings::base64UrlEncode($data_type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($algorithm);
 
 		// GNU Social format
-		$signature   = base64url_encode(Crypto::rsaSign($data . $precomputed, $owner['sprvkey']));
+		$signature   = Strings::base64UrlEncode(Crypto::rsaSign($data . $precomputed, $owner['sprvkey']));
 
 		// Compliant format
-		$signature2  = base64url_encode(Crypto::rsaSign(str_replace('=', '', $data . $precomputed), $owner['sprvkey']));
+		$signature2  = Strings::base64UrlEncode(Crypto::rsaSign(str_replace('=', '', $data . $precomputed), $owner['sprvkey']));
 
 		// Old Status.net format
-		$signature3  = base64url_encode(Crypto::rsaSign($data, $owner['sprvkey']));
+		$signature3  = Strings::base64UrlEncode(Crypto::rsaSign($data, $owner['sprvkey']));
 
 		// At first try the non compliant method that works for GNU Social
 		$xmldata = ["me:env" => ["me:data" => $data,
@@ -208,6 +209,6 @@ class Salmon
 	public static function salmonKey($pubkey)
 	{
 		Crypto::pemToMe($pubkey, $m, $e);
-		return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true);
+		return 'RSA' . '.' . Strings::base64UrlEncode($m, true) . '.' . Strings::base64UrlEncode($e, true);
 	}
 }
diff --git a/src/Util/Crypto.php b/src/Util/Crypto.php
index 7dd0dee5c..3426babe3 100644
--- a/src/Util/Crypto.php
+++ b/src/Util/Crypto.php
@@ -7,6 +7,7 @@ namespace Friendica\Util;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
 use Friendica\Core\Logger;
+use Friendica\Util\Strings;
 use ASN_BASE;
 use ASNValue;
 
@@ -159,8 +160,8 @@ class Crypto
 
 		$r = ASN_BASE::parseASNString($x);
 
-		$m = base64url_decode($r[0]->asnData[0]->asnData);
-		$e = base64url_decode($r[0]->asnData[1]->asnData);
+		$m = Strings::base64UrlDecode($r[0]->asnData[0]->asnData);
+		$e = Strings::base64UrlDecode($r[0]->asnData[1]->asnData);
 	}
 
 	/**
@@ -198,8 +199,8 @@ class Crypto
 
 		$r = ASN_BASE::parseASNString($x);
 
-		$m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData);
-		$e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData);
+		$m = Strings::base64UrlDecode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData);
+		$e = Strings::base64UrlDecode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData);
 	}
 
 	/**
@@ -355,7 +356,7 @@ class Crypto
 			$result = ['encrypted' => true];
 			$key = random_bytes(256);
 			$iv  = random_bytes(256);
-			$result['data'] = base64url_encode(self::$fn($data, $key, $iv), true);
+			$result['data'] = Strings::base64UrlEncode(self::$fn($data, $key, $iv), true);
 
 			// log the offending call so we can track it down
 			if (!openssl_public_encrypt($key, $k, $pubkey)) {
@@ -364,9 +365,9 @@ class Crypto
 			}
 
 			$result['alg'] = $alg;
-			$result['key'] = base64url_encode($k, true);
+			$result['key'] = Strings::base64UrlEncode($k, true);
 			openssl_public_encrypt($iv, $i, $pubkey);
-			$result['iv'] = base64url_encode($i, true);
+			$result['iv'] = Strings::base64UrlEncode($i, true);
 
 			return $result;
 		} else {
@@ -395,7 +396,7 @@ class Crypto
 		$key = random_bytes(32);
 		$iv  = random_bytes(16);
 		$result = ['encrypted' => true];
-		$result['data'] = base64url_encode(self::encryptAES256CBC($data, $key, $iv), true);
+		$result['data'] = Strings::base64UrlEncode(self::encryptAES256CBC($data, $key, $iv), true);
 
 		// log the offending call so we can track it down
 		if (!openssl_public_encrypt($key, $k, $pubkey)) {
@@ -404,9 +405,9 @@ class Crypto
 		}
 
 		$result['alg'] = 'aes256cbc';
-		$result['key'] = base64url_encode($k, true);
+		$result['key'] = Strings::base64UrlEncode($k, true);
 		openssl_public_encrypt($iv, $i, $pubkey);
-		$result['iv'] = base64url_encode($i, true);
+		$result['iv'] = Strings::base64UrlEncode($i, true);
 
 		return $result;
 	}
@@ -448,10 +449,10 @@ class Crypto
 		$fn = 'decrypt' . strtoupper($alg);
 
 		if (method_exists(__CLASS__, $fn)) {
-			openssl_private_decrypt(base64url_decode($data['key']), $k, $prvkey);
-			openssl_private_decrypt(base64url_decode($data['iv']), $i, $prvkey);
+			openssl_private_decrypt(Strings::base64UrlDecode($data['key']), $k, $prvkey);
+			openssl_private_decrypt(Strings::base64UrlDecode($data['iv']), $i, $prvkey);
 
-			return self::$fn(base64url_decode($data['data']), $k, $i);
+			return self::$fn(Strings::base64UrlDecode($data['data']), $k, $i);
 		} else {
 			$x = ['data' => $data, 'prvkey' => $prvkey, 'alg' => $alg, 'result' => $data];
 			Addon::callHooks('other_unencapsulate', $x);
@@ -471,10 +472,10 @@ class Crypto
 	 */
 	private static function unencapsulateAes($data, $prvkey)
 	{
-		openssl_private_decrypt(base64url_decode($data['key']), $k, $prvkey);
-		openssl_private_decrypt(base64url_decode($data['iv']), $i, $prvkey);
+		openssl_private_decrypt(Strings::base64UrlDecode($data['key']), $k, $prvkey);
+		openssl_private_decrypt(Strings::base64UrlDecode($data['iv']), $i, $prvkey);
 
-		return self::decryptAES256CBC(base64url_decode($data['data']), $k, $i);
+		return self::decryptAES256CBC(Strings::base64UrlDecode($data['data']), $k, $i);
 	}
 
 
diff --git a/src/Util/JsonLD.php b/src/Util/JsonLD.php
index bed7a67d6..78d81816e 100644
--- a/src/Util/JsonLD.php
+++ b/src/Util/JsonLD.php
@@ -90,7 +90,8 @@ class JsonLD
 			'dfrn' => (object)['@id' => 'http://purl.org/macgirvin/dfrn/1.0/', '@type' => '@id'],
 			'diaspora' => (object)['@id' => 'https://diasporafoundation.org/ns/', '@type' => '@id'],
 			'ostatus' => (object)['@id' => 'http://ostatus.org#', '@type' => '@id'],
-			'dc' => (object)['@id' => 'http://purl.org/dc/terms/', '@type' => '@id']];
+			'dc' => (object)['@id' => 'http://purl.org/dc/terms/', '@type' => '@id'],
+			'toot' => (object)['@id' => 'http://joinmastodon.org/ns#', '@type' => '@id']];
 
 		$jsonobj = json_decode(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
 
diff --git a/src/Util/LDSignature.php b/src/Util/LDSignature.php
index e53590cf3..c9fde441f 100644
--- a/src/Util/LDSignature.php
+++ b/src/Util/LDSignature.php
@@ -54,7 +54,7 @@ class LDSignature
 	{
 		$options = [
 			'type' => 'RsaSignature2017',
-			'nonce' => random_string(64),
+			'nonce' => Strings::getRandomHex(64),
 			'creator' => $owner['url'] . '#main-key',
 			'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM)
 		];
diff --git a/src/Util/Network.php b/src/Util/Network.php
index 0ff34f120..0ac0f2aeb 100644
--- a/src/Util/Network.php
+++ b/src/Util/Network.php
@@ -9,6 +9,7 @@ use Friendica\Core\Logger;
 use Friendica\Core\System;
 use Friendica\Core\Config;
 use Friendica\Network\CurlResult;
+use Friendica\Util\Strings;
 use DOMDocument;
 use DomXPath;
 
@@ -718,8 +719,8 @@ class Network
 			return "";
 		}
 
-		$url1 = normalise_link($url1);
-		$url2 = normalise_link($url2);
+		$url1 = Strings::normaliseLink($url1);
+		$url2 = Strings::normaliseLink($url2);
 
 		$parts1 = parse_url($url1);
 		$parts2 = parse_url($url2);
@@ -790,7 +791,7 @@ class Network
 
 		$match .= $path;
 
-		return normalise_link($match);
+		return Strings::normaliseLink($match);
 	}
 
 	/**
diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php
index 6530959f2..118821420 100644
--- a/src/Util/ParseUrl.php
+++ b/src/Util/ParseUrl.php
@@ -12,6 +12,7 @@ use Friendica\Core\Addon;
 use Friendica\Core\Logger;
 use Friendica\Database\DBA;
 use Friendica\Object\Image;
+use Friendica\Util\Strings;
 
 require_once 'include/dba.php';
 
@@ -49,7 +50,7 @@ class ParseUrl
 		}
 
 		$parsed_url = DBA::selectFirst('parsed_url', ['content'],
-			['url' => normalise_link($url), 'guessing' => !$no_guessing, 'oembed' => $do_oembed]
+			['url' => Strings::normaliseLink($url), 'guessing' => !$no_guessing, 'oembed' => $do_oembed]
 		);
 		if (!empty($parsed_url['content'])) {
 			$data = unserialize($parsed_url['content']);
@@ -61,7 +62,7 @@ class ParseUrl
 		DBA::insert(
 			'parsed_url',
 			[
-				'url' => normalise_link($url), 'guessing' => !$no_guessing,
+				'url' => Strings::normaliseLink($url), 'guessing' => !$no_guessing,
 				'oembed' => $do_oembed, 'content' => serialize($data),
 				'created' => DateTimeFormat::utcNow()
 			],
diff --git a/src/Util/Proxy.php b/src/Util/Proxy.php
index 8478ce868..be70077f1 100644
--- a/src/Util/Proxy.php
+++ b/src/Util/Proxy.php
@@ -6,6 +6,7 @@ use Friendica\BaseModule;
 use Friendica\BaseObject;
 use Friendica\Core\Config;
 use Friendica\Core\System;
+use Friendica\Util\Strings;
 
 /**
  * @brief Proxy utilities class
@@ -76,7 +77,7 @@ class Proxy
 
 		// Only continue if it isn't a local image and the isn't deactivated
 		if (self::isLocalImage($url)) {
-			$url = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $url);
+			$url = str_replace(Strings::normaliseLink(System::baseUrl()) . '/', System::baseUrl() . '/', $url);
 			return $url;
 		}
 
@@ -140,7 +141,7 @@ class Proxy
 	 */
 	public static function proxifyHtml($html)
 	{
-		$html = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $html);
+		$html = str_replace(Strings::normaliseLink(System::baseUrl()) . '/', System::baseUrl() . '/', $html);
 
 		return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
 	}
@@ -162,8 +163,8 @@ class Proxy
 		}
 
 		// links normalised - bug #431
-		$baseurl = normalise_link(System::baseUrl());
-		$url = normalise_link($url);
+		$baseurl = Strings::normaliseLink(System::baseUrl());
+		$url = Strings::normaliseLink($url);
 
 		return (substr($url, 0, strlen($baseurl)) == $baseurl);
 	}
diff --git a/src/Util/Strings.php b/src/Util/Strings.php
new file mode 100644
index 000000000..48e580d67
--- /dev/null
+++ b/src/Util/Strings.php
@@ -0,0 +1,315 @@
+<?php
+/**
+ * @file src/Util/Strings.php
+ */
+
+namespace Friendica\Util;
+
+use Friendica\Content\ContactSelector;
+use Friendica\Core\Logger;
+
+/**
+ * @brief This class handles string functions
+ */
+class Strings
+{
+    /**
+     * @brief Generates a pseudo-random string of hexadecimal characters
+     *
+     * @param int $size
+     * @return string
+     */
+    public static function getRandomHex($size = 64)
+    {
+        $byte_size = ceil($size / 2);
+
+        $bytes = random_bytes($byte_size);
+
+        $return = substr(bin2hex($bytes), 0, $size);
+
+        return $return;
+    }
+
+    /**
+     * @brief This is our primary input filter.
+     *
+     * Use this on any text input where angle chars are not valid or permitted
+     * They will be replaced with safer brackets. This may be filtered further
+     * if these are not allowed either.
+     *
+     * @param string $string Input string
+     * @return string Filtered string
+     */
+    public static function escapeTags($string)
+    {
+        return str_replace(["<", ">"], ['[', ']'], $string);
+    }
+
+    /**
+     * @brief Use this on "body" or "content" input where angle chars shouldn't be removed,
+     * and allow them to be safely displayed.
+     * @param string $string
+     * 
+     * @return string
+     */
+    public static function escapeHtml($string)
+    {
+        return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
+    }
+
+    /**
+     * @brief Generate a string that's random, but usually pronounceable. Used to generate initial passwords
+     * 
+     * @param int $len  length
+     * 
+     * @return string
+     */
+    public static function getRandomName($len)
+    {
+        if ($len <= 0) {
+            return '';
+        }
+
+        $vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u'];
+
+        if (mt_rand(0, 5) == 4) {
+            $vowels[] = 'y';
+        }
+
+        $cons = [
+                'b', 'bl', 'br',
+                'c', 'ch', 'cl', 'cr',
+                'd', 'dr',
+                'f', 'fl', 'fr',
+                'g', 'gh', 'gl', 'gr',
+                'h',
+                'j',
+                'k', 'kh', 'kl', 'kr',
+                'l',
+                'm',
+                'n',
+                'p', 'ph', 'pl', 'pr',
+                'qu',
+                'r', 'rh',
+                's' ,'sc', 'sh', 'sm', 'sp', 'st',
+                't', 'th', 'tr',
+                'v',
+                'w', 'wh',
+                'x',
+                'z', 'zh'
+            ];
+
+        $midcons = ['ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp',
+                    'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt'];
+
+        $noend = ['bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr',
+                    'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q'];
+
+        $start = mt_rand(0, 2);
+        if ($start == 0) {
+            $table = $vowels;
+        } else {
+            $table = $cons;
+        }
+
+        $word = '';
+
+        for ($x = 0; $x < $len; $x ++) {
+            $r = mt_rand(0, count($table) - 1);
+            $word .= $table[$r];
+
+            if ($table == $vowels) {
+                $table = array_merge($cons, $midcons);
+            } else {
+                $table = $vowels;
+            }
+
+        }
+
+        $word = substr($word, 0, $len);
+
+        foreach ($noend as $noe) {
+            $noelen = strlen($noe);
+            if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) {
+                $word = self::getRandomName($len);
+                break;
+            }
+        }
+
+        return $word;
+    }
+
+    /**
+     * @brief translate and format the networkname of a contact
+     *
+     * @param string $network   Networkname of the contact (e.g. dfrn, rss and so on)
+     * @param string $url       The contact url
+     * 
+     * @return string   Formatted network name
+     */
+    public static function formatNetworkName($network, $url = 0)
+    {
+        if ($network != "") {
+            if ($url != "") {
+                $network_name = '<a href="' . $url  .'">' . ContactSelector::networkToName($network, $url) . "</a>";
+            } else {
+                $network_name = ContactSelector::networkToName($network);
+            }
+
+            return $network_name;
+        }
+    }
+
+    /**
+     * @brief Remove intentation from a text
+     * 
+     * @param string $text  String to be transformed.
+     * @param string $chr   Optional. Indentation tag. Default tab (\t).
+     * @param int    $count Optional. Default null.
+     * 
+     * @return string       Transformed string.
+     */
+    public static function deindent($text, $chr = "[\t ]", $count = NULL)
+    {
+        $lines = explode("\n", $text);
+
+        if (is_null($count)) {
+            $m = [];
+            $k = 0;
+            while ($k < count($lines) && strlen($lines[$k]) == 0) {
+                $k++;
+            }
+            preg_match("|^" . $chr . "*|", $lines[$k], $m);
+            $count = strlen($m[0]);
+        }
+
+        for ($k = 0; $k < count($lines); $k++) {
+            $lines[$k] = preg_replace("|^" . $chr . "{" . $count . "}|", "", $lines[$k]);
+        }
+
+        return implode("\n", $lines);
+    }
+
+    /**
+     * @brief Get byte size returned in a Data Measurement (KB, MB, GB)
+     * 
+     * @param int $bytes    The number of bytes to be measured
+     * @param int $precision    Optional. Default 2.
+     * 
+     * @return string   Size with measured units.
+     */
+    public static function formatBytes($bytes, $precision = 2)
+    {
+        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
+        $bytes = max($bytes, 0);
+        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
+        $pow = min($pow, count($units) - 1);
+        $bytes /= pow(1024, $pow);
+
+        return round($bytes, $precision) . ' ' . $units[$pow];
+    }
+
+    /**
+     * @brief Protect percent characters in sprintf calls
+     * 
+     * @param string $s String to transform.
+     * 
+     * @return string   Transformed string.
+     */
+    public static function protectSprintf($s)
+    {
+        return str_replace('%', '%%', $s);
+    }
+
+    /**
+     * @brief Base64 Encode URL and translate +/ to -_ Optionally strip padding.
+     * 
+     * @param string $s                 URL to encode
+     * @param boolean $strip_padding    Optional. Default false
+     * 
+     * @return string   Encoded URL
+     */
+    public static function base64UrlEncode($s, $strip_padding = false)
+    {
+        $s = strtr(base64_encode($s), '+/', '-_');
+
+        if ($strip_padding) {
+            $s = str_replace('=', '', $s);
+        }
+
+        return $s;
+    }
+
+    /**
+     * @brief Decode Base64 Encoded URL and translate -_ to +/
+     * @param string $s URL to decode
+     * 
+     * @return string   Decoded URL
+     */
+    public static function base64UrlDecode($s)
+    {
+        if (is_array($s)) {
+            Logger::log('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
+            return $s;
+        }
+
+        /*
+        *  // Placeholder for new rev of salmon which strips base64 padding.
+        *  // PHP base64_decode handles the un-padded input without requiring this step
+        *  // Uncomment if you find you need it.
+        *
+        *	$l = strlen($s);
+        *	if (!strpos($s,'=')) {
+        *		$m = $l % 4;
+        *		if ($m == 2)
+        *			$s .= '==';
+        *		if ($m == 3)
+        *			$s .= '=';
+        *	}
+        *
+        */
+
+        return base64_decode(strtr($s, '-_', '+/'));
+    }
+
+    /**
+     * @brief Normalize url
+     *
+     * @param string $url   URL to be normalized.
+     * 
+     * @return string   Normalized URL.
+     */
+    public static function normaliseLink($url)
+    {
+        $ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
+        return rtrim($ret, '/');
+    }
+
+    /**
+     * @brief Normalize OpenID identity
+     * 
+     * @param string $s OpenID Identity
+     * 
+     * @return string   normalized OpenId Identity
+     */
+    function normaliseOpenID($s)
+    {
+        return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
+    }
+
+    /**
+     * @brief Compare two URLs to see if they are the same, but ignore
+     * slight but hopefully insignificant differences such as if one
+     * is https and the other isn't, or if one is www.something and
+     * the other isn't - and also ignore case differences.
+     *
+     * @param string $a first url
+     * @param string $b second url
+     * @return boolean True if the URLs match, otherwise False
+     *
+     */
+    public static function compareLink($a, $b)
+    {
+        return (strcasecmp(self::normaliseLink($a), self::normaliseLink($b)) === 0);
+    }
+}
diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php
index 59d506af3..40230e3bd 100644
--- a/src/Worker/Delivery.php
+++ b/src/Worker/Delivery.php
@@ -18,6 +18,7 @@ use Friendica\Model\User;
 use Friendica\Protocol\DFRN;
 use Friendica\Protocol\Diaspora;
 use Friendica\Protocol\Email;
+use Friendica\Util\Strings;
 
 require_once 'include/items.php';
 
@@ -247,8 +248,8 @@ class Delivery extends BaseObject
 
 		// perform local delivery if we are on the same site
 
-		if (link_compare($basepath, System::baseUrl())) {
-			$condition = ['nurl' => normalise_link($contact['url']), 'self' => true];
+		if (Strings::compareLink($basepath, System::baseUrl())) {
+			$condition = ['nurl' => Strings::normaliseLink($contact['url']), 'self' => true];
 			$target_self = DBA::selectFirst('contact', ['uid'], $condition);
 			if (!DBA::isResult($target_self)) {
 				return;
diff --git a/src/Worker/DiscoverPoCo.php b/src/Worker/DiscoverPoCo.php
index 72df3420e..55eeec98f 100644
--- a/src/Worker/DiscoverPoCo.php
+++ b/src/Worker/DiscoverPoCo.php
@@ -15,6 +15,7 @@ use Friendica\Network\Probe;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 
 class DiscoverPoCo
 {
@@ -80,7 +81,7 @@ class DiscoverPoCo
 				return;
 			}
 			$server_url = filter_var($server_url, FILTER_SANITIZE_URL);
-			if (substr(normalise_link($server_url), 0, 7) != "http://") {
+			if (substr(Strings::normaliseLink($server_url), 0, 7) != "http://") {
 				return;
 			}
 			$result = "Checking server ".$server_url." - ";
@@ -162,7 +163,7 @@ class DiscoverPoCo
 			$urlparts = parse_url($user["url"]);
 			if (!isset($urlparts["scheme"])) {
 				DBA::update('gcontact', ['network' => Protocol::PHANTOM],
-					['nurl' => normalise_link($user["url"])]);
+					['nurl' => Strings::normaliseLink($user["url"])]);
 				continue;
 			 }
 
@@ -170,7 +171,7 @@ class DiscoverPoCo
 				$networks = ["twitter.com" => Protocol::TWITTER, "identi.ca" => Protocol::PUMPIO];
 
 				DBA::update('gcontact', ['network' => $networks[$urlparts["host"]]],
-					['nurl' => normalise_link($user["url"])]);
+					['nurl' => Strings::normaliseLink($user["url"])]);
 				continue;
 			}
 
@@ -179,7 +180,7 @@ class DiscoverPoCo
 
 			if ($user["server_url"] != "") {
 
-				$force_update = (normalise_link($user["server_url"]) != normalise_link($server_url));
+				$force_update = (Strings::normaliseLink($user["server_url"]) != Strings::normaliseLink($server_url));
 
 				$server_url = $user["server_url"];
 			}
@@ -193,7 +194,7 @@ class DiscoverPoCo
 				}
 			} else {
 				DBA::update('gcontact', ['last_failure' => DateTimeFormat::utcNow()],
-					['nurl' => normalise_link($user["url"])]);
+					['nurl' => Strings::normaliseLink($user["url"])]);
 			}
 
 			// Quit the loop after 3 minutes
@@ -220,7 +221,7 @@ class DiscoverPoCo
 		if (!empty($j->results)) {
 			foreach ($j->results as $jj) {
 				// Check if the contact already exists
-				$exists = q("SELECT `id`, `last_contact`, `last_failure`, `updated` FROM `gcontact` WHERE `nurl` = '%s'", normalise_link($jj->url));
+				$exists = q("SELECT `id`, `last_contact`, `last_failure`, `updated` FROM `gcontact` WHERE `nurl` = '%s'", Strings::normaliseLink($jj->url));
 				if (DBA::isResult($exists)) {
 					Logger::log("Profile ".$jj->url." already exists (".$search.")", Logger::DEBUG);
 
diff --git a/src/Worker/GProbe.php b/src/Worker/GProbe.php
index 55da28f91..4f51db2df 100644
--- a/src/Worker/GProbe.php
+++ b/src/Worker/GProbe.php
@@ -12,6 +12,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\GContact;
 use Friendica\Network\Probe;
 use Friendica\Protocol\PortableContact;
+use Friendica\Util\Strings;
 
 class GProbe {
 	public static function execute($url = '')
@@ -22,10 +23,10 @@ class GProbe {
 
 		$r = q(
 			"SELECT `id`, `url`, `network` FROM `gcontact` WHERE `nurl` = '%s' ORDER BY `id` LIMIT 1",
-			DBA::escape(normalise_link($url))
+			DBA::escape(Strings::normaliseLink($url))
 		);
 
-		Logger::log("gprobe start for ".normalise_link($url), Logger::DEBUG);
+		Logger::log("gprobe start for ".Strings::normaliseLink($url), Logger::DEBUG);
 
 		if (!DBA::isResult($r)) {
 			// Is it a DDoS attempt?
@@ -51,7 +52,7 @@ class GProbe {
 
 			$r = q(
 				"SELECT `id`, `url`, `network` FROM `gcontact` WHERE `nurl` = '%s' ORDER BY `id` LIMIT 1",
-				DBA::escape(normalise_link($url))
+				DBA::escape(Strings::normaliseLink($url))
 			);
 		}
 		if (DBA::isResult($r)) {
@@ -61,7 +62,7 @@ class GProbe {
 			}
 		}
 
-		Logger::log("gprobe end for ".normalise_link($url), Logger::DEBUG);
+		Logger::log("gprobe end for ".Strings::normaliseLink($url), Logger::DEBUG);
 		return;
 	}
 }
diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php
index 77745b807..22fbf700a 100644
--- a/src/Worker/OnePoll.php
+++ b/src/Worker/OnePoll.php
@@ -18,6 +18,7 @@ use Friendica\Protocol\Email;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
 require_once 'include/dba.php';
@@ -474,9 +475,9 @@ class OnePoll
 									$datarray['title'] .= $subpart->text;
 								}
 							}
-							$datarray['title'] = notags(trim($datarray['title']));
+							$datarray['title'] = Strings::escapeTags(trim($datarray['title']));
 
-							//$datarray['title'] = notags(trim($meta->subject));
+							//$datarray['title'] = Strings::escapeTags(trim($meta->subject));
 							$datarray['created'] = DateTimeFormat::utc($meta->date);
 
 							// Is it a reply?
@@ -506,7 +507,7 @@ class OnePoll
 								Logger::log("Mail: can't fetch msg ".$msg_uid." for ".$mailconf['user']);
 								continue;
 							}
-							$datarray['body'] = escape_tags($r['body']);
+							$datarray['body'] = Strings::escapeHtml($r['body']);
 							$datarray['body'] = BBCode::limitBodySize($datarray['body']);
 
 							Logger::log("Mail: Importing ".$msg_uid." for ".$mailconf['user']);
diff --git a/src/Worker/UpdateGContact.php b/src/Worker/UpdateGContact.php
index b7a78b51f..b927e61bb 100644
--- a/src/Worker/UpdateGContact.php
+++ b/src/Worker/UpdateGContact.php
@@ -12,6 +12,7 @@ use Friendica\Database\DBA;
 use Friendica\Network\Probe;
 use Friendica\Protocol\PortableContact;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Strings;
 
 class UpdateGContact
 {
@@ -78,13 +79,13 @@ class UpdateGContact
 					DBA::escape($data["nick"]),
 					DBA::escape($data["addr"]),
 					DBA::escape($data["photo"]),
-					DBA::escape(normalise_link($data["url"]))
+					DBA::escape(Strings::normaliseLink($data["url"]))
 		);
 
 		q("UPDATE `contact` SET `addr` = '%s'
 					WHERE `uid` != 0 AND `addr` = '' AND `nurl` = '%s'",
 					DBA::escape($data["addr"]),
-					DBA::escape(normalise_link($data["url"]))
+					DBA::escape(Strings::normaliseLink($data["url"]))
 		);
 	}
 }
diff --git a/tests/Util/DBAMockTrait.php b/tests/Util/DBAMockTrait.php
index 1bb69c27b..2ee54adaa 100644
--- a/tests/Util/DBAMockTrait.php
+++ b/tests/Util/DBAMockTrait.php
@@ -69,4 +69,117 @@ trait DBAMockTrait
 			->times($times)
 			->andReturn($return);
 	}
+
+
+	/**
+	 * Mocking DBA::select()
+	 *
+	 * @param string $tableName The name of the table
+	 * @param array $select The Select Array (Default is [])
+	 * @param array $where The Where Array (Default is [])
+	 * @param object $return The array to return (Default is [])
+	 * @param null|int $times How often the method will get used
+	 */
+	public function mockSelect($tableName, $select = [], $where = [], $return = null, $times = null)
+	{
+		if (!isset($this->dbaMock)) {
+			$this->dbaMock = \Mockery::mock('alias:Friendica\Database\DBA');
+		}
+
+		$this->dbaMock
+			->shouldReceive('select')
+			->with($tableName, $select, $where)
+			->times($times)
+			->andReturn($return);
+	}
+
+	/**
+	 * Mocking DBA::selectFirst()
+	 *
+	 * @param string $tableName The name of the table
+	 * @param array $select The Select Array (Default is [])
+	 * @param array $where The Where Array (Default is [])
+	 * @param array $return The array to return (Default is [])
+	 * @param null|int $times How often the method will get used
+	 */
+	public function mockSelectFirst($tableName, $select = [], $where = [], $return = [], $times = null)
+	{
+		if (!isset($this->dbaMock)) {
+			$this->dbaMock = \Mockery::mock('alias:Friendica\Database\DBA');
+		}
+
+		$this->dbaMock
+			->shouldReceive('selectFirst')
+			->with($tableName, $select, $where)
+			->times($times)
+			->andReturn($return);
+	}
+
+	/**
+	 * Mocking DBA::isResult()
+	 *
+	 * @param object $record The record to test
+	 * @param bool $return True, if the DB is connected, otherwise false
+	 * @param null|int $times How often the method will get used
+	 */
+	public function mockIsResult($record, $return = true, $times = null)
+	{
+		if (!isset($this->dbaMock)) {
+			$this->dbaMock = \Mockery::mock('alias:Friendica\Database\DBA');
+		}
+
+		$this->dbaMock
+			->shouldReceive('isResult')
+			->with($record)
+			->times($times)
+			->andReturn($return);
+	}
+
+	/**
+	 * Mocking DBA::isResult()
+	 *
+	 * @param object $record The record to test
+	 * @param array $return The array to return
+	 * @param null|int $times How often the method will get used
+	 */
+	public function mockToArray($record = null, $return = [], $times = null)
+	{
+		if (!isset($this->dbaMock)) {
+			$this->dbaMock = \Mockery::mock('alias:Friendica\Database\DBA');
+		}
+
+		$this->dbaMock
+			->shouldReceive('toArray')
+			->with($record)
+			->times($times)
+			->andReturn($return);
+	}
+
+
+	/**
+	 * Mocking DBA::p()
+	 *
+	 * @param string $sql The SQL statement
+	 * @param object $return The object to return
+	 * @param null|int $times How often the method will get used
+	 */
+	public function mockP($sql = null, $return = null, $times = null)
+	{
+		if (!isset($this->dbaMock)) {
+			$this->dbaMock = \Mockery::mock('alias:Friendica\Database\DBA');
+		}
+
+		if (!isset($sql)) {
+			$this->dbaMock
+				->shouldReceive('p')
+				->times($times)
+				->andReturn($return);
+		} else {
+			$this->dbaMock
+				->shouldReceive('p')
+				->with($sql)
+				->times($times)
+				->andReturn($return);
+		}
+	}
 }
diff --git a/tests/include/TextTest.php b/tests/include/TextTest.php
index 1422ee2ae..e516fe824 100644
--- a/tests/include/TextTest.php
+++ b/tests/include/TextTest.php
@@ -12,61 +12,6 @@ use PHPUnit\Framework\TestCase;
  */
 class TextTest extends TestCase
 {
-
-	/**
-	 *autonames should be random, even length
-	 */
-	public function testAutonameEven()
-	{
-		$autoname1=autoname(10);
-		$autoname2=autoname(10);
-
-		$this->assertNotEquals($autoname1, $autoname2);
-	}
-
-	/**
-	 *autonames should be random, odd length
-	 */
-	public function testAutonameOdd()
-	{
-		$autoname1=autoname(9);
-		$autoname2=autoname(9);
-
-		$this->assertNotEquals($autoname1, $autoname2);
-	}
-
-	/**
-	 * try to fail autonames
-	 */
-	public function testAutonameNoLength()
-	{
-		$autoname1=autoname(0);
-		$this->assertEquals(0, strlen($autoname1));
-	}
-
-	/**
-	 * try to fail it with invalid input
-	 *
-	 * @todo What's corect behaviour here? An exception?
-	 */
-	public function testAutonameNegativeLength()
-	{
-		$autoname1=autoname(-23);
-		$this->assertEquals(0, strlen($autoname1));
-	}
-
-	/**
-	 * test with a length, that may be too short
-	 */
-	public function testAutonameLength1()
-	{
-		$autoname1=autoname(1);
-		$this->assertEquals(1, strlen($autoname1));
-
-		$autoname2=autoname(1);
-		$this->assertEquals(1, strlen($autoname2));
-	}
-
 	/**
 	 * test attribute contains
 	 */
@@ -232,23 +177,6 @@ class TextTest extends TestCase
 		$this->assertEquals(array(1,3), expand_acl($text));
 	}
 
-	/**
-	 * test, that tags are escaped
-	 */
-	public function testEscapeTags()
-	{
-		$invalidstring='<submit type="button" onclick="alert(\'failed!\');" />';
-
-		$validstring=notags($invalidstring);
-		$escapedString=escape_tags($invalidstring);
-
-		$this->assertEquals('[submit type="button" onclick="alert(\'failed!\');" /]', $validstring);
-		$this->assertEquals(
-			"&lt;submit type=&quot;button&quot; onclick=&quot;alert('failed!');&quot; /&gt;",
-			$escapedString
-		);
-	}
-
 	/**
 	 * test hex2bin and reverse
 	 */
diff --git a/tests/src/Model/UserTest.php b/tests/src/Model/UserTest.php
new file mode 100644
index 000000000..3e224fb4c
--- /dev/null
+++ b/tests/src/Model/UserTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Friendica\Test\Model;
+
+use Friendica\Model\User;
+use Friendica\Test\MockedTest;
+use Friendica\Test\Util\DBAMockTrait;
+
+/**
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class UserTest extends MockedTest
+{
+	use DBAMockTrait;
+
+	private $parent;
+	private $child;
+	private $manage;
+
+	protected function setUp()
+	{
+		parent::setUp();
+
+		$this->parent = [
+			'uid'        => 1,
+			'username'   => 'maxmuster',
+			'nickname'   => 'Max Muster'
+		];
+
+		$this->child = [
+			'uid'        => 2,
+			'username'   => 'johndoe',
+			'nickname'   => 'John Doe'
+		];
+
+		$this->manage = [
+			'uid'        => 3,
+			'username'   => 'janesmith',
+			'nickname'   => 'Jane Smith'
+		];
+	}
+
+	public function testIdentitiesEmpty()
+	{
+		$this->mockSelectFirst('user',
+			['uid', 'nickname', 'username', 'parent-uid'],
+			['uid' => $this->parent['uid']],
+			$this->parent,
+			1
+		);
+		$this->mockIsResult($this->parent, false, 1);
+
+		$record = User::identities($this->parent['uid']);
+
+		$this->assertEquals([], $record);
+	}
+
+	public function testIdentitiesAsParent()
+	{
+		$parentSelect = $this->parent;
+		$parentSelect['parent-uid'] = 0;
+
+		// Select the user itself (=parent)
+		$this->mockSelectFirst('user',
+			['uid', 'nickname', 'username', 'parent-uid'],
+			['uid' => $this->parent['uid']],
+			$parentSelect,
+			1
+		);
+		$this->mockIsResult($parentSelect, true, 1);
+
+		// Select one child
+		$this->mockSelect('user',
+			['uid', 'username', 'nickname'],
+			[
+				'parent-uid' => $this->parent['uid'],
+				'account_removed' => false
+			],
+			'objectReturn',
+			1
+		);
+		$this->mockIsResult('objectReturn', true, 1);
+		$this->mockToArray('objectReturn', [ $this->child ], 1);
+
+		// Select the manage
+		$this->mockP(null, 'objectTwo', 1);
+		$this->mockIsResult('objectTwo', true, 1);
+		$this->mockToArray('objectTwo', [ $this->manage ], 1);
+
+		$record = User::identities($this->parent['uid']);
+
+		$this->assertEquals([
+			$this->parent,
+			$this->child,
+			$this->manage
+		], $record);
+	}
+
+	public function testIdentitiesAsChild()
+	{
+		$childSelect = $this->child;
+		$childSelect['parent-uid'] = $this->parent['uid'];
+
+		// Select the user itself (=child)
+		$this->mockSelectFirst('user',
+			['uid', 'nickname', 'username', 'parent-uid'],
+			['uid' => $this->child['uid']],
+			$childSelect,
+			1
+		);
+		$this->mockIsResult($childSelect, true, 1);
+
+		// Select the parent
+		$this->mockSelect('user',
+			['uid', 'username', 'nickname'],
+			[
+				'uid' => $this->parent['uid'],
+				'account_removed' => false
+			],
+			'objectReturn',
+			1
+		);
+		$this->mockIsResult('objectReturn', true, 1);
+		$this->mockToArray('objectReturn', [ $this->parent ], 1);
+
+		// Select the childs (user & manage)
+		$this->mockSelect('user',
+			['uid', 'username', 'nickname'],
+			[
+				'parent-uid' => $this->parent['uid'],
+				'account_removed' => false
+			],
+			'objectReturn',
+			1
+		);
+		$this->mockIsResult('objectReturn', true, 1);
+		$this->mockToArray('objectReturn', [ $this->child, $this->manage ], 1);
+
+		// Select the manage
+		$this->mockP(null, 'objectTwo', 1);
+		$this->mockIsResult('objectTwo', false, 1);
+
+		$record = User::identities($this->child['uid']);
+
+		$this->assertEquals([
+			$this->parent,
+			$this->child,
+			$this->manage
+		], $record);
+	}
+}
diff --git a/tests/src/Util/StringsTest.php b/tests/src/Util/StringsTest.php
new file mode 100644
index 000000000..21349c848
--- /dev/null
+++ b/tests/src/Util/StringsTest.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @file tests/src/Util/StringsTest.php
+ */
+namespace Friendica\Test\Util;
+
+use Friendica\Util\Strings;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @brief Strings utility test class
+ */
+class StringsTest extends TestCase
+{
+    /**
+	 * randomnames should be random, even length
+	 */
+	public function testRandomEven()
+	{
+		$randomname1 = Strings::getRandomName(10);
+		$randomname2 = Strings::getRandomName(10);
+
+		$this->assertNotEquals($randomname1, $randomname2);
+	}
+
+	/**
+	 * randomnames should be random, odd length
+	 */
+	public function testRandomOdd()
+	{
+		$randomname1 = Strings::getRandomName(9);
+		$randomname2 = Strings::getRandomName(9);
+
+		$this->assertNotEquals($randomname1, $randomname2);
+	}
+
+	/**
+	 * try to fail ramdonnames
+	 */
+	public function testRandomNameNoLength()
+	{
+		$randomname1 = Strings::getRandomName(0);
+		$this->assertEquals(0, strlen($randomname1));
+	}
+
+	/**
+	 * try to fail it with invalid input
+	 *
+	 * @todo What's corect behaviour here? An exception?
+	 */
+	public function testRandomNameNegativeLength()
+	{
+		$randomname1 = Strings::getRandomName(-23);
+		$this->assertEquals(0, strlen($randomname1));
+	}
+
+	/**
+	 * test with a length, that may be too short
+	 */
+	public function testRandomNameLength1()
+	{
+		$randomname1 = Strings::getRandomName(1);
+		$this->assertEquals(1, strlen($randomname1));
+
+		$randomname2 = Strings::getRandomName(1);
+		$this->assertEquals(1, strlen($randomname2));
+    }
+    
+    /**
+	 * test, that tags are escaped
+	 */
+	public function testEscapeHtml()
+	{
+		$invalidstring='<submit type="button" onclick="alert(\'failed!\');" />';
+
+		$validstring = Strings::escapeTags($invalidstring);
+		$escapedString = Strings::escapeHtml($invalidstring);
+
+		$this->assertEquals('[submit type="button" onclick="alert(\'failed!\');" /]', $validstring);
+		$this->assertEquals(
+			"&lt;submit type=&quot;button&quot; onclick=&quot;alert('failed!');&quot; /&gt;",
+			$escapedString
+		);
+	}
+}
diff --git a/view/global.css b/view/global.css
index b46862d5a..48755b89d 100644
--- a/view/global.css
+++ b/view/global.css
@@ -602,3 +602,8 @@ img.invalid-src:after { vertical-align: top;}
 #register-explicid-content {
   font-weight: bold;
 }
+
+span.emoji.mastodon img {
+  height: 1.2em;
+  vertical-align: middle;
+}
diff --git a/view/theme/frio/theme.php b/view/theme/frio/theme.php
index c7d38baeb..aa59081d9 100644
--- a/view/theme/frio/theme.php
+++ b/view/theme/frio/theme.php
@@ -20,6 +20,7 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model;
 use Friendica\Module;
+use Friendica\Util\Strings;
 
 $frio = 'view/theme/frio';
 
@@ -300,7 +301,7 @@ function frio_remote_nav($a, &$nav)
  */
 function frio_acl_lookup(App $a, &$results)
 {
-	$nets = x($_GET, 'nets') ? notags(trim($_GET['nets'])) : '';
+	$nets = x($_GET, 'nets') ? Strings::escapeTags(trim($_GET['nets'])) : '';
 
 	// we introduce a new search type, r should do the same query like it's
 	// done in /src/Module/Contact.php for connections
@@ -310,7 +311,7 @@ function frio_acl_lookup(App $a, &$results)
 
 	$sql_extra = '';
 	if ($results['search']) {
-		$search_txt = DBA::escape(protect_sprintf(preg_quote($results['search'])));
+		$search_txt = DBA::escape(Strings::protectSprintf(preg_quote($results['search'])));
 		$sql_extra .= " AND (`attag` LIKE '%%" . $search_txt . "%%' OR `name` LIKE '%%" . $search_txt . "%%' OR `nick` LIKE '%%" . $search_txt . "%%') ";
 	}
 
diff --git a/view/theme/vier/theme.php b/view/theme/vier/theme.php
index fb4f66431..9427f1f23 100644
--- a/view/theme/vier/theme.php
+++ b/view/theme/vier/theme.php
@@ -21,6 +21,7 @@ use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\GContact;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 
 function vier_init(App $a)
 {
@@ -277,7 +278,7 @@ function vier_community_info()
 					$query .= ",";
 				}
 
-				$query .= "'".DBA::escape(normalise_link(trim($helper)))."'";
+				$query .= "'".DBA::escape(Strings::normaliseLink(trim($helper)))."'";
 			}
 
 			$r = q("SELECT `url`, `name` FROM `gcontact` WHERE `nurl` IN (%s)", $query);