├── .gitignore ├── LICENSE ├── README.md ├── backup.sh ├── config.sh.ini └── vendor └── dropbox_uploader.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /backups 2 | config.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dimitrios Savvopoulos ds@dimsav.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Backupish 2 | ========= 3 | 4 | A bash script to create backups of directories and mysql databases. Optionally upload to dropbox. 5 | 6 | ## How to use 7 | 8 | * Copy `config.sh.ini` to `config.sh` and fill it with your settings 9 | * Run `backup.sh` 10 | 11 | ## Dropbox configuration 12 | 13 | Running the script for the fist time with dropbox enabled will prompt for the dropbox configuration. When finished, run the script once again to see it in action. 14 | 15 | Requirements: 16 | 17 | * zip command 18 | * mysqldump command 19 | -------------------------------------------------------------------------------- /backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CURRENT_DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | 5 | # ================= 6 | # Import the config 7 | # ================= 8 | 9 | . $CURRENT_DIR/config.sh 10 | 11 | # ============= 12 | # The script... 13 | # ============= 14 | 15 | mkdir -p $backup_dir 16 | 17 | echo "Compressing files" 18 | for dir in $directories; do 19 | zip_file_name=$(echo "$dir" | tr / -) 20 | zip -r -q "$backup_dir/$zip_file_name.zip" "$directories_base_dir/$dir" 21 | done 22 | 23 | echo "Creating db dumps" 24 | for db in $databases; do 25 | mysqldump --force --opt --user=$MYSQL_USER -p$MYSQL_PASSWORD --databases $db > "$db.sql" 26 | gzip "$db.sql" 27 | mv "$db.sql.gz" "$backup_dir/db_$db.sql.gz" 28 | done 29 | 30 | if [ "$upload_to_dropbox" = "true" ]; then 31 | echo "Uploading to dropbox" 32 | bash $CURRENT_DIR/vendor/dropbox_uploader.sh upload $backup_dir $thetime 33 | fi 34 | 35 | if [ "$delete_created_backups" = "true" ]; then 36 | echo "Deleting created files" 37 | rm -rf $backup_dir 38 | fi 39 | -------------------------------------------------------------------------------- /config.sh.ini: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ======================================================================== 4 | # Base directory: Absolute path of root directory of files to be backed up 5 | # ======================================================================== 6 | 7 | directories_base_dir='/project/directory' 8 | 9 | # ============================================================================ 10 | # Directories: List of folders/files to backup, relative to the base directory 11 | # ============================================================================ 12 | 13 | directories=' 14 | myproject 1 15 | myproject 2 16 | ' 17 | 18 | # ================================================= 19 | # Databases: List of database names to be backed up 20 | # ================================================= 21 | 22 | databases=' 23 | mydb 24 | another_db 25 | ' 26 | 27 | # ============================================ 28 | # Settings for the mysql connection 29 | # ============================================ 30 | 31 | MYSQL_USER="homestead" 32 | MYSQL_PASSWORD="secret" 33 | 34 | # =============================================== 35 | # Directory containing the temporary backup files 36 | # =============================================== 37 | 38 | backups_dir='backups' 39 | thetime=`date +%Y-%m-%d_%H-%M-%S` 40 | backup_dir="$backups_dir/$thetime" 41 | 42 | # ======================================================== 43 | # Choose if you want to delete the backups after uploading 44 | # ======================================================== 45 | 46 | delete_created_backups=true 47 | 48 | # ==================================================== 49 | # Choose if you want to upload backup files to dropbox 50 | # ==================================================== 51 | 52 | upload_to_dropbox=true -------------------------------------------------------------------------------- /vendor/dropbox_uploader.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Dropbox Uploader 4 | # 5 | # Copyright (C) 2010-2014 Andrea Fabrizi 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | # 21 | 22 | #Default configuration file 23 | CONFIG_FILE=~/.dropbox_uploader 24 | 25 | #Default chunk size in Mb for the upload process 26 | #It is recommended to increase this value only if you have enough free space on your /tmp partition 27 | #Lower values may increase the number of http requests 28 | CHUNK_SIZE=4 29 | 30 | #Curl location 31 | #If not set, curl will be searched into the $PATH 32 | #CURL_BIN="/usr/bin/curl" 33 | 34 | #Default values 35 | TMP_DIR="/tmp" 36 | DEBUG=0 37 | QUIET=0 38 | SHOW_PROGRESSBAR=0 39 | SKIP_EXISTING_FILES=0 40 | ERROR_STATUS=0 41 | 42 | #Don't edit these... 43 | API_REQUEST_TOKEN_URL="https://api.dropbox.com/1/oauth/request_token" 44 | API_USER_AUTH_URL="https://www.dropbox.com/1/oauth/authorize" 45 | API_ACCESS_TOKEN_URL="https://api.dropbox.com/1/oauth/access_token" 46 | API_CHUNKED_UPLOAD_URL="https://api-content.dropbox.com/1/chunked_upload" 47 | API_CHUNKED_UPLOAD_COMMIT_URL="https://api-content.dropbox.com/1/commit_chunked_upload" 48 | API_UPLOAD_URL="https://api-content.dropbox.com/1/files_put" 49 | API_DOWNLOAD_URL="https://api-content.dropbox.com/1/files" 50 | API_DELETE_URL="https://api.dropbox.com/1/fileops/delete" 51 | API_MOVE_URL="https://api.dropbox.com/1/fileops/move" 52 | API_COPY_URL="https://api.dropbox.com/1/fileops/copy" 53 | API_METADATA_URL="https://api.dropbox.com/1/metadata" 54 | API_INFO_URL="https://api.dropbox.com/1/account/info" 55 | API_MKDIR_URL="https://api.dropbox.com/1/fileops/create_folder" 56 | API_SHARES_URL="https://api.dropbox.com/1/shares" 57 | APP_CREATE_URL="https://www.dropbox.com/developers/apps" 58 | RESPONSE_FILE="$TMP_DIR/du_resp_$RANDOM" 59 | CHUNK_FILE="$TMP_DIR/du_chunk_$RANDOM" 60 | TEMP_FILE="$TMP_DIR/du_tmp_$RANDOM" 61 | BIN_DEPS="sed basename date grep stat dd mkdir" 62 | VERSION="0.14" 63 | 64 | umask 077 65 | 66 | #Check the shell 67 | if [ -z "$BASH_VERSION" ]; then 68 | echo -e "Error: this script requires the BASH shell!" 69 | exit 1 70 | fi 71 | 72 | shopt -s nullglob #Bash allows filename patterns which match no files to expand to a null string, rather than themselves 73 | shopt -s dotglob #Bash includes filenames beginning with a "." in the results of filename expansion 74 | 75 | #Look for optional config file parameter 76 | while getopts ":qpskdf:" opt; do 77 | case $opt in 78 | 79 | f) 80 | CONFIG_FILE=$OPTARG 81 | ;; 82 | 83 | d) 84 | DEBUG=1 85 | ;; 86 | 87 | q) 88 | QUIET=1 89 | ;; 90 | 91 | p) 92 | SHOW_PROGRESSBAR=1 93 | ;; 94 | 95 | k) 96 | CURL_ACCEPT_CERTIFICATES="-k" 97 | ;; 98 | 99 | s) 100 | SKIP_EXISTING_FILES=1 101 | ;; 102 | 103 | \?) 104 | echo "Invalid option: -$OPTARG" >&2 105 | exit 1 106 | ;; 107 | 108 | :) 109 | echo "Option -$OPTARG requires an argument." >&2 110 | exit 1 111 | ;; 112 | 113 | esac 114 | done 115 | 116 | if [[ $DEBUG != 0 ]]; then 117 | echo $VERSION 118 | set -x 119 | RESPONSE_FILE="$TMP_DIR/du_resp_debug" 120 | fi 121 | 122 | if [[ $CURL_BIN == "" ]]; then 123 | BIN_DEPS="$BIN_DEPS curl" 124 | CURL_BIN="curl" 125 | fi 126 | 127 | #Dependencies check 128 | which $BIN_DEPS > /dev/null 129 | if [[ $? != 0 ]]; then 130 | for i in $BIN_DEPS; do 131 | which $i > /dev/null || 132 | NOT_FOUND="$i $NOT_FOUND" 133 | done 134 | echo -e "Error: Required program could not be found: $NOT_FOUND" 135 | exit 1 136 | fi 137 | 138 | #Check if readlink is installed and supports the -m option 139 | #It's not necessary, so no problem if it's not installed 140 | which readlink > /dev/null 141 | if [[ $? == 0 && $(readlink -m "//test" 2> /dev/null) == "/test" ]]; then 142 | HAVE_READLINK=1 143 | else 144 | HAVE_READLINK=0 145 | fi 146 | 147 | #Forcing to use the builtin printf, if it's present, because it's better 148 | #otherwise the external printf program will be used 149 | #Note that the external printf command can cause character encoding issues! 150 | builtin printf "" 2> /dev/null 151 | if [[ $? == 0 ]]; then 152 | PRINTF="builtin printf" 153 | PRINTF_OPT="-v o" 154 | else 155 | PRINTF=$(which printf) 156 | if [[ $? != 0 ]]; then 157 | echo -e "Error: Required program could not be found: printf" 158 | fi 159 | PRINTF_OPT="" 160 | fi 161 | 162 | #Print the message based on $QUIET variable 163 | function print 164 | { 165 | if [[ $QUIET == 0 ]]; then 166 | echo -ne "$1"; 167 | fi 168 | } 169 | 170 | #Returns unix timestamp 171 | function utime 172 | { 173 | echo $(date +%s) 174 | } 175 | 176 | #Remove temporary files 177 | function remove_temp_files 178 | { 179 | if [[ $DEBUG == 0 ]]; then 180 | rm -fr "$RESPONSE_FILE" 181 | rm -fr "$CHUNK_FILE" 182 | rm -fr "$TEMP_FILE" 183 | fi 184 | } 185 | 186 | #Returns the file size in bytes 187 | # generic GNU Linux: linux-gnu 188 | # windows cygwin: cygwin 189 | # raspberry pi: linux-gnueabihf 190 | # macosx: darwin10.0 191 | # freebsd: FreeBSD 192 | # qnap: linux-gnueabi 193 | # iOS: darwin9 194 | function file_size 195 | { 196 | #Some embedded linux devices 197 | if [[ $OSTYPE == "linux-gnueabi" || $OSTYPE == "linux-gnu" ]]; then 198 | stat -c "%s" "$1" 199 | return 200 | 201 | #Generic Unix 202 | elif [[ ${OSTYPE:0:5} == "linux" || $OSTYPE == "cygwin" || ${OSTYPE:0:7} == "solaris" ]]; then 203 | stat --format="%s" "$1" 204 | return 205 | 206 | #BSD, OSX and other OSs 207 | else 208 | stat -f "%z" "$1" 209 | return 210 | fi 211 | } 212 | 213 | #Usage 214 | function usage 215 | { 216 | echo -e "Dropbox Uploader v$VERSION" 217 | echo -e "Andrea Fabrizi - andrea.fabrizi@gmail.com\n" 218 | echo -e "Usage: $0 COMMAND [PARAMETERS]..." 219 | echo -e "\nCommands:" 220 | 221 | echo -e "\t upload " 222 | echo -e "\t download [LOCAL_FILE/DIR]" 223 | echo -e "\t delete " 224 | echo -e "\t move " 225 | echo -e "\t copy " 226 | echo -e "\t mkdir " 227 | echo -e "\t list [REMOTE_DIR]" 228 | echo -e "\t share " 229 | echo -e "\t info" 230 | echo -e "\t unlink" 231 | 232 | echo -e "\nOptional parameters:" 233 | echo -e "\t-f Load the configuration file from a specific file" 234 | echo -e "\t-s Skip already existing files when download/upload. Default: Overwrite" 235 | echo -e "\t-d Enable DEBUG mode" 236 | echo -e "\t-q Quiet mode. Don't show messages" 237 | echo -e "\t-p Show cURL progress meter" 238 | echo -e "\t-k Doesn't check for SSL certificates (insecure)" 239 | 240 | echo -en "\nFor more info and examples, please see the README file.\n\n" 241 | remove_temp_files 242 | exit 1 243 | } 244 | 245 | #Check the curl exit code 246 | function check_http_response 247 | { 248 | CODE=$? 249 | 250 | #Checking curl exit code 251 | case $CODE in 252 | 253 | #OK 254 | 0) 255 | 256 | ;; 257 | 258 | #Proxy error 259 | 5) 260 | print "\nError: Couldn't resolve proxy. The given proxy host could not be resolved.\n" 261 | 262 | remove_temp_files 263 | exit 1 264 | ;; 265 | 266 | #Missing CA certificates 267 | 60|58) 268 | print "\nError: cURL is not able to performs peer SSL certificate verification.\n" 269 | print "Please, install the default ca-certificates bundle.\n" 270 | print "To do this in a Debian/Ubuntu based system, try:\n" 271 | print " sudo apt-get install ca-certificates\n\n" 272 | print "If the problem persists, try to use the -k option (insecure).\n" 273 | 274 | remove_temp_files 275 | exit 1 276 | ;; 277 | 278 | 6) 279 | print "\nError: Couldn't resolve host.\n" 280 | 281 | remove_temp_files 282 | exit 1 283 | ;; 284 | 285 | 7) 286 | print "\nError: Couldn't connect to host.\n" 287 | 288 | remove_temp_files 289 | exit 1 290 | ;; 291 | 292 | esac 293 | 294 | #Checking response file for generic errors 295 | if grep -q "HTTP/1.1 400" "$RESPONSE_FILE"; then 296 | ERROR_MSG=$(sed -n -e 's/{"error": "\([^"]*\)"}/\1/p' "$RESPONSE_FILE") 297 | 298 | case $ERROR_MSG in 299 | *access?attempt?failed?because?this?app?is?not?configured?to?have*) 300 | echo -e "\nError: The Permission type/Access level configured doesn't match the DropBox App settings!\nPlease run \"$0 unlink\" and try again." 301 | exit 1 302 | ;; 303 | esac 304 | 305 | fi 306 | 307 | } 308 | 309 | #Urlencode 310 | function urlencode 311 | { 312 | local string="${1}" 313 | local strlen=${#string} 314 | local encoded="" 315 | 316 | for (( pos=0 ; pos /dev/null 353 | check_http_response 354 | 355 | #Even if the file/dir has been deleted from DropBox we receive a 200 OK response 356 | #So we must check if the file exists or if it has been deleted 357 | if grep -q "\"is_deleted\":" "$RESPONSE_FILE"; then 358 | local IS_DELETED=$(sed -n 's/.*"is_deleted":.\([^,]*\).*/\1/p' "$RESPONSE_FILE") 359 | else 360 | local IS_DELETED="false" 361 | fi 362 | 363 | #Exits... 364 | grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE" 365 | if [[ $? == 0 && $IS_DELETED != "true" ]]; then 366 | 367 | local IS_DIR=$(sed -n 's/^\(.*\)\"contents":.\[.*/\1/p' "$RESPONSE_FILE") 368 | 369 | #It's a directory 370 | if [[ $IS_DIR != "" ]]; then 371 | echo "DIR" 372 | #It's a file 373 | else 374 | echo "FILE" 375 | fi 376 | 377 | #Doesn't exists 378 | else 379 | echo "ERR" 380 | fi 381 | } 382 | 383 | #Generic upload wrapper around db_upload_file and db_upload_dir functions 384 | #$1 = Local source file/dir 385 | #$2 = Remote destination file/dir 386 | function db_upload 387 | { 388 | local SRC=$(normalize_path "$1") 389 | local DST=$(normalize_path "$2") 390 | 391 | #Checking if the file/dir exists 392 | if [[ ! -e $SRC && ! -d $SRC ]]; then 393 | print " > No such file or directory: $SRC\n" 394 | ERROR_STATUS=1 395 | return 396 | fi 397 | 398 | #Checking if the file/dir has read permissions 399 | if [[ ! -r $SRC ]]; then 400 | print " > Error reading file $SRC: permission denied\n" 401 | ERROR_STATUS=1 402 | return 403 | fi 404 | 405 | TYPE=$(db_stat "$DST") 406 | 407 | #If DST it's a file, do nothing, it's the default behaviour 408 | if [[ $TYPE == "FILE" ]]; then 409 | DST="$DST" 410 | 411 | #if DST doesn't exists and doesn't ends with a /, it will be the destination file name 412 | elif [[ $TYPE == "ERR" && "${DST: -1}" != "/" ]]; then 413 | DST="$DST" 414 | 415 | #if DST doesn't exists and ends with a /, it will be the destination folder 416 | elif [[ $TYPE == "ERR" && "${DST: -1}" == "/" ]]; then 417 | local filename=$(basename "$SRC") 418 | DST="$DST/$filename" 419 | 420 | #If DST it'a directory, it will be the destination folder 421 | elif [[ $TYPE == "DIR" ]]; then 422 | local filename=$(basename "$SRC") 423 | DST="$DST/$filename" 424 | fi 425 | 426 | #It's a directory 427 | if [[ -d $SRC ]]; then 428 | db_upload_dir "$SRC" "$DST" 429 | 430 | #It's a file 431 | elif [[ -e $SRC ]]; then 432 | db_upload_file "$SRC" "$DST" 433 | 434 | #Unsupported object... 435 | else 436 | print " > Skipping not regular file \"$SRC\"\n" 437 | fi 438 | } 439 | 440 | #Generic upload wrapper around db_chunked_upload_file and db_simple_upload_file 441 | #The final upload function will be choosen based on the file size 442 | #$1 = Local source file 443 | #$2 = Remote destination file 444 | function db_upload_file 445 | { 446 | local FILE_SRC=$(normalize_path "$1") 447 | local FILE_DST=$(normalize_path "$2") 448 | 449 | shopt -s nocasematch 450 | 451 | #Checking not allowed file names 452 | basefile_dst=$(basename "$FILE_DST") 453 | if [[ $basefile_dst == "thumbs.db" || \ 454 | $basefile_dst == "desktop.ini" || \ 455 | $basefile_dst == ".ds_store" || \ 456 | $basefile_dst == "icon\r" || \ 457 | $basefile_dst == ".dropbox" || \ 458 | $basefile_dst == ".dropbox.attr" \ 459 | ]]; then 460 | print " > Skipping not allowed file name \"$FILE_DST\"\n" 461 | return 462 | fi 463 | 464 | shopt -u nocasematch 465 | 466 | #Checking file size 467 | FILE_SIZE=$(file_size "$FILE_SRC") 468 | 469 | #Checking if the file already exists 470 | TYPE=$(db_stat "$FILE_DST") 471 | if [[ $TYPE != "ERR" && $SKIP_EXISTING_FILES == 1 ]]; then 472 | print " > Skipping already existing file \"$FILE_DST\"\n" 473 | return 474 | fi 475 | 476 | if [[ $FILE_SIZE -gt 157286000 ]]; then 477 | #If the file is greater than 150Mb, the chunked_upload API will be used 478 | db_chunked_upload_file "$FILE_SRC" "$FILE_DST" 479 | else 480 | db_simple_upload_file "$FILE_SRC" "$FILE_DST" 481 | fi 482 | 483 | } 484 | 485 | #Simple file upload 486 | #$1 = Local source file 487 | #$2 = Remote destination file 488 | function db_simple_upload_file 489 | { 490 | local FILE_SRC=$(normalize_path "$1") 491 | local FILE_DST=$(normalize_path "$2") 492 | 493 | if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then 494 | CURL_PARAMETERS="--progress-bar" 495 | LINE_CR="\n" 496 | else 497 | CURL_PARAMETERS="-s" 498 | LINE_CR="" 499 | fi 500 | 501 | print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR" 502 | $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" 503 | check_http_response 504 | 505 | #Check 506 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 507 | print "DONE\n" 508 | else 509 | print "FAILED\n" 510 | print "An error occurred requesting /upload\n" 511 | ERROR_STATUS=1 512 | fi 513 | } 514 | 515 | #Chunked file upload 516 | #$1 = Local source file 517 | #$2 = Remote destination file 518 | function db_chunked_upload_file 519 | { 520 | local FILE_SRC=$(normalize_path "$1") 521 | local FILE_DST=$(normalize_path "$2") 522 | 523 | print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\"" 524 | 525 | local FILE_SIZE=$(file_size "$FILE_SRC") 526 | local OFFSET=0 527 | local UPLOAD_ID="" 528 | local UPLOAD_ERROR=0 529 | local CHUNK_PARAMS="" 530 | 531 | #Uploading chunks... 532 | while ([[ $OFFSET != $FILE_SIZE ]]); do 533 | 534 | let OFFSET_MB=$OFFSET/1024/1024 535 | 536 | #Create the chunk 537 | dd if="$FILE_SRC" of="$CHUNK_FILE" bs=1048576 skip=$OFFSET_MB count=$CHUNK_SIZE 2> /dev/null 538 | 539 | #Only for the first request these parameters are not included 540 | if [[ $OFFSET != 0 ]]; then 541 | CHUNK_PARAMS="upload_id=$UPLOAD_ID&offset=$OFFSET" 542 | fi 543 | 544 | #Uploading the chunk... 545 | $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 546 | check_http_response 547 | 548 | #Check 549 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 550 | print "." 551 | UPLOAD_ERROR=0 552 | UPLOAD_ID=$(sed -n 's/.*"upload_id": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") 553 | OFFSET=$(sed -n 's/.*"offset": *\([^}]*\).*/\1/p' "$RESPONSE_FILE") 554 | else 555 | print "*" 556 | let UPLOAD_ERROR=$UPLOAD_ERROR+1 557 | 558 | #On error, the upload is retried for max 3 times 559 | if [[ $UPLOAD_ERROR -gt 2 ]]; then 560 | print " FAILED\n" 561 | print "An error occurred requesting /chunked_upload\n" 562 | ERROR_STATUS=1 563 | return 564 | fi 565 | fi 566 | 567 | done 568 | 569 | UPLOAD_ERROR=0 570 | 571 | #Commit the upload 572 | while (true); do 573 | 574 | $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 575 | check_http_response 576 | 577 | #Check 578 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 579 | print "." 580 | UPLOAD_ERROR=0 581 | break 582 | else 583 | print "*" 584 | let UPLOAD_ERROR=$UPLOAD_ERROR+1 585 | 586 | #On error, the commit is retried for max 3 times 587 | if [[ $UPLOAD_ERROR -gt 2 ]]; then 588 | print " FAILED\n" 589 | print "An error occurred requesting /commit_chunked_upload\n" 590 | ERROR_STATUS=1 591 | return 592 | fi 593 | fi 594 | 595 | done 596 | 597 | print " DONE\n" 598 | } 599 | 600 | #Directory upload 601 | #$1 = Local source dir 602 | #$2 = Remote destination dir 603 | function db_upload_dir 604 | { 605 | local DIR_SRC=$(normalize_path "$1") 606 | local DIR_DST=$(normalize_path "$2") 607 | 608 | #Creatig remote directory 609 | db_mkdir "$DIR_DST" 610 | 611 | for file in "$DIR_SRC/"*; do 612 | db_upload "$file" "$DIR_DST" 613 | done 614 | } 615 | 616 | #Returns the free space on DropBox in bytes 617 | function db_free_quota 618 | { 619 | $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 620 | check_http_response 621 | 622 | #Check 623 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 624 | 625 | quota=$(sed -n 's/.*"quota": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") 626 | used=$(sed -n 's/.*"normal": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") 627 | let free_quota=$quota-$used 628 | echo $free_quota 629 | 630 | else 631 | echo 0 632 | fi 633 | } 634 | 635 | #Generic download wrapper 636 | #$1 = Remote source file/dir 637 | #$2 = Local destination file/dir 638 | function db_download 639 | { 640 | local SRC=$(normalize_path "$1") 641 | local DST=$(normalize_path "$2") 642 | 643 | TYPE=$(db_stat "$SRC") 644 | 645 | #It's a directory 646 | if [[ $TYPE == "DIR" ]]; then 647 | 648 | #If the DST folder is not specified, I assume that is the current directory 649 | if [[ $DST == "" ]]; then 650 | DST="." 651 | fi 652 | 653 | #Checking if the destination directory exists 654 | if [[ ! -d $DST ]]; then 655 | local basedir="" 656 | else 657 | local basedir=$(basename "$SRC") 658 | fi 659 | 660 | local DEST_DIR=$(normalize_path "$DST/$basedir") 661 | print " > Downloading \"$SRC\" to \"$DEST_DIR\"... \n" 662 | print " > Creating local directory \"$DEST_DIR\"... " 663 | mkdir -p "$DEST_DIR" 664 | 665 | #Check 666 | if [[ $? == 0 ]]; then 667 | print "DONE\n" 668 | else 669 | print "FAILED\n" 670 | ERROR_STATUS=1 671 | return 672 | fi 673 | 674 | #Extracting directory content [...] 675 | #and replacing "}, {" with "}\n{" 676 | #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code... 677 | local DIR_CONTENT=$(sed -n 's/.*: \[{\(.*\)/\1/p' "$RESPONSE_FILE" | sed 's/}, *{/}\ 678 | {/g') 679 | 680 | #Extracting files and subfolders 681 | TMP_DIR_CONTENT_FILE="${RESPONSE_FILE}_$RANDOM" 682 | echo "$DIR_CONTENT" | sed -n 's/.*"path": *"\([^"]*\)",.*"is_dir": *\([^"]*\),.*/\1:\2/p' > $TMP_DIR_CONTENT_FILE 683 | 684 | #For each entry... 685 | while read -r line; do 686 | 687 | local FILE=${line%:*} 688 | local TYPE=${line#*:} 689 | 690 | #Removing unneeded / 691 | FILE=${FILE##*/} 692 | 693 | if [[ $TYPE == "false" ]]; then 694 | db_download_file "$SRC/$FILE" "$DEST_DIR/$FILE" 695 | else 696 | db_download "$SRC/$FILE" "$DEST_DIR" 697 | fi 698 | 699 | done < $TMP_DIR_CONTENT_FILE 700 | 701 | rm -fr $TMP_DIR_CONTENT_FILE 702 | 703 | #It's a file 704 | elif [[ $TYPE == "FILE" ]]; then 705 | 706 | #Checking DST 707 | if [[ $DST == "" ]]; then 708 | DST=$(basename "$SRC") 709 | fi 710 | 711 | #If the destination is a directory, the file will be download into 712 | if [[ -d $DST ]]; then 713 | DST="$DST/$SRC" 714 | fi 715 | 716 | db_download_file "$SRC" "$DST" 717 | 718 | #Doesn't exists 719 | else 720 | print " > No such file or directory: $SRC\n" 721 | ERROR_STATUS=1 722 | return 723 | fi 724 | } 725 | 726 | #Simple file download 727 | #$1 = Remote source file 728 | #$2 = Local destination file 729 | function db_download_file 730 | { 731 | local FILE_SRC=$(normalize_path "$1") 732 | local FILE_DST=$(normalize_path "$2") 733 | 734 | if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then 735 | CURL_PARAMETERS="--progress-bar" 736 | LINE_CR="\n" 737 | else 738 | CURL_PARAMETERS="-s" 739 | LINE_CR="" 740 | fi 741 | 742 | #Checking if the file already exists 743 | if [[ -e $FILE_DST && $SKIP_EXISTING_FILES == 1 ]]; then 744 | print " > Skipping already existing file \"$FILE_DST\"\n" 745 | return 746 | fi 747 | 748 | #Creating the empty file, that for two reasons: 749 | #1) In this way I can check if the destination file is writable or not 750 | #2) Curl doesn't automatically creates files with 0 bytes size 751 | dd if=/dev/zero of="$FILE_DST" count=0 2> /dev/null 752 | if [[ $? != 0 ]]; then 753 | print " > Error writing file $FILE_DST: permission denied\n" 754 | ERROR_STATUS=1 755 | return 756 | fi 757 | 758 | print " > Downloading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR" 759 | $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" 760 | check_http_response 761 | 762 | #Check 763 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 764 | print "DONE\n" 765 | else 766 | print "FAILED\n" 767 | rm -fr "$FILE_DST" 768 | ERROR_STATUS=1 769 | return 770 | fi 771 | } 772 | 773 | #Prints account info 774 | function db_account_info 775 | { 776 | print "Dropbox Uploader v$VERSION\n\n" 777 | print " > Getting info... " 778 | $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 779 | check_http_response 780 | 781 | #Check 782 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 783 | 784 | name=$(sed -n 's/.*"display_name": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") 785 | echo -e "\n\nName:\t$name" 786 | 787 | uid=$(sed -n 's/.*"uid": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") 788 | echo -e "UID:\t$uid" 789 | 790 | email=$(sed -n 's/.*"email": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") 791 | echo -e "Email:\t$email" 792 | 793 | quota=$(sed -n 's/.*"quota": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") 794 | let quota_mb=$quota/1024/1024 795 | echo -e "Quota:\t$quota_mb Mb" 796 | 797 | used=$(sed -n 's/.*"normal": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") 798 | let used_mb=$used/1024/1024 799 | echo -e "Used:\t$used_mb Mb" 800 | 801 | let free_mb=($quota-$used)/1024/1024 802 | echo -e "Free:\t$free_mb Mb" 803 | 804 | echo "" 805 | 806 | else 807 | print "FAILED\n" 808 | ERROR_STATUS=1 809 | fi 810 | } 811 | 812 | #Account unlink 813 | function db_unlink 814 | { 815 | echo -ne "Are you sure you want unlink this script from your Dropbox account? [y/n]" 816 | read answer 817 | if [[ $answer == "y" ]]; then 818 | rm -fr "$CONFIG_FILE" 819 | echo -ne "DONE\n" 820 | fi 821 | } 822 | 823 | #Delete a remote file 824 | #$1 = Remote file to delete 825 | function db_delete 826 | { 827 | local FILE_DST=$(normalize_path "$1") 828 | 829 | print " > Deleting \"$FILE_DST\"... " 830 | $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 831 | check_http_response 832 | 833 | #Check 834 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 835 | print "DONE\n" 836 | else 837 | print "FAILED\n" 838 | ERROR_STATUS=1 839 | fi 840 | } 841 | 842 | #Move/Rename a remote file 843 | #$1 = Remote file to rename or move 844 | #$2 = New file name or location 845 | function db_move 846 | { 847 | local FILE_SRC=$(normalize_path "$1") 848 | local FILE_DST=$(normalize_path "$2") 849 | 850 | TYPE=$(db_stat "$FILE_DST") 851 | 852 | #If the destination it's a directory, the source will be moved into it 853 | if [[ $TYPE == "DIR" ]]; then 854 | local filename=$(basename "$FILE_SRC") 855 | FILE_DST=$(normalize_path "$FILE_DST/$filename") 856 | fi 857 | 858 | print " > Moving \"$FILE_SRC\" to \"$FILE_DST\" ... " 859 | $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 860 | check_http_response 861 | 862 | #Check 863 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 864 | print "DONE\n" 865 | else 866 | print "FAILED\n" 867 | ERROR_STATUS=1 868 | fi 869 | } 870 | 871 | #Copy a remote file to a remote location 872 | #$1 = Remote file to rename or move 873 | #$2 = New file name or location 874 | function db_copy 875 | { 876 | local FILE_SRC=$(normalize_path "$1") 877 | local FILE_DST=$(normalize_path "$2") 878 | 879 | TYPE=$(db_stat "$FILE_DST") 880 | 881 | #If the destination it's a directory, the source will be copied into it 882 | if [[ $TYPE == "DIR" ]]; then 883 | local filename=$(basename "$FILE_SRC") 884 | FILE_DST=$(normalize_path "$FILE_DST/$filename") 885 | fi 886 | 887 | print " > Copying \"$FILE_SRC\" to \"$FILE_DST\" ... " 888 | $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 889 | check_http_response 890 | 891 | #Check 892 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 893 | print "DONE\n" 894 | else 895 | print "FAILED\n" 896 | ERROR_STATUS=1 897 | fi 898 | } 899 | 900 | #Create a new directory 901 | #$1 = Remote directory to create 902 | function db_mkdir 903 | { 904 | local DIR_DST=$(normalize_path "$1") 905 | 906 | print " > Creating Directory \"$DIR_DST\"... " 907 | $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 908 | check_http_response 909 | 910 | #Check 911 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 912 | print "DONE\n" 913 | elif grep -q "^HTTP/1.1 403 Forbidden" "$RESPONSE_FILE"; then 914 | print "ALREADY EXISTS\n" 915 | else 916 | print "FAILED\n" 917 | ERROR_STATUS=1 918 | fi 919 | } 920 | 921 | #List remote directory 922 | #$1 = Remote directory 923 | function db_list 924 | { 925 | local DIR_DST=$(normalize_path "$1") 926 | 927 | print " > Listing \"$DIR_DST\"... " 928 | $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 929 | check_http_response 930 | 931 | #Check 932 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 933 | 934 | local IS_DIR=$(sed -n 's/^\(.*\)\"contents":.\[.*/\1/p' "$RESPONSE_FILE") 935 | 936 | #It's a directory 937 | if [[ $IS_DIR != "" ]]; then 938 | 939 | print "DONE\n" 940 | 941 | #Extracting directory content [...] 942 | #and replacing "}, {" with "}\n{" 943 | #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code... 944 | local DIR_CONTENT=$(sed -n 's/.*: \[{\(.*\)/\1/p' "$RESPONSE_FILE" | sed 's/}, *{/}\ 945 | {/g') 946 | 947 | #Converting escaped quotes to unicode format 948 | echo "$DIR_CONTENT" | sed 's/\\"/\\u0022/' > "$TEMP_FILE" 949 | 950 | #Extracting files and subfolders 951 | rm -fr "$RESPONSE_FILE" 952 | while read -r line; do 953 | 954 | local FILE=$(echo "$line" | sed -n 's/.*"path": *"\([^"]*\)".*/\1/p') 955 | local IS_DIR=$(echo "$line" | sed -n 's/.*"is_dir": *\([^,]*\).*/\1/p') 956 | local SIZE=$(echo "$line" | sed -n 's/.*"bytes": *\([0-9]*\).*/\1/p') 957 | 958 | echo -e "$FILE:$IS_DIR;$SIZE" >> "$RESPONSE_FILE" 959 | 960 | done < "$TEMP_FILE" 961 | 962 | #Looking for the biggest file size 963 | #to calculate the padding to use 964 | local padding=0 965 | while read -r line; do 966 | local FILE=${line%:*} 967 | local META=${line##*:} 968 | local SIZE=${META#*;} 969 | 970 | if [[ ${#SIZE} -gt $padding ]]; then 971 | padding=${#SIZE} 972 | fi 973 | done < "$RESPONSE_FILE" 974 | 975 | #For each entry, printing directories... 976 | while read -r line; do 977 | 978 | local FILE=${line%:*} 979 | local META=${line##*:} 980 | local TYPE=${META%;*} 981 | local SIZE=${META#*;} 982 | 983 | #Removing unneeded / 984 | FILE=${FILE##*/} 985 | 986 | if [[ $TYPE == "true" ]]; then 987 | FILE=$(echo -e "$FILE") 988 | $PRINTF " [D] %-${padding}s %s\n" "$SIZE" "$FILE" 989 | fi 990 | 991 | done < "$RESPONSE_FILE" 992 | 993 | #For each entry, printing files... 994 | while read -r line; do 995 | 996 | local FILE=${line%:*} 997 | local META=${line##*:} 998 | local TYPE=${META%;*} 999 | local SIZE=${META#*;} 1000 | 1001 | #Removing unneeded / 1002 | FILE=${FILE##*/} 1003 | 1004 | if [[ $TYPE == "false" ]]; then 1005 | FILE=$(echo -e "$FILE") 1006 | $PRINTF " [F] %-${padding}s %s\n" "$SIZE" "$FILE" 1007 | fi 1008 | 1009 | done < "$RESPONSE_FILE" 1010 | 1011 | #It's a file 1012 | else 1013 | print "FAILED: $DIR_DST is not a directory!\n" 1014 | ERROR_STATUS=1 1015 | fi 1016 | 1017 | else 1018 | print "FAILED\n" 1019 | ERROR_STATUS=1 1020 | fi 1021 | } 1022 | 1023 | #Share remote file 1024 | #$1 = Remote file 1025 | function db_share 1026 | { 1027 | local FILE_DST=$(normalize_path "$1") 1028 | 1029 | $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 1030 | check_http_response 1031 | 1032 | #Check 1033 | if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then 1034 | print " > Share link: " 1035 | SHARE_LINK=$(sed -n 's/.*"url": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") 1036 | echo "$SHARE_LINK" 1037 | else 1038 | print "FAILED\n" 1039 | ERROR_STATUS=1 1040 | fi 1041 | } 1042 | 1043 | ################ 1044 | #### SETUP #### 1045 | ################ 1046 | 1047 | #CHECKING FOR AUTH FILE 1048 | if [[ -e $CONFIG_FILE ]]; then 1049 | 1050 | #Loading data... and change old format config if necesary. 1051 | source "$CONFIG_FILE" 2>/dev/null || { 1052 | sed -i'' 's/:/=/' "$CONFIG_FILE" && source "$CONFIG_FILE" 2>/dev/null 1053 | } 1054 | 1055 | #Checking the loaded data 1056 | if [[ $APPKEY == "" || $APPSECRET == "" || $OAUTH_ACCESS_TOKEN_SECRET == "" || $OAUTH_ACCESS_TOKEN == "" ]]; then 1057 | echo -ne "Error loading data from $CONFIG_FILE...\n" 1058 | echo -ne "It is recommended to run $0 unlink\n" 1059 | remove_temp_files 1060 | exit 1 1061 | fi 1062 | 1063 | #Back compatibility with previous Dropbox Uploader versions 1064 | if [[ $ACCESS_LEVEL == "" ]]; then 1065 | ACCESS_LEVEL="dropbox" 1066 | fi 1067 | 1068 | #NEW SETUP... 1069 | else 1070 | 1071 | echo -ne "\n This is the first time you run this script.\n\n" 1072 | echo -ne " 1) Open the following URL in your Browser, and log in using your account: $APP_CREATE_URL\n" 1073 | echo -ne " 2) Click on \"Create App\", then select \"Dropbox API app\"\n" 1074 | echo -ne " 3) Select \"Files and datastores\"\n" 1075 | echo -ne " 4) Now go on with the configuration, choosing the app permissions and access restrictions to your DropBox folder\n" 1076 | echo -ne " 5) Enter the \"App Name\" that you prefer (e.g. MyUploader$RANDOM$RANDOM$RANDOM)\n\n" 1077 | 1078 | echo -ne " Now, click on the \"Create App\" button.\n\n" 1079 | 1080 | echo -ne " When your new App is successfully created, please type the\n" 1081 | echo -ne " App Key, App Secret and the Permission type shown in the confirmation page:\n\n" 1082 | 1083 | #Getting the app key and secret from the user 1084 | while (true); do 1085 | 1086 | echo -n " # App key: " 1087 | read APPKEY 1088 | 1089 | echo -n " # App secret: " 1090 | read APPSECRET 1091 | 1092 | echo -n " # Permission type, App folder or Full Dropbox [a/f]: " 1093 | read ACCESS_LEVEL 1094 | 1095 | if [[ $ACCESS_LEVEL == "a" ]]; then 1096 | ACCESS_LEVEL="sandbox" 1097 | ACCESS_MSG="App Folder" 1098 | else 1099 | ACCESS_LEVEL="dropbox" 1100 | ACCESS_MSG="Full Dropbox" 1101 | fi 1102 | 1103 | echo -ne "\n > App key is $APPKEY, App secret is $APPSECRET and Access level is $ACCESS_MSG. Looks ok? [y/n]: " 1104 | read answer 1105 | if [[ $answer == "y" ]]; then 1106 | break; 1107 | fi 1108 | 1109 | done 1110 | 1111 | #TOKEN REQUESTS 1112 | echo -ne "\n > Token request... " 1113 | $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 1114 | check_http_response 1115 | OAUTH_TOKEN_SECRET=$(sed -n 's/oauth_token_secret=\([a-z A-Z 0-9]*\).*/\1/p' "$RESPONSE_FILE") 1116 | OAUTH_TOKEN=$(sed -n 's/.*oauth_token=\([a-z A-Z 0-9]*\)/\1/p' "$RESPONSE_FILE") 1117 | 1118 | if [[ $OAUTH_TOKEN != "" && $OAUTH_TOKEN_SECRET != "" ]]; then 1119 | echo -ne "OK\n" 1120 | else 1121 | echo -ne " FAILED\n\n Please, check your App key and secret...\n\n" 1122 | remove_temp_files 1123 | exit 1 1124 | fi 1125 | 1126 | while (true); do 1127 | 1128 | #USER AUTH 1129 | echo -ne "\n Please open the following URL in your browser, and allow Dropbox Uploader\n" 1130 | echo -ne " to access your DropBox folder:\n\n --> ${API_USER_AUTH_URL}?oauth_token=$OAUTH_TOKEN\n" 1131 | echo -ne "\nPress enter when done...\n" 1132 | read 1133 | 1134 | #API_ACCESS_TOKEN_URL 1135 | echo -ne " > Access Token request... " 1136 | $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 1137 | check_http_response 1138 | OAUTH_ACCESS_TOKEN_SECRET=$(sed -n 's/oauth_token_secret=\([a-z A-Z 0-9]*\)&.*/\1/p' "$RESPONSE_FILE") 1139 | OAUTH_ACCESS_TOKEN=$(sed -n 's/.*oauth_token=\([a-z A-Z 0-9]*\)&.*/\1/p' "$RESPONSE_FILE") 1140 | OAUTH_ACCESS_UID=$(sed -n 's/.*uid=\([0-9]*\)/\1/p' "$RESPONSE_FILE") 1141 | 1142 | if [[ $OAUTH_ACCESS_TOKEN != "" && $OAUTH_ACCESS_TOKEN_SECRET != "" && $OAUTH_ACCESS_UID != "" ]]; then 1143 | echo -ne "OK\n" 1144 | 1145 | #Saving data in new format, compatible with source command. 1146 | echo "APPKEY=$APPKEY" > "$CONFIG_FILE" 1147 | echo "APPSECRET=$APPSECRET" >> "$CONFIG_FILE" 1148 | echo "ACCESS_LEVEL=$ACCESS_LEVEL" >> "$CONFIG_FILE" 1149 | echo "OAUTH_ACCESS_TOKEN=$OAUTH_ACCESS_TOKEN" >> "$CONFIG_FILE" 1150 | echo "OAUTH_ACCESS_TOKEN_SECRET=$OAUTH_ACCESS_TOKEN_SECRET" >> "$CONFIG_FILE" 1151 | 1152 | echo -ne "\n Setup completed!\n" 1153 | break 1154 | else 1155 | print " FAILED\n" 1156 | ERROR_STATUS=1 1157 | fi 1158 | 1159 | done; 1160 | 1161 | remove_temp_files 1162 | exit $ERROR_STATUS 1163 | fi 1164 | 1165 | ################ 1166 | #### START #### 1167 | ################ 1168 | 1169 | COMMAND=${@:$OPTIND:1} 1170 | ARG1=${@:$OPTIND+1:1} 1171 | ARG2=${@:$OPTIND+2:1} 1172 | 1173 | let argnum=$#-$OPTIND 1174 | 1175 | #CHECKING PARAMS VALUES 1176 | case $COMMAND in 1177 | 1178 | upload) 1179 | 1180 | if [[ $argnum -lt 2 ]]; then 1181 | usage 1182 | fi 1183 | 1184 | FILE_DST=${@:$#:1} 1185 | 1186 | for (( i=$OPTIND+1; i<$#; i++ )); do 1187 | FILE_SRC=${@:$i:1} 1188 | db_upload "$FILE_SRC" "/$FILE_DST" 1189 | done 1190 | 1191 | ;; 1192 | 1193 | download) 1194 | 1195 | if [[ $argnum -lt 1 ]]; then 1196 | usage 1197 | fi 1198 | 1199 | FILE_SRC=$ARG1 1200 | FILE_DST=$ARG2 1201 | 1202 | db_download "/$FILE_SRC" "$FILE_DST" 1203 | 1204 | ;; 1205 | 1206 | share) 1207 | 1208 | if [[ $argnum -lt 1 ]]; then 1209 | usage 1210 | fi 1211 | 1212 | FILE_DST=$ARG1 1213 | 1214 | db_share "/$FILE_DST" 1215 | 1216 | ;; 1217 | 1218 | info) 1219 | 1220 | db_account_info 1221 | 1222 | ;; 1223 | 1224 | delete|remove) 1225 | 1226 | if [[ $argnum -lt 1 ]]; then 1227 | usage 1228 | fi 1229 | 1230 | FILE_DST=$ARG1 1231 | 1232 | db_delete "/$FILE_DST" 1233 | 1234 | ;; 1235 | 1236 | move|rename) 1237 | 1238 | if [[ $argnum -lt 2 ]]; then 1239 | usage 1240 | fi 1241 | 1242 | FILE_SRC=$ARG1 1243 | FILE_DST=$ARG2 1244 | 1245 | db_move "/$FILE_SRC" "/$FILE_DST" 1246 | 1247 | ;; 1248 | 1249 | copy) 1250 | 1251 | if [[ $argnum -lt 2 ]]; then 1252 | usage 1253 | fi 1254 | 1255 | FILE_SRC=$ARG1 1256 | FILE_DST=$ARG2 1257 | 1258 | db_copy "/$FILE_SRC" "/$FILE_DST" 1259 | 1260 | ;; 1261 | 1262 | mkdir) 1263 | 1264 | if [[ $argnum -lt 1 ]]; then 1265 | usage 1266 | fi 1267 | 1268 | DIR_DST=$ARG1 1269 | 1270 | db_mkdir "/$DIR_DST" 1271 | 1272 | ;; 1273 | 1274 | list) 1275 | 1276 | DIR_DST=$ARG1 1277 | 1278 | #Checking DIR_DST 1279 | if [[ $DIR_DST == "" ]]; then 1280 | DIR_DST="/" 1281 | fi 1282 | 1283 | db_list "/$DIR_DST" 1284 | 1285 | ;; 1286 | 1287 | unlink) 1288 | 1289 | db_unlink 1290 | 1291 | ;; 1292 | 1293 | *) 1294 | 1295 | if [[ $COMMAND != "" ]]; then 1296 | print "Error: Unknown command: $COMMAND\n\n" 1297 | ERROR_STATUS=1 1298 | fi 1299 | usage 1300 | 1301 | ;; 1302 | 1303 | esac 1304 | 1305 | remove_temp_files 1306 | exit $ERROR_STATUS --------------------------------------------------------------------------------