当前位置 博文首页 > 编写shell脚本将VPS上的数据备份到Dropbox网盘的方法

    编写shell脚本将VPS上的数据备份到Dropbox网盘的方法

    作者:admin 时间:2021-07-05 18:29

    看到有人用dropbox备份网站数据,所以今天也试了一下,记得以前是一个python脚本,这是用的是bash 脚本,利用dropbox的api来上传下载的,很方便,脚本的地址是Dropbox-Uploader/dropbox_uploader.sh at master · andreafabrizi/Dropbox-Uploader · GitHub ,感谢作者分享这个脚本。

    可以到git下载dropbox_uploader.sh,地址为:https://github.com/andreafabrizi/Dropbox-Uploader
    或者也可以直接拷贝代码,保存为dropbox_uploader.sh,注意拷贝的时候最好是复制到文本编辑器里面,如notepad++之类的

    #!/usr/bin/env bash
    #
    # Dropbox Uploader
    #
    # Copyright (C) 2010-2014 Andrea Fabrizi <andrea.fabrizi@gmail.com>
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation; either version 2 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    #
    
    #Default configuration file
    CONFIG_FILE=~/.dropbox_uploader
    
    #Default chunk size in Mb for the upload process
    #It is recommended to increase this value only if you have enough free space on your /tmp partition
    #Lower values may increase the number of http requests
    CHUNK_SIZE=4
    
    #Curl location
    #If not set, curl will be searched into the $PATH
    #CURL_BIN="/usr/bin/curl"
    
    #Default values
    TMP_DIR="/tmp"
    DEBUG=0
    QUIET=0
    SHOW_PROGRESSBAR=0
    SKIP_EXISTING_FILES=0
    ERROR_STATUS=0
    
    #Don't edit these...
    API_REQUEST_TOKEN_URL="https://api.dropbox.com/1/oauth/request_token"
    API_USER_AUTH_URL="https://www2.dropbox.com/1/oauth/authorize"
    API_ACCESS_TOKEN_URL="https://api.dropbox.com/1/oauth/access_token"
    API_CHUNKED_UPLOAD_URL="https://api-content.dropbox.com/1/chunked_upload"
    API_CHUNKED_UPLOAD_COMMIT_URL="https://api-content.dropbox.com/1/commit_chunked_upload"
    API_UPLOAD_URL="https://api-content.dropbox.com/1/files_put"
    API_DOWNLOAD_URL="https://api-content.dropbox.com/1/files"
    API_DELETE_URL="https://api.dropbox.com/1/fileops/delete"
    API_MOVE_URL="https://api.dropbox.com/1/fileops/move"
    API_COPY_URL="https://api.dropbox.com/1/fileops/copy"
    API_METADATA_URL="https://api.dropbox.com/1/metadata"
    API_INFO_URL="https://api.dropbox.com/1/account/info"
    API_MKDIR_URL="https://api.dropbox.com/1/fileops/create_folder"
    API_SHARES_URL="https://api.dropbox.com/1/shares"
    APP_CREATE_URL="https://www2.dropbox.com/developers/apps"
    RESPONSE_FILE="$TMP_DIR/du_resp_$RANDOM"
    CHUNK_FILE="$TMP_DIR/du_chunk_$RANDOM"
    TEMP_FILE="$TMP_DIR/du_tmp_$RANDOM"
    BIN_DEPS="sed basename date grep stat dd mkdir"
    VERSION="0.14"
    
    umask 077
    
    #Check the shell
    if [ -z "$BASH_VERSION" ]; then
      echo -e "Error: this script requires the BASH shell!"
      exit 1
    fi
    
    shopt -s nullglob #Bash allows filename patterns which match no files to expand to a null string, rather than themselves
    shopt -s dotglob #Bash includes filenames beginning with a "." in the results of filename expansion
    
    #Look for optional config file parameter
    while getopts ":qpskdf:" opt; do
      case $opt in
    
      f)
       CONFIG_FILE=$OPTARG
      ;;
    
      d)
       DEBUG=1
      ;;
    
      q)
       QUIET=1
      ;;
    
      p)
       SHOW_PROGRESSBAR=1
      ;;
    
      k)
       CURL_ACCEPT_CERTIFICATES="-k"
      ;;
    
      s)
       SKIP_EXISTING_FILES=1
      ;;
    
      \?)
       echo "Invalid option: -$OPTARG" >&2
       exit 1
      ;;
    
      :)
       echo "Option -$OPTARG requires an argument." >&2
       exit 1
      ;;
    
     esac
    done
    
    if [[ $DEBUG != 0 ]]; then
      echo $VERSION
      set -x
      RESPONSE_FILE="$TMP_DIR/du_resp_debug"
    fi
    
    if [[ $CURL_BIN == "" ]]; then
      BIN_DEPS="$BIN_DEPS curl"
      CURL_BIN="curl"
    fi
    
    #Dependencies check
    which $BIN_DEPS > /dev/null
    if [[ $? != 0 ]]; then
      for i in $BIN_DEPS; do
        which $i > /dev/null ||
          NOT_FOUND="$i $NOT_FOUND"
      done
      echo -e "Error: Required program could not be found: $NOT_FOUND"
      exit 1
    fi
    
    #Check if readlink is installed and supports the -m option
    #It's not necessary, so no problem if it's not installed
    which readlink > /dev/null
    if [[ $? == 0 && $(readlink -m "//test" 2> /dev/null) == "/test" ]]; then
      HAVE_READLINK=1
    else
      HAVE_READLINK=0
    fi
    
    #Forcing to use the builtin printf, if it's present, because it's better
    #otherwise the external printf program will be used
    #Note that the external printf command can cause character encoding issues!
    builtin printf "" 2> /dev/null
    if [[ $? == 0 ]]; then
      PRINTF="builtin printf"
      PRINTF_OPT="-v o"
    else
      PRINTF=$(which printf)
      if [[ $? != 0 ]]; then
        echo -e "Error: Required program could not be found: printf"
      fi
      PRINTF_OPT=""
    fi
    
    #Print the message based on $QUIET variable
    function print
    {
      if [[ $QUIET == 0 ]]; then
    	  echo -ne "$1";
      fi
    }
    
    #Returns unix timestamp
    function utime
    {
      echo $(date +%s)
    }
    
    #Remove temporary files
    function remove_temp_files
    {
      if [[ $DEBUG == 0 ]]; then
        rm -fr "$RESPONSE_FILE"
        rm -fr "$CHUNK_FILE"
        rm -fr "$TEMP_FILE"
      fi
    }
    
    #Returns the file size in bytes
    # generic GNU Linux: linux-gnu
    # windows cygwin:  cygwin
    # raspberry pi:   linux-gnueabihf
    # macosx:      darwin10.0
    # freebsd:      FreeBSD
    # qnap:       linux-gnueabi
    # iOS:        darwin9
    function file_size
    {
      #Some embedded linux devices
      if [[ $OSTYPE == "linux-gnueabi" || $OSTYPE == "linux-gnu" ]]; then
        stat -c "%s" "$1"
        return
    
      #Generic Unix
      elif [[ ${OSTYPE:0:5} == "linux" || $OSTYPE == "cygwin" || ${OSTYPE:0:7} == "solaris" ]]; then
        stat --format="%s" "$1"
        return
    
      #BSD, OSX and other OSs
      else
        stat -f "%z" "$1"
        return
      fi
    }
    
    #Usage
    function usage
    {
      echo -e "Dropbox Uploader v$VERSION"
      echo -e "Andrea Fabrizi - andrea.fabrizi@gmail.com\n"
      echo -e "Usage: $0 COMMAND [PARAMETERS]..."
      echo -e "\nCommands:"
    
      echo -e "\t upload  <LOCAL_FILE/DIR ...> <REMOTE_FILE/DIR>"
      echo -e "\t download <REMOTE_FILE/DIR> [LOCAL_FILE/DIR]"
      echo -e "\t delete  <REMOTE_FILE/DIR>"
      echo -e "\t move   <REMOTE_FILE/DIR> <REMOTE_FILE/DIR>"
      echo -e "\t copy   <REMOTE_FILE/DIR> <REMOTE_FILE/DIR>"
      echo -e "\t mkdir  <REMOTE_DIR>"
      echo -e "\t list   [REMOTE_DIR]"
      echo -e "\t share  <REMOTE_FILE>"
      echo -e "\t info"
      echo -e "\t unlink"
    
      echo -e "\nOptional parameters:"
      echo -e "\t-f <FILENAME> Load the configuration file from a specific file"
      echo -e "\t-s      Skip already existing files when download/upload. Default: Overwrite"
      echo -e "\t-d      Enable DEBUG mode"
      echo -e "\t-q      Quiet mode. Don't show messages"
      echo -e "\t-p      Show cURL progress meter"
      echo -e "\t-k      Doesn't check for SSL certificates (insecure)"
    
      echo -en "\nFor more info and examples, please see the README file.\n\n"
      remove_temp_files
      exit 1
    }
    
    #Check the curl exit code
    function check_http_response
    {
      CODE=$?
    
      #Checking curl exit code
      case $CODE in
    
        #OK
        0)
    
        ;;
    
        #Proxy error
        5)
          print "\nError: Couldn't resolve proxy. The given proxy host could not be resolved.\n"
    
          remove_temp_files
          exit 1
        ;;
    
        #Missing CA certificates
        60|58)
          print "\nError: cURL is not able to performs peer SSL certificate verification.\n"
          print "Please, install the default ca-certificates bundle.\n"
          print "To do this in a Debian/Ubuntu based system, try:\n"
          print " sudo apt-get install ca-certificates\n\n"
          print "If the problem persists, try to use the -k option (insecure).\n"
    
          remove_temp_files
          exit 1
        ;;
    
        6)
          print "\nError: Couldn't resolve host.\n"
    
          remove_temp_files
          exit 1
        ;;
    
        7)
          print "\nError: Couldn't connect to host.\n"
    
          remove_temp_files
          exit 1
        ;;
    
      esac
    
      #Checking response file for generic errors
      if grep -q "HTTP/1.1 400" "$RESPONSE_FILE"; then
        ERROR_MSG=$(sed -n -e 's/{"error": "\([^"]*\)"}/\1/p' "$RESPONSE_FILE")
    
        case $ERROR_MSG in
           *access?attempt?failed?because?this?app?is?not?configured?to?have*)
            echo -e "\nError: The Permission type/Access level configured doesn't match the DropBox App settings!\nPlease run \"$0 unlink\" and try again."
            exit 1
          ;;
        esac
    
      fi
    
    }
    
    #Urlencode
    function urlencode
    {
      local string="${1}"
      local strlen=${#string}
      local encoded=""
    
      for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
          [-_.~a-zA-Z0-9] ) o="${c}" ;;
          * ) $PRINTF $PRINTF_OPT '%%%02x' "'$c"
        esac
        encoded+="${o}"
      done
    
      echo "$encoded"
    }
    
    function normalize_path
    {
      path=$(echo -e "$1")
      if [[ $HAVE_READLINK == 1 ]]; then
        readlink -m "$path"
      else
        echo "$path"
      fi
    }
    
    #Check if it's a file or directory
    #Returns FILE/DIR/ERR
    function db_stat
    {
      local FILE=$(normalize_path "$1")
    
      #Checking if it's a file or a directory
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" "$API_METADATA_URL/$ACCESS_LEVEL/$(urlencode "$FILE")?oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" 2> /dev/null
      check_http_response
    
      #Even if the file/dir has been deleted from DropBox we receive a 200 OK response
      #So we must check if the file exists or if it has been deleted
      if grep -q "\"is_deleted\":" "$RESPONSE_FILE"; then
        local IS_DELETED=$(sed -n 's/.*"is_deleted":.\([^,]*\).*/\1/p' "$RESPONSE_FILE")
      else
        local IS_DELETED="false"
      fi
    
      #Exits...
      grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"
      if [[ $? == 0 && $IS_DELETED != "true" ]]; then
    
        local IS_DIR=$(sed -n 's/^\(.*\)\"contents":.\[.*/\1/p' "$RESPONSE_FILE")
    
        #It's a directory
        if [[ $IS_DIR != "" ]]; then
          echo "DIR"
        #It's a file
        else
          echo "FILE"
        fi
    
      #Doesn't exists
      else
        echo "ERR"
      fi
    }
    
    #Generic upload wrapper around db_upload_file and db_upload_dir functions
    #$1 = Local source file/dir
    #$2 = Remote destination file/dir
    function db_upload
    {
      local SRC=$(normalize_path "$1")
      local DST=$(normalize_path "$2")
    
      #Checking if the file/dir exists
      if [[ ! -e $SRC && ! -d $SRC ]]; then
        print " > No such file or directory: $SRC\n"
        ERROR_STATUS=1
        return
      fi
    
      #Checking if the file/dir has read permissions
      if [[ ! -r $SRC ]]; then
        print " > Error reading file $SRC: permission denied\n"
        ERROR_STATUS=1
        return
      fi
    
      #Checking if DST it's a folder or if it doesn' exists (in this case will be the destination name)
      TYPE=$(db_stat "$DST")
      if [[ $TYPE == "DIR" ]]; then
        local filename=$(basename "$SRC")
        DST="$DST/$filename"
      fi
    
      #It's a directory
      if [[ -d $SRC ]]; then
        db_upload_dir "$SRC" "$DST"
    
      #It's a file
      elif [[ -e $SRC ]]; then
        db_upload_file "$SRC" "$DST"
    
      #Unsupported object...
      else
        print " > Skipping not regular file \"$SRC\"\n"
      fi
    }
    
    #Generic upload wrapper around db_chunked_upload_file and db_simple_upload_file
    #The final upload function will be choosen based on the file size
    #$1 = Local source file
    #$2 = Remote destination file
    function db_upload_file
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      shopt -s nocasematch
    
      #Checking not allowed file names
      basefile_dst=$(basename "$FILE_DST")
      if [[ $basefile_dst == "thumbs.db" || \
         $basefile_dst == "desktop.ini" || \
         $basefile_dst == ".ds_store" || \
         $basefile_dst == "icon\r" || \
         $basefile_dst == ".dropbox" || \
         $basefile_dst == ".dropbox.attr" \
        ]]; then
        print " > Skipping not allowed file name \"$FILE_DST\"\n"
        return
      fi
    
      shopt -u nocasematch
    
      #Checking file size
      FILE_SIZE=$(file_size "$FILE_SRC")
    
      #Checking if the file already exists
      TYPE=$(db_stat "$FILE_DST")
      if [[ $TYPE != "ERR" && $SKIP_EXISTING_FILES == 1 ]]; then
        print " > Skipping already existing file \"$FILE_DST\"\n"
        return
      fi
    
      if [[ $FILE_SIZE -gt 157286000 ]]; then
        #If the file is greater than 150Mb, the chunked_upload API will be used
        db_chunked_upload_file "$FILE_SRC" "$FILE_DST"
      else
        db_simple_upload_file "$FILE_SRC" "$FILE_DST"
      fi
    
    }
    
    #Simple file upload
    #$1 = Local source file
    #$2 = Remote destination file
    function db_simple_upload_file
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then
        CURL_PARAMETERS="--progress-bar"
        LINE_CR="\n"
      else
        CURL_PARAMETERS="-s"
        LINE_CR=""
      fi
    
      print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR"
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES $CURL_PARAMETERS -i --globoff -o "$RESPONSE_FILE" --upload-file "$FILE_SRC" "$API_UPLOAD_URL/$ACCESS_LEVEL/$(urlencode "$FILE_DST")?oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM"
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      else
        print "FAILED\n"
        print "An error occurred requesting /upload\n"
        ERROR_STATUS=1
      fi
    }
    
    #Chunked file upload
    #$1 = Local source file
    #$2 = Remote destination file
    function db_chunked_upload_file
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\""
    
      local FILE_SIZE=$(file_size "$FILE_SRC")
      local OFFSET=0
      local UPLOAD_ID=""
      local UPLOAD_ERROR=0
      local CHUNK_PARAMS=""
    
      #Uploading chunks...
      while ([[ $OFFSET != $FILE_SIZE ]]); do
    
        let OFFSET_MB=$OFFSET/1024/1024
    
        #Create the chunk
        dd if="$FILE_SRC" of="$CHUNK_FILE" bs=1048576 skip=$OFFSET_MB count=$CHUNK_SIZE 2> /dev/null
    
        #Only for the first request these parameters are not included
        if [[ $OFFSET != 0 ]]; then
          CHUNK_PARAMS="upload_id=$UPLOAD_ID&offset=$OFFSET"
        fi
    
        #Uploading the chunk...
        $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --upload-file "$CHUNK_FILE" "$API_CHUNKED_UPLOAD_URL?$CHUNK_PARAMS&oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" 2> /dev/null
        check_http_response
    
        #Check
        if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
          print "."
          UPLOAD_ERROR=0
          UPLOAD_ID=$(sed -n 's/.*"upload_id": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE")
          OFFSET=$(sed -n 's/.*"offset": *\([^}]*\).*/\1/p' "$RESPONSE_FILE")
        else
          print "*"
          let UPLOAD_ERROR=$UPLOAD_ERROR+1
    
          #On error, the upload is retried for max 3 times
          if [[ $UPLOAD_ERROR -gt 2 ]]; then
            print " FAILED\n"
            print "An error occurred requesting /chunked_upload\n"
            ERROR_STATUS=1
            return
          fi
        fi
    
      done
    
      UPLOAD_ERROR=0
    
      #Commit the upload
      while (true); do
    
        $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "upload_id=$UPLOAD_ID&oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" "$API_CHUNKED_UPLOAD_COMMIT_URL/$ACCESS_LEVEL/$(urlencode "$FILE_DST")" 2> /dev/null
        check_http_response
    
        #Check
        if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
          print "."
          UPLOAD_ERROR=0
          break
        else
          print "*"
          let UPLOAD_ERROR=$UPLOAD_ERROR+1
    
          #On error, the commit is retried for max 3 times
          if [[ $UPLOAD_ERROR -gt 2 ]]; then
            print " FAILED\n"
            print "An error occurred requesting /commit_chunked_upload\n"
            ERROR_STATUS=1
            return
          fi
        fi
    
      done
    
      print " DONE\n"
    }
    
    #Directory upload
    #$1 = Local source dir
    #$2 = Remote destination dir
    function db_upload_dir
    {
      local DIR_SRC=$(normalize_path "$1")
      local DIR_DST=$(normalize_path "$2")
    
      #Creatig remote directory
      db_mkdir "$DIR_DST"
    
      for file in "$DIR_SRC/"*; do
        db_upload "$file" "$DIR_DST"
      done
    }
    
    #Returns the free space on DropBox in bytes
    function db_free_quota
    {
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" "$API_INFO_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
    
        quota=$(sed -n 's/.*"quota": \([0-9]*\).*/\1/p' "$RESPONSE_FILE")
        used=$(sed -n 's/.*"normal": \([0-9]*\).*/\1/p' "$RESPONSE_FILE")
        let free_quota=$quota-$used
        echo $free_quota
    
      else
        echo 0
      fi
    }
    
    #Generic download wrapper
    #$1 = Remote source file/dir
    #$2 = Local destination file/dir
    function db_download
    {
      local SRC=$(normalize_path "$1")
      local DST=$(normalize_path "$2")
    
      TYPE=$(db_stat "$SRC")
    
      #It's a directory
      if [[ $TYPE == "DIR" ]]; then
    
        #If the DST folder is not specified, I assume that is the current directory
        if [[ $DST == "" ]]; then
          DST="."
        fi
    
        #Checking if the destination directory exists
        if [[ ! -d $DST ]]; then
          local basedir=""
        else
          local basedir=$(basename "$SRC")
        fi
    
        local DEST_DIR=$(normalize_path "$DST/$basedir")
        print " > Downloading \"$SRC\" to \"$DEST_DIR\"... \n"
        print " > Creating local directory \"$DEST_DIR\"... "
        mkdir -p "$DEST_DIR"
    
        #Check
        if [[ $? == 0 ]]; then
          print "DONE\n"
        else
          print "FAILED\n"
          ERROR_STATUS=1
          return
        fi
    
        #Extracting directory content [...]
        #and replacing "}, {" with "}\n{"
        #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code...
        local DIR_CONTENT=$(sed -n 's/.*: \[{\(.*\)/\1/p' "$RESPONSE_FILE" | sed 's/}, *{/}\
    {/g')
    
        #Extracting files and subfolders
        TMP_DIR_CONTENT_FILE="${RESPONSE_FILE}_$RANDOM"
        echo "$DIR_CONTENT" | sed -n 's/.*"path": *"\([^"]*\)",.*"is_dir": *\([^"]*\),.*/\1:\2/p' > $TMP_DIR_CONTENT_FILE
    
        #For each entry...
        while read -r line; do
    
          local FILE=${line%:*}
          local TYPE=${line#*:}
    
          #Removing unneeded /
          FILE=${FILE##*/}
    
          if [[ $TYPE == "false" ]]; then
            db_download_file "$SRC/$FILE" "$DEST_DIR/$FILE"
          else
            db_download "$SRC/$FILE" "$DEST_DIR"
          fi
    
        done < $TMP_DIR_CONTENT_FILE
    
        rm -fr $TMP_DIR_CONTENT_FILE
    
      #It's a file
      elif [[ $TYPE == "FILE" ]]; then
    
        #Checking DST
        if [[ $DST == "" ]]; then
          DST=$(basename "$SRC")
        fi
    
        #If the destination is a directory, the file will be download into
        if [[ -d $DST ]]; then
          DST="$DST/$SRC"
        fi
    
        db_download_file "$SRC" "$DST"
    
      #Doesn't exists
      else
        print " > No such file or directory: $SRC\n"
        ERROR_STATUS=1
        return
      fi
    }
    
    #Simple file download
    #$1 = Remote source file
    #$2 = Local destination file
    function db_download_file
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then
        CURL_PARAMETERS="--progress-bar"
        LINE_CR="\n"
      else
        CURL_PARAMETERS="-s"
        LINE_CR=""
      fi
    
      #Checking if the file already exists
      if [[ -e $FILE_DST && $SKIP_EXISTING_FILES == 1 ]]; then
        print " > Skipping already existing file \"$FILE_DST\"\n"
        return
      fi
    
      #Creating the empty file, that for two reasons:
      #1) In this way I can check if the destination file is writable or not
      #2) Curl doesn't automatically creates files with 0 bytes size
      dd if=/dev/zero of="$FILE_DST" count=0 2> /dev/null
      if [[ $? != 0 ]]; then
        print " > Error writing file $FILE_DST: permission denied\n"
        ERROR_STATUS=1
        return
      fi
    
      print " > Downloading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR"
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES $CURL_PARAMETERS --globoff -D "$RESPONSE_FILE" -o "$FILE_DST" "$API_DOWNLOAD_URL/$ACCESS_LEVEL/$(urlencode "$FILE_SRC")?oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM"
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      else
        print "FAILED\n"
        rm -fr "$FILE_DST"
        ERROR_STATUS=1
        return
      fi
    }
    
    #Prints account info
    function db_account_info
    {
      print "Dropbox Uploader v$VERSION\n\n"
      print " > Getting info... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" "$API_INFO_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
    
        name=$(sed -n 's/.*"display_name": "\([^"]*\).*/\1/p' "$RESPONSE_FILE")
        echo -e "\n\nName:\t$name"
    
        uid=$(sed -n 's/.*"uid": \([0-9]*\).*/\1/p' "$RESPONSE_FILE")
        echo -e "UID:\t$uid"
    
        email=$(sed -n 's/.*"email": "\([^"]*\).*/\1/p' "$RESPONSE_FILE")
        echo -e "Email:\t$email"
    
        quota=$(sed -n 's/.*"quota": \([0-9]*\).*/\1/p' "$RESPONSE_FILE")
        let quota_mb=$quota/1024/1024
        echo -e "Quota:\t$quota_mb Mb"
    
        used=$(sed -n 's/.*"normal": \([0-9]*\).*/\1/p' "$RESPONSE_FILE")
        let used_mb=$used/1024/1024
        echo -e "Used:\t$used_mb Mb"
    
        let free_mb=($quota-$used)/1024/1024
        echo -e "Free:\t$free_mb Mb"
    
        echo ""
    
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #Account unlink
    function db_unlink
    {
      echo -ne "Are you sure you want unlink this script from your Dropbox account? [y/n]"
      read answer
      if [[ $answer == "y" ]]; then
        rm -fr "$CONFIG_FILE"
        echo -ne "DONE\n"
      fi
    }
    
    #Delete a remote file
    #$1 = Remote file to delete
    function db_delete
    {
      local FILE_DST=$(normalize_path "$1")
    
      print " > Deleting \"$FILE_DST\"... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM&root=$ACCESS_LEVEL&path=$(urlencode "$FILE_DST")" "$API_DELETE_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #Move/Rename a remote file
    #$1 = Remote file to rename or move
    #$2 = New file name or location
    function db_move
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      TYPE=$(db_stat "$FILE_DST")
    
      #If the destination it's a directory, the source will be moved into it
      if [[ $TYPE == "DIR" ]]; then
        local filename=$(basename "$FILE_SRC")
        FILE_DST=$(normalize_path "$FILE_DST/$filename")
      fi
    
      print " > Moving \"$FILE_SRC\" to \"$FILE_DST\" ... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM&root=$ACCESS_LEVEL&from_path=$(urlencode "$FILE_SRC")&to_path=$(urlencode "$FILE_DST")" "$API_MOVE_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #Copy a remote file to a remote location
    #$1 = Remote file to rename or move
    #$2 = New file name or location
    function db_copy
    {
      local FILE_SRC=$(normalize_path "$1")
      local FILE_DST=$(normalize_path "$2")
    
      TYPE=$(db_stat "$FILE_DST")
    
      #If the destination it's a directory, the source will be copied into it
      if [[ $TYPE == "DIR" ]]; then
        local filename=$(basename "$FILE_SRC")
        FILE_DST=$(normalize_path "$FILE_DST/$filename")
      fi
    
      print " > Copying \"$FILE_SRC\" to \"$FILE_DST\" ... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM&root=$ACCESS_LEVEL&from_path=$(urlencode "$FILE_SRC")&to_path=$(urlencode "$FILE_DST")" "$API_COPY_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #Create a new directory
    #$1 = Remote directory to create
    function db_mkdir
    {
      local DIR_DST=$(normalize_path "$1")
    
      print " > Creating Directory \"$DIR_DST\"... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM&root=$ACCESS_LEVEL&path=$(urlencode "$DIR_DST")" "$API_MKDIR_URL" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print "DONE\n"
      elif grep -q "^HTTP/1.1 403 Forbidden" "$RESPONSE_FILE"; then
        print "ALREADY EXISTS\n"
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #List remote directory
    #$1 = Remote directory
    function db_list
    {
      local DIR_DST=$(normalize_path "$1")
    
      print " > Listing \"$DIR_DST\"... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" "$API_METADATA_URL/$ACCESS_LEVEL/$(urlencode "$DIR_DST")?oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
    
        local IS_DIR=$(sed -n 's/^\(.*\)\"contents":.\[.*/\1/p' "$RESPONSE_FILE")
    
        #It's a directory
        if [[ $IS_DIR != "" ]]; then
    
          print "DONE\n"
    
          #Extracting directory content [...]
          #and replacing "}, {" with "}\n{"
          #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code...
          local DIR_CONTENT=$(sed -n 's/.*: \[{\(.*\)/\1/p' "$RESPONSE_FILE" | sed 's/}, *{/}\
    {/g')
    
          #Converting escaped quotes to unicode format
          echo "$DIR_CONTENT" | sed 's/\\"/\\u0022/' > "$TEMP_FILE"
    
          #Extracting files and subfolders
          rm -fr "$RESPONSE_FILE"
          while read -r line; do
    
            local FILE=$(echo "$line" | sed -n 's/.*"path": *"\([^"]*\)".*/\1/p')
            local IS_DIR=$(echo "$line" | sed -n 's/.*"is_dir": *\([^,]*\).*/\1/p')
            local SIZE=$(echo "$line" | sed -n 's/.*"bytes": *\([0-9]*\).*/\1/p')
    
            echo -e "$FILE:$IS_DIR;$SIZE" >> "$RESPONSE_FILE"
    
          done < "$TEMP_FILE"
    
          #Looking for the biggest file size
          #to calculate the padding to use
          local padding=0
          while read -r line; do
            local FILE=${line%:*}
            local META=${line##*:}
            local SIZE=${META#*;}
    
            if [[ ${#SIZE} -gt $padding ]]; then
              padding=${#SIZE}
            fi
          done < "$RESPONSE_FILE"
    
          #For each entry, printing directories...
          while read -r line; do
    
            local FILE=${line%:*}
            local META=${line##*:}
            local TYPE=${META%;*}
            local SIZE=${META#*;}
    
            #Removing unneeded /
            FILE=${FILE##*/}
    
            if [[ $TYPE != "false" ]]; then
              FILE=$(echo -e "$FILE")
              $PRINTF " [D] %-${padding}s %s\n" "$SIZE" "$FILE"
            fi
    
          done < "$RESPONSE_FILE"
    
          #For each entry, printing files...
          while read -r line; do
    
            local FILE=${line%:*}
            local META=${line##*:}
            local TYPE=${META%;*}
            local SIZE=${META#*;}
    
            #Removing unneeded /
            FILE=${FILE##*/}
    
            if [[ $TYPE == "false" ]]; then
              FILE=$(echo -e "$FILE")
              $PRINTF " [F] %-${padding}s %s\n" "$SIZE" "$FILE"
            fi
    
          done < "$RESPONSE_FILE"
    
        #It's a file
        else
          print "FAILED: $DIR_DST is not a directory!\n"
          ERROR_STATUS=1
        fi
    
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    #Share remote file
    #$1 = Remote file
    function db_share
    {
      local FILE_DST=$(normalize_path "$1")
    
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" "$API_SHARES_URL/$ACCESS_LEVEL/$(urlencode "$FILE_DST")?oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_ACCESS_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_ACCESS_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM&short_url=false" 2> /dev/null
      check_http_response
    
      #Check
      if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then
        print " > Share link: "
        echo $(sed -n 's/.*"url": "\([^"]*\).*/\1/p' "$RESPONSE_FILE")
      else
        print "FAILED\n"
        ERROR_STATUS=1
      fi
    }
    
    ################
    #### SETUP ####
    ################
    
    #CHECKING FOR AUTH FILE
    if [[ -e $CONFIG_FILE ]]; then
    
      #Loading data... and change old format config if necesary.
      source "$CONFIG_FILE" 2>/dev/null || {
        sed -i'' 's/:/=/' "$CONFIG_FILE" && source "$CONFIG_FILE" 2>/dev/null
      }
    
      #Checking the loaded data
      if [[ $APPKEY == "" || $APPSECRET == "" || $OAUTH_ACCESS_TOKEN_SECRET == "" || $OAUTH_ACCESS_TOKEN == "" ]]; then
        echo -ne "Error loading data from $CONFIG_FILE...\n"
        echo -ne "It is recommended to run $0 unlink\n"
        remove_temp_files
        exit 1
      fi
    
      #Back compatibility with previous Dropbox Uploader versions
      if [[ $ACCESS_LEVEL == "" ]]; then
        ACCESS_LEVEL="dropbox"
      fi
    
    #NEW SETUP...
    else
    
      echo -ne "\n This is the first time you run this script.\n\n"
      echo -ne " 1) Open the following URL in your Browser, and log in using your account: $APP_CREATE_URL\n"
      echo -ne " 2) Click on \"Create App\", then select \"Dropbox API app\"\n"
      echo -ne " 3) Select \"Files and datastores\"\n"
      echo -ne " 4) Now go on with the configuration, choosing the app permissions and access restrictions to your DropBox folder\n"
      echo -ne " 5) Enter the \"App Name\" that you prefer (e.g. MyUploader$RANDOM$RANDOM$RANDOM)\n\n"
    
      echo -ne " Now, click on the \"Create App\" button.\n\n"
    
      echo -ne " When your new App is successfully created, please type the\n"
      echo -ne " App Key, App Secret and the Permission type shown in the confirmation page:\n\n"
    
      #Getting the app key and secret from the user
      while (true); do
    
        echo -n " # App key: "
        read APPKEY
    
        echo -n " # App secret: "
        read APPSECRET
    
        echo -n " # Permission type, App folder or Full Dropbox [a/f]: "
        read ACCESS_LEVEL
    
        if [[ $ACCESS_LEVEL == "a" ]]; then
          ACCESS_LEVEL="sandbox"
          ACCESS_MSG="App Folder"
        else
          ACCESS_LEVEL="dropbox"
          ACCESS_MSG="Full Dropbox"
        fi
    
        echo -ne "\n > App key is $APPKEY, App secret is $APPSECRET and Access level is $ACCESS_MSG. Looks ok? [y/n]: "
        read answer
        if [[ $answer == "y" ]]; then
          break;
        fi
    
      done
    
      #TOKEN REQUESTS
      echo -ne "\n > Token request... "
      $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" "$API_REQUEST_TOKEN_URL" 2> /dev/null
      check_http_response
      OAUTH_TOKEN_SECRET=$(sed -n 's/oauth_token_secret=\([a-z A-Z 0-9]*\).*/\1/p' "$RESPONSE_FILE")
      OAUTH_TOKEN=$(sed -n 's/.*oauth_token=\([a-z A-Z 0-9]*\)/\1/p' "$RESPONSE_FILE")
    
      if [[ $OAUTH_TOKEN != "" && $OAUTH_TOKEN_SECRET != "" ]]; then
        echo -ne "OK\n"
      else
        echo -ne " FAILED\n\n Please, check your App key and secret...\n\n"
        remove_temp_files
        exit 1
      fi
    
      while (true); do
    
        #USER AUTH
        echo -ne "\n Please open the following URL in your browser, and allow Dropbox Uploader\n"
        echo -ne " to access your DropBox folder:\n\n --> ${API_USER_AUTH_URL}?oauth_token=$OAUTH_TOKEN\n"
        echo -ne "\nPress enter when done...\n"
        read
    
        #API_ACCESS_TOKEN_URL
        echo -ne " > Access Token request... "
        $CURL_BIN $CURL_ACCEPT_CERTIFICATES -s --show-error --globoff -i -o "$RESPONSE_FILE" --data "oauth_consumer_key=$APPKEY&oauth_token=$OAUTH_TOKEN&oauth_signature_method=PLAINTEXT&oauth_signature=$APPSECRET%26$OAUTH_TOKEN_SECRET&oauth_timestamp=$(utime)&oauth_nonce=$RANDOM" "$API_ACCESS_TOKEN_URL" 2> /dev/null
        check_http_response
        OAUTH_ACCESS_TOKEN_SECRET=$(sed -n 's/oauth_token_secret=\([a-z A-Z 0-9]*\)&.*/\1/p' "$RESPONSE_FILE")
        OAUTH_ACCESS_TOKEN=$(sed -n 's/.*oauth_token=\([a-z A-Z 0-9]*\)&.*/\1/p' "$RESPONSE_FILE")
        OAUTH_ACCESS_UID=$(sed -n 's/.*uid=\([0-9]*\)/\1/p' "$RESPONSE_FILE")
    
        if [[ $OAUTH_ACCESS_TOKEN != "" && $OAUTH_ACCESS_TOKEN_SECRET != "" && $OAUTH_ACCESS_UID != "" ]]; then
          echo -ne "OK\n"
    
          #Saving data in new format, compatible with source command.
          echo "APPKEY=$APPKEY" > "$CONFIG_FILE"
          echo "APPSECRET=$APPSECRET" >> "$CONFIG_FILE"
          echo "ACCESS_LEVEL=$ACCESS_LEVEL" >> "$CONFIG_FILE"
          echo "OAUTH_ACCESS_TOKEN=$OAUTH_ACCESS_TOKEN" >> "$CONFIG_FILE"
          echo "OAUTH_ACCESS_TOKEN_SECRET=$OAUTH_ACCESS_TOKEN_SECRET" >> "$CONFIG_FILE"
    
          echo -ne "\n Setup completed!\n"
          break
        else
          print " FAILED\n"
          ERROR_STATUS=1
        fi
    
      done;
    
      remove_temp_files
      exit $ERROR_STATUS
    fi
    
    ################
    #### START ####
    ################
    
    COMMAND=${@:$OPTIND:1}
    ARG1=${@:$OPTIND+1:1}
    ARG2=${@:$OPTIND+2:1}
    
    let argnum=$#-$OPTIND
    
    #CHECKING PARAMS VALUES
    case $COMMAND in
    
      upload)
    
        if [[ $argnum -lt 2 ]]; then
          usage
        fi
    
        FILE_DST=${@:$#:1}
    
        for (( i=$OPTIND+1; i<$#; i++ )); do
          FILE_SRC=${@:$i:1}
          db_upload "$FILE_SRC" "/$FILE_DST"
        done
    
      ;;
    
      download)
    
        if [[ $argnum -lt 1 ]]; then
          usage
        fi
    
        FILE_SRC=$ARG1
        FILE_DST=$ARG2
    
        db_download "/$FILE_SRC" "$FILE_DST"
    
      ;;
    
      share)
    
        if [[ $argnum -lt 1 ]]; then
          usage
        fi
    
        FILE_DST=$ARG1
    
        db_share "/$FILE_DST"
    
      ;;
    
      info)
    
        db_account_info
    
      ;;
    
      delete|remove)
    
        if [[ $argnum -lt 1 ]]; then
          usage
        fi
    
        FILE_DST=$ARG1
    
        db_delete "/$FILE_DST"
    
      ;;
    
      move|rename)
    
        if [[ $argnum -lt 2 ]]; then
          usage
        fi
    
        FILE_SRC=$ARG1
        FILE_DST=$ARG2
    
        db_move "/$FILE_SRC" "/$FILE_DST"
    
      ;;
    
      copy)
    
        if [[ $argnum -lt 2 ]]; then
          usage
        fi
    
        FILE_SRC=$ARG1
        FILE_DST=$ARG2
    
        db_copy "/$FILE_SRC" "/$FILE_DST"
    
      ;;
    
      mkdir)
    
        if [[ $argnum -lt 1 ]]; then
          usage
        fi
    
        DIR_DST=$ARG1
    
        db_mkdir "/$DIR_DST"
    
      ;;
    
      list)
    
        DIR_DST=$ARG1
    
        #Checking DIR_DST
        if [[ $DIR_DST == "" ]]; then
          DIR_DST="/"
        fi
    
        db_list "/$DIR_DST"
    
      ;;
    
      unlink)
    
        db_unlink
    
      ;;
    
      *)
    
        if [[ $COMMAND != "" ]]; then
          print "Error: Unknown command: $COMMAND\n\n"
          ERROR_STATUS=1
        fi
        usage
    
      ;;
    
    esac
    
    remove_temp_files
    exit $ERROR_STATUS