Skip to content

Commit 4ea89b7

Browse files
fix(send-to/windows): reliable, windowless upload + options + context menu
Root cause of "token used but nothing uploaded": a SendTo shortcut to powershell.exe did not forward the dropped files to the script ($args was empty). Now a tiny launch.vbs receives the files reliably and starts the uploader HIDDEN (no PowerShell window flash). - send.ps1: writes send.log for diagnosis; notification is now optional. - installer: interactive options (notification on/off; Send-to entry; "Drop on OpenCoperLock" / "Multi-Drop on OpenCoperLock" right-click entries). Registry writes use the .NET API to avoid the literal "*" all-files key being treated as a wildcard. - new scripts/windows-context-menu-manager.{ps1,cmd}: enable/disable any right-click entry (classic verbs via LegacyDisable, shell extensions via the Blocked list); fully reversible, nothing deleted. - docs + README updated; new download button for the menu manager.
1 parent bb686ab commit 4ea89b7

7 files changed

Lines changed: 275 additions & 47 deletions

File tree

apps/web/src/app/docs/page.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ curl -H "Authorization: Bearer ocl_…" -F "file=@backup.tgz" \\
389389
{ h: L('Envoyer vers OpenCoperLock (clic droit)', 'Send to OpenCoperLock (right-click)') },
390390
{
391391
p: L(
392-
'Ajoute une entrée « Envoyer vers → OpenCoperLock » au clic droit de votre explorateur de fichiers (Windows et Linux). Les fichiers sélectionnés sont envoyés en un clic dans un espace « ComputerShared » de votre Drive, via WebDAV. L’installeur mémorise votre serveur et votre jeton d’API.',
393-
'Adds a “Send to → OpenCoperLock” entry to your file manager’s right-click menu (Windows and Linux). Selected files upload in one click to a “ComputerShared” space in your Drive, over WebDAV. The installer remembers your server and API token.',
392+
'Ajoute au clic droit de votre explorateur (Windows et Linux) de quoi envoyer les fichiers sélectionnés en un clic dans un espace « ComputerShared » de votre Drive, via WebDAV. Sous Windows, l’installeur propose « Envoyer vers → OpenCoperLock » (envoie tout d’un coup) et/ou des entrées « Drop / Multi-Drop on OpenCoperLock ». Tout s’exécute sans fenêtre PowerShell, avec une petite notification en fin de transfert (désactivable).',
393+
'Adds right-click uploads to your file manager (Windows and Linux): selected files go in one click to a “ComputerShared” space in your Drive, over WebDAV. On Windows the installer offers “Send to → OpenCoperLock” (uploads all at once) and/or “Drop / Multi-Drop on OpenCoperLock” entries. It runs with no PowerShell window and shows a small notification when done (optional).',
394394
),
395395
},
396396
{ note: L('Créez d’abord un jeton d’API non restreint : Compte → Jetons d’API.', 'First create an unrestricted API token: Account → API tokens.') },
@@ -426,6 +426,19 @@ curl -H "Authorization: Bearer ocl_…" -F "file=@backup.tgz" \\
426426
],
427427
},
428428
{ note: L('Un prompt UAC apparaît uniquement pour la partie qui règle le service WebClient. Le mappage du lecteur reste en session utilisateur.', 'A UAC prompt appears only for the part that configures the WebClient service. The drive mapping stays in your user session.') },
429+
{ h: L('Ranger le menu clic droit (Windows)', 'Tidy the right-click menu (Windows)') },
430+
{
431+
p: L(
432+
'Les applications ajoutent sans cesse des entrées au menu contextuel de l’Explorateur. Ce petit outil liste toutes les entrées (verbes classiques et extensions shell) et permet d’activer/désactiver celles qui ne servent pas. Rien n’est supprimé — la désactivation est réversible.',
433+
'Apps keep piling entries into Explorer’s context menu. This small tool lists every entry (classic verbs and shell extensions) and lets you enable/disable the ones you don’t use. Nothing is deleted — disabling is reversible.',
434+
),
435+
},
436+
{
437+
btns: [
438+
{ label: L('Télécharger l’outil (.cmd)', 'Download the tool (.cmd)'), href: 'https://github.com/softpython2884/OpenCoperLock/raw/main/scripts/windows-context-menu-manager.cmd' },
439+
{ label: L('Voir le script (.ps1)', 'View the script (.ps1)'), href: 'https://github.com/softpython2884/OpenCoperLock/blob/main/scripts/windows-context-menu-manager.ps1' },
440+
],
441+
},
429442
],
430443
},
431444
{

scripts/send-to/README.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,29 @@ new server code — just your existing `ocl_…` API token.
2020
```
2121
- **Or** double-click **`windows/install-windows.cmd`** from a checkout.
2222
23-
Paste your WebDAV URL (e.g. `https://copper.forgenet.fr/api/dav`) and the token when asked.
24-
3. Done — right-click any file(s) → **Send to → OpenCoperLock**.
25-
26-
The installer copies `send.ps1` + the icon into `%LOCALAPPDATA%\OpenCoperLock`, stores your token
27-
**DPAPI-encrypted** (readable only by your Windows user, never leaves the machine), pre-creates the
28-
`ComputerShared` space, and drops the shortcut into your *Send To* folder (`shell:sendto`).
29-
Re-run the installer any time to change the URL/token. To uninstall, delete the shortcut from
30-
`shell:sendto` and the `%LOCALAPPDATA%\OpenCoperLock` folder.
23+
Paste your WebDAV URL (e.g. `https://copper.forgenet.fr/api/dav`) and the token when asked, then
24+
pick your options (notification on/off; *Send to* menu; *Drop / Multi-Drop* right-click entries).
25+
3. Done. Depending on your choices, right-click any file(s) →
26+
- **Send to → OpenCoperLock** — uploads **all** selected files in one go, or
27+
- **Drop on OpenCoperLock** / **Multi-Drop on OpenCoperLock** — top-level right-click entries.
28+
29+
Everything runs **hidden** (a tiny `launch.vbs` starts the uploader with no PowerShell window) and
30+
shows a small tray notification when done. The installer stores your token **DPAPI-encrypted**
31+
(readable only by your Windows user, never leaves the machine), pre-creates the `ComputerShared`
32+
space, and writes a `%LOCALAPPDATA%\OpenCoperLock\send.log` you can check if an upload misbehaves.
33+
Re-run the installer any time to change settings. To uninstall: re-run and answer *no* to the
34+
integrations (or delete the `shell:sendto\OpenCoperLock.lnk`, the `HKCU:\Software\Classes\*\shell\
35+
OpenCoperLock.*` keys, and the `%LOCALAPPDATA%\OpenCoperLock` folder).
3136
3237
> Uses `curl.exe`, which ships with Windows 10 1803+ and Windows 11.
3338
39+
### Bonus: tidy your right-click menu
40+
41+
Apps love to pile entries into the Explorer context menu. `scripts/windows-context-menu-manager.cmd`
42+
(double-click; it elevates) lists every classic verb **and** shell-extension entry and lets you
43+
turn any of them on/off. Nothing is deleted — disabling just sets a reversible flag, so you can
44+
re-enable anything later.
45+
3446
## Linux
3547
3648
1. Get an unrestricted API token (as above).
Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
<#
2-
OpenCoperLock — install the "Send to > OpenCoperLock" integration (Windows).
2+
OpenCoperLock — install the right-click "send to OpenCoperLock" integration (Windows).
33
44
Per-user, NO administrator rights needed. It:
5-
1. copies the uploader (send.ps1) and the brand icon into %LOCALAPPDATA%\OpenCoperLock,
6-
2. asks for your WebDAV URL + API token and stores them there (the token is DPAPI-encrypted for
7-
your Windows user — unreadable by other users, and never leaves your machine),
5+
1. installs the uploader (send.ps1), a windowless launcher (launch.vbs) and the brand icon into
6+
%LOCALAPPDATA%\OpenCoperLock,
7+
2. asks for your WebDAV URL + API token (the token is DPAPI-encrypted for your Windows user —
8+
unreadable by others, never leaves your machine) and your preferences,
89
3. pre-creates the "ComputerShared" space in your Drive,
9-
4. adds an "OpenCoperLock" entry (with the logo) to Explorer's right-click "Send to" menu.
10+
4. adds the integration points you chose:
11+
• Explorer's "Send to > OpenCoperLock" (uploads ALL selected files in one go), and/or
12+
• right-click "Drop on OpenCoperLock" / "Multi-Drop on OpenCoperLock" entries.
1013
11-
After this, right-click any file(s) -> Send to -> OpenCoperLock uploads them to ComputerShared.
14+
Uploads land in the "ComputerShared" space. Everything runs hidden (no PowerShell window flash)
15+
and shows a small tray notification when done (can be turned off).
1216
1317
Run: right-click install-windows.cmd, or
1418
powershell -ExecutionPolicy Bypass -File .\install-windows.ps1
1519
#>
1620
[CmdletBinding()]
1721
param(
1822
[string]$Base,
19-
[string]$Token
23+
[string]$Token,
24+
[ValidateSet('yes', 'no')] [string]$Notify,
25+
[ValidateSet('yes', 'no')] [string]$ContextMenu,
26+
[ValidateSet('yes', 'no')] [string]$SendTo
2027
)
2128
$ErrorActionPreference = 'Stop'
2229

23-
# Files come from the local checkout when run from one, or from the official repo when this script
24-
# is piped straight from the web (irm ... | iex), so a one-line install works either way.
30+
# Components come from the local checkout when run from one, or from the official repo when this
31+
# script is piped from the web (irm ... | iex), so a one-line install works either way.
2532
$RepoRaw = 'https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/send-to'
2633
$self = $PSCommandPath # null under irm|iex
2734
$sendToRoot = if ($self) { Split-Path -Parent (Split-Path -Parent $self) } else { $null }
@@ -33,6 +40,12 @@ function Get-Component($rel, $dst) {
3340
if ($local -and (Test-Path $local)) { Copy-Item $local $dst -Force }
3441
else { Invoke-WebRequest -UseBasicParsing "$RepoRaw/$rel" -OutFile $dst }
3542
}
43+
function Ask-YesNo($prompt, $default) {
44+
$d = if ($default) { 'Y/n' } else { 'y/N' }
45+
$a = (Read-Host "$prompt [$d]").Trim().ToLower()
46+
if ($a -eq '') { return $default }
47+
return ($a -eq 'y' -or $a -eq 'yes' -or $a -eq 'o' -or $a -eq 'oui')
48+
}
3649

3750
# --- gather settings -----------------------------------------------------------------------------
3851
$defaultBase = 'https://copper.forgenet.fr/api/dav'
@@ -50,32 +63,63 @@ if (-not $Token) {
5063
}
5164
if ([string]::IsNullOrWhiteSpace($Token)) { throw "No token provided." }
5265

66+
$wantNotify = if ($Notify) { $Notify -eq 'yes' } else { Ask-YesNo "Show a small notification after each send?" $true }
67+
$wantSendTo = if ($SendTo) { $SendTo -eq 'yes' } else { Ask-YesNo "Add 'OpenCoperLock' to the 'Send to' menu (best for many files at once)?" $true }
68+
$wantContext = if ($ContextMenu) { $ContextMenu -eq 'yes' } else { Ask-YesNo "Add 'Drop / Multi-Drop on OpenCoperLock' to the right-click menu?" $true }
69+
5370
# --- install files -------------------------------------------------------------------------------
54-
Get-Component 'windows/send.ps1' (Join-Path $dstDir 'send.ps1')
55-
Get-Component 'assets/opencoperlock.ico' (Join-Path $dstDir 'opencoperlock.ico')
71+
Get-Component 'windows/send.ps1' (Join-Path $dstDir 'send.ps1')
72+
Get-Component 'windows/launch.vbs' (Join-Path $dstDir 'launch.vbs')
73+
Get-Component 'assets/opencoperlock.ico' (Join-Path $dstDir 'opencoperlock.ico')
5674

5775
# DPAPI-encrypt the token (current-user scope) and write the config.
5876
$enc = ConvertTo-SecureString $Token -AsPlainText -Force | ConvertFrom-SecureString
59-
[pscustomobject]@{ base = $Base; token = $enc } | ConvertTo-Json | Set-Content (Join-Path $dstDir 'config.json') -Encoding UTF8
77+
[pscustomobject]@{ base = $Base; token = $enc; notify = $wantNotify } | ConvertTo-Json | Set-Content (Join-Path $dstDir 'config.json') -Encoding UTF8
6078

6179
# --- pre-create the ComputerShared space ---------------------------------------------------------
6280
try { & curl.exe -s -u "me:$Token" -X MKCOL "$Base/ComputerShared/" | Out-Null } catch { }
6381
$Token = $null
6482

65-
# --- Send To shortcut ----------------------------------------------------------------------------
66-
$sendTo = [Environment]::GetFolderPath('SendTo')
67-
$lnkPath = Join-Path $sendTo 'OpenCoperLock.lnk'
68-
$ps = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
69-
$ws = New-Object -ComObject WScript.Shell
70-
$sc = $ws.CreateShortcut($lnkPath)
71-
$sc.TargetPath = $ps
72-
$sc.Arguments = '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "' + (Join-Path $dstDir 'send.ps1') + '"'
73-
$sc.IconLocation = (Join-Path $dstDir 'opencoperlock.ico') + ',0'
74-
$sc.Description = 'Send to OpenCoperLock (ComputerShared)'
75-
$sc.WorkingDirectory = $dstDir
76-
$sc.Save()
83+
$vbs = Join-Path $dstDir 'launch.vbs'
84+
$ico = Join-Path $dstDir 'opencoperlock.ico'
85+
$wscript = Join-Path $env:SystemRoot 'System32\wscript.exe'
86+
87+
# --- "Send to" shortcut (targets wscript+launch.vbs so files pass reliably and no window shows) ---
88+
$sendToLnk = Join-Path ([Environment]::GetFolderPath('SendTo')) 'OpenCoperLock.lnk'
89+
if ($wantSendTo) {
90+
$ws = New-Object -ComObject WScript.Shell
91+
$sc = $ws.CreateShortcut($sendToLnk)
92+
$sc.TargetPath = $wscript
93+
$sc.Arguments = '"' + $vbs + '"'
94+
$sc.IconLocation = "$ico,0"
95+
$sc.Description = 'Send to OpenCoperLock (ComputerShared)'
96+
$sc.WorkingDirectory = $dstDir
97+
$sc.Save()
98+
} elseif (Test-Path $sendToLnk) { Remove-Item $sendToLnk -Force }
99+
100+
# --- right-click context-menu verbs (per-user HKCU, applies to all files) -------------------------
101+
# Use the .NET registry API: the all-files key is literally named "*", which the PowerShell registry
102+
# provider would treat as a wildcard.
103+
function Set-Verb($keyName, $label) {
104+
$k = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey("Software\Classes\*\shell\$keyName")
105+
$k.SetValue('MUIVerb', $label)
106+
$k.SetValue('Icon', $ico)
107+
$c = $k.CreateSubKey('command')
108+
$c.SetValue('', '"' + $wscript + '" "' + $vbs + '" "%1"') # '' = the key's (default) value
109+
$c.Close(); $k.Close()
110+
}
111+
if ($wantContext) {
112+
Set-Verb 'OpenCoperLock.Drop' 'Drop on OpenCoperLock'
113+
Set-Verb 'OpenCoperLock.MultiDrop' 'Multi-Drop on OpenCoperLock'
114+
} else {
115+
foreach ($k in 'OpenCoperLock.Drop', 'OpenCoperLock.MultiDrop') {
116+
try { [Microsoft.Win32.Registry]::CurrentUser.DeleteSubKeyTree("Software\Classes\*\shell\$k", $false) } catch { }
117+
}
118+
}
77119

78120
Write-Host ""
79-
Write-Host "Installed. Right-click any file(s) -> Send to -> OpenCoperLock." -ForegroundColor Green
80-
Write-Host "Uploads land in the 'ComputerShared' space of your Drive." -ForegroundColor DarkGray
81-
Write-Host "To change the URL/token later, just run this installer again." -ForegroundColor DarkGray
121+
Write-Host "Installed." -ForegroundColor Green
122+
if ($wantSendTo) { Write-Host " - Right-click file(s) -> Send to -> OpenCoperLock (uploads all selected at once)." -ForegroundColor DarkGray }
123+
if ($wantContext) { Write-Host " - Right-click file(s) -> Drop / Multi-Drop on OpenCoperLock." -ForegroundColor DarkGray }
124+
Write-Host " Uploads land in the 'ComputerShared' space. Log: $dstDir\send.log" -ForegroundColor DarkGray
125+
Write-Host " Re-run this installer any time to change settings." -ForegroundColor DarkGray

scripts/send-to/windows/launch.vbs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
' OpenCoperLock — windowless launcher.
2+
'
3+
' Invoked by the "Send to", "Drop on OpenCoperLock" and "Multi-Drop on OpenCoperLock" entries with
4+
' the selected file path(s) as arguments. It forwards them to send.ps1 and runs PowerShell HIDDEN
5+
' (window style 0) so no console flashes. VBScript via wscript.exe is the reliable way to receive
6+
' the selected files AND stay windowless.
7+
Option Explicit
8+
Dim sh, dst, cmd, i
9+
Set sh = CreateObject("WScript.Shell")
10+
dst = sh.ExpandEnvironmentStrings("%LOCALAPPDATA%") & "\OpenCoperLock"
11+
12+
cmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -File """ & dst & "\send.ps1"""
13+
For i = 0 To WScript.Arguments.Count - 1
14+
cmd = cmd & " """ & WScript.Arguments(i) & """"
15+
Next
16+
17+
' 0 = hidden window, False = don't wait (return immediately so Explorer isn't blocked).
18+
sh.Run cmd, 0, False

scripts/send-to/windows/send.ps1

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
<#
22
OpenCoperLock — "Send to" uploader (Windows).
33
4-
Invoked by the "Send to > OpenCoperLock" shortcut with one or more file paths as arguments.
5-
Uploads each selected file to the "ComputerShared" space (a top-level folder in your Drive) over
6-
WebDAV, then shows a tray notification. Configuration (WebDAV URL + API token) is written by
7-
install-windows.cmd into %LOCALAPPDATA%\OpenCoperLock; the token is DPAPI-encrypted for your
8-
Windows user, so this file holds no secret.
4+
Invoked (via launch.vbs, hidden) with one or more file paths as arguments. Uploads each selected
5+
file to the "ComputerShared" space (a top-level folder in your Drive) over WebDAV, shows a small
6+
tray notification, and appends a line to send.log so problems can be diagnosed. Configuration
7+
(WebDAV URL + DPAPI-encrypted token + options) lives in %LOCALAPPDATA%\OpenCoperLock\config.json.
98
#>
109
$ErrorActionPreference = 'Stop'
1110
$dir = Join-Path $env:LOCALAPPDATA 'OpenCoperLock'
1211
$cfgPath = Join-Path $dir 'config.json'
1312
$icon = Join-Path $dir 'opencoperlock.ico'
13+
$logPath = Join-Path $dir 'send.log'
14+
15+
function Log($msg) {
16+
try { Add-Content -LiteralPath $logPath -Value ("{0} {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg) } catch { }
17+
}
1418

1519
function Show-Toast($title, $text) {
20+
if ($script:notify -eq $false) { return }
1621
try {
1722
Add-Type -AssemblyName System.Windows.Forms
1823
Add-Type -AssemblyName System.Drawing
@@ -22,13 +27,16 @@ function Show-Toast($title, $text) {
2227
$ni.ShowBalloonTip(4000, $title, $text, [System.Windows.Forms.ToolTipIcon]::Info)
2328
Start-Sleep -Milliseconds 4200
2429
$ni.Dispose()
25-
} catch { }
30+
} catch { Log "toast failed: $($_.Exception.Message)" }
2631
}
2732

28-
if (-not (Test-Path $cfgPath)) { Show-Toast 'OpenCoperLock' 'Not configured yet — run the installer.'; exit 1 }
33+
Log "invoked with $($args.Count) arg(s): $($args -join ' | ')"
34+
35+
if (-not (Test-Path $cfgPath)) { Log 'no config'; Show-Toast 'OpenCoperLock' 'Not configured yet — run the installer.'; exit 1 }
2936

30-
$cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json
37+
$cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json
3138
$base = ($cfg.base).TrimEnd('/')
39+
$script:notify = -not ($cfg.PSObject.Properties.Name -contains 'notify' -and $cfg.notify -eq $false)
3240

3341
# Decrypt the DPAPI-protected token.
3442
$sec = ConvertTo-SecureString $cfg.token
@@ -37,7 +45,7 @@ $tok = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
3745
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
3846

3947
$files = @($args | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Leaf) })
40-
if ($files.Count -eq 0) { Show-Toast 'OpenCoperLock' 'No files to send (folders are skipped).'; exit 0 }
48+
if ($files.Count -eq 0) { Log 'no files after filter'; Show-Toast 'OpenCoperLock' 'No files to send (folders are skipped).'; exit 0 }
4149

4250
$cred = "me:$tok"
4351
# Make sure the space exists (a 405 "already there" is fine and ignored).
@@ -47,9 +55,11 @@ $ok = 0; $fail = 0
4755
foreach ($f in $files) {
4856
$name = [uri]::EscapeDataString((Split-Path -LiteralPath $f -Leaf))
4957
$code = & curl.exe -s -o NUL -w "%{http_code}" -u $cred -T $f "$base/ComputerShared/$name"
58+
Log "PUT $f -> $base/ComputerShared/$name = $code"
5059
if ($code -match '^2') { $ok++ } else { $fail++ }
5160
}
5261
$tok = $null; $cred = $null
5362

63+
Log "done: ok=$ok fail=$fail"
5464
if ($fail -eq 0) { Show-Toast 'OpenCoperLock' "Sent $ok file(s) to ComputerShared." }
55-
else { Show-Toast 'OpenCoperLock' "Sent $ok, $fail failed — check your token or connection." }
65+
else { Show-Toast 'OpenCoperLock' "Sent $ok, $fail failed — see send.log." }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@echo off
2+
REM ===========================================================================
3+
REM OpenCoperLock — Windows right-click menu manager (elevated).
4+
REM Double-click to tidy the Explorer right-click menu. Requests Administrator
5+
REM so it can also manage system-wide entries. Nothing is deleted — items are
6+
REM just disabled/enabled reversibly.
7+
REM ===========================================================================
8+
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
9+
"Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-NoExit','-File','\"%~dp0windows-context-menu-manager.ps1\"'"

0 commit comments

Comments
 (0)