383 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
	
| #!/bin/bash
 | |
| set -e
 | |
| 
 | |
| # PairDrop version when this file was last changed
 | |
| version="v1.10.4"
 | |
| 
 | |
| ############################################################
 | |
| # Help                                                     #
 | |
| ############################################################
 | |
| help()
 | |
| {
 | |
|    # Display Help
 | |
|    echo "Send files or text with PairDrop via command-line interface."
 | |
|    echo "Current domain: ${DOMAIN}"
 | |
|    echo
 | |
|    echo "Usage:"
 | |
|    echo -e "Open PairDrop:\t\t$(basename "$0")"
 | |
|    echo -e "Send files:\t\t$(basename "$0") file1/directory1 (file2/directory2 file3/directory3 ...)"
 | |
|    echo -e "Send text:\t\t$(basename "$0") -t \"text\""
 | |
|    echo -e "Specify domain:\t\t$(basename "$0") -d \"https://pairdrop.net/\""
 | |
|    echo -e "Show this help text:\t$(basename "$0") (-h|--help)"
 | |
|    echo
 | |
|    echo "This pairdrop-cli version was released alongside ${version}"
 | |
| }
 | |
| 
 | |
| openPairDrop()
 | |
| {
 | |
|   url="$DOMAIN"
 | |
|   if [[ -n $params ]];then
 | |
|     url="${url}?${params}"
 | |
|   fi
 | |
|   if [[ -n $hash ]];then
 | |
|     url="${url}#${hash}"
 | |
|   fi
 | |
| 
 | |
|   echo "PairDrop is opening at $DOMAIN"
 | |
|   if [[ $OS == "Windows" ]];then
 | |
|     start "$url"
 | |
|   elif [[ $OS == "Mac" ]];then
 | |
|     open "$url"
 | |
|   elif [[ $OS == "WSL" || $OS == "WSL2" ]];then
 | |
|     powershell.exe /c "Start-Process ${url}"
 | |
|   else
 | |
|     xdg-open "$url" > /dev/null 2>&1
 | |
|   fi
 | |
| 
 | |
| 
 | |
|   exit
 | |
| 
 | |
| }
 | |
| 
 | |
| setOs()
 | |
| {
 | |
|   unameOut=$(uname -a)
 | |
|   case "${unameOut}" in
 | |
|       *Microsoft*)     OS="WSL";; #must be first since Windows subsystem for linux will have Linux in the name too
 | |
|       *microsoft*)     OS="WSL2";; #WARNING: My v2 uses ubuntu 20.4 at the moment slightly different name may not always work
 | |
|       Linux*)          OS="Linux";;
 | |
|       Darwin*)         OS="Mac";;
 | |
|       CYGWIN*)         OS="Cygwin";;
 | |
|       MINGW*)          OS="Windows";;
 | |
|       *Msys)           OS="Windows";;
 | |
|       *)               OS="UNKNOWN:${unameOut}"
 | |
|   esac
 | |
| }
 | |
| 
 | |
| specifyDomain()
 | |
| {
 | |
|   [[ ! $1 = http* ]] || [[ ! $1 = */ ]] && echo "Incorrect format. Specify domain like https://pairdrop.net/" && exit
 | |
|   echo "DOMAIN=${1}" > "$config_path"
 | |
|   echo -e "Domain is now set to:\n$1\n"
 | |
| }
 | |
| 
 | |
| sendText()
 | |
| {
 | |
|     params="base64text=hash"
 | |
|     hash=$(echo -n "${OPTARG}" | base64)
 | |
| 
 | |
|     if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then
 | |
|       params="base64text=paste"
 | |
|       if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
 | |
|         echo -n "$hash" | clip.exe
 | |
|       elif [[ $OS == "Mac" ]];then
 | |
|         echo -n "$hash" | pbcopy
 | |
|       else
 | |
|         (echo -n "$hash" | xclip) || echo "You need to install xclip for sending bigger files from cli"
 | |
|       fi
 | |
|       hash=
 | |
|     fi
 | |
| 
 | |
|     openPairDrop
 | |
|     exit
 | |
| }
 | |
| 
 | |
| escapePSPath()
 | |
| {
 | |
|   local path=$1
 | |
|   
 | |
|   # escape '[' and ']' with grave accent (`) character
 | |
|   pathPS=${path//[/\`[}
 | |
|   pathPS=${pathPS//]/\`]}
 | |
|   # escape single quote (') with another single quote (')
 | |
|   pathPS=${pathPS//\'/\'\'}
 | |
|   
 | |
|   # Convert GitHub bash path "/i/path" to Windows path "I:/path"
 | |
|   if [[ $pathPS == /* ]]; then
 | |
|     # Remove preceding slash
 | |
|     pathPS="${pathPS#/}"
 | |
|     # Convert drive letter to uppercase
 | |
|     driveLetter=$(echo "${pathPS::1}" | tr '[:lower:]' '[:upper:]')
 | |
|     # Put together absolute path as used in Windows
 | |
|     pathPS="${driveLetter}:${pathPS:1}"        
 | |
|   fi
 | |
|   
 | |
|   echo "$pathPS"
 | |
| }
 | |
| 
 | |
| sendFiles()
 | |
| {
 | |
|   params="base64zip=hash"
 | |
|   workingDir="$(pwd)"
 | |
|   tmpDir="/tmp/pairdrop-cli-temp/"
 | |
|   tmpDirPS="\$env:TEMP/pairdrop-cli-temp/"
 | |
|   
 | |
|   index=0
 | |
|   directoryBaseNamesUnix=()
 | |
|   directoryPathsUnix=()
 | |
|   filePathsUnix=()
 | |
|   directoryCount=0
 | |
|   fileCount=0
 | |
|   pathsPS=""
 | |
| 
 | |
|   #create tmp folder if it does not exist already
 | |
|   if [[ ! -d "$tmpDir" ]]; then
 | |
|     mkdir "$tmpDir"
 | |
|   fi
 | |
| 
 | |
|   for arg in "$@"; do
 | |
|    echo "$arg"
 | |
|    [[ ! -e "$arg" ]] && echo "The given path $arg does not exist." && exit
 | |
| 
 | |
|     # Remove trailing slash from directory
 | |
|     arg="${arg%/}"
 | |
| 
 | |
|     # get absolute path and basename of file/directory
 | |
|     absolutePath=$(realpath "$arg")
 | |
|     baseName=$(basename "$absolutePath")
 | |
|     directoryPath=$(dirname "$absolutePath")
 | |
| 
 | |
|     if [[ -d $absolutePath ]]; then
 | |
|       # is directory
 | |
|       ((directoryCount+=1))
 | |
|       # add basename and directory path to arrays
 | |
|       directoryBaseNamesUnix+=("$baseName")
 | |
|       directoryPathsUnix+=("$directoryPath")
 | |
|     else
 | |
|       # is file
 | |
|       ((fileCount+=1))
 | |
|       absolutePathUnix=$absolutePath
 | |
|       # append new path and separate paths with space
 | |
|       filePathsUnix+=("$absolutePathUnix")
 | |
|     fi
 | |
| 
 | |
|     # Prepare paths for PowerShell on Windows
 | |
|     if [[ $OS == "Windows" ]];then
 | |
|       absolutePathPS=$(escapePSPath "$absolutePath")
 | |
|       
 | |
|       # append new path and separate paths with commas
 | |
|       pathsPS+="'${absolutePathPS}', "
 | |
|     fi
 | |
|     
 | |
|     # set fileNames on first loop
 | |
|     if [[ $index == 0 ]]; then
 | |
|       baseNameU=${baseName// /_}
 | |
| 
 | |
|       # Prevent baseNameU being empty for hidden files by removing the preceding dot
 | |
|       if [[ $baseNameU == .* ]]; then
 | |
|         baseNameU=${baseNameU#.*}
 | |
|       fi
 | |
| 
 | |
|       # only use trunk of basename "document.txt" -> "document"
 | |
|       baseNameTrunk=${baseNameU%.*}
 | |
|       
 | |
|       # remove all special characters
 | |
|       zipName=${baseNameTrunk//[^a-zA-Z0-9_]/}
 | |
|       
 | |
|       zipToSendAbs="${tmpDir}${zipName}_pairdrop.zip"
 | |
|       wrapperZipAbs="${tmpDir}${zipName}_pairdrop_wrapper.zip"
 | |
| 
 | |
|       if [[ $OS == "Windows" ]];then
 | |
|         zipToSendAbsPS="${tmpDirPS}${zipName}_pairdrop.zip"
 | |
|         wrapperZipAbsPS="${tmpDirPS}${zipName}_pairdrop_wrapper.zip"
 | |
|       fi
 | |
|     fi
 | |
| 
 | |
|     ((index+=1)) # somehow ((index++)) stops the script
 | |
|   done
 | |
| 
 | |
|   # Prepare paths for PowerShell on Windows
 | |
|   if [[ $OS == "Windows" ]];then
 | |
|     # remove trailing comma
 | |
|     pathsPS=${pathsPS%??}
 | |
|   fi
 | |
| 
 | |
|   echo "Preparing ${fileCount} files and ${directoryCount} directories..."
 | |
| 
 | |
|   # if arguments include files only -> zip files once so files it is unzipped by sending browser
 | |
|   # if arguments include directories -> wrap first zip in a second wrapper zip so that after unzip by sending browser a zip file is sent to receiver
 | |
|   #
 | |
|   # Preferred zip structure:
 | |
|   # pairdrop "d1/d2/d3/f1" "../../d4/d5/d6/f2" "d7/" "../d8/" "f5"
 | |
|   # zip structure: pairdrop.zip
 | |
|   #                |-f1
 | |
|   #                |-f2
 | |
|   #                |-d7/
 | |
|   #                |-d8/
 | |
|   #                |-f5
 | |
|   # -> truncate (relative) paths but keep directories
 | |
|   
 | |
|   [[ -e "$zipToSendAbs" ]] && echo "Cannot overwrite $zipToSendAbs. Please remove first." && exit
 | |
| 
 | |
|   if [[ $OS == "Windows" ]];then
 | |
|     # Powershell does preferred zip structure natively
 | |
|     powershell.exe -Command "Compress-Archive -Path ${pathsPS} -DestinationPath ${zipToSendAbsPS}"
 | |
|   else
 | |
|     # Workaround needed to create preferred zip structure on unix systems
 | |
|     # Create zip file with all single files by junking the path
 | |
|     if [[ $fileCount != 0 ]]; then
 | |
|       zip -q -b /tmp/ -j -0 -r "$zipToSendAbs" "${filePathsUnix[@]}"
 | |
|     fi
 | |
| 
 | |
|     # Add directories recursively to zip file
 | |
|     index=0
 | |
|     while [[ $index < $directoryCount ]]; do
 | |
|       # workaround to keep directory name but junk the rest of the paths
 | |
| 
 | |
|       # cd to path above directory
 | |
|       cd "${directoryPathsUnix[index]}"
 | |
| 
 | |
|       # add directory to zip without junking the path
 | |
|       zip -q -b /tmp/ -0 -u -r "$zipToSendAbs" "${directoryBaseNamesUnix[index]}"
 | |
| 
 | |
|       # cd back to working directory
 | |
|       cd "$workingDir"
 | |
| 
 | |
|       ((index+=1)) # somehow ((index++)) stops the script
 | |
|     done
 | |
|   fi
 | |
| 
 | |
|   # If directories are included send as zip
 | |
|   # -> Create additional zip wrapper which will be unzipped by the sending browser
 | |
|   if [[ "$directoryCount" != 0 ]]; then
 | |
|     echo "Bundle as ZIP file..."
 | |
| 
 | |
|     # Prevent filename from being absolute zip path by "cd"ing to directory before zipping
 | |
|     zipToSendDirectory=$(dirname "$zipToSendAbs")
 | |
|     zipToSendBaseName=$(basename "$zipToSendAbs")
 | |
| 
 | |
|     cd "$zipToSendDirectory"
 | |
|     
 | |
|     [[ -e "$wrapperZipAbs" ]] && echo "Cannot overwrite $wrapperZipAbs. Please remove first." && exit
 | |
|     
 | |
|     if [[ $OS == "Windows" ]];then
 | |
|       powershell.exe -Command "Compress-Archive -Path ${zipToSendBaseName} -DestinationPath ${wrapperZipAbsPS} -CompressionLevel Optimal"
 | |
|     else
 | |
|       zip -q -b /tmp/ -0 "$wrapperZipAbs" "$zipToSendBaseName"
 | |
|     fi
 | |
|     cd "$workingDir"
 | |
| 
 | |
|     # remove inner zip file and set wrapper as zipToSend (do not differentiate between OS as this is done via Git Bash on Windows)
 | |
|     rm "$zipToSendAbs"
 | |
| 
 | |
|     zipToSendAbs=$wrapperZipAbs
 | |
|   fi
 | |
| 
 | |
|   # base64 encode zip file
 | |
|   if [[ $OS == "Mac" ]];then
 | |
|     hash=$(base64 -i "$zipToSendAbs")
 | |
|   else
 | |
|     hash=$(base64 -w 0 "$zipToSendAbs")
 | |
|   fi
 | |
| 
 | |
|   # remove zip file (do not differentiate between OS as this is done via Git Bash on Windows)
 | |
|   rm "$zipToSendAbs"
 | |
| 
 | |
|   if [[ $(echo -n "$hash" | wc -m) -gt 1000 ]];then
 | |
|     params="base64zip=paste"
 | |
| 
 | |
|     # Copy $hash to clipboard
 | |
|     if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
 | |
|       echo -n "$hash" | clip.exe
 | |
|     elif [[ $OS == "Mac" ]];then
 | |
|       echo -n "$hash" | pbcopy
 | |
|     elif [ -n "$WAYLAND_DISPLAY" ]; then
 | |
|       # Wayland
 | |
|       if ! command -v wl-copy &> /dev/null; then
 | |
|           echo -e "You need to install 'wl-copy' to send bigger filePathsUnix from cli"
 | |
|           echo "Try: sudo apt install wl-clipboard"
 | |
|           exit 1
 | |
|       fi
 | |
|       # Workaround to prevent use of Pipe which has a max letter limit
 | |
|       echo -n "$hash" > /tmp/pairdrop-cli-temp/pairdrop_hash_temp
 | |
|       wl-copy < /tmp/pairdrop-cli-temp/pairdrop_hash_temp
 | |
|       rm /tmp/pairdrop-cli-temp/pairdrop_hash_temp
 | |
|     else
 | |
|       # X11
 | |
|       if ! command -v xclip &> /dev/null; then
 | |
|           echo -e "You need to install 'xclip' to send bigger filePathsUnix from cli"
 | |
|           echo "Try: sudo apt install xclip"
 | |
|           exit 1
 | |
|       fi
 | |
|       echo -n "$hash" | xclip -sel c
 | |
|     fi
 | |
|     hash=
 | |
|   fi
 | |
| 
 | |
|   openPairDrop
 | |
|   exit
 | |
| }
 | |
| 
 | |
| ############################################################
 | |
| ############################################################
 | |
| # Main program                                             #
 | |
| ############################################################
 | |
| ############################################################
 | |
| script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
 | |
| 
 | |
| pushd . > '/dev/null';
 | |
| script_path="${BASH_SOURCE[0]:-$0}";
 | |
| 
 | |
| while [ -h "$script_path" ];
 | |
| do
 | |
|   cd "$( dirname -- "$script_path"; )";
 | |
|   script_path="$( readlink -f -- "$script_path"; )";
 | |
| done
 | |
| 
 | |
| cd "$( dirname -- "$script_path"; )" > '/dev/null';
 | |
| script_path="$( pwd; )";
 | |
| popd  > '/dev/null';
 | |
| 
 | |
| config_path="${script_path}/.pairdrop-cli-config"
 | |
| 
 | |
| # If config file does not exist, try to create it. If it fails log error message and exit
 | |
| [ ! -f "$config_path" ] &&
 | |
|   specifyDomain "https://pairdrop.net/" &&
 | |
|   [ ! -f "$config_path" ] &&
 | |
|   echo "Could not create config file. Add 'DOMAIN=https://pairdrop.net/' to a file called .pairdrop-cli-config in the same file as this 'pairdrop' bash file (${script_path})" &&
 | |
|   exit
 | |
| 
 | |
| # Read config variables
 | |
| export "$(grep -v '^#' "$config_path" | xargs)"
 | |
| 
 | |
| setOs
 | |
| 
 | |
| ############################################################
 | |
| # Process the input options. Add options as needed.        #
 | |
| ############################################################
 | |
| # Get the options
 | |
| # open PairDrop if no options are given
 | |
| [[ $# -eq 0 ]] && openPairDrop && exit
 | |
| 
 | |
| #  display help and exit if first argument is "--help" or more than 2 arguments are given
 | |
| [ "$1" == "--help" ] && help && exit
 | |
| 
 | |
| while getopts "d:ht:*" option; do
 | |
|   case $option in
 | |
|     d) # specify domain - show help and exit if too many arguments
 | |
|       [[ $# -gt 2 ]] && help && exit
 | |
|       specifyDomain "$2"
 | |
|       exit;;
 | |
|     t) # Send text - show help and exit if too many arguments
 | |
|       [[ $# -gt 2 ]] && help && exit
 | |
|       sendText
 | |
|       exit;;
 | |
|     h | ?) # display help and exit
 | |
|       help
 | |
|       exit;;
 | |
|     esac
 | |
| done
 | |
| 
 | |
| # Send file(s)
 | |
| sendFiles "$@"
 |