Skip to content

Commit

Permalink
Fixed and enhanced
Browse files Browse the repository at this point in the history
- encryptName now checks for illegal chars
- encrypt command now honors -f switch
- decryptFile now handles a long name correctly
- mkdir can operate on real file system with switch -R
- mv command does not terminate shell anymore in case of errors
- (de-/en-)cryptFile behavior is unified and enhanced in multi-sources operations
- new -F switch instructs encrypt/decrypt to preserve full path specified on the command line (default: only base name is used)
  • Loading branch information
maxpat78 committed Oct 29, 2024
1 parent be9b2c6 commit 6bbf687
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 30 deletions.
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,23 @@ make the specified vault's directory the current one in the pycryptomator
internal shell

```
decrypt [-m] [-f] <virtual_pathname_source1...> <real_pathname_destination>
decrypt [-fmF] <virtual_pathname_source1...> <real_pathname_destination>
decrypt <virtual_pathname_source> -
```
decrypt one or more files and/or directories to the specified destination in the
real file system. Option `-f` forces to overwrite existing files, `-m` moves
(i.e. deletes) the source files after decryption. With `-` as destination, a
file is decrypted and printed to standard output.

`encrypt [-m] <real_pathname_source1...> <virtual_pathname_destination>`
encrypt one or more files and/or directories to the specified destination. If
the destination is a directory, it must exist. `-m` moves (i.e. deletes) the
source files after encryption.
real file system.
`-f` forces to overwrite existing files, `-m` moves (i.e. deletes) the source
files after decryption, `-F` replicates the full command line path of source
in destination (by default only filenames are copied).
With `-` as destination, a file is decrypted and printed to standard output.

`encrypt [-fmF] <real_pathname_source1...> <virtual_pathname_destination>`
encrypt one or more files and/or directories to the specified destination.
If multiple sources are specified, the destination directory will be created
if not existent.
`-f` forces to overwrite existing files, `-m` moves (i.e. deletes) the source
files after encryption, `-F` replicates the full command line path of source
in destination (by default only filenames are copied).

`ln <target> <link>`
make a symbolic link to a target file or directory in the vault.
Expand All @@ -109,9 +114,9 @@ write time, name, symbolic link target).
`-s` sorts results by one or more criteria: `N`ame, `S`ize, `D`ate, `E`xtension
(a.k.a. file type), `-` sorts in reverse order and `!` puts directories first.

`mkdir <dir1> [...<dirN>]`
`mkdir [-R] <dir1> [...<dirN>]`
make one or more directories or directory trees (i.e. intermediate directories
get created) in the vault.
get created) in the vault or in the real file system if `-R` is specified.

`mv <source> [<source2>...<sourceN>] <destination>`
rename or move files and directories. If more files or directories are specified,
Expand Down
2 changes: 1 addition & 1 deletion pycryptomator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
COPYRIGHT = '''Copyright (C)2024, by maxpat78.'''
__version__ = '1.10'
__version__ = '1.11'
__all__ = ["Vault", "init_vault", "backupDirIds"]
from .cryptomator import *
61 changes: 44 additions & 17 deletions pycryptomator/cmshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ def do_decrypt(p, arg):
if move: argl.remove('-m')
force = '-f' in argl
if force: argl.remove('-f')
fulltree = '-F' in argl
if fulltree: argl.remove('-F')
if not argl or argl[0] == '-h' or len(argl) < 2:
print('use: decrypt [-m] [-f] <virtual_pathname_source1...> <real_pathname_destination>')
print('use: decrypt [-fmF] <virtual_pathname_source1...> <real_pathname_destination>')
print('use: decrypt <virtual_pathname_source> -')
return
try:
Expand All @@ -110,40 +112,57 @@ def do_decrypt(p, arg):
else:
dest = argl[-1]
if len(argl) > 2:
if not os.path.isdir(dest):
print('Destination directory %s does not exist!' % dest)
if os.path.exists(dest) and not os.path.isdir(dest):
print('Destination %s exists and is not a directory!' % dest)
return
dest = CMShell._join(dest, it)
print(dest)
# else it will be created
if fulltree:
dest = CMShell._join(dest, it)
else:
dest = CMShell._join(dest, basename(it))
print(dest)
p.vault.decryptFile(p._prep_cd(it), dest, force, move)
if argl[-1] == '-': print()
except:
print(sys.exception())
#~ import traceback; print(traceback.format_exc())

def do_encrypt(p, arg):
'Encrypt files or directories into the vault, eventually moving them'
argl = split(arg)
move = '-m' in argl
if move: argl.remove('-m')
force = '-f' in argl
if force: argl.remove('-f')
fulltree = '-F' in argl
if fulltree: argl.remove('-F')
if not argl or argl[0] == '-h' or len(argl) < 2:
print('use: encrypt [-m] <real_pathname_source1...> <virtual_pathname_destination>')
print('use: encrypt [-Ffm] <real_pathname_source1...> <virtual_pathname_destination>')
return
try:
for it in argl[:-1]:
dest = p._prep_cd(argl[-1])
if isdir(it):
p.vault.encryptDir(it, p._prep_cd(argl[-1]), move=move)
p.vault.encryptDir(it, dest, force, move)
else:
dest = p._prep_cd(argl[-1])
x = p.vault.getInfo(dest)
# In many-to-one, one must exist as dir, or not exist
if len(argl) > 2:
x = p.vault.getInfo(dest)
if not x.isDir:
print('Destination directory %s does not exist!' % dest)
if x.exists and not x.isDir:
print('Destination %s exists and is not a directory!' % dest)
return
dest = CMShell._join(dest, it)
print(dest)
p.vault.encryptFile(it, dest, move=move)
if not x.exists: # dir will be created
x.isDir = 1
if x.isDir:
if fulltree:
dest = CMShell._join(dest, it)
else:
dest = CMShell._join(dest, basename(it))
print(dest)
p.vault.encryptFile(it, dest, force, move)
except:
print(sys.exception())
#~ import traceback; print(traceback.format_exc())

def do_ls(p, arg):
'List files and directories'
Expand Down Expand Up @@ -190,12 +209,17 @@ def do_ln(p, arg):
def do_mkdir(p, arg):
'Make a directory or directory tree'
argl = split(arg)
realfs = '-R' in argl
if realfs: argl.remove('-R')
if not argl or argl[0] == '-h':
print('use: mkdir <dir1> [...<dirN>]')
print('use: mkdir [-R] <dir1> [...<dirN>]')
return
for it in argl:
try:
p.vault.mkdir(p._prep_cd(it))
if realfs:
os.makedirs(it)
else:
p.vault.mkdir(p._prep_cd(it))
except:
print(sys.exception())

Expand All @@ -206,7 +230,10 @@ def do_mv(p, arg):
print('please use: mv <source> [<source2>...<sourceN>] <destination>')
return
for it in argl[:-1]:
p.vault.mv(p._prep_cd(it), p._prep_cd(argl[-1]))
try:
p.vault.mv(p._prep_cd(it), p._prep_cd(argl[-1]))
except:
print(sys.exception())

def do_rm(p, arg):
'Remove files and directories'
Expand Down
20 changes: 19 additions & 1 deletion pycryptomator/cryptomator.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ def hashDirId(p, dirId):

def encryptName(p, dirId, name):
"Encrypt a name contained in a given directory"
i = check_name(name)
if i: raise BaseException('''Illegal character '%s' in "%s"''' % (chr(name[i-1]), name.decode()))
dirIdE = aes_siv_encrypt(p.pk, p.hk, name, dirId)
# concatenated 128-bit digest and encrypted name
return base64.urlsafe_b64encode(dirIdE) + b'.c9r'
Expand Down Expand Up @@ -275,6 +277,9 @@ def encryptFile(p, src, virtualpath, force=False, move=False):
if not exists(src):
raise BaseException('Source file does not exist: '+src)
f = open(src, 'rb')
x = p.getInfo(virtualpath)
if x.exists and not force:
raise BaseException('destination file "%s" exists and won\'t get overwritten!'%virtualpath)
if not basename(virtualpath).endswith('dirid.c9r'):
rp = p.create(virtualpath)
else:
Expand Down Expand Up @@ -351,7 +356,7 @@ def decryptFile(p, virtualpath, dest, force=False, move=False):
info = p.getInfo(virtualpath)
while info.pointsTo:
info = p.getInfo(info.pointsTo)
rp = info.realPathName
rp = info.contentsC9
f = open(rp, 'rb')
if hasattr(dest, 'write'): # if it's file
out = dest
Expand Down Expand Up @@ -952,3 +957,16 @@ def calc_rel_path(base, child):
n -= 1
relpath += '/'.join(base_parts[i:])
return relpath

def check_name(name):
if os.name == 'nt':
illegal_chars = b'\x00<>:"/\\|?*'
else:
illegal_chars = b'\x00/'
i = 0
while i < len(name):
c = name[i]
i += 1
if c in illegal_chars: return i
if os.name == 'nt' and c in b' .' and i+1 == len(name): return i
return 0

0 comments on commit 6bbf687

Please sign in to comment.