all repos — dotfiles @ dd2db8ae42d358267a4fcdc13fa096d431d0d08d

linux dotfiles

bin/scripts/pd

#!/usr/bin/env python3
#
# -------------------------------------------------------------
# Journal Keeping Script [aka Personal Diary (pd)]
# Author : Prithu Goswami <prithugoswami524@gmail.com>
# -------------------------------------------------------------
# Prerequisites : make sure these programs are available on the
#                 machine:
#                 'rclone'
#                 'vim'
#                 'gpg' (v2.X)
#
# This is a very messy script and is just a quick hack and is not
# a very super secure way of keeping a journal but I hope to improve on
# it with time. It uses symmetric AES-128 encryption.
#
# This script opens a temp file in a vim buffer and after wrting
# and quiting from vim, it appends the contents to the file
# 'pd_path' that is in the 'pd_dir' in the $HOME dir of the machine,
# with a date and timestamp. It then encrypts the file using gpg
# and uploads it to the cloud storage.
#
# NOTE : 'rclone' should be configured
#        with a remote first. run 'rlone config'.
#        The remote should have directory with the encrypted 'pd' file
#        Path to remote directory should be stored in 'rclone_dir'
#
# I use 'rclone' to sync the journal to my dropbox
# the script first fetches any changes done to the pd file,
# decryptis it using 'gpg' using the passhprase-file as '$HOME/.pdkey'
# and then appends the entry, encrypts it and uploads it.
# 
# VARIABLES
#
# 'key_path' : passphrase for AES encrypted file
#              default alogrithm used by gpg v2.2 is AES-128 according
#              to the man page
# 'pd_dir' : The directory where the pd file is stored
# 'pd_path' : The path to the pd file. This is just "pd_dir+'pd'"
# 'rclone_dir' ; The direrctory of the pd file on the 'remote'
#                in this case the 'remote' is 'drop:'
#
# NOTE :
# Make sure there already exists a symmetrically encrypted AES file
# in 'pd_path' and also in 'rclone_dir'
# 
# To symmetrically encrypt an empty file run:
# `touch empty ; gpg2 --passphrase <secret> --batch -o pd -c empty`
# this should create a symmetrically encrypted AES file named 'pd' with
# the passphrase <secret>
# Now, save this file ('pd') in the 'pd_dir' and also upload it to the
# remote dir using rclone.
# This is not the most secure thing in the world I don't even intend it
# to be. So anyways, the <secret> has to be saved as .pdkey in your home
# directory or elsewhere ( if you're saving it elsewhere, then change
# the 'key_path' variable accordingly


import os
from datetime import datetime as dt
import tempfile
import subprocess
import shlex
import sys
import hashlib


## VARIABLES ##
# Change these accordingly
env_home = os.environ['HOME']
tmp_pd_path = '/tmp/pd'
key_path = env_home + '/.pdkey'
pd_dir = env_home + '/dropbox/pd'
pd_path = pd_dir + '/pd'
rclone_dir = 'drop:/pd'


if os.path.exists(tmp_pd_path):
    os.remove(tmp_pd_path)


ping_cmd = "ping -c 1 8.8.8.8"
ping_args = shlex.split(ping_cmd)
p = subprocess.Popen(ping_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if p.returncode:
    print("Network Unreachable...exiting now\nWrite a better script that can "
           "cache the entry locally you lazy ass")
    exit(p.returncode)


def save_entry(entry):
    entry_filename = dt.now().strftime('%s') + ".pdentry"
    entry_file_path = env_home + "/" + entry_filename
    print("Saving the entry to {}".format(entry_file_path))
    with open(entry_file_path, 'w', encoding='utf8') as entry_file:
        entry_file.write(entry)


def fetch_and_decrypt():
    """
    Fetches the cloud copy first (sync) and then decrypts 
    it to tmp_pd_path
    """
    print("Fetching changes...")
    if not os.system('rclone sync {}/ {}/'.format(rclone_dir, pd_dir)):
        print("Done")
        decrypt_cmd = ('gpg -o {} -d {}'.format(tmp_pd_path, pd_path))
        args = shlex.split(decrypt_cmd)
        p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                             universal_newlines=True)
        out, err = p.communicate()
        if p.returncode: # if gpg exits with a non-zero return code
            print("Error while decrypting :\n" + err)
            return(p.returncode)
    else:
        print("Something went wrong")
        return(1)


def encrypt_and_push():
    """
    Encrypts the file at tmp_pd_path and then syncs to the cloud.
    i.e replace the copy in the cloud with the local one.
    """
    if os.path.exists(pd_path):
        os.remove(pd_path)
    encrypt_cmd = ('gpg -o {} -c {}'.format( pd_path, tmp_pd_path))
    args = shlex.split(encrypt_cmd)
    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                         universal_newlines=True)
    out, err = p.communicate()
    if p.returncode:
        print("Error while encrypting :\n" + err)
        return(p.returncode)

    os.remove(tmp_pd_path)
    print("Pushing changes...")
    if not os.system('rclone sync {}/ {}/'.format(pd_dir, rclone_dir)):
        print("Done")
    else:
        print("Something went wrong")
        return(1)



# PD edit argument
# with the 'edit' argument passed to the script it will fetch the latest pd
# version from the cloud and let you edit and then push it back

if len(sys.argv) > 1:
    if sys.argv[1] == 'edit':
        if os.path.exists(tmp_pd_path):
            os.remove(tmp_pd_path)
        fetch_and_decrypt()

        with open(tmp_pd_path, 'r') as pdtemp:
            hash_before_edit = (hashlib.sha1(pdtemp.read().encode('utf-8'))
                                .hexdigest())

        os.system("vim {}".format(tmp_pd_path))

        with open(tmp_pd_path, 'r') as pdtemp:
            hash_after_edit = (hashlib.sha1(pdtemp.read().encode('utf-8'))
                                .hexdigest())

        if hash_before_edit == hash_after_edit:
            print("Nothing was changed. Exiting...")
            exit()
        encrypt_and_push()
        exit()



entry = None
with tempfile.NamedTemporaryFile(suffix='.pdentry') as temp:
    command = "vim {}".format(temp.name)
    os.system(command)
    entry = open(temp.name, 'r').read()
    if len(entry.strip()) == 0:
        print("Nothing was entered...")
        exit()

if fetch_and_decrypt(): # if there was any error while fetching and decrypting
    save_entry(entry)
    exit(1)

with open(tmp_pd_path, 'a', encoding='utf8') as fp:
    now = dt.now()
    date_string = now.strftime('%a, %d %b %Y')
    time_string = now.strftime('%I:%M %p')
    date_and_time = '[' + date_string + ' | ' + time_string + ']'

    fp.write('\n\n\n===============================\n' + date_and_time + '\n')
    fp.write(entry)

if encrypt_and_push():
    save_entry(entry)
    exit(1)