What is the algorithm used to create the "Tiny links"
Asked by danielss:

Hi,

I've seen this question: [Is there a way to get a Confluence page's tiny link programmatically?](/questions/79198/is-there-a-way-to-get-a-confluence-page-s-tiny-link-programmatically) and I have one in a similar vein.

What is the algorithm used to create the tiny links?

I'm assuming it's some kind of base 64/62 encoder of some sort? If anyone knows where in the source to look for the code, a path would be greatly appreciated.

Steve


Answers:
author: David at David Simpson Apps
body: Check out the source of **com.atlassian.confluence.pages.TinyUrl**...

```
Base64.encodeBase64(longToByteArray(id));
```

id is the pageId.


author: cloudbolt_sam
body: I recently ported this to python:

```
def page_id_to_tiny(page_id):
  return base64.b64encode((page_id).to_bytes(4, byteorder='little')).decode().replace('/','-').replace('+','_').rstrip('A=')


def tiny_to_page_id(tiny):
  return int.from_bytes(base64.b64decode(tiny.ljust(8,'A').replace('_','+').replace('-','/').encode()),byteorder='little')

```

Updated as per feedback below from [@Sebastian Schmachtel](/t5/user/viewprofilepage/user-id/1246065) and [@Fabian Sturm](/t5/user/viewprofilepage/user-id/2166748) that noticed that Atlassian do not actually implement url safe encoding from rfc4648 correctly.

comments: author: Fabian Sturm
body: This would have been a perfect answer but I think Atlassian screwed up and + is replaced by _ and / by - instead of the other way round as it is defined for the url safe encoding rfc4648.


author: Sebastian Schmachtel
body: I used this functions and started to see weird effects. So I returned here et voila a hint just in time :-)

My (working) code looks now like this

```
def page_id_to_tiny(page_ids: List[int]) -> List[str]:
"""converts a list of pageids (int) to a list of last part of shortlink (/x/$PART)"""
shortlinks: List[str] = []
for page_id in page_ids:
encoded = base64.b64encode(page_id.to_bytes(4, byteorder='little')).decode()
encoded_fixed = encoded.replace("/", "-").replace("+", "_").rstrip('A=')
shortlinks.append(encoded_fixed)
return shortlinks


def tiny_to_page_id(tiny_links: List[str]) -> List[int]:
"""converts a list of last part of shortlink (/x/$PART) to a list of pageids (int)"""
page_ids: List[int] = []
for tiny_link in tiny_links:
tiny_link = tiny_link.replace("-", "/").replace("_", "+")
page_ids.append(int.from_bytes(base64.b64decode(tiny_link.ljust(8, 'A').encode()), byteorder='little'))
return page_ids
```


author: Tilman Schweitzer
body: Here a solution in JS. Please check if the browsers are compatible with btoa and atob (<https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa>).

```
// The long to byte array conversion is taken from this source:
// https://stackoverflow.com/questions/8482309/converting-javascript-integer-to-byte-array-and-back
function longToByteArray(long) {
  var byteArray = [0, 0, 0, 0];
  for ( var index = 0; index < byteArray.length; index ++ ) {
    var byte = long & 0xff;
    byteArray [ index ] = byte;
    long = (long - byte) / 256 ;
  }
  return byteArray;
}

function pageIdToShortUrl(pageId) {
  return btoa(longToByteArray(pageId)
    .map(String.fromCharCode)
    .map(function (s) { return s[0] })
    .join(""))
    .replace(/=/g,"");
}

function byteArrayToLong(byteArray) {
  var value = 0;
  for ( var i = byteArray.length - 1; i >= 0; i--) {
    value = (value * 256) + byteArray[i];
  }
  return value;
};

function shortUrlToPageId(shortUrl) {
  return byteArrayToLong(atob(shortUrl)
    .split("")
    .map(function (b) { return b.charCodeAt()}));
}

```


author: Mircea Vutcovici
body: I used the following functions in PHP based on the Steve's code:

```
function id2tinylink($id){    $bytes = pack("L", (int)$id);  // convert the long to bytes    $base64string=base64_encode($bytes);    $base64string_escaped=strtr($base64string,'+/','_-');    return rtrim($base64string_escaped, "A=");}function tinylink2id($tinylink){    $base64string_encoded = str_pad($tinylink, 8, "A", STR_PAD_RIGHT);    $base64string = strtr($base64string_encoded, '_-', '+/');    $bytes = base64_decode($base64string);    return unpack("L",$bytes)[1];}
```


author: Maxime Boyer
body: I used your examples to write a PostgreSQL equivalent. Probably not as efficient, but could be useful.

```
-- Encode to short URL
SELECT TRIM(
TRAILING 'A=' FROM 
translate(encode(('\x' || regexp_replace(
lpad(to_hex(content.contentid), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bytea, 'base64'), '-_', '/+')) "short_url"
FROM (SELECT 200999755 "contentid") "content"
```

An example from the Confluence table directly:

```
SELECT contentid, TRIM(
TRAILING 'A=' FROM 
translate(encode(('\x' || regexp_replace(
lpad(to_hex(content.contentid), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bytea, 'base64'), '-_', '/+')) "short_url",
title
FROM content
LIMIT 10
```

To decode a short URL to it's content ID:

```
?-- Decode to content ID
SELECT ('x' || regexp_replace(
rpad(encode(decode(translate(short.url || repeat('A', 8 - length(short.url)), '_-', '+/'), 'base64'), 'hex'), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bit(64)::bigint "content_id"
FROM (SELECT 'SwP7Cw' "url") "short"
```


author: Sam Liddicott
body: Here is a rough bash implementation. I don't bother chopping a trailing A because [this official implementation](https://confluence.atlassian.com/confkb/how-to-programmatically-generate-the-tiny-link-of-a-confluence-page-956713432.html) also didn't

```
confluence-tiny-code() {
  local id="${1?}"
  local tiny
  printf -v tiny "\\\x%02x" $(( id & 0xff )) $(( (id >> 8 ) & 0xff )) $(( (id >> 16 ) & 0xff )) $(( (id >> 24 ) & 0xff ))
  tiny=$(printf "$tiny" | base64)
  tiny="${tiny//\+/_}"
  tiny="${tiny//\//-}"
  echo "${tiny%%=*}"
}
```


author: N-A okuda
body: Could you describe a method of decoding?


author: danielss
body: If anyone would like to emulate this functionality in PHP for whatever reason:

```
function tinylinkid($id){	$bytes = pack("L", $id);  // convert the long to bytes	$bytes = rtrim($bytes);  // trim any null characters	$bytestring = "";	$len = strlen($bytes); 	for($i = 0; $i &lt; $len; $i++) {		$bytestring = $bytestring . $bytes[$i]; // build the byte string	}	$base64string = base64_encode($bytestring); // base64 encode the byte string	$base64string = rtrim($base64string, "="); // trim any base64 padding characters	return $base64string;}
```

Thanks for David for the pointer.

Steve


Tags: link