2
\$\begingroup\$

I wrote a simple program to encode a local text file with password and decode it back. There is one text file and it is either enocoded with password or is in plain text.

I had a version in python for this, but nim compiles to an executable, so I gave it a try.

I'm not perfectly happy with command line argument handling, as well as with some repeated code in case statement, but maybe there are other things to improve too.

import os
import system
import strutils
import terminal 

import xxtea

const FILENAME = "passwords.txt"
const USAGE = """Encrypt and decrypt a file using a password.

Usage: 
    vault open [file]
    vault close [file]

Default file:
    passwords.txt    

WARNING: this program may overwrite or delete file content without 
a possibilty of recovery. Passwords cannot be recovered. Information 
can be destroyed by repeated attempts of encryption/decryption. Use 
at you own risk. 
"""

proc getpass(message: string = "Password: "): string =
   return terminal.readPasswordFromStdin(message)

proc getpass2(): string =
    let key = getpass()
    let key2 = getpass("Repeat password: ") 
    if key == key2:
        return key    
    else: 
        quitWithMessage("Passwords do not match")

proc write(content: string): void =
    writeFile(filepath(), content)

proc notify(message: string): void =
    echo message, " ", filepath()

# cli args helpers

proc filepath(): string =
    try:
       return paramStr(2)
    except IndexError: 
       return FILENAME

proc command(): string = 
    try:
       return paramStr(1).toLowerAscii()
    except IndexError: 
       quitWithMessage(USAGE)

# flow control 

proc quitWithMessage(message: string): void =
    echo message
    quit(1)

# main

if command() notin ["open", "close", "help"]:
    quitWithMessage(USAGE)   

let existingContent = readFile(filepath())

case command()    
    of "open":        
        let key = getpass()
        let newContent = xxtea.decrypt(existingContent, key)
        write(newContent)
        notify("Decoded")
    of "close":
        let key = getpass2()
        let newContent = xxtea.encrypt(existingContent, key)
        write(newContent)
        notify("Encoded")
    of "help": 
        quitWithMessage(USAGE)

EDIT: I noticed nim has try... except statement and used it with paramStr().

\$\endgroup\$
0
\$\begingroup\$

An improvement in case statement:

if command() notin ["open", "close"]:
    quitWithMessage(USAGE)   

let existingContent = readFile(filepath())
case command()    
    of "open":        
        let key = getpass()
        notify("Decoding")
        let newContent = xxtea.decrypt(existingContent, key)
    of "close":
        let key = getpass2()
        notify("Encoding")
        let newContent = xxtea.encrypt(existingContent, key)
write(newContent)
notify("Finished processing")

I wish I knew how to declare and assign f = xxtea.decrypt and f = xxtea.encrypt and call f in nim - would have same even more repeated code.

Also documentation is not immediately clear about a dictionary in nim, even though some light here. A dictionary can be used to encode notify() messages by command names.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.