126
|
1 #!/usr/bin/env bash
|
|
2 #
|
|
3 # Dropbox Uploader
|
|
4 #
|
|
5 # Copyright (C) 2010-2014 Andrea Fabrizi <[email protected]>
|
|
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://www2.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://www2.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 - [email protected]\n"
|
|
218 echo -e "Usage: $0 COMMAND [PARAMETERS]..."
|
|
219 echo -e "\nCommands:"
|
|
220
|
|
221 echo -e "\t upload <LOCAL_FILE/DIR ...> <REMOTE_FILE/DIR>"
|
|
222 echo -e "\t download <REMOTE_FILE/DIR> [LOCAL_FILE/DIR]"
|
|
223 echo -e "\t delete <REMOTE_FILE/DIR>"
|
|
224 echo -e "\t move <REMOTE_FILE/DIR> <REMOTE_FILE/DIR>"
|
|
225 echo -e "\t copy <REMOTE_FILE/DIR> <REMOTE_FILE/DIR>"
|
|
226 echo -e "\t mkdir <REMOTE_DIR>"
|
|
227 echo -e "\t list [REMOTE_DIR]"
|
|
228 echo -e "\t share <REMOTE_FILE>"
|
|
229 echo -e "\t info"
|
|
230 echo -e "\t unlink"
|
|
231
|
|
232 echo -e "\nOptional parameters:"
|
|
233 echo -e "\t-f <FILENAME> 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<strlen ; pos++ )); do
|
|
317 c=${string:$pos:1}
|
|
318 case "$c" in
|
|
319 [-_.~a-zA-Z0-9] ) o="${c}" ;;
|
|
320 * ) $PRINTF $PRINTF_OPT '%%%02x' "'$c"
|
|
321 esac
|
|
322 encoded+="${o}"
|
|
323 done
|
|
324
|
|
325 echo "$encoded"
|
|
326 }
|
|
327
|
|
328 function normalize_path
|
|
329 {
|
|
330 path=$(echo -e "$1")
|
|
331 if [[ $HAVE_READLINK == 1 ]]; then
|
|
332 new_path=$(readlink -m "$path")
|
|
333
|
|
334 #Adding back the final slash, if present in the source
|
|
335 if [[ "${path: -1}" == "/" ]]; then
|
|
336 new_path="$new_path/"
|
|
337 fi
|
|
338
|
|
339 echo "$new_path"
|
|
340 else
|
|
341 echo "$path"
|
|
342 fi
|
|
343 }
|
|
344
|
|
345 #Check if it's a file or directory
|
|
346 #Returns FILE/DIR/ERR
|
|
347 function db_stat
|
|
348 {
|
|
349 local FILE=$(normalize_path "$1")
|
|
350
|
|
351 #Checking if it's a file or a directory
|
|
352 $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
|
|
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
|