theca 1.0.0

a simple, fully featured, command line note taking tool
Documentation

	#  _   _                    
	# | |_| |__   ___  ___ __ _
 	# | __|  _ \ / _ \/ __/ _` |
	# | |_| | | |  __/ (_| (_| |
	#  \__|_| |_|\___|\___\__,_|
	#

![example usage of theca]screenshots/main.png

a simple, fully featured, command line note taking tool written in
[*Rust*]http://www.rust-lang.org/. 

[![Build Status](https://travis-ci.org/pwoolcoc/theca.svg?branch=travis-build)](https://travis-ci.org/pwoolcoc/theca)

## Features

* Multiple profile support
* Plaintext or 256-bit AES encrypted profiles
* *JSON* profile format for easy scripting/integration
* Traditional and condensed printing modes
* Add/edit/delete notes
* Add/edit note body using command line arguments, `STDIN`, or using the editor set via `$VISUAL`
  or `$EDITOR`
* Transfer notes between profiles
* Search notes (title or body using keyword or regex pattern)

## Contents

- [Installation]#installation
	- [From crates.io]#binaries
	- [From source]#from-source
- [Usage]#usage
	- [First run]#first-run
	- [Adding notes]#adding-notes
	- [Editing notes]#editing-notes
	- [Deleting notes]#deleting-notes
	- [List all notes]#list-all-notes
	- [View a single note]#view-a-single-note
	- [Searching notes]#searching-notes
	- [A quick note on *statuses*]#a-quick-note-on-statuses
	- [Non-default profiles]#non-default-profiles
		- [Setting the default profile]#setting-the-default-profile
		- [Setting the default profile folder]#setting-the-default-profile-folder
		- [List all profiles]#list-all-profiles
		- [Transfer a note to another profile]#transfer-a-note-to-another-profile
		- [Import a note from another profile]#import-a-note-from-another-profile
		- [Encrypted profiles]#encrypted-profiles
			- [Encrypt a plaintext profile]#encrypt-a-plaintext-profile
			- [Decrypt a encrypted profile]#decrypt-a-encrypted-profile
			- [Changing the encryption key for an already encrypted profile]#changing-the-encryption-key-for-an-already-encrypted-profile
		- [Synchronizing profiles]#synchronizing-profiles
	- [JSON output mode]#json-output-mode
- [Tab completion]#tab-completion
- [man page]#man-page
- [Contributing]#contributing
	- [Bugs]#bugs
	- [TODO]#todo
- [Development]#development
	- [JSON profile format]#json-profile-format
	- [Cryptographic design]#cryptographic-design
		- [Basic Python implementation]#basic-python-implementation
	- [`tools/build.sh`]#buildsh
	- [`tools/theca_test_harness.py`]#theca_test_harnesspy
		- [Test suite file format]#test-suite-file-format
			- [Test formats]#test-formats
- [License]#license

## Installation

### From crates.io

`theca` is available from crates.io, so you can just do `cargo install theca`
to install the binary to your `$CARGO_HOME/bin` directory.

### From source

All that's needed to build theca is a copy of the `rustc` compiler and the `cargo` packaging tool which can
be downloaded directly from the [Rust website](http://www.rust-lang.org/install.html) or by running

	$ curl https://sh.rustup.rs -sSf | sh

to get the nightly `rustc` and `cargo` binaries, once those have finished building we can clone and build `theca`

	$ git clone https://github.com/pwoolcoc/theca.git
	...

	$ cd theca
	$ cargo build [--release]
	...

	$ sudo bash tools/build.sh install [--release, --man, --bash-complete, --zsh-complete]

The `cargo` flag `--release` enables `rustc` optimizations. F
The `cargo` flag `--release` enables `rustc` optimizations.or the `install` the flag `--man`
will additionally install the man page and `--bash-complete` and `--zsh-complete` will
additionally install the `bash` or `zsh` tab completion scripts. `cargo` will automatically
download and compile `theca`s dependencies for you.

## Usage

	$ theca --help
	theca - simple cli note taking tool

	Usage:
	    theca [options] new-profile [<name>]
	    theca [options] encrypt-profile [--new-key KEY]
	    theca [options] decrypt-profile
	    theca [options] info
	    theca [options] clear
	    theca [options]
	    theca [options] <id>
	    theca [options] search [--regex, --search-body] <pattern>
	    theca [options] transfer <id> to <name>
	    theca [options] import <id> from <name>
	    theca [options] add <title> [-s|-u] [-b BODY|-t|-]
	    theca [options] edit <id> [<title>] [-s|-u|-n] [-b BODY|-t|-]
	    theca [options] del <id>...

	Profiles:
	    -f PATH, --profile-folder PATH      Path to folder containing profile.json
	                                        files [default can be set with env var 
	                                        THECA_PROFILE_FOLDER].
	    -p PROFILE, --profile PROFILE       Specify non-default profile [default
	                                        can be set with env var 
	                                        THECA_DEFAULT_PROFILE].

	Printing format:
	    -c, --condensed                     Use the condensed printing format.
	    -j, --json                          Print list output as a JSON object.

	Note list formatting:
	    -l LIMIT, --limit LIMIT             Limit output to LIMIT notes
	                                        [default: 0].
	    -d, --datesort                      Sort notes by date.
	    -r, --reverse                       Reverse list.

	Input:
	    -y, --yes                           Silently agree to any [y/n] prompts.

	Statuses:
	    -n, --none                          No status. (note default)
	    -s, --started                       Started status.
	    -u, --urgent                        Urgent status.

	Body:
	    -b BODY, --body BODY                Set body of the note to BODY.
	    -t, --editor                        Drop to $EDITOR to set/edit note body.
	    -                                   Set body of the note from STDIN.

	Encryption:
	    -e, --encrypted                     Specifies using an encrypted profile.
	    -k KEY, --key KEY                   Encryption key to use for encryption/
	                                        decryption, a prompt will be
	                                        displayed if no key is provided.
	    --new-key KEY                       Specifies the encryption key for a
	                                        profile when using `encrypt-profile`,
	                                        a prompt will be displayed if no key
	                                        is provided.

	Search:
	    --search-body                       Search the body of notes instead of
	                                        the title.
	    --regex                             Set search pattern to regex (default
	                                        is keyword).

	Miscellaneous:
	    -h, --help                          Display this help and exit.
	    -v, --version                       Display the version of theca and exit.

### First run

![new default profile](screenshots/first_run.png)

`theca new-profile` will create the `~/.theca` folder as well as the default
note profile in `~/.theca/default.json`. If you would like to use a non-standard
profile folder you can use `--profile-folder PATH`.

### Adding notes

![adding a basic note](screenshots/add_simple_note.png)

`theca add <title>` will add a note to the default profile with no body or status.
These flags can be used to add a note with a status and/or a body

	Statuses:
	    -s, --started                       Started status.
	    -u, --urgent                        Urgent status.

	Body:
	    -b BODY, --body BODY                Set body of the note to BODY.
	    -t, --editor                        Drop to $EDITOR to set/edit note body.
	    -                                   Set body of the note from STDIN.

### Editing notes

![editing a notes status](screenshots/edit_notes.png)

`theca edit <id>` is used to edit the title, status, or body of a note.

	Statuses:
	    -n, --none                          No status. (note default)
	    -s, --started                       Started status.
	    -u, --urgent                        Urgent status.

	Body:
	    -b BODY, --body BODY                Set body of the note to BODY.
	    -t, --editor                        Drop to $EDITOR to set/edit note body.
	    -                                   Set body of the note from STDIN.

### Deleting notes

![deleting some notes](screenshots/delete_note.png)

`theca del <id>..` deletes one or more notes specified by space separated note ids.

### List all notes

![list all notes](screenshots/list_notes.png)

`theca` prints out all notes in the current profile, the following options can be
used to limit/sort the resulting list

	Printing format:
	    -c, --condensed                     Use the condensed printing format.
	    -j, --json                          Print list output as a JSON object.

	Note list formatting:
	    -l LIMIT, --limit LIMIT             Limit output to LIMIT notes.
	                                        [default: 0].
	    -d, --datesort                      Sort notes by date.
	    -r, --reverse                       Reverse list.

### View a single note

![view a note](screenshots/view_note.png)

![view a note using the short print style](screenshots/view_note_condensed.png)

`theca <id>` prints out a single note, including the status and body, the following
options can be used to alter the output style

	Printing format:
	    -c, --condensed                     Use the condensed printing format.
	    -j, --json                          Print list output as a JSON object.

### Searching notes

![searching notes](screenshots/search_notes.png)

Notes can be search using either keyword or regex matching against note titles or bodies
using `theca search`. `theca` doesn't support any kind of tagging (beyond the two basic
statuses) but you can implement this quite simply by just appending or prepending a *tag*
of sorts to the note title or body, e.g. `"(THECA) something about theca"`, and then 
do a keyword search for `"(THECA)"` to get all notes *tagged* as such.

	Search:
	    --search-body                       Search the body of notes instead of
	                                        the title.
	    --regex                             Set search pattern to regex (default
	                                        is keyword).

### A quick note on *statuses*

During initial development of `theca` I spent quite a bit of time trying to figure out
what statuses I should include (or if I should allow completely custom statuses) and after
playing with quite a few I ended up realising I only ever used three (well... two, if that).

* No status at all (`-n` or `--none`)
* `Started` (`-s` or `--started`)
* `Urgent` (`-u` or `--urgent`)

These flags can be used when adding notes, editing notes, searching notes, and listing events
to either specify the note status or filter lists by status.

### Non-default profiles

![new non default profile](screenshots/new_second_profile.png)

New named profiles can be created with the `theca new-profile <name>` command and will be
stored alongside `default.json` in either `~/.theca/` or in the folder specified by
`--profile-folder PATH`.

#### Setting the default profile

![setting the THECA_DEFAULT_PROFILE env var](screenshots/default_profile_env.png)

The default profile that `theca` loads (normalled `default`) can be changed by setting the
environment variable `THECA_DEFAULT_PROFILE`.

#### Setting the default profile folder

The default profile folder can also be set via a enviroment variable, `THECA_PROFILE_FOLDER`.

#### List all profiles

![list all profiles in the current profile folder](screenshots/list_all_profiles.png)

All profiles in the current profile folder can be view using `theca list-profiles`.

#### Transfer a note to another profile

![transfer a note](screenshots/transfer_note.png)

`theca transfer <id> to <name>` transfers a note from the current profile (in this case
`default`) to another profile.

#### Import a note from another profile

![import a note](screenshots/import_note.png)

`theca import <id> from <name>` transfers a note from the profile `<name>` to
the current profile (in this case `default`).

#### Encrypted profiles

![new encrypted profile](screenshots/new_encrypted_profile.png)

Using `--encrypted` tells theca that it should be dealing with encrypted profiles, so
using `theca --encrypted new-profile secrets` theca knows to create an encrypted profile
and will ask you for a key to encrypt the resulting `secrets.json`. If you'd like not to
be prompted you can specify it with the argument `--key KEY`.

`--encrypted` and `--key` can be used with all the other commands that read or write a
profile to specify that the profile you want to use will need to be encrypted/decrypted.

	Encryption:
	    -e, --encrypted                     Specifies using an encrypted profile.
	    -k KEY, --key KEY                   Encryption key to use for encryption/
	                                        decryption, a prompt will be
	                                        displayed if no key is provided.

##### Decrypt a encrypted profile

![decrypt a profile](screenshots/decrypt_profile.png)

You can decrypt an encrypted profile in place using `theca decrypt-profile`.

##### Encrypt a plaintext profile

![encrypt a profile](screenshots/encrypt_profile.png)

You can also encrypt a profile in place using `theca encrypt-profile [--new-key KEY]`, if `--new-key`
isn't used you will be prompted for the encryption key to use to encrypt the profile.

    --new-key KEY                       Specifies the encryption key for a
                                        profile when using `encrypt-profile`,
                                        a prompt will be displayed if no key
                                        is provided.

##### Changing the encryption key for an already encrypted profile

![change encryption key](screenshots/change_key.png)

You can also use `theca encrypt-profile --new-key KEY` to change the encryption key of an already encrypted profile which is pretty cool and avoids the user having to do `encrypted with old key -> plaintext 
-> encrypted with new key`!

#### Synchronizing profiles

If you use a synchronization tool like Dropbox, ownCloud, BitTorrent Sync, or even some obscure
`rsync` setup you can easily share your note profiles between machines by using
`--profile-folder` to specify a folder for your profiles that is synced and your sync'r should
do the rest for you. Since `theca` makes transactional*-ish* updates to the profile files it
should be perfectly safe, unless you concurrently edit a profile, *though* `theca` will *attempt*
to merge changes when this happens. You could even store a profle in a *git* repository if you
really wanted to.

### JSON output mode

![view list as json](screenshots/json_list.png)

![view note as json](screenshots/json_note.png)

You can view a single note or note list (using `theca` or `theca search`) to output the
result as either a JSON object or list of JSON objects by passing the `--json` or `-j` flag.
This works with the standard limit formatting arguments like `-r`, `-d`, and `-l LIMIT`.

	Printing format:
	    -j, --json                          Print list output as a JSON object.

	Note list formatting:
	    -l LIMIT, --limit LIMIT             Limit output to LIMIT notes
	                                        [default: 0].
	    -d, --datesort                      Sort notes by date.
	    -r, --reverse                       Reverse list.

## Tab completion

There are preliminary `bash` and `zsh` tab completion scripts in the `completion/` directory
which can be installed manually or by using the `--bash-complete` or `--zsh-complete` flags with
`sudo bash tools/build.sh install` when installing the `theca` binary or by default when using the binary `installer.sh`. They both need quite a bit of 
work but are still relatively usable for the time being.

## man page

![the man page](screenshots/man-page.png)

`theca` uses `md2man-roff` from [md2man](https://github.com/sunaku/md2man) to convert
`docs/THECA.1.md` to the roff format man page `docs/THECA.1`.

## Contributing

If you think I've left out some necessary feature feel free to open an issue or to fork the
project and work on a patch that introduces it.

I'm pretty sure there are quite a few places where memory optimizations could be made, as well
as various other performance and (extensive) design improvements.

Any and all pull requests will be considered and tremendously appreciated.

### Bugs

`theca` almost certainly contains bugs, I haven't had the time to write as many test cases as are really
necessary to fully cover the codebase. if you find one, please submit a issue explaining how to trigger
the bug, and if you're really awesome a test case that exposes it.

### TODO

* clean-ups/optimizations pretty much everywhere
* `Profile` and `Item` and assosiated functions should be moved out of `src/theca/lib.rs`
  to their own file
* `list-profiles` should be alphabetic
* `bash_complete.sh` could use a lot of improvement, `_theca` also, but less...
* `save_to_file` and `transfer_note` (and inherently the `import` logic) could use some
  work, specifically the profile changed stuff... <-- because of that we have pass pretty
  much all of the `Args` struct
* probably the bold/plain line printing could be done cleaner... (macro perhaps?)
* (long term) `remote` encrypted storage (some kind of super simple standalone REST API to hold
  encrypted profile blobs + client integrated into `theca` to retrieve them)

## Development

### JSON profile format

As described much more verbosely in `docs/schema.json`, this is what a note profile might look like

    {
        "encrypted": false,
        "notes": [
            {
                "id": 1,
                "title": "\\(◕ ◡ ◕\\)",
                "status": "",
                "body": "",
                "last_touched": "2015-01-22 15:01:39 -0800"
            },
            {
                "id": 3,
                "title": "(THECA) add super secret stuff",
                "status": "",
                "body": "",
                "last_touched": "2015-01-22 15:21:01 -0800"
            }
        ]
    }

### Cryptographic design

`theca` uses the AES CBC mode symmetric cipher (implementation provided by [*rust-crypto*](https://github.com/DaGenix/rust-crypto)) with a 256-bit key to encrypt/decrypt
profile files. The key is derived using *pbkdf2* (using the sha-256 *PRF*, again from *rust-crypto*) with 2056 rounds
salted with the sha256 hash of the password used for the key derivation (probably not the best idea).

#### Basic Python implementation

During development it can be quite useful to encrypt/decrypt profiles using a scripting
language like Python. A key can be derived quite quickly using `hashlib` and `passlib`

	from hashlib import sha256
	from passlib.utils.pbkdf2 import pbkdf2

	passphrase = "DEBUG"
	key = pbkdf2(
        bytes(passphrase.encode("utf-8")),
        sha256(bytes(passphrase.encode("utf-8"))).hexdigest().encode("utf-8"),
        2056,
        32,
        "hmac-sha256"
    )

and the ciphertext can be decrypted using the AES implementation from `pycrypto`

	from Crypto.Cipher import AES

	# the IV makes up the first 16 bytes of the ciphertext
	iv = ciphertext[0:16]
    decryptor = AES.new(key, AES.MODE_CBC, iv)
    plaintext = decryptor.decrypt(ciphertext[16:])

    # remove any padding from the end of the final block
    plaintext = plaintext[:-plaintext[-1]].decode("utf-8")

### `tools/build.sh`

`build.sh` is a pretty simple `bash` holdall in lieu of a `Makefile` (ew) that really exists
because I have a bad memory and forget some of the commands i'm supposed to remember. It will also
set the build version environment variable (`THECA_BUILD_VER`) which is used to set the verson `theca -v`.

Usage is pretty simple

	$ bash tools/build.sh
	Usage: build.sh {build|build-man|test|install|clean}

* `build` passes through any argument to `cargo build` so things like `--release` and
  `--verbose` work fine
* `build-man` requires the `md2man-roff` tool to convert the Markdown man page to the
  roff man page format
* `test` runs both all the Rust tests (`cargo test`) and all the Python harness tests
* `install` copies the binary to `/usr/local/bin`. It can also be used with `--man`,
  `--bash-complete`, or `--zsh-complete` to install the man page, `bash` completion script,
  or the `zsh` completion script manually.
* `clean` deletes the binary in `.`, the `target/` folder, and the man page in `docs/`
  if they exist

### `tools/theca_test_harness.py`

`theca_test_harness.py` is a *relatively* simple python3 test harness for the compiled `theca` binary.
It reads in JSON files which describe test cases and executes them, providing relatively simple
information like passed/failed/time taken.

The harness can preform three different output checks, against
 * the resulting profile file
 * the JSON output of view, list, and search commands
 * the text output of add, edit, delete commands, etc

The python script has a number of arguments that may or may not be helpful

	$ python3 tools/theca_test_harness.py -h
	usage: theca_test_harness.py [-h] [-tc THECA_COMMAND] [-tf TEST_FILE] [-pt]
	                             [-jt] [-tt]

	test harness for the theca cli binary.

	optional arguments:
	  -h, --help            show this help message and exit
	  -tc THECA_COMMAND, --theca-command THECA_COMMAND
	                        where is the theca binary
	  -tf TEST_FILE, --test-file TEST_FILE
	                        path to specific test file to run
	  -pt, --profile-tests  only run the profile output tests
	  -jt, --json-tests     only run the json output tests
	  -tt, --text-tests     only run the text output tests


#### Test suite file format

A JSON test suite file for `theca_test_harness.py` looks something like this

	{
	  "title": "GOOD DEFAULT TESTS",
	  "desc": "testing correct input with the default profile.",
	  "tests": [
	  	...
	  ]
	}

##### Test formats

* a profile result test looks something like this

		{
	      "name": "add note",
	      "cmds": [
	        ["new-profile"],
	        ["add", "this is the title"]
	      ],
	      "result_path": "default.json",
	      "result": {
	        "encrypted": false,
	        "notes": [
	          {
	            "id": 1,
	            "title": "this is the title",
	            "status": "",
	            "body": ""
	          }
	        ]
	      }
	    }

* a JSON output test looks something like this

		{
	      "name": "list",
	      "cmds": [
	        ["new-profile"],
	        ["add", "a title this is"],
	        ["add", "another title this is"],
	        ["-j"]
	      ],
	      "result_type": "json",
	      "results": [
	        null,
	        null,
	        null,
	        [
	          {
	            "id": 1,
	            "title": "a title this is",
	            "status": "",
	            "body": ""
	          },{
	            "id": 2,
	            "title": "another title this is",
	            "status": "",
	            "body": ""
	          }
	        ]
	      ]
	    }

* a text output test looks something like this

		{
	      "name": "new-profile",
	      "cmds": [
	        ["new-profile"]
	      ],
	      "result_type": "text",
	      "results": [
	        "creating profile 'default'\n"
	      ]
	    }

## Author

`theca` was originally written by roland shoemaker (<rolandshoemaker@gmail.com>),
and has since been taken up by Paul Woolcock (<paul@woolcock.us>).

## License

`theca` is licensed under the MIT license, the full text of which can be found at
<http://opensource.org/licenses/MIT> or in `LICENSE`.