Categories
WebOps

How to backup and restore Nextcloud bookmarks

There is a bookmark manager for Nextcloud that can be used with the floccus browser extension to synchronize bookmarks using Chrome, Firefox, and Opera. I will show you how to perform API calls to automate the 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 an application password

Create a 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://blogsleeplessbeastie.wpcomstaging.com/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 the 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 the custom file with a 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 the request token to pass CSRF verification.

Shell script

#!/bin/bash
# Upload and restore Nextcloud bookmarks
# https://blogsleeplessbeastie.wpcomstaging.com/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 "bm_import=@./${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

The structure of the exported file.

<!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://blogsleeplessbeastie.wpcomstaging.com/" TAGS="floccus:>Prywatne">sleeplessbeastie's notes</A>
<DT><A HREF="https://blogsleeplessbeastie.wpcomstaging.com/" TAGS="floccus:>Prywatne">sleeplessbeastie's home</A>

It is possible to use a 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 "bm_import=@./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 the next application version, cool.

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

Download source code.