Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX]: Handle special chars in filenames | Resolve #156 and #160 #159

Merged
merged 2 commits into from
May 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bash/auth-utils.bash
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ _check_credentials() {
else
"${QUIET:-_print_center}" "normal" " Above accounts are configured, but default one not set. " "="
if [[ -t 1 ]]; then
\ "${QUIET:-_print_center}" "normal" " Choose default account: " "-"
"${QUIET:-_print_center}" "normal" " Choose default account: " "-"
until [[ -n ${ACCOUNT_NAME} ]]; do
printf -- "-> \e[?7l"
read -r account_name
Expand Down
31 changes: 17 additions & 14 deletions bash/common-utils.bash
Original file line number Diff line number Diff line change
Expand Up @@ -192,24 +192,27 @@ _get_latest_sha() {
###################################################
# Encode the given string to parse properly as json
# Globals: None
# Arguments: 1
# ${1} = string
# Result: print encoded string
# Arguments: 2
# ${1} = json or something else
# ${2} = input
# Result: if ${1} is j, then escape all chars, else only special chars
# Reference:
# https://tools.ietf.org/html/rfc7159#section-7
###################################################
_json_escape() {
declare input="${1:?Provide Input}"
# \ and /
: "${input//\\/\\\\}"
: "${_//\//\\\/}"
: "${_//\'/\\\'}" # ' (not strictly needed ?)
: "${_//\"/\\\"}" # "
: "${_//$'\t'/\\\t}" # \t (tab)
: "${_//$'\n'/\\\n}" # \n (newline)
: "${_//$'\r'/\\\r}" # \r (carriage return)
: "${_//$'\f'/\\\f}" # \f (form feed)
: "${_//$'\b'/\\\b}" # \b (backspace)
declare mode="${1:?}" input="${2:?Provide Input}"
[[ ${mode} = "j" ]] && {
# \ and /
: "${input//\\/\\\\}"
: "${_//\//\\\/}"
# : "${_//\'/\\\'}" # ' (not strictly needed ?)
input="${_//\"/\\\"}" # "
}
: "${input//$'\t'/\\\t}" # \t (tab)
: "${_//$'\n'/\\\n}" # \n (newline)
: "${_//$'\r'/\\\r}" # \r (carriage return)
: "${_//$'\f'/\\\f}" # \f (form feed)
: "${_//$'\b'/\\\b}" # \b (backspace)
printf "%s" "${_}"
}

Expand Down
73 changes: 37 additions & 36 deletions bash/drive-utils.bash
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ _check_existing_file() {
declare name="${1##*/}" rootdir="${2}" query search_response id

"${EXTRA_LOG}" "justify" "Checking if file" " exists on gdrive.." "-" 1>&2
query="$(_url_encode "name='${name}' and '${rootdir}' in parents and trashed=false")"
query="$(_url_encode "name=\"${name}\" and '${rootdir}' in parents and trashed=false")"

search_response="$(_api_request "${CURL_PROGRESS_EXTRA}" \
"${API_URL}/drive/${API_VERSION}/files?q=${query}&fields=files(id,name,mimeType)&supportsAllDrives=true&includeItemsFromAllDrives=true" || :)" && _clear_line 1 1>&2
Expand All @@ -29,9 +29,9 @@ _check_existing_file() {

###################################################
# Copy/Clone a public gdrive file/folder from another/same gdrive account
# Globals: 6 variables, 2 functions
# Globals: 6 variables, 6 functions
# Variables - API_URL, API_VERSION, CURL_PROGRESS, LOG_FILE_ID, QUIET, ACCESS_TOKEN, DESCRIPTION_FILE
# Functions - _print_center, _check_existing_file, _json_value, _bytes_to_human, _clear_line
# Functions - _print_center, _check_existing_file, _json_value, _json_escape _bytes_to_human, _clear_line
# Arguments: 5
# ${1} = update or upload ( upload type )
# ${2} = file id to upload
Expand All @@ -47,32 +47,32 @@ _check_existing_file() {
_clone_file() {
[[ $# -lt 5 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 1
declare job="${1}" file_id="${2}" file_root_id="${3}" name="${4}" size="${5}"
declare clone_file_post_data clone_file_response readable_size _file_id description && STRING="Cloned"
readable_size="$(_bytes_to_human "${size}")"
declare clone_file_post_data clone_file_response readable_size _file_id description escaped_name && STRING="Cloned"
escaped_name="$(_json_escape j "${name}")" print_name="$(_json_escape p "${name}")" readable_size="$(_bytes_to_human "${size}")"

# create description data
[[ -n ${DESCRIPTION_FILE} ]] && {
: "${DESCRIPTION_FILE//%f/${name}}" && : "${_//%s/${readable_size}}"
description="$(_json_escape "${_}")" # escape for json
description="$(_json_escape j "${_}")" # escape for json
}

clone_file_post_data="{\"parents\": [\"${file_root_id}\"]${description:+,\"description\":\"${description}\"}}"

_print_center "justify" "${name} " "| ${readable_size}" "="
_print_center "justify" "${print_name} " "| ${readable_size}" "="

if [[ ${job} = update ]]; then
declare file_check_json
# Check if file actually exists.
if file_check_json="$(_check_existing_file "${name}" "${file_root_id}")"; then
if file_check_json="$(_check_existing_file "${escaped_name}" "${file_root_id}")"; then
if [[ -n ${SKIP_DUPLICATES} ]]; then
_collect_file_info "${file_check_json}" || return 1
_clear_line 1
"${QUIET:-_print_center}" "justify" "${name}" " already exists." "=" && return 0
"${QUIET:-_print_center}" "justify" "${print_name}" " already exists." "=" && return 0
else
_print_center "justify" "Overwriting file.." "-"
{ _file_id="$(_json_value id 1 1 <<< "${file_check_json}")" &&
clone_file_post_data="$(_drive_info "${_file_id}" "parents,writersCanShare")"; } ||
{ _error_logging_upload "${name}" "${post_data:-${file_check_json}}" || return 1; }
{ _error_logging_upload "${print_name}" "${post_data:-${file_check_json}}" || return 1; }
if [[ ${_file_id} != "${file_id}" ]]; then
_api_request -s \
-X DELETE \
Expand All @@ -97,15 +97,15 @@ _clone_file() {
"${API_URL}/drive/${API_VERSION}/files/${file_id}/copy?supportsAllDrives=true&includeItemsFromAllDrives=true" || :)"
for _ in 1 2 3; do _clear_line 1; done
_collect_file_info "${clone_file_response}" || return 1
"${QUIET:-_print_center}" "justify" "${name} " "| ${readable_size} | ${STRING}" "="
"${QUIET:-_print_center}" "justify" "${print_name} " "| ${readable_size} | ${STRING}" "="
return 0
}

###################################################
# Create/Check directory in google drive.
# Globals: 3 variables, 2 functions
# Globals: 3 variables, 3 functions
# Variables - API_URL, API_VERSION, ACCESS_TOKEN
# Functions - _url_encode, _json_value
# Functions - _url_encode, _json_value, _json_escape
# Arguments: 2
# ${1} = dir name
# ${2} = root dir id of given dir
Expand All @@ -115,17 +115,18 @@ _clone_file() {
###################################################
_create_directory() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 1
declare dirname="${1##*/}" rootdir="${2}" query search_response folder_id
declare dirname="${1##*/}" escaped_dirname rootdir="${2}" query search_response folder_id
escaped_dirname="$(_json_escape j "${dirname}")" print_dirname="$(_json_escape p "${dirname}")"

"${EXTRA_LOG}" "justify" "Creating gdrive folder:" " ${dirname}" "-" 1>&2
query="$(_url_encode "mimeType='application/vnd.google-apps.folder' and name='${dirname}' and trashed=false and '${rootdir}' in parents")"
"${EXTRA_LOG}" "justify" "Creating gdrive folder:" " ${print_dirname}" "-" 1>&2
query="$(_url_encode "mimeType='application/vnd.google-apps.folder' and name=\"${escaped_dirname}\" and trashed=false and '${rootdir}' in parents")"

search_response="$(_api_request "${CURL_PROGRESS_EXTRA}" \
"${API_URL}/drive/${API_VERSION}/files?q=${query}&fields=files(id)&supportsAllDrives=true&includeItemsFromAllDrives=true" || :)" && _clear_line 1 1>&2

if ! folder_id="$(printf "%s\n" "${search_response}" | _json_value id 1 1)"; then
declare create_folder_post_data create_folder_response
create_folder_post_data="{\"mimeType\": \"application/vnd.google-apps.folder\",\"name\": \"${dirname}\",\"parents\": [\"${rootdir}\"]}"
create_folder_post_data="{\"mimeType\": \"application/vnd.google-apps.folder\",\"name\": \"${escaped_dirname}\",\"parents\": [\"${rootdir}\"]}"
create_folder_response="$(_api_request "${CURL_PROGRESS_EXTRA}" \
-X POST \
-H "Content-Type: application/json; charset=UTF-8" \
Expand Down Expand Up @@ -187,9 +188,9 @@ _extract_id() {
###################################################
# Upload ( Create/Update ) files on gdrive.
# Interrupted uploads can be resumed.
# Globals: 8 variables, 10 functions
# Globals: 8 variables, 11 functions
# Variables - API_URL, API_VERSION, QUIET, VERBOSE, VERBOSE_PROGRESS, CURL_PROGRESS, LOG_FILE_ID, ACCESS_TOKEN, DESCRIPTION_FILE
# Functions - _url_encode, _json_value, _print_center, _bytes_to_human
# Functions - _url_encode, _json_value, _json_escape _print_center, _bytes_to_human
# _generate_upload_link, _upload_file_from_uri, _log_upload_session, _remove_upload_session
# _full_upload, _collect_file_info
# Arguments: 3
Expand All @@ -207,10 +208,10 @@ _extract_id() {
_upload_file() {
[[ $# -lt 3 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 1
declare job="${1}" input="${2}" folder_id="${3}" \
slug inputname extension inputsize readable_size request_method url postdata uploadlink upload_body mime_type description \
slug escaped_slug inputname extension inputsize readable_size request_method url postdata uploadlink upload_body mime_type description \
resume_args1 resume_args2 resume_args3

slug="${input##*/}"
slug="${input##*/}" escaped_slug="$(_json_escape j "${slug}")" print_slug="$(_json_escape p "${slug}")"
inputname="${slug%.*}"
extension="${slug##*.}"
inputsize="$(($(wc -c < "${input}")))" && content_length="${inputsize}"
Expand All @@ -227,28 +228,28 @@ _upload_file() {
# create description data
[[ -n ${DESCRIPTION_FILE} ]] && {
: "${DESCRIPTION_FILE//%f/${slug}}" && : "${_//%s/${inputsize}}" && : "${_//%m/${mime_type}}"
description="$(_json_escape "${_}")" # escape for json
description="$(_json_escape j "${_}")" # escape for json
}

_print_center "justify" "${input##*/}" " | ${readable_size}" "="
_print_center "justify" "${print_slug}" " | ${readable_size}" "="

# Set proper variables for overwriting files
[[ ${job} = update ]] && {
declare file_check_json
# Check if file actually exists, and create if not.
if file_check_json="$(_check_existing_file "${slug}" "${folder_id}")"; then
if file_check_json="$(_check_existing_file "${escaped_slug}" "${folder_id}")"; then
if [[ -n ${SKIP_DUPLICATES} ]]; then
# Stop upload if already exists ( -d/--skip-duplicates )
_collect_file_info "${file_check_json}" "${slug}" || return 1
_collect_file_info "${file_check_json}" "${escaped_slug}" || return 1
_clear_line 1
"${QUIET:-_print_center}" "justify" "${slug}" " already exists." "=" && return 0
"${QUIET:-_print_center}" "justify" "${print_slug}" " already exists." "=" && return 0
else
request_method="PATCH"
_file_id="$(_json_value id 1 1 <<< "${file_check_json}")" ||
{ _error_logging_upload "${slug}" "${file_check_json}" || return 1; }
{ _error_logging_upload "${print_slug}" "${file_check_json}" || return 1; }
url="${API_URL}/upload/drive/${API_VERSION}/files/${_file_id}?uploadType=resumable&supportsAllDrives=true&includeItemsFromAllDrives=true"
# JSON post data to specify the file name and folder under while the file to be updated
postdata="{\"mimeType\": \"${mime_type}\",\"name\": \"${slug}\",\"addParents\": [\"${folder_id}\"]${description:+,\"description\":\"${description}\"}}"
postdata="{\"mimeType\": \"${mime_type}\",\"name\": \"${escaped_slug}\",\"addParents\": [\"${folder_id}\"]${description:+,\"description\":\"${description}\"}}"
STRING="Updated"
fi
else
Expand All @@ -261,11 +262,11 @@ _upload_file() {
url="${API_URL}/upload/drive/${API_VERSION}/files?uploadType=resumable&supportsAllDrives=true&includeItemsFromAllDrives=true"
request_method="POST"
# JSON post data to specify the file name and folder under while the file to be created
postdata="{\"mimeType\": \"${mime_type}\",\"name\": \"${slug}\",\"parents\": [\"${folder_id}\"]${description:+,\"description\":\"${description}\"}}"
postdata="{\"mimeType\": \"${mime_type}\",\"name\": \"${escaped_slug}\",\"parents\": [\"${folder_id}\"]${description:+,\"description\":\"${description}\"}}"
STRING="Uploaded"
}

__file="${HOME}/.google-drive-upload/${slug}__::__${folder_id}__::__${inputsize}"
__file="${HOME}/.google-drive-upload/${print_slug}__::__${folder_id}__::__${inputsize}"
# https://developers.google.com/drive/api/v3/manage-uploads
if [[ -r "${__file}" ]]; then
uploadlink="$(< "${__file}")"
Expand All @@ -283,7 +284,7 @@ _upload_file() {
# Resuming interrupted uploads needs http1.1
resume_args1='-s' resume_args2='--http1.1' resume_args3="Content-Range: ${content_range}"
_upload_file_from_uri _clear_line
_collect_file_info "${upload_body}" "${slug}" || return 1
_collect_file_info "${upload_body}" "${print_slug}" || return 1
_normal_logging_upload
_remove_upload_session
else
Expand All @@ -292,7 +293,7 @@ _upload_file() {
;;
201 | 200) # Completed Resumable URI give 20* status
upload_body="${http_code}"
_collect_file_info "${upload_body}" "${slug}" || return 1
_collect_file_info "${upload_body}" "${print_slug}" || return 1
_normal_logging_upload
_remove_upload_session
;;
Expand Down Expand Up @@ -337,7 +338,7 @@ _upload_file_from_uri() {
-X PUT \
-H "Content-Type: ${mime_type}" \
-H "Content-Length: ${content_length}" \
-H "Slug: ${slug}" \
-H "Slug: ${print_slug}" \
-T "${input}" \
-o- \
--url "${uploadlink}" \
Expand All @@ -351,7 +352,7 @@ _upload_file_from_uri() {
# logging in case of successful upload
_normal_logging_upload() {
[[ -z ${VERBOSE_PROGRESS} ]] && _clear_line 1
"${QUIET:-_print_center}" "justify" "${slug} " "| ${readable_size} | ${STRING}" "="
"${QUIET:-_print_center}" "justify" "${print_slug} " "| ${readable_size} | ${STRING}" "="
return 0
}

Expand All @@ -369,10 +370,10 @@ _remove_upload_session() {

# wrapper to fully upload a file from scratch
_full_upload() {
_generate_upload_link || { _error_logging_upload "${slug}" "${uploadlink}" || return 1; }
_generate_upload_link || { _error_logging_upload "${print_slug}" "${uploadlink}" || return 1; }
_log_upload_session
_upload_file_from_uri
_collect_file_info "${upload_body}" "${slug}" || return 1
_collect_file_info "${upload_body}" "${print_slug}" || return 1
_normal_logging_upload
_remove_upload_session
return 0
Expand Down
31 changes: 17 additions & 14 deletions bash/release/gsync
Original file line number Diff line number Diff line change
Expand Up @@ -193,24 +193,27 @@ _get_latest_sha() {
###################################################
# Encode the given string to parse properly as json
# Globals: None
# Arguments: 1
# ${1} = string
# Result: print encoded string
# Arguments: 2
# ${1} = json or something else
# ${2} = input
# Result: if ${1} is j, then escape all chars, else only special chars
# Reference:
# https://tools.ietf.org/html/rfc7159#section-7
###################################################
_json_escape() {
declare input="${1:?Provide Input}"
# \ and /
: "${input//\\/\\\\}"
: "${_//\//\\\/}"
: "${_//\'/\\\'}" # ' (not strictly needed ?)
: "${_//\"/\\\"}" # "
: "${_//$'\t'/\\\t}" # \t (tab)
: "${_//$'\n'/\\\n}" # \n (newline)
: "${_//$'\r'/\\\r}" # \r (carriage return)
: "${_//$'\f'/\\\f}" # \f (form feed)
: "${_//$'\b'/\\\b}" # \b (backspace)
declare mode="${1:?}" input="${2:?Provide Input}"
[[ ${mode} = "j" ]] && {
# \ and /
: "${input//\\/\\\\}"
: "${_//\//\\\/}"
# : "${_//\'/\\\'}" # ' (not strictly needed ?)
input="${_//\"/\\\"}" # "
}
: "${input//$'\t'/\\\t}" # \t (tab)
: "${_//$'\n'/\\\n}" # \n (newline)
: "${_//$'\r'/\\\r}" # \r (carriage return)
: "${_//$'\f'/\\\f}" # \f (form feed)
: "${_//$'\b'/\\\b}" # \b (backspace)
printf "%s" "${_}"
}

Expand Down
Loading