How to backup and restore Nextcloud bookmarks

There is a bookmark manager for Nextcloud that can be used with floccus browser extension to synchronize bookmarks using Chrome, Firefox and Opera. I will show you how to perform API calls to automate backup process.

Prerequisites

Install curl utility to perform API calls.

$ sudo apt-get install curl

Install jq utility to parse JSON files.

$ sudo apt-get install jq

Create application password

Create Nextcloud application password, do not use your regular credentials.

Download Nextcloud bookmarks

This usage scenario use curl and jq to export bookmarks.

Shell script

#!/bin/bash
# Download Nextcloud bookmarks
# https://blog.sleeplessbeastie.eu/2018/04/18/how-to-backup-and-restore-nextcloud-bookmarks/

# current date and time
current_datetime="$(date +%Y%m%d_%H%M%S)"

# usage info
usage(){
  echo "Usage:"
  echo "  $0 -r nextcloud_url -u username -p passsword [-f filename|-t]"
  echo ""
  echo "Parameters:"
  echo "  -r nextcloud_url   : set Nextcloud URL (required)"
  echo "  -u username        : set username (required)"
  echo "  -p password        : set password (required)"
  echo "  -f filename        : set filename w/o suffix (optional)"
  echo "  -t                 : add timestamp to output filename (optional)"
  echo ""
}

# parse parameters
while getopts "r:u:p:f:t" option; do
  case $option in
    "r")
      param_nextcloud_address="${OPTARG}"
      param_nextcloud_address_defined=true
      ;;
    "u")
      param_username="${OPTARG}"
      param_username_defined=true
      ;;
    "p")
      param_password="${OPTARG}"
      param_password_defined=true
      ;;
    "f")
      param_filename="${OPTARG}"
      param_filename_defined=true
      ;;
    "t")
      param_date_prefix=true
      ;;
    \?|:|*)
      usage
      exit
      ;;
  esac
done

if [ "${param_nextcloud_address_defined}" = true ] && \
   [ "${param_username_defined}"          = true ] && \
   [ "${param_password_defined}"          = true ]; then

  if [ "${param_filename_defined}" = true ]; then
    filename="${param_filename}"
  else
    filename="nextcloud-bookmarks"
  fi

  if [ "${param_date_prefix}" = true ]; then
    filename="${filename}-${current_datetime}.html"
  else
    filename="${filename}.html"
  fi

  result=$(curl --silent --output - -X GET --user "${param_username}:${param_password}" --header "Accept: application/json" "${param_nextcloud_address}/apps/bookmarks/public/rest/v2/bookmark" | \
           jq -r 'select(.status != "success") | .status')
  if [ -n "${result}" ]; then
    echo "There was an error \"${result}\' when downloading Nextcloud bookmarks for user ${param_username}. Skipping."
  else
    curl --silent --output "${filename}"  -X GET --user "${param_username}:${param_password}"  "${param_nextcloud_address}/apps/bookmarks/public/rest/v2/bookmark/export"
    echo "Downloaded Nextcloud bookmarks to file \"${filename}\""
  fi
else
  usage
fi

Usage

Display usage information.

$ nextcloud_bookmarks_get.sh 
Usage:
  nextcloud_bookmarks_get.sh -r nextcloud_url -u username -p passsword [-f filename|-t]

Parameters:
  -r nextcloud_url   : set Nextcloud URL (required)
  -u username        : set username (required)
  -p password        : set password (required)
  -f filename        : set filename w/o suffix (optional)
  -t                 : add timestamp to output filename (optional)

Export bookmarks.

$ nextcloud_bookmarks_get.sh -r https://cloud.example.org -u milosz -p "Mjdu3-kDnru-4UksA-fYs0w"
Downloaded Nextcloud bookmarks to file "nextcloud-bookmarks.html"

Export bookmarks and append timestamp to filename.

$ nextcloud_bookmarks_get.sh -r https://cloud.example.org -u milosz -p "Mjdu3-kDnru-4UksA-fYs0w"
Downloaded Nextcloud bookmarks to file "nextcloud-bookmarks-20180416_214516.html"

Export bookmarks to defined filename and append timestamp.

$ nextcloud_bookmarks_get.sh -r https://cloud.example.org -u milosz -p "Mjdu3-kDnru-4UksA-fYs0w" -t -f milosz-bookmarks
Downloaded Nextcloud bookmarks to file "milosz-bookmarks-20180416_214645.html"

Upload Nextcloud bookmarks

This usage scenario is more complicated as you need to extract and use request token to pass CSRF verification.

Shell script

#!/bin/bash
# Upload and restore Nextcloud bookmarks
# https://blog.sleeplessbeastie.eu/2018/04/18/how-to-backup-and-restore-nextcloud-bookmarks/

# temporary file to store cookie
cookie_file=$(mktemp)

# delete temporary file on exit
trap "unlink $cookie_file" EXIT

# usage info
usage(){
  echo "Usage:"
  echo "  $0 -r nextcloud_url -u username -p passsword -f json_board_file"
  echo ""
  echo "Parameters:"
  echo "  -r nextcloud_url   : set Nextcloud URL (required)"
  echo "  -u username        : set username (required)"
  echo "  -p password        : set password (required)"
  echo "  -f file            : exported bookmarks (required)"
  echo ""
}

# parse parameters
while getopts "r:u:p:f:" option; do
  case $option in
    "r")
      param_nextcloud_address="${OPTARG}"
      param_nextcloud_address_defined=true
      ;;
    "u")
      param_username="${OPTARG}"
      param_username_defined=true
      ;;
    "p")
      param_password="${OPTARG}"
      param_password_defined=true
      ;;
    "f")
      param_file="${OPTARG}"
      param_file_defined=true
      ;;
    \?|:|*)
      usage
      exit
      ;;
  esac
done

if [ "${param_nextcloud_address_defined}" = true ] && \
   [ "${param_username_defined}"          = true ] && \
   [ "${param_password_defined}"          = true ] && \
   [ "${param_file_defined}"              = true ] && \
   [ -f "${param_file}"                          ]; then

  request_token=$(curl -c ${cookie_file} --silent --location  -X GET --user "${param_username}:${param_password}" "${param_nextcloud_address}/apps/bookmarks/bookmark/" | grep "data-requesttoken" | sed "s/.*<head.*\"\(.*\)\">/\1/")

  result=$(curl -b ${cookie_file} --silent --header "OCS-REQUEST: true" --header "requesttoken: ${request_token}" -F "[email protected]/${param_file};type=text/html" --location -X POST --user "${param_username}:${param_password}" --header 'Accept: application/json' "${param_nextcloud_address}/index.php/apps/bookmarks/bookmark/import" | jq -r 'select(.status != "success") | .status')
  if [ -n "${result}" ]; then
    echo "There was an error \"${result}\". Skipping."
  else
    echo "Uploaded Nextcloud bookmarks from file \"${param_file}\""
  fi
else
  usage
fi

Usage

Display usage information.

$ nextcloud_bookmarks_put.sh 
Usage:
  nextcloud_bookmarks_put.sh -r nextcloud_url -u username -p passsword -f json_board_file

Parameters:
  -r nextcloud_url   : set Nextcloud URL (required)
  -u username        : set username (required)
  -p password        : set password (required)
  -f file            : exported bookmarks (required)

Restore bookmarks from a file.

$ nextcloud_bookmarks_put.sh -r https://cloud.example.org -u milosz -p "Mjdu3-kDnru-4UksA-fYs0w" -f milosz-bookmarks-20180416_214645.html 
Uploaded Nextcloud bookmarks from file "milosz-bookmarks-20180416_214645.html"

Additional notes

Structure of the exported file is very simple.

<!DOCTYPE NETSCAPE-Bookmark-file-1>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<!-- This is an automatically generated file.
It will be read and overwritten.
Do Not Edit! -->
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p><DT><A HREF="https://blog.sleeplessbeastie.eu/" TAGS="floccus:>Prywatne">sleeplessbeastie's notes</A>
<DT><A HREF="https://sleeplessbeastie.eu/" TAGS="floccus:>Prywatne">sleeplessbeastie's home</A>

It is possible to use single API request to import bookmarks, but it requires setting the @NoCSRFRequired annotation before the importBookmark controller method.

controller/rest/bookmarkcontroller.php
@@ -330,6 +330,8 @@ public function clickBookmark($url = "") {
 	 * @return \OCP\AppFramework\Http\JSONResponse
 	 *
 	 * @NoAdminRequired
+	 * @NoCSRFRequired
+	 * @CORS	 
 	 */
 	public function importBookmark() {
 		$full_input = $this->request->getUploadedFile("bm_import");
$ curl -F "[email protected]/nextcloud-bookmarks.html;type=text/html" -L -X POST --user milosz:Mjdu3-kDnru-4UksA-fYs0w https://cloud.example.org/apps/bookmarks/public/rest/v2/bookmark/import
{"status":"success"}

This modification will be available in next application version, cool.

Please read Open Collaboration Services v2.0 and Nextcloud 13 Developer Manual.

Download source code.

About Milosz Galazka

Milosz is a Linux Foundation Certified Engineer working for a successful Polish company as a system administrator and a long time supporter of Free Software Foundation and Debian operating system. He is also open for new opportunities and challenges.