From 0f9bbf9bbb0031b62b3b75cc822969df7abae4b0 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Mon, 20 Feb 2023 17:42:02 +0100 Subject: [PATCH 1/4] enable sending from cli by adding bash script --- pairdrop | 123 ++++++++++++++++++ public/index.html | 6 +- public/scripts/ui.js | 148 +++++++++++++++------- public/styles.css | 8 +- public_included_ws_fallback/index.html | 6 +- public_included_ws_fallback/scripts/ui.js | 148 +++++++++++++++------- public_included_ws_fallback/styles.css | 8 +- 7 files changed, 347 insertions(+), 100 deletions(-) create mode 100644 pairdrop diff --git a/pairdrop b/pairdrop new file mode 100644 index 0000000..baebfd8 --- /dev/null +++ b/pairdrop @@ -0,0 +1,123 @@ +#!/bin/bash +set -e + +############################################################ +# Help # +############################################################ +help() +{ + # Display Help + echo "Send files or text via PairDrop via commandline." + echo + echo -e "To send files:\t$(basename "$0") file" + echo -e "To send text:\t$(basename "$0") -t \"text\"" +} + +openPairDrop() +{ + # openPairDrop + url=$domain + if [[ -n $params ]];then + url=$url"?"$params + fi + if [[ -n $hash ]];then + url=$url"#"$hash + fi + echo "PairDrop is opening in a browser." + + if [[ $OS == "Windows" ]];then + start "$url" + elif [[ $OS == "Mac" ]];then + open "$url" + else + xdg-open "$url" + 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 +} + +############################################################ +############################################################ +# Main program # +############################################################ +############################################################ +domain="https://pairdrop.net/" +domain="https://192.168.2.90:8443/" +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" ] | [[ $# -gt 2 ]] && help && exit + +while getopts "ht:*" option; do + case $option in + t) # Send text + 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;; + h | ?) # display help and exit + help + exit;; + esac +done + +# Send file(s) +# display help and exit if 2 arguments are given or if file does not exist +[[ $# -eq 2 ]] || [[ ! -a $1 ]] && help && exit +params="base64zip=hash" +if [[ -d $1 ]]; then + zipPath="${1::-1}_pairdrop_temp.zip" + [[ -a "$zipPath" ]] && echo "Cannot overwrite $zipPath. Please remove first." && exit + zip -r -q -b /tmp/ "$zipPath" "$1" + hash=$(zip -q -b /tmp/ - "$zipPath" | base64 -w 0) + rm "$zipPath" +else + hash=$(zip -q -b /tmp/ - "$1" | base64 -w 0) +fi + +if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then + params="base64zip=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 diff --git a/public/index.html b/public/index.html index 08eed7d..14d50cf 100644 --- a/public/index.html +++ b/public/index.html @@ -213,11 +213,11 @@ - - + + - + diff --git a/public/scripts/ui.js b/public/scripts/ui.js index ce484e4..1d38868 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -143,7 +143,7 @@ class PeersUI { descriptor = `${files[0].name} and ${files.length-1} other files`; noPeersMessage = `Open PairDrop on other devices to send
${descriptor}`; } else { - descriptor = "pasted text"; + descriptor = "shared text"; noPeersMessage = `Open PairDrop on other devices to send
${descriptor}`; } @@ -1081,67 +1081,129 @@ class ReceiveTextDialog extends Dialog { class Base64ZipDialog extends Dialog { constructor() { - super('base64ZipDialog'); + super('base64PasteDialog'); const urlParams = new URL(window.location).searchParams; - const base64Zip = urlParams.get('base64zip'); const base64Text = urlParams.get('base64text'); - this.$pasteBtn = this.$el.querySelector('#base64ZipPasteBtn') - this.$pasteBtn.addEventListener('click', _ => this.processClipboard()) + const base64Zip = urlParams.get('base64zip'); + const base64Hash = window.location.hash.substring(1); + + this.$pasteBtn = this.$el.querySelector('#base64PasteBtn'); if (base64Text) { - this.processBase64Text(base64Text); - } else if (base64Zip) { - if (!navigator.clipboard.readText) { - setTimeout(_ => Events.fire('notify-user', 'This feature is not available on your device.'), 500); - this.clearBrowserHistory(); - return; - } this.show(); + if (base64Text === "paste") { + // ?base64text=paste + // base64 encoded string is ready to be pasted from clipboard + this.$pasteBtn.innerText = 'Tap here to paste text'; + this.$pasteBtn.addEventListener('click', _ => this.processClipboard('text')); + } else if (base64Text === "hash") { + // ?base64text=hash#BASE64ENCODED + // base64 encoded string is url hash which is never sent to server and faster (recommended) + this.processBase64Text(base64Hash) + .catch(_ => { + Events.fire('notify-user', 'Text content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + // ?base64text=BASE64ENCODED + // base64 encoded string was part of url param (not recommended) + this.processBase64Text(base64Text) + .catch(_ => { + Events.fire('notify-user', 'Text content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } + } else if (base64Zip) { + this.show(); + if (base64Zip === "hash") { + // ?base64zip=hash#BASE64ENCODED + // base64 encoded zip file is url hash which is never sent to the server + this.processBase64Zip(base64Hash) + .catch(_ => { + Events.fire('notify-user', 'File content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + // ?base64zip=paste || ?base64zip=true + this.$pasteBtn.innerText = 'Tap here to paste files'; + this.$pasteBtn.addEventListener('click', _ => this.processClipboard('file')); + } + } + } + + _setPasteBtnToProcessing() { + this.$pasteBtn.pointerEvents = "none"; + this.$pasteBtn.innerText = "Processing..."; + } + + async processClipboard(type) { + if (!navigator.clipboard.readText) { + Events.fire('notify-user', 'This feature is not available on your device.'); + this.hide(); + return; + } + + this._setPasteBtnToProcessing(); + + const base64 = await navigator.clipboard.readText(); + + if (!base64) return; + + if (type === "text") { + this.processBase64Text(base64) + .catch(_ => { + Events.fire('notify-user', 'Clipboard content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + this.processBase64Zip(base64) + .catch(_ => { + Events.fire('notify-user', 'Clipboard content is incorrect.'); + }).finally(_ => { + this.hide(); + }); } } processBase64Text(base64Text){ - try { + return new Promise((resolve) => { + this._setPasteBtnToProcessing(); let decodedText = decodeURIComponent(escape(window.atob(base64Text))); Events.fire('activate-paste-mode', {files: [], text: decodedText}); - } catch (e) { - setTimeout(_ => Events.fire('notify-user', 'Content incorrect.'), 500); - } finally { - this.clearBrowserHistory(); - this.hide(); - } + resolve(); + }); } - async processClipboard() { - this.$pasteBtn.pointerEvents = "none"; - this.$pasteBtn.innerText = "Processing..."; - try { - const base64zip = await navigator.clipboard.readText(); - let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n); - while (n--) { - u8arr[n] = bstr.charCodeAt(n); - } - - const zipBlob = new File([u8arr], 'archive.zip'); - - let files = []; - const zipEntries = await zipper.getEntries(zipBlob); - for (let i = 0; i < zipEntries.length; i++) { - let fileBlob = await zipper.getData(zipEntries[i]); - files.push(new File([fileBlob], zipEntries[i].filename)); - } - Events.fire('activate-paste-mode', {files: files, text: ""}) - } catch (e) { - Events.fire('notify-user', 'Clipboard content is incorrect.') - } finally { - this.clearBrowserHistory(); - this.hide(); + async processBase64Zip(base64zip) { + this._setPasteBtnToProcessing(); + let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); } + + const zipBlob = new File([u8arr], 'archive.zip'); + + let files = []; + const zipEntries = await zipper.getEntries(zipBlob); + for (let i = 0; i < zipEntries.length; i++) { + let fileBlob = await zipper.getData(zipEntries[i]); + files.push(new File([fileBlob], zipEntries[i].filename)); + } + Events.fire('activate-paste-mode', {files: files, text: ""}); } clearBrowserHistory() { window.history.replaceState({}, "Rewrite URL", '/'); } + + hide() { + this.clearBrowserHistory(); + super.hide(); + } } class Toast extends Dialog { diff --git a/public/styles.css b/public/styles.css index 7497b77..dcec033 100644 --- a/public/styles.css +++ b/public/styles.css @@ -605,21 +605,21 @@ x-dialog .row-reverse { margin-bottom: 25px; } -#base64ZipPasteBtn { +#base64PasteBtn { width: 100%; height: 40vh; border: solid 12px #438cff; } -#base64ZipDialog button { +#base64PasteDialog button { margin: auto; border-radius: 8px; } -#base64ZipDialog button[close] { +#base64PasteDialog button[close] { margin-top: 20px; } -#base64ZipDialog button[close]:before { +#base64PasteDialog button[close]:before { border-radius: 8px; } diff --git a/public_included_ws_fallback/index.html b/public_included_ws_fallback/index.html index 534d06d..bc7bc22 100644 --- a/public_included_ws_fallback/index.html +++ b/public_included_ws_fallback/index.html @@ -216,11 +216,11 @@
- - + + - + diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index ba48292..e7dd79d 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -143,7 +143,7 @@ class PeersUI { descriptor = `${files[0].name} and ${files.length-1} other files`; noPeersMessage = `Open PairDrop on other devices to send
${descriptor}`; } else { - descriptor = "pasted text"; + descriptor = "shared text"; noPeersMessage = `Open PairDrop on other devices to send
${descriptor}`; } @@ -1082,67 +1082,129 @@ class ReceiveTextDialog extends Dialog { class Base64ZipDialog extends Dialog { constructor() { - super('base64ZipDialog'); + super('base64PasteDialog'); const urlParams = new URL(window.location).searchParams; - const base64Zip = urlParams.get('base64zip'); const base64Text = urlParams.get('base64text'); - this.$pasteBtn = this.$el.querySelector('#base64ZipPasteBtn') - this.$pasteBtn.addEventListener('click', _ => this.processClipboard()) + const base64Zip = urlParams.get('base64zip'); + const base64Hash = window.location.hash.substring(1); + + this.$pasteBtn = this.$el.querySelector('#base64PasteBtn'); if (base64Text) { - this.processBase64Text(base64Text); - } else if (base64Zip) { - if (!navigator.clipboard.readText) { - setTimeout(_ => Events.fire('notify-user', 'This feature is not available on your device.'), 500); - this.clearBrowserHistory(); - return; - } this.show(); + if (base64Text === "paste") { + // ?base64text=paste + // base64 encoded string is ready to be pasted from clipboard + this.$pasteBtn.innerText = 'Tap here to paste text'; + this.$pasteBtn.addEventListener('click', _ => this.processClipboard('text')); + } else if (base64Text === "hash") { + // ?base64text=hash#BASE64ENCODED + // base64 encoded string is url hash which is never sent to server and faster (recommended) + this.processBase64Text(base64Hash) + .catch(_ => { + Events.fire('notify-user', 'Text content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + // ?base64text=BASE64ENCODED + // base64 encoded string was part of url param (not recommended) + this.processBase64Text(base64Text) + .catch(_ => { + Events.fire('notify-user', 'Text content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } + } else if (base64Zip) { + this.show(); + if (base64Zip === "hash") { + // ?base64zip=hash#BASE64ENCODED + // base64 encoded zip file is url hash which is never sent to the server + this.processBase64Zip(base64Hash) + .catch(_ => { + Events.fire('notify-user', 'File content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + // ?base64zip=paste || ?base64zip=true + this.$pasteBtn.innerText = 'Tap here to paste files'; + this.$pasteBtn.addEventListener('click', _ => this.processClipboard('file')); + } + } + } + + _setPasteBtnToProcessing() { + this.$pasteBtn.pointerEvents = "none"; + this.$pasteBtn.innerText = "Processing..."; + } + + async processClipboard(type) { + if (!navigator.clipboard.readText) { + Events.fire('notify-user', 'This feature is not available on your device.'); + this.hide(); + return; + } + + this._setPasteBtnToProcessing(); + + const base64 = await navigator.clipboard.readText(); + + if (!base64) return; + + if (type === "text") { + this.processBase64Text(base64) + .catch(_ => { + Events.fire('notify-user', 'Clipboard content is incorrect.'); + }).finally(_ => { + this.hide(); + }); + } else { + this.processBase64Zip(base64) + .catch(_ => { + Events.fire('notify-user', 'Clipboard content is incorrect.'); + }).finally(_ => { + this.hide(); + }); } } processBase64Text(base64Text){ - try { + return new Promise((resolve) => { + this._setPasteBtnToProcessing(); let decodedText = decodeURIComponent(escape(window.atob(base64Text))); Events.fire('activate-paste-mode', {files: [], text: decodedText}); - } catch (e) { - setTimeout(_ => Events.fire('notify-user', 'Content incorrect.'), 500); - } finally { - this.clearBrowserHistory(); - this.hide(); - } + resolve(); + }); } - async processClipboard() { - this.$pasteBtn.pointerEvents = "none"; - this.$pasteBtn.innerText = "Processing..."; - try { - const base64zip = await navigator.clipboard.readText(); - let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n); - while (n--) { - u8arr[n] = bstr.charCodeAt(n); - } - - const zipBlob = new File([u8arr], 'archive.zip'); - - let files = []; - const zipEntries = await zipper.getEntries(zipBlob); - for (let i = 0; i < zipEntries.length; i++) { - let fileBlob = await zipper.getData(zipEntries[i]); - files.push(new File([fileBlob], zipEntries[i].filename)); - } - Events.fire('activate-paste-mode', {files: files, text: ""}) - } catch (e) { - Events.fire('notify-user', 'Clipboard content is incorrect.') - } finally { - this.clearBrowserHistory(); - this.hide(); + async processBase64Zip(base64zip) { + this._setPasteBtnToProcessing(); + let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); } + + const zipBlob = new File([u8arr], 'archive.zip'); + + let files = []; + const zipEntries = await zipper.getEntries(zipBlob); + for (let i = 0; i < zipEntries.length; i++) { + let fileBlob = await zipper.getData(zipEntries[i]); + files.push(new File([fileBlob], zipEntries[i].filename)); + } + Events.fire('activate-paste-mode', {files: files, text: ""}); } clearBrowserHistory() { window.history.replaceState({}, "Rewrite URL", '/'); } + + hide() { + this.clearBrowserHistory(); + super.hide(); + } } class Toast extends Dialog { diff --git a/public_included_ws_fallback/styles.css b/public_included_ws_fallback/styles.css index 8d3c5e0..9df5852 100644 --- a/public_included_ws_fallback/styles.css +++ b/public_included_ws_fallback/styles.css @@ -614,21 +614,21 @@ x-dialog .row-reverse { margin-bottom: 25px; } -#base64ZipPasteBtn { +#base64PasteBtn { width: 100%; height: 40vh; border: solid 12px #438cff; } -#base64ZipDialog button { +#base64PasteDialog button { margin: auto; border-radius: 8px; } -#base64ZipDialog button[close] { +#base64PasteDialog button[close] { margin-top: 20px; } -#base64ZipDialog button[close]:before { +#base64PasteDialog button[close]:before { border-radius: 8px; } From 8f0e465b8e6250737407705bceda5fa5da6e7fa8 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Tue, 21 Feb 2023 23:44:41 +0100 Subject: [PATCH 2/4] pairdrop-cli: change domain via flag, move bash file to separate folder and add console logs to ui.js --- pairdrop | 123 --------------- pairdrop-cli/pairdrop | 181 ++++++++++++++++++++++ public/scripts/ui.js | 8 +- public_included_ws_fallback/scripts/ui.js | 8 +- 4 files changed, 195 insertions(+), 125 deletions(-) delete mode 100644 pairdrop create mode 100644 pairdrop-cli/pairdrop diff --git a/pairdrop b/pairdrop deleted file mode 100644 index baebfd8..0000000 --- a/pairdrop +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash -set -e - -############################################################ -# Help # -############################################################ -help() -{ - # Display Help - echo "Send files or text via PairDrop via commandline." - echo - echo -e "To send files:\t$(basename "$0") file" - echo -e "To send text:\t$(basename "$0") -t \"text\"" -} - -openPairDrop() -{ - # openPairDrop - url=$domain - if [[ -n $params ]];then - url=$url"?"$params - fi - if [[ -n $hash ]];then - url=$url"#"$hash - fi - echo "PairDrop is opening in a browser." - - if [[ $OS == "Windows" ]];then - start "$url" - elif [[ $OS == "Mac" ]];then - open "$url" - else - xdg-open "$url" - 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 -} - -############################################################ -############################################################ -# Main program # -############################################################ -############################################################ -domain="https://pairdrop.net/" -domain="https://192.168.2.90:8443/" -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" ] | [[ $# -gt 2 ]] && help && exit - -while getopts "ht:*" option; do - case $option in - t) # Send text - 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;; - h | ?) # display help and exit - help - exit;; - esac -done - -# Send file(s) -# display help and exit if 2 arguments are given or if file does not exist -[[ $# -eq 2 ]] || [[ ! -a $1 ]] && help && exit -params="base64zip=hash" -if [[ -d $1 ]]; then - zipPath="${1::-1}_pairdrop_temp.zip" - [[ -a "$zipPath" ]] && echo "Cannot overwrite $zipPath. Please remove first." && exit - zip -r -q -b /tmp/ "$zipPath" "$1" - hash=$(zip -q -b /tmp/ - "$zipPath" | base64 -w 0) - rm "$zipPath" -else - hash=$(zip -q -b /tmp/ - "$1" | base64 -w 0) -fi - -if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then - params="base64zip=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 diff --git a/pairdrop-cli/pairdrop b/pairdrop-cli/pairdrop new file mode 100644 index 0000000..1d577dd --- /dev/null +++ b/pairdrop-cli/pairdrop @@ -0,0 +1,181 @@ +#!/bin/bash +set -e + +############################################################ +# Help # +############################################################ +help() +{ + # Display Help + echo "Send files or text with PairDrop via commandline." + echo "Current domain: ${DOMAIN}" + echo + echo "Usage:" + echo -e "Send files:\t\t$(basename "$0") file/directory" + echo -e "Send text:\t\t$(basename "$0") -t \"text\"" + echo -e "Specify domain:\t$(basename "$0") -d \"https://pairdrop.net/\"" +} + +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" + 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}" > "${SCRIPTPATH}/.pairdrop-cli-config" + echo -e "Domain is now set to:\n$1" +} + +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 +} + +sendFiles() +{ + params="base64zip=hash" + if [[ $1 == */ ]]; then + path="${1::-1}" + else + path=$1 + fi + zipPath="${path}_pairdrop.zip" + zipPath=${zipPath// /_} + + [[ -a "$zipPath" ]] && echo "Cannot overwrite $zipPath. Please remove first." && exit + + if [[ -d $path ]]; then + zipPathTemp="temp_${zipPath}" + [[ -a "$zipPathTemp" ]] && echo "Cannot overwrite $zipPathTemp. Please remove first." && exit + echo "Processing directory..." + + # Create zip files temporarily to send directory + zip -q -b /tmp/ -r "$zipPath" "$path" + zip -q -b /tmp/ "$zipPathTemp" "$zipPath" + + hash=$(base64 -w 0 "$zipPathTemp") + + # remove temporary temp file + rm "$zipPathTemp" + else + echo "Processing file..." + + # Create zip file temporarily to send file + zip -q -b /tmp/ "$zipPath" "$path" + + hash=$(base64 -w 0 "$zipPath") + fi + + # remove temporary temp file + rm "$zipPath" + + if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then + params="base64zip=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 +} + +############################################################ +############################################################ +# Main program # +############################################################ +############################################################ +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +[ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] && + specifyDomain "https://pairdrop.net/" && + [ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] && + 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" + +[ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] || export "$(grep -v '^#' "${SCRIPTPATH}/.pairdrop-cli-config" | 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" ] | [[ $# -gt 2 ]] && help && exit + +while getopts "d:ht:*" option; do + case $option in + d) # specify domain + specifyDomain "$2" + exit;; + t) # Send text + sendText + exit;; + h | ?) # display help and exit + help + exit;; + esac +done + +# Send file(s) +# display help and exit if 2 arguments are given or if file does not exist +[[ $# -eq 2 ]] || [[ ! -a $1 ]] && help && exit +sendFiles "$1" +exit diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 1d38868..f829f32 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -1102,6 +1102,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64Hash) .catch(_ => { Events.fire('notify-user', 'Text content is incorrect.'); + console.log("Text content incorrect.") }).finally(_ => { this.hide(); }); @@ -1111,6 +1112,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64Text) .catch(_ => { Events.fire('notify-user', 'Text content is incorrect.'); + console.log("Text content incorrect.") }).finally(_ => { this.hide(); }); @@ -1123,6 +1125,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Zip(base64Hash) .catch(_ => { Events.fire('notify-user', 'File content is incorrect.'); + console.log("File content incorrect.") }).finally(_ => { this.hide(); }); @@ -1141,7 +1144,8 @@ class Base64ZipDialog extends Dialog { async processClipboard(type) { if (!navigator.clipboard.readText) { - Events.fire('notify-user', 'This feature is not available on your device.'); + Events.fire('notify-user', 'This feature is not available on your browser.'); + console.log("navigator.clipboard.readText() is not available on your browser.") this.hide(); return; } @@ -1156,6 +1160,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64) .catch(_ => { Events.fire('notify-user', 'Clipboard content is incorrect.'); + console.log("Clipboard content is incorrect.") }).finally(_ => { this.hide(); }); @@ -1163,6 +1168,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Zip(base64) .catch(_ => { Events.fire('notify-user', 'Clipboard content is incorrect.'); + console.log("Clipboard content is incorrect.") }).finally(_ => { this.hide(); }); diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index e7dd79d..796230f 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -1103,6 +1103,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64Hash) .catch(_ => { Events.fire('notify-user', 'Text content is incorrect.'); + console.log("Text content incorrect.") }).finally(_ => { this.hide(); }); @@ -1112,6 +1113,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64Text) .catch(_ => { Events.fire('notify-user', 'Text content is incorrect.'); + console.log("Text content incorrect.") }).finally(_ => { this.hide(); }); @@ -1124,6 +1126,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Zip(base64Hash) .catch(_ => { Events.fire('notify-user', 'File content is incorrect.'); + console.log("File content incorrect.") }).finally(_ => { this.hide(); }); @@ -1142,7 +1145,8 @@ class Base64ZipDialog extends Dialog { async processClipboard(type) { if (!navigator.clipboard.readText) { - Events.fire('notify-user', 'This feature is not available on your device.'); + Events.fire('notify-user', 'This feature is not available on your browser.'); + console.log("navigator.clipboard.readText() is not available on your browser.") this.hide(); return; } @@ -1157,6 +1161,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Text(base64) .catch(_ => { Events.fire('notify-user', 'Clipboard content is incorrect.'); + console.log("Clipboard content is incorrect.") }).finally(_ => { this.hide(); }); @@ -1164,6 +1169,7 @@ class Base64ZipDialog extends Dialog { this.processBase64Zip(base64) .catch(_ => { Events.fire('notify-user', 'Clipboard content is incorrect.'); + console.log("Clipboard content is incorrect.") }).finally(_ => { this.hide(); }); From 3cb4e6d476adc1b6e9fb48dbe847dbe88d6a115a Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Wed, 22 Feb 2023 02:22:33 +0100 Subject: [PATCH 3/4] pairdrop-cli now working on windows via `bash pairdrop` or git bash --- pairdrop-cli/pairdrop | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/pairdrop-cli/pairdrop b/pairdrop-cli/pairdrop index 1d577dd..5a36d2e 100644 --- a/pairdrop-cli/pairdrop +++ b/pairdrop-cli/pairdrop @@ -7,13 +7,15 @@ set -e help() { # Display Help - echo "Send files or text with PairDrop via commandline." + 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") file/directory" echo -e "Send text:\t\t$(basename "$0") -t \"text\"" - echo -e "Specify domain:\t$(basename "$0") -d \"https://pairdrop.net/\"" + echo -e "Specify domain:\t\t$(basename "$0") -d \"https://pairdrop.net/\"" + echo -e "Show this help text:\t$(basename "$0") (-h|--help)" } openPairDrop() @@ -57,8 +59,8 @@ setOs() specifyDomain() { [[ ! $1 = http* ]] || [[ ! $1 = */ ]] && echo "Incorrect format. Specify domain like https://pairdrop.net/" && exit - echo "DOMAIN=${1}" > "${SCRIPTPATH}/.pairdrop-cli-config" - echo -e "Domain is now set to:\n$1" + echo "DOMAIN=${1}" > "$CONFIGPATH" + echo -e "Domain is now set to:\n$1\n" } sendText() @@ -143,12 +145,27 @@ sendFiles() ############################################################ SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -[ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] && - specifyDomain "https://pairdrop.net/" && - [ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] && - 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" +pushd . > '/dev/null'; +SCRIPTPATH="${BASH_SOURCE[0]:-$0}"; -[ ! -f "${SCRIPTPATH}/.pairdrop-cli-config" ] || export "$(grep -v '^#' "${SCRIPTPATH}/.pairdrop-cli-config" | xargs)" +while [ -h "$SCRIPTPATH" ]; +do + cd "$( dirname -- "$SCRIPTPATH"; )"; + SCRIPTPATH="$( readlink -f -- "$SCRIPTPATH"; )"; +done + +cd "$( dirname -- "$SCRIPTPATH"; )" > '/dev/null'; +SCRIPTPATH="$( pwd; )"; +popd > '/dev/null'; + +CONFIGPATH="${SCRIPTPATH}/.pairdrop-cli-config" + +[ ! -f "$CONFIGPATH" ] && + specifyDomain "https://pairdrop.net/" && + [ ! -f "$CONFIGPATH" ] && + 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" + +[ ! -f "$CONFIGPATH" ] || export "$(grep -v '^#' "$CONFIGPATH" | xargs)" setOs ############################################################ @@ -157,8 +174,9 @@ setOs # 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" ] | [[ $# -gt 2 ]] && help && exit +[ "$1" == "--help" ] || [[ $# -gt 2 ]] && help && exit while getopts "d:ht:*" option; do case $option in @@ -177,5 +195,5 @@ done # Send file(s) # display help and exit if 2 arguments are given or if file does not exist [[ $# -eq 2 ]] || [[ ! -a $1 ]] && help && exit + sendFiles "$1" -exit From f8d49754d2ce4545b66f2568ce31f05a9c735cbe Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Wed, 22 Feb 2023 02:22:51 +0100 Subject: [PATCH 4/4] added pairdrop-cli to documentation --- README.md | 10 +++++----- docs/faq.md | 14 +++++++++----- docs/how-to.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 61a5407..9d2930f 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,11 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop) * On iOS and Android the devices share menu is opened instead of downloading the files * Multiple files are transferred at once with an overall progress indicator -### Share Files Directly From Share / Context Menu -* [Share files directly from context menu on Windows](/docs/how-to.md#share-files-directly-from-context-menu-on-windows) -* [Share directly from share menu on iOS](/docs/how-to.md#share-directly-from-share-menu-on-ios) -* [Share directly from share menu on Android](/docs/how-to.md#share-directly-from-share-menu-on-android) - +### Send Files or Text Directly From Share Menu, Context Menu or CLI +* [Send files directly from context menu on Windows](/docs/how-to.md#send-files-directly-from-context-menu-on-windows) +* [Send directly from share menu on iOS](/docs/how-to.md#send-directly-from-share-menu-on-ios) +* [Send directly from share menu on Android](/docs/how-to.md#send-directly-from-share-menu-on-android) +* [Send directly via command-line interface](/docs/how-to.md#send-directly-via-command-line-interface) ### Other changes * [Paste Mode](https://github.com/RobinLinus/snapdrop/pull/534) diff --git a/docs/faq.md b/docs/faq.md index 2385a47..e19770e 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -33,12 +33,16 @@ iOS Shortcuts to the win: I created a simple iOS shortcut that takes your photos and saves them to your gallery: https://routinehub.co/shortcut/13988/ -### Is it possible to share files directly from the context / share menu? -Yes it finally is! -* [Share files directly from context menu on Windows](/docs/how-to.md#share-files-directly-from-context-menu-on-windows) -* [Share directly from share menu on iOS](/docs/how-to.md#share-directly-from-share-menu-on-ios) -* [Share directly from share menu on Android](/docs/how-to.md#share-directly-from-share-menu-on-android) +### Is it possible to send files or text directly from the context or share menu? +Yes, it finally is! +* [Send files directly from context menu on Windows](/docs/how-to.md#send-files-directly-from-context-menu-on-windows) +* [Send directly from share menu on iOS](/docs/how-to.md#send-directly-from-share-menu-on-ios) +* [Send directly from share menu on Android](/docs/how-to.md#send-directly-from-share-menu-on-android) +### Is it possible to send files or text directly via CLI? +Yes, it is! + +* [Send directly from command-line interface](/docs/how-to.md#send-directly-via-command-line-interface) ### What about the connection? Is it a P2P-connection directly from device to device or is there any third-party-server? It uses a P2P connection if WebRTC is supported by the browser. WebRTC needs a Signaling Server, but it is only used to establish a connection and is not involved in the file transfer. diff --git a/docs/how-to.md b/docs/how-to.md index 18c0fbd..621ce5a 100644 --- a/docs/how-to.md +++ b/docs/how-to.md @@ -1,5 +1,5 @@ # How-To -## Share files directly from context menu on Windows +## Send files directly from context menu on Windows ### Registering to open files with PairDrop The [File Handling API](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files) is implemented @@ -25,17 +25,58 @@ Outstandingly, it is also possible to send multiple files to PairDrop via the co [//]: # (Todo: add screenshots) -## Share directly from share menu on iOS +## Send directly from share menu on iOS I created an iOS shortcut to send images, files, folder, URLs or text directly from the share-menu https://routinehub.co/shortcut/13990/ [//]: # (Todo: add doku with screenshots) -## Share directly from share menu on Android +## Send directly from share menu on Android The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented but not yet tested. When the PWA is installed, it should register itself to the share-menu of the device automatically. -Please test this feature and create an issue if it does not work. +This feature is still under development. Please test this feature and create an issue if it does not work. + +## Send directly via command-line interface +Send files or text with PairDrop via command-line interface. + +This opens PairDrop in the default browser where you can choose the receiver. + +### Usage +```bash +$ pairdrop -h +Current domain: https://pairdrop.net/ + +Usage: +Open PairDrop: pairdrop +Send files: pairdrop file/directory +Send text: pairdrop -t "text" +Specify domain: pairdrop -d "https://pairdrop.net/" +Show this help text: pairdrop (-h|--help) +``` + +On Windows Command Prompt you need to use bash: `bash pairdrop -h` + + +### Setup +Download the bash file: [pairdrop-cli/pairdrop](/pairdrop-cli/pairdrop). + +#### Linux +1. Put file in a preferred folder e.g. `/usr/local/bin` +2. Make sure the bash file is executable. Otherwise, use `chmod +x pairdrop` +3. Add absolute path of the folder to PATH variable to make `pairdrop` available globally by executing + `export PATH=$PATH:/opt/pairdrop-cli` + +#### Mac +1. add bash file to `/usr/local/bin` + +#### Windows +1. Put file in a preferred folder e.g. `C:\Users\Public\pairdrop-cli` +2. Search for and open `Edit environment variables for your account` +3. Click `Environment Variables...` +4. Under *System Variables* select `Path` and click *Edit...* +5. Click *New*, insert the preferred folder (`C:\Users\Public\pairdrop-cli`), click *OK* until all windows are closed +6. Reopen Command prompt window [< Back](/README.md)