Created
May 9, 2014 18:35
-
-
Save vnykmshr/89b4663f0df95743f268 to your computer and use it in GitHub Desktop.
Dropbox Uploader Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Dropbox Uploader | |
# | |
# Copyright (C) 2010-2014 Andrea Fabrizi <[email protected]> | |
# | |
# 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" | |
BIN_DEPS="sed basename date grep stat dd mkdir" | |
VERSION="0.13" | |
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 | |
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 > 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 | |
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 and extracting files and subfolders | |
echo "$DIR_CONTENT" | sed 's/\\"/\\u0022/' | sed -n 's/.*"bytes": *\([0-9]*\),.*"path": *"\([^"]*\)",.*"is_dir": *\([^"]*\),.*/\2:\3;\1/p' > $RESPONSE_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} > $padding )); then | |
padding=${#SIZE} | |
fi | |
done < $RESPONSE_FILE | |
#For each entry... | |
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 | |
TYPE="F" | |
else | |
TYPE="D" | |
fi | |
FILE=$(echo -e "$FILE") | |
printf " [$TYPE] %-${padding}s %s\n" "$SIZE" "$FILE" | |
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") | |
db_copy "/$FILE_SRC" "/$FILE_DST" | |
;; | |
mkdir) | |
if [[ $argnum < 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment