From a5ce00ca99c87c2bbe9488366fb70192ae9e07e3 Mon Sep 17 00:00:00 2001
From: Nick <linickx@gmail.com>
Date: Fri, 2 Dec 2016 11:05:57 +0000
Subject: [PATCH] Public Upload

---
 .editorconfig                 |  14 +
 .gitignore                    |   3 +
 README.rst                    |  73 +++++
 docs/snsync_screenshot.gif    | Bin 0 -> 41791 bytes
 simplenote_sync/config.py     |  74 +++++
 simplenote_sync/db.py         | 311 ++++++++++++++++++++
 simplenote_sync/notes.py      | 179 ++++++++++++
 simplenote_sync/simplenote.py | 389 +++++++++++++++++++++++++
 simplenote_sync/snsync.py     | 519 ++++++++++++++++++++++++++++++++++
 simplenote_sync/version.py    |   1 +
 snsync                        |  10 +
 11 files changed, 1573 insertions(+)
 create mode 100644 .editorconfig
 create mode 100644 README.rst
 create mode 100644 docs/snsync_screenshot.gif
 create mode 100644 simplenote_sync/config.py
 create mode 100644 simplenote_sync/db.py
 create mode 100644 simplenote_sync/notes.py
 create mode 100644 simplenote_sync/simplenote.py
 create mode 100644 simplenote_sync/snsync.py
 create mode 100644 simplenote_sync/version.py
 create mode 100755 snsync

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..58c0808
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+# editorconfig.org
+root = true
+
+# Defaults
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+# Python
+[*.py]
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
index 72364f9..70def6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# Project specific stuff
+testing/
+
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..d145bf2
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,73 @@
+snsync, like rsync for Simplenote 
+##################################
+
+snsync is a kinda rsync implementation for Simplenote where your notes can be downloaded (& and uploaded) from plain text files.
+
+The primary use case is for periodic synchronisation by cron, with all the *useful* output going to a log file and the console output being *pretty* for when humans sync manually.
+
+The configuration file
+----------------------
+
+By default, you need `~/.snsync` but the command line options do allow to select another file, the minimal info needed is a username and password::
+
+    [snsync]
+    cfg_sn_username = me@here.com
+    cfg_sn_password = secret
+
+*IMPORTANT! Protect your .snsync file with the correct permissions and disk encryption*
+
+A few additional options are possible:
+
+* `cfg_nt_path = /Users/linickx/mynotes`  to change the default note path (`~/Simplenote`)
+* `cfg_log_path = /Users/Library/Logs/snsync.log` to change the default log path (which is typically within `cfg_nt_path`). Use the keyword `DISABLED` to enable console logging.
+* `cfg_log_level = debug` the default logging level is `info`, the brave can change this to `error`, ninja's can enable `debug`
+
+The command line options
+------------------------
+
+The following usage/options are available::
+
+    Usage: snsync [OPTIONS]
+
+    OPTIONS:
+     -h, --help         Help!
+     -d, --dry-run      Dry Run Mode (no changes made/saved)
+     -s, --silent       Silent Mode (no std output)
+     -c, --config=      Config file to read (default: ~/.snsync)
+     
+For example: just `snsync` on it's own should work, but something like this can be used for cron: `snsync -s --config=something.txt`
+
+File Deletions
+--------------
+
+snsync doesn't delete any files, you can check the source code yourself ;)
+
+When a file is marked for deletion on Simplenote, the local note (*text file*) equivalent is moved to a `.trash` directory. When a file is deleted locally the Simplenote equivalent is marked with Trash tag.
+
+File Conflicts
+--------------
+
+If your cron job is very sporadic it possible that a change could be made on the Simplenote server and locally, when this happens the local file is renamed, for example `hello world.txt` would become  `DUP_date_hello world.txt` (*where date is the date/time the file was moved*). Duplicates are then uploaded back into Simplenote for safe keeping.
+
+Local file names are based on the first line of the Simplenote "note". Filenames are generated on a first come, first served basis, for example if you create a Simplenote online with the first line "hello world" then `hello world.txt` will be created, if you create a 2nd note, with completely different contents but the first line is "hello world" then the 2nd file will be called `date_hello world.txt` (*where date is the date/time the file was created*)
+
+File Modifications
+------------------
+
+snsync works by maintaining a local sqlite database, typically `.snsycn.sqlite` inside your `cfg_nt_path`. The database maintains a copy of the Simplenote list and a meta table that links Simplenotes to text files.
+
+The script works by comparing the latest Simplenote list to the local cache, and then compares the last modified dates of local files; moves/adds/changes/deletions are then replicated by-directionally. The `--dry-run` option can be used to observe what is going to happen without making any changes.
+
+For those wondering what the log file strings like `agtzaW1wbZRiusssu5sIDAasdfuhas` are; that's the "key" used in the Simplenote cloud to store your note, the local meta database keeps track of those and associates a file name... the cloud don't need no file names dude! ;-)
+
+Large Note Databases
+--------------------
+
+The Simplenote API is rate limited, if your note database is large (like mine -> 1,200 notes) then the first full sync will take a long time (mine -> approx 15mins) you will also find a high number of `HTTP ERRORS` reported, just wait and re-run the script, missed notes will be downloaded.
+
+AoB
+---
+
+No warranty is offered, use this at your own risk; I use this for my personal production notes but I always keep backups. The recommended approach is to manually download all your notes for a backup, then use the `--dry-run` option to observe changes until you are happy.
+
+Credz, props and big-ups to https://github.com/insanum/sncli and https://github.com/mrtazz/Simplenote.py as without these opensource projects, snsync would not have got off the ground :)
\ No newline at end of file
diff --git a/docs/snsync_screenshot.gif b/docs/snsync_screenshot.gif
new file mode 100644
index 0000000000000000000000000000000000000000..c5b0f8f456e0d850928c168f808e7c0aa8bca087
GIT binary patch
literal 41791
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
QD328Io^mVyvw#2qJILntssI20

literal 0
HcmV?d00001

diff --git a/simplenote_sync/config.py b/simplenote_sync/config.py
new file mode 100644
index 0000000..3333b38
--- /dev/null
+++ b/simplenote_sync/config.py
@@ -0,0 +1,74 @@
+"""
+    Configuration settings for snsync
+"""
+# pylint: disable=W0702
+# pylint: disable=C0301
+
+import os
+import collections
+import configparser
+
+class Config:
+    """
+        Config Object
+    """
+
+    def __init__(self, custom_file=None):
+        """
+            Defult settings and the like.
+        """
+        self.home = os.path.abspath(os.path.expanduser('~'))
+        # Static Defaults
+        defaults = \
+        {
+            'cfg_sn_username'       : '',
+            'cfg_sn_password'       : '',
+            'cfg_nt_ext'       : 'txt',
+            'cfg_nt_path'       : os.path.join(self.home, 'Simplenote'),
+            'cfg_nt_trashpath'       : '.trash',
+            'cfg_nt_filenamelen'       : '60',
+            'cfg_log_level'       : 'info'
+        }
+
+        cp = configparser.SafeConfigParser(defaults)
+        if custom_file is not None:
+            self.configs_read = cp.read([custom_file])
+        else:
+            self.configs_read = cp.read([os.path.join(self.home, '.snsync')])
+
+        cfg_sec = 'snsync'
+
+        if not cp.has_section(cfg_sec):
+            cp.add_section(cfg_sec)
+
+        self.configs = collections.OrderedDict()
+        self.configs['sn_username'] = [cp.get(cfg_sec, 'cfg_sn_username', raw=True), 'Simplenote Username']
+        self.configs['sn_password'] = [cp.get(cfg_sec, 'cfg_sn_password', raw=True), 'Simplenote Password']
+        self.configs['cfg_nt_ext'] = [cp.get(cfg_sec, 'cfg_nt_ext'), 'Note file extension']
+        self.configs['cfg_nt_path'] = [cp.get(cfg_sec, 'cfg_nt_path'), 'Note storage path']
+        self.configs['cfg_nt_trashpath'] = [cp.get(cfg_sec, 'cfg_nt_trashpath'), 'Note Trash Bin Folder for deleted notes']
+        self.configs['cfg_nt_filenamelen'] = [cp.get(cfg_sec, 'cfg_nt_filenamelen'), 'Length of Filename']
+        self.configs['cfg_log_level'] = [cp.get(cfg_sec, 'cfg_log_level'), 'snsync log level']
+
+        # Dynamic Defaults
+        if cp.has_option(cfg_sec, 'cfg_db_path'):
+            self.configs['cfg_db_path'] = [cp.get(cfg_sec, 'cfg_db_path'), 'snsync database location']
+        else:
+            self.configs['cfg_db_path'] = [os.path.join(cp.get(cfg_sec, 'cfg_nt_path'), '.snsync.sqlite'), 'snsync database location']
+
+        if cp.has_option(cfg_sec, 'cfg_log_path'):
+            self.configs['cfg_log_path'] = [cp.get(cfg_sec, 'cfg_log_path'), 'snsync log location']
+        else:
+            self.configs['cfg_log_path'] = [os.path.join(cp.get(cfg_sec, 'cfg_nt_path'), '.snsync.log'), 'snsync log location']
+
+    def get_config(self, name):
+        """
+            Return a config setting
+        """
+        return self.configs[name][0]
+
+    def get_config_descr(self, name):
+        """
+            Return a config description (future use in docs)
+        """
+        return self.configs[name][1]
diff --git a/simplenote_sync/db.py b/simplenote_sync/db.py
new file mode 100644
index 0000000..dce9df2
--- /dev/null
+++ b/simplenote_sync/db.py
@@ -0,0 +1,311 @@
+"""
+    Database functions/settings for snsync
+"""
+import os
+import sys
+import sqlite3
+import json
+# pylint: disable=W0702
+# pylint: disable=C0301
+
+
+db_version = int("1") # Increment this with DB/Schema updates.
+
+class Database:
+    """
+        Main Database object
+    """
+
+    def __init__(self, config, logger):
+        """"
+            Initial Database Setup
+        """
+
+        filename = config.get_config('cfg_db_path')
+        self.log = logger
+
+        if not self.isSQLite3(filename):
+            self.log.warning("404 DB not found: %s", filename)
+            self.dbconn, self.db = self.connect(filename)
+            self.createdb_schmea_1()
+        else:
+            self.dbconn, self.db = self.connect(filename)
+
+            # Future proof, i.e. if the table structure changes.
+            version = self.get_schema_version()
+            if version == db_version:
+                self.log.debug("File Version: %s  Our Version: %s", version, db_version)
+            else:
+                self.log.critical("Database Version/Schemea Mismatch! - File Version: %s  Our Version: %s", version, db_version)
+                sys.exit(1)
+
+    def isSQLite3(self, filename):
+        """"
+            Check if a file is an sqlite3 file.
+            # http://stackoverflow.com/a/15355790
+        """
+
+        if not os.path.isfile(filename):
+            return False
+        if os.path.getsize(filename) < 100: # SQLite database file header is 100 bytes
+            return False
+
+        with open(filename, 'rb') as fd:
+            header = fd.read(100)
+
+        return header[:16] == b'SQLite format 3\x00'
+
+    def connect(self, filename):
+        """
+            Connection Setup
+        """
+        self.log.debug("Connecting DB: %s", filename)
+        conn = sqlite3.connect(filename)
+        c = conn.cursor()
+        return conn, c
+
+    def commit(self):
+        """
+            Commit to DB
+        """
+        self.dbconn.commit()
+
+    def disconnect(self):
+        """
+            Connection teardown
+        """
+        self.dbconn.commit()
+        self.dbconn.close()
+        self.log.debug("Disconnecting DB")
+
+    def get_schema_version(self):
+        """
+            Get the Schema Version
+            # http://stackoverflow.com/a/19332352
+        """
+        cursor = self.dbconn.execute('PRAGMA user_version')
+        return cursor.fetchone()[0]
+
+    def set_schema_version(self, version):
+        """
+            Set the Schema Version
+        """
+        self.dbconn.execute('PRAGMA user_version={:d}'.format(version))
+
+    def createdb_schmea_1(self):
+        """
+            Create a DB (Schema Version 1)
+        """
+        self.log.info("Creating new version 1 database")
+        version = int("1")
+
+        try:
+            self.dbconn.execute('CREATE TABLE simplenote (\
+                key TEXT PRIMARY KEY,\
+                createdate BLOB,\
+                deleted TEXT,\
+                minversion TEXT,\
+                modifydate BLOB,\
+                syncnum TEXT,\
+                systemtags TEXT,\
+                tags TEXT,\
+                version TEXT\
+                )')
+            self.dbconn.execute('CREATE TABLE notefile (\
+                key TEXT PRIMARY KEY,\
+                createdate TEXT,\
+                deleted TEXT,\
+                modifydate TEXT,\
+                filename TEXT\
+                )')
+            self.dbconn.execute('CREATE TABLE snsync (\
+                name TEXT PRIMARY KEY,\
+                value BLOB\
+                )')
+            self.set_schema_version(version)
+        except sqlite3.OperationalError:
+            self.log.error("Unabled to setup local database")
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            sys.exit(1)
+
+    def find_sn_by_key(self, key):
+        """
+            Find a simple note by key
+        """
+
+        self.log.debug("Looking for SN: %s", key)
+
+        try:
+            self.db.execute('SELECT * FROM simplenote WHERE key=?', (key,))
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        try:
+            key_row = self.db.fetchone()
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        if key_row == None:
+            self.log.debug("404 Not Found: %s", key)
+            return False
+        else:
+            note = {}
+            note['key'] = key_row[0]
+            note['createdate'] = key_row[1]
+            note['deleted'] = key_row[2]
+            note['minversion'] = key_row[3]
+            note['modifydate'] = key_row[4]
+            note['syncnum'] = key_row[5]
+            note['systemtags'] = key_row[6]
+            note['tags'] = key_row[7]
+            note['version'] = key_row[8]
+            self.log.debug("SIMPLENOTE: %s", note)
+            return note
+
+    def sn(self, note):
+        """
+            Insert a note to the DB from an existing Simplenote
+            - or replace (for updates)
+        """
+        try:
+            self.log.debug("Updating SN Database: %s", note)
+
+            self.db.execute('INSERT OR REPLACE INTO Simplenote \
+                (key, createdate, deleted, minversion, modifydate, syncnum, systemtags, tags, version) \
+                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', (\
+                note['key'],note['createdate'],note['deleted'],\
+                note['minversion'],note['modifydate'],note['syncnum'],\
+                json.dumps(note['systemtags']),json.dumps(note['tags']),note['version'],)\
+                )
+            return True
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            return False
+
+    def update_snsync(self, name, value):
+        """
+            Add or updated meta data in snsync table
+        """
+        try:
+            self.log.debug("Updating %s -> %s", name, value)
+            self.db.execute('INSERT OR REPLACE INTO snsync (name, value) VALUES (?,?)', \
+                (name, value,))
+            return True
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            return False
+
+    def get_snsync_meta(self, name):
+        """
+            Get snsync meta data
+        """
+
+        self.log.debug("Looking for Meta %s", name)
+
+        try:
+            self.db.execute('SELECT * FROM snsync WHERE name=?', (name,))
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        try:
+            row = self.db.fetchone()
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        if row == None:
+            self.log.debug("404 SN Not Found: %s", name)
+            return False
+        else:
+            value = row[1]
+            self.log.debug("Meta Value  %s", value)
+            return value
+
+    def nf(self, nf_meta):
+        """
+            Insert a note file meta to the local DB
+        """
+        try:
+            self.log.debug("Updating Note File DB: %s", nf_meta)
+
+            self.db.execute('INSERT OR REPLACE INTO notefile \
+                (key, createdate, deleted, modifydate, filename) \
+                VALUES (?, ?, ?, ?, ?)', (\
+                nf_meta['key'],nf_meta['createdate'],nf_meta['deleted'],\
+                nf_meta['modifydate'],nf_meta['filename'],)\
+                )
+            return True
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            return False
+
+    def del_nf(self, key):
+        """
+            Delete Notefile Meta - to "forget a file"
+        """
+        try:
+            self.log.debug("Deleting NF Meta for : %s", key)
+            self.db.execute("DELETE FROM notefile WHERE key=?", (key,))
+            return True
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            return False
+
+    def find_nf_by_key(self, key):
+        """
+            Find a note file by key
+        """
+
+        self.log.debug("Looking for NF: %s", key)
+
+        try:
+            self.db.execute('SELECT * FROM notefile WHERE key=?', (key,))
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        try:
+            key_row = self.db.fetchone()
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        if key_row == None:
+            self.log.debug("404 NF Not Found: %s", key)
+            return False
+        else:
+            note = {}
+            note['key'] = key_row[0]
+            note['createdate'] = key_row[1]
+            note['deleted'] = key_row[2]
+            note['modifydate'] = key_row[3]
+            note['filename'] = key_row[4]
+            self.log.debug("NOTEFILE: %s", note)
+            return note
+
+    def find_nf_by_name(self, filename):
+        """
+            Find a note file by name (filename)
+        """
+
+        self.log.debug("Looking for NF META: %s", filename)
+
+        try:
+            self.db.execute('SELECT * FROM notefile WHERE filename=?', (filename,))
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        try:
+            key_row = self.db.fetchone()
+        except sqlite3.OperationalError:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        if key_row == None:
+            self.log.debug("404 NF Meta Not Found: %s", filename)
+            return False
+        else:
+            note = {}
+            note['key'] = key_row[0]
+            note['createdate'] = key_row[1]
+            note['deleted'] = key_row[2]
+            note['modifydate'] = key_row[3]
+            note['filename'] = key_row[4]
+            self.log.debug("NOTEFILE: %s", note)
+            return note
diff --git a/simplenote_sync/notes.py b/simplenote_sync/notes.py
new file mode 100644
index 0000000..7b40904
--- /dev/null
+++ b/simplenote_sync/notes.py
@@ -0,0 +1,179 @@
+"""
+    Note File (local file) functions/settings for snsync
+"""
+# pylint: disable=W0702
+# pylint: disable=C0301
+
+import sys
+import os
+import string
+import hashlib
+import time
+import datetime
+import re
+
+class Note:
+    """
+        Main Note File (local file) object
+    """
+
+    def __init__(self, config, logger):
+        """"
+            Initial Notes Setup
+        """
+
+        self.log = logger
+        self.config = config
+
+        # create note dir if it does not exist - cfg_nt_path
+        if not os.path.exists(self.config.get_config('cfg_nt_path')):
+            try:
+                os.mkdir(self.config.get_config('cfg_nt_path'))
+                self.log.info("Creating directory %s", self.config.get_config('cfg_nt_path'))
+            except:
+                self.log.critical("Error creating directory %s", self.config.get_config('cfg_nt_path'))
+                self.log.debug("Exception: %s", sys.exc_info()[1])
+                sys.exit(1)
+
+        # Try to create Recycle Bin (Trash) - cfg_nt_trashpath
+        if not os.path.exists(self.config.get_config('cfg_nt_path') + "/" + self.config.get_config('cfg_nt_trashpath')):
+            try:
+                os.mkdir(self.config.get_config('cfg_nt_path') + "/" + self.config.get_config('cfg_nt_trashpath'))
+                self.log.info("Creating directory %s", self.config.get_config('cfg_nt_path') + "/" + self.config.get_config('cfg_nt_trashpath'))
+            except:
+                self.log.critical("Error creating directory %s/%s", self.config.get_config('cfg_nt_path'), self.config.get_config('cfg_nt_trashpath'))
+                self.log.debug("Exception: %s", sys.exc_info()[1])
+                sys.exit(1)
+
+    def new(self, note):
+        """
+            Create a new note file, returns filename
+        """
+        path = self.config.get_config('cfg_nt_path')
+        filename = self.get_filename(note['content'])
+        access_time = time.time()
+        filetime = datetime.datetime.now().strftime("%y%m%d-%H%M%S")
+
+        if filename:
+            if os.path.isfile(path + "/" + filename):
+                filename = filetime + "_" + filename  # Don't blast over files with same name, i.e. same first line.
+
+            try:
+                f = open(path + "/" + filename, 'w')
+                f.write(note['content'])
+                f.close()
+                self.log.info("Writing %s", filename)
+
+                os.utime(path + "/" + filename, (access_time, float(note['modifydate'])))
+
+                return filename
+            except:
+                self.log.error("Error writing note: %s", note['key'])
+                self.log.debug("Exception: %s", sys.exc_info()[1])
+        else:
+            self.log.error("Error generating filename for note: %s", note['key'])
+
+        return False
+
+    def get_filename(self, content):
+        """
+            Generate Safe Filename from Note Content
+        """
+        note_data = str.splitlines(content)
+        line_one = note_data[0]
+        file_ext = self.config.get_config('cfg_nt_ext')
+        filename_len = int(self.config.get_config('cfg_nt_filenamelen'))
+
+        # http://stackoverflow.com/a/295146
+        try:
+            safechars = string.ascii_letters + string.digits + " -_."
+            safename = ''.join(c for c in line_one if c in safechars)
+
+            if len(safename) >= filename_len: # truncate long names
+                safename = safename[:filename_len]
+
+            self.log.debug("Make Safe In: %s Out: %s", line_one, safename)
+            filename = safename.strip() + "." + file_ext
+            return filename
+        except:
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+            return False
+
+    def gen_meta(self, filename):
+        """
+            Generate notefile meta from filename - returns dict
+        """
+        nf_meta = {}
+        nf_meta['filename'] = filename
+        nf_meta['deleted'] = 0
+
+        # http://stackoverflow.com/a/5297483
+        nf_meta['key'] = hashlib.md5(str(filename).encode('utf-8')).hexdigest()
+        self.log.debug("Note File Meta Key: %s", nf_meta['key'])
+
+        path = self.config.get_config('cfg_nt_path')
+
+        # WARNING THIS IS PLATFORM SPECIFIC
+        nf_meta['createdate'] = os.stat(path + "/" + filename).st_birthtime
+        self.log.debug("Note File Meta Created: %s [%s]", nf_meta['createdate'], time.ctime(nf_meta['createdate']))
+
+        nf_meta['modifydate'] = os.stat(path + "/" + filename).st_mtime
+        self.log.debug("Note File Meta Modified: %s [%s]", nf_meta['modifydate'], time.ctime(nf_meta['modifydate']))
+
+        return nf_meta
+
+    def update(self, note, nf_meta):
+        """
+            Create a new note file, returns filename
+        """
+        path = self.config.get_config('cfg_nt_path')
+        filename = nf_meta['filename']
+        access_time = time.time()
+
+        try:
+            f = open(path + "/" + filename, 'w')
+            f.write(note['content'])
+            f.close()
+            self.log.info("Writing %s", filename)
+
+            os.utime(path + "/" + filename, (access_time, float(note['modifydate'])))
+
+            return True
+        except:
+            self.log.error("Error writing note: %s", note['key'])
+            self.log.debug("Exception: %s", sys.exc_info()[1])
+
+        return False
+
+    def open(self, filename):
+        """
+            Open a notefile, returns Dict: content & modifydate
+        """
+        notefile = {}
+        path = self.config.get_config('cfg_nt_path')
+
+        if os.path.isfile(path + "/" + filename):
+            try:
+                f = open(path + "/" + filename, 'r')
+                notefile['content'] = f.read()
+                f.close()
+            except:
+                self.log.error("Failed to OPEN/READ: %s", path + "/" + filename)
+                self.log.debug("Exception: %s", sys.exc_info()[1])
+                return False
+        else:
+            self.log.error("Notefile not found: %s", path + "/" + filename)
+            return False
+
+        notefile['modifydate'] = os.stat(path + "/" + filename).st_mtime
+        self.log.debug("Note File Modified: %s [%s]", notefile['modifydate'], time.ctime(notefile['modifydate']))
+
+        if re.match('darwin', sys.platform):
+            # WARNING THIS IS PLATFORM SPECIFIC
+            notefile['createdate'] = os.stat(path + "/" + filename).st_birthtime
+            self.log.debug("Note File Created: %s [%s]", notefile['createdate'], time.ctime(notefile['createdate']))
+        else:
+            notefile['createdate'] = notefile['modifydate']
+            self.log.debug("Using Modify Date for Birth/Create Date")
+
+        return notefile
diff --git a/simplenote_sync/simplenote.py b/simplenote_sync/simplenote.py
new file mode 100644
index 0000000..6e914b2
--- /dev/null
+++ b/simplenote_sync/simplenote.py
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+"""
+    simplenote.py
+    ~~~~~~~~~~~~~~
+
+    Python library for accessing the Simplenote API
+
+    :copyright: (c) 2011 by Daniel Schauenberg
+    :license: MIT, see LICENSE for more details.
+"""
+import sys
+if sys.version_info > (3, 0):
+    import urllib.request as urllib2
+    import urllib.error
+    from urllib.error import HTTPError
+    import urllib.parse as urllib
+else:
+    import urllib2
+    from urllib2 import HTTPError
+    import urllib
+
+
+import base64
+import time
+import datetime
+
+# crappy hack for inserting user-agent into Simplenote requests
+from .version import __version__ as snsync_version
+custom_user_agent = 'snsync/' + snsync_version + '(https://github.com/linickx/snsync)'
+
+try:
+    import json
+except ImportError:
+    try:
+        import simplejson as json
+    except ImportError:
+        # For Google AppEngine
+        from django.utils import simplejson as json
+
+AUTH_URL = 'https://app.simplenote.com/api/login'
+DATA_URL = 'https://app.simplenote.com/api2/data'
+INDX_URL = 'https://app.simplenote.com/api2/index?'
+NOTE_FETCH_LENGTH = 100
+
+class SimplenoteLoginFailed(Exception):
+    pass
+
+
+class Simplenote(object):
+    """ Class for interacting with the simplenote web service """
+
+    def __init__(self, username, password):
+        """ object constructor """
+        self.username = username
+        self.password = password
+        self.token = None
+        self.mark = "mark"
+
+    def authenticate(self, user, password):
+        """ Method to get simplenote auth token
+
+        Arguments:
+            - user (string):     simplenote email address
+            - password (string): simplenote password
+
+        Returns:
+            Simplenote API token as string
+
+        """
+        auth_params = "email={0}&password={1}".format(user, password)
+        try:
+            values = base64.b64encode(bytes(auth_params,'utf-8'))
+        except TypeError:
+            values = base64.encodestring(auth_params)
+
+        request = Request(AUTH_URL, values)
+        try:
+            res = urllib2.urlopen(request).read()
+            token = res
+        except HTTPError:
+            raise SimplenoteLoginFailed('Login to Simplenote API failed!')
+        except IOError: # no connection exception
+            token = None
+        return token
+
+    def get_token(self):
+        """ Method to retrieve an auth token.
+
+        The cached global token is looked up and returned if it exists. If it
+        is `None` a new one is requested and returned.
+
+        Returns:
+            Simplenote API token as string
+
+        """
+        if self.token == None:
+            self.token = self.authenticate(self.username, self.password)
+        try:
+            return str(self.token,'utf-8')
+        except TypeError:
+            return self.token
+
+
+
+    def get_note(self, noteid, version=None):
+        """ method to get a specific note
+
+        Arguments:
+            - noteid (string): ID of the note to get
+            - version (int): optional version of the note to get
+
+        Returns:
+            A tuple `(note, status)`
+
+            - note (dict): note object
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+        # request note
+        params_version = ""
+        if version is not None:
+            params_version = '/' + str(version)
+         
+        params = '/{0}{1}?auth={2}&email={3}'.format(noteid, params_version, self.get_token(), self.username)
+        request = Request(DATA_URL+params)
+        try:
+            response = urllib2.urlopen(request)
+        except HTTPError as e:
+            return e, -1
+        except IOError as e:
+            return e, -1
+        note = json.loads(response.read().decode('utf-8'))
+        note = self.__encode(note)
+        return note, 0
+
+    def update_note(self, note):
+        """ function to update a specific note object, if the note object does not
+        have a "key" field, a new note is created
+
+        Arguments
+            - note (dict): note object to update
+
+        Returns:
+            A tuple `(note, status)`
+
+            - note (dict): note object
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+        note = self.__decode(note)
+        # determine whether to create a new note or update an existing one
+        if "key" in note:
+            # set modification timestamp if not set by client
+            if 'modifydate' not in note:
+                note["modifydate"] = time.time()
+
+            url = '{0}/{1}?auth={2}&email={3}'.format(DATA_URL, note["key"],
+                                                      self.get_token(), self.username)
+        else:
+            url = '{0}?auth={1}&email={2}'.format(DATA_URL, self.get_token(), self.username)
+        request = Request(url, urllib.quote(json.dumps(note)).encode('utf-8'))
+        response = ""
+        try:
+            response = urllib2.urlopen(request)
+        except IOError as e:
+            return e, -1
+        note = json.loads(response.read().decode('utf-8'))
+        note = self.__encode(note)
+        return note, 0
+
+    def add_note(self, note):
+        """wrapper function to add a note
+
+        The function can be passed the note as a dict with the `content`
+        property set, which is then directly send to the web service for
+        creation. Alternatively, only the body as string can also be passed. In
+        this case the parameter is used as `content` for the new note.
+
+        Arguments:
+            - note (dict or string): the note to add
+
+        Returns:
+            A tuple `(note, status)`
+
+            - note (dict): the newly created note
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+
+        if type(note) == str:
+            return self.update_note({"content": note})
+        elif (type(note) == dict) and "content" in note:
+            return self.update_note(note)
+        else:
+            return "No string or valid note.", -1
+
+    def get_note_list(self, since=None, tags=[]):
+        """ function to get the note list
+
+        The function can be passed optional arguments to limit the
+        date range of the list returned and/or limit the list to notes
+        containing a certain tag. If omitted a list of all notes
+        is returned.
+
+        Arguments:
+            - since=YYYY-MM-DD string: only return notes modified
+              since this date
+            - tags=[] list of tags as string: return notes that have
+              at least one of these tags
+
+        Returns:
+            A tuple `(notes, status)`
+
+            - notes (list): A list of note objects with all properties set except
+            `content`.
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+        # initialize data
+        status = 0
+        ret = []
+        notes = { "data" : [] }
+        self.mark = "mark"
+
+        params = 'auth={0}&email={1}&length={2}'.format(self.get_token(), self.username,
+                                                        NOTE_FETCH_LENGTH)
+
+        try:
+            sinceUT = time.mktime(datetime.datetime.strptime(since, "%Y-%m-%d").timetuple())
+            params += '&since={0}'.format(sinceUT)
+        except (TypeError, ValueError):
+            #I.e. None or invalid date format
+            pass
+
+        # get notes
+        while self.mark:
+            notes, status = self.__get_notes(notes, params)
+
+        # parse data fields in response
+        note_list = notes["data"]
+
+        # Can only filter for tags at end, once all notes have been retrieved.
+        #Below based on simplenote.vim, except we return deleted notes as well
+        if (len(tags) > 0):
+            note_list = [n for n in note_list if (len(set(n["tags"]).intersection(tags)) > 0)]
+
+        return note_list, status
+
+    def trash_note(self, note_id):
+        """ method to move a note to the trash
+
+        Arguments:
+            - note_id (string): key of the note to trash
+
+        Returns:
+            A tuple `(note, status)`
+
+            - note (dict): the newly created note or an error message
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+        # get note
+        note, status = self.get_note(note_id)
+        if (status == -1):
+            return note, status
+        # set deleted property
+        note["deleted"] = 1
+        # update note
+        return self.update_note(note)
+
+    def delete_note(self, note_id):
+        """ method to permanently delete a note
+
+        Arguments:
+            - note_id (string): key of the note to trash
+
+        Returns:
+            A tuple `(note, status)`
+
+            - note (dict): an empty dict or an error message
+            - status (int): 0 on sucesss and -1 otherwise
+
+        """
+        # notes have to be trashed before deletion
+        note, status = self.trash_note(note_id)
+        if (status == -1):
+            return note, status
+
+        params = '/{0}?auth={1}&email={2}'.format(str(note_id), self.get_token(),
+                                                  self.username)
+        request = Request(url=DATA_URL+params, method='DELETE')
+        try:
+            urllib2.urlopen(request)
+        except IOError as e:
+            return e, -1
+        return {}, 0
+
+    def __encode(self, note):
+        """ Private method to UTF-8 encode for Python 2
+
+        Arguments:
+            A note
+
+        Returns:
+            A note
+
+        """
+
+        if sys.version_info < (3, 0):
+            if "content" in note:
+                # use UTF-8 encoding
+                note["content"] = note["content"].encode('utf-8')
+                # For early versions of notes, tags not always available
+            if "tags" in note:
+                note["tags"] = [t.encode('utf-8') for t in note["tags"]]
+        return note
+
+    def __decode(self, note):
+        """ Utility method to UTF-8 decode for Python 2
+
+        Arguments:
+            A note
+
+        Returns:
+            A note
+
+        """
+        if sys.version_info < (3, 0):
+            if "content" in note:
+                note["content"] = unicode(note["content"], 'utf-8')
+            if "tags" in note:
+                note["tags"] = [unicode(t, 'utf-8') for t in note["tags"]]
+        return note
+
+    def __get_notes(self, notes, params):
+        """ Private method to fetch a chunk of notes
+
+        Arguments:
+            - Notes
+            - URL parameters
+            - since date
+
+        Returns:
+            - Notes
+            - Status
+
+        """
+
+        notes_index = {}
+
+        if self.mark != "mark":
+            params += '&mark={0}'.format(self.mark)
+        # perform HTTP request
+        try:
+            request = Request(INDX_URL+params)
+            response = urllib2.urlopen(request)
+            notes_index = json.loads(response.read().decode('utf-8'))
+            notes["data"].extend(notes_index["data"])
+            status = 0
+        except IOError:
+            status = -1
+        if "mark" in notes_index:
+            self.mark = notes_index["mark"]
+        else:
+            self.mark = ""
+        return notes, status
+
+
+class Request(urllib2.Request):
+    """ monkey patched version of urllib2's Request to support HTTP DELETE
+        Taken from http://python-requests.org, thanks @kennethreitz
+    """
+
+    if sys.version_info < (3, 0):
+        def __init__(self, url, data=None, headers={}, origin_req_host=None,
+                    unverifiable=False, method=None):
+
+            headers = {'user-agent': custom_user_agent} # Nick woz ere, hacky, crap, hack.
+
+            urllib2.Request.__init__(self, url, data, headers, origin_req_host, unverifiable)
+            self.method = method
+
+        def get_method(self):
+            if self.method:
+                return self.method
+
+            return urllib2.Request.get_method(self)
+    else:
+        pass
diff --git a/simplenote_sync/snsync.py b/simplenote_sync/snsync.py
new file mode 100644
index 0000000..62b0f25
--- /dev/null
+++ b/simplenote_sync/snsync.py
@@ -0,0 +1,519 @@
+#!/usr/bin/env python3
+# coding=utf-8
+# I like catch-all excepts
+# pylint: disable=W0702
+# Widescreen baby!
+# pylint: disable=C0301
+# No idea why pylint import fails.
+# pylint: disable=F0401
+
+"""
+
+    Like rsync for Simplenote.
+        Sync simple notes to local text files
+
+    .. currentmodule:: snsync
+    .. moduleauthor:: Nick Bettison - www.linickx.com
+
+    # Logic
+    1. Get list of notes from Simplenote (LOOP1)
+    2. Add new Simplenotes to local SN DB (cache) and create txt file (with meta DB).
+    3. Existing notes: Compare & update based on modifieddate (update contents & modifieddate)
+    4. Deleted on SN (move local files to trash)
+    5. Deleted locally (mark deleted/trashed on SN)
+    6. Add new local note (file) to Simple Note (LOOP2)
+
+    --
+    IDEAS:
+        - Markdown - use Simplenote markdown tag to create .md files
+        - delta (fast) sync - use the last sync time to limit the processing (sn_last_sync)
+        - check file permissions of config file
+        - Merge (difflib) conflicts instead of creating files (maybe?)
+        - garbage collection - empty trash folder (every x days?)
+        - external [web?] hook - create customisable notification of status
+    BUGS:
+        - pylint
+            - R0912 Too many branches / R0914 Too many local vars - Both: Line 82
+            - R01101 Too many nested blocks (line 216)
+            - W0612 Unused Var (lastsync) - See ^ideas^ this is for delta/fast sync.
+            - W0612 Unused Var (args) - RTFM: I'm sure I did this for a reason :-/
+    --
+
+"""
+
+import time
+import datetime
+import logging
+import os
+import sys
+import re
+import getopt
+
+from .simplenote import Simplenote
+from .config import Config
+from .db import Database
+from .notes import Note
+from .version import __version__
+
+
+__version__ = "0.1" # Version Ctl
+start_time = time.monotonic() # Simple Performance Monitoring
+
+logger = logging.getLogger("snsync")
+logger.setLevel(logging.DEBUG)
+log_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
+
+# Log to console until file is setup.
+chandler = logging.StreamHandler()
+chandler.setFormatter(log_formatter)
+chandler.setLevel(logging.DEBUG)
+logger.addHandler(chandler)
+
+def usage():
+    """
+        Print Help / Usage
+    """
+    print('''
+Usage: snsync [OPTIONS]
+
+OPTIONS:
+ -h, --help         Help!
+ -d, --dry-run      Dry Run Mode (no changes made/saved)
+ -s, --silent       Silent Mode (no std output)
+ -c, --config=      Config file to read (default: ~/.snsync) 
+
+Version: %s
+''' % __version__)
+    sys.exit(0)
+
+def main(argv=sys.argv[1:]):
+    """
+        Main body, system argements, 2 loops.
+    """
+
+    # Default Vars
+    dry_run = False
+    silent_mode = False
+    config_file = None
+
+    # CMD Line options
+    try:
+        opts, args = getopt.getopt(argv,
+                                   'hds:c:',
+                                   ['help', 'dry-run', 'silent', 'config='])
+    except:
+        logger.debug("Exception: %s", sys.exc_info()[1])
+        usage()
+
+    for opt, arg in opts:
+        if opt in ['-h', '--help']:
+            usage()
+        elif opt in ['-d', '--dry-run']:
+            dry_run = True
+        elif opt in ['-s', '--silent']:
+            silent_mode = True
+        elif opt in ['-c', '--config']:
+            config_file = arg
+        else:
+            print('ERROR: Unhandled option')
+            usage()
+
+    config = Config(config_file) # Config Setup
+
+    note = Note(config, logger) # Local Notes Setup (folders)
+
+    log_file = config.get_config('cfg_log_path')
+    log_level = config.get_config('cfg_log_level')
+
+    # https://docs.python.org/2.6/library/logging.html
+    LEVELS = {'debug': logging.DEBUG,
+              'info': logging.INFO,
+              'warning': logging.WARNING,
+              'error': logging.ERROR,
+              'critical': logging.CRITICAL}
+
+    if log_file == "DISABLED":
+        logger.info("Console Errors Enabled")
+        silent_mode = True # Disable other output (progress bar)
+    else: # logfile is ready
+        if not silent_mode:
+            print("Logging to File: %s" % log_file)
+        fhandler = logging.FileHandler(log_file)
+        fhandler.setFormatter(log_formatter)
+        level = LEVELS.get(log_level, logging.INFO)
+        fhandler.setLevel(level)
+        logger.addHandler(fhandler) # add file handler
+        logger.removeHandler(chandler) # remove console handler
+        logger.info('--[ START Version %s ]--', __version__) # Begining of log file
+
+    # Catch missing configs
+    if config_file is not None:
+        if not os.path.isfile(config_file):
+            logger.critical("Config file not found: %s", config_file)
+            if not silent_mode:
+                print('Config file not found: %s' % config_file)
+            sys.exit(1)
+
+    db = Database(config, logger) # DB setup
+
+    # System Vars
+    filetime = datetime.datetime.now().strftime("%y%m%d-%H%M%S") # timestamp for files
+    the_os = sys.platform
+
+    # Uer config vars (DO NOT CHANGE THESE)
+    path = config.get_config('cfg_nt_path')
+    trash_path = config.get_config('cfg_nt_trashpath')
+    file_ext = config.get_config('cfg_nt_ext')
+    sn_username = config.get_config('sn_username')
+    sn_password = config.get_config('sn_password')
+
+    # Catch blank credentials
+    if sn_username == '' or sn_password == '':
+        logger.critical("Simplenote Username/Password not set, probably no ~/.snsync config file.")
+        if not silent_mode:
+            print('Simplenote Username/Password not set, probably no ~/.snsync config file.')
+        sys.exit(1)
+
+    # Main simplenote object
+    simplenote = Simplenote(sn_username, sn_password)
+
+    if re.match('linux', the_os):
+        logger.debug('OS: Linux')
+    elif re.match('darwin', the_os):
+        logger.debug('OS: MacOS')
+    else:
+        logger.warning('Unsupported OS')
+
+    if dry_run:
+        logger.warning('DRY RUN Mode')
+
+    try:
+        notes = simplenote.get_note_list() # the mac daddy important bit!
+    except:
+        logger.debug("Exception: %s", sys.exc_info()[1])
+        logger.critical("Simplenote Login Failed")
+        if not silent_mode:
+            print('Simplenote Login Failed')
+        sys.exit(1)
+
+    logger.debug('API Result: %s', notes)
+
+    if notes[1] == 0:  # success
+        notes = notes[0]
+    else:
+        logger.error('Simplenote LIST Request FAILED')
+        sys.exit()
+
+    # Counters!
+    counter_changes = 0
+    counter_modified = 0
+    counter_added = 0
+    counter_deleted = 0
+    counter_http_errors = 0
+
+    if not silent_mode:
+        print("Scanning %s Simplenotes" % len(notes))
+
+        # Progress bar
+        sys.stdout.write("[%s]" % (" " * len(notes)))
+        sys.stdout.flush()
+        sys.stdout.write("\b" * (len(notes)+1)) # return to start of line, after '['
+
+    # Loop 1
+    for n in notes:
+
+        db.commit() # Commit the last note
+
+        if not silent_mode:
+            time.sleep(0.05) # print doesn't work if too fast
+            sys.stdout.write("#")
+            sys.stdout.flush()
+
+        thisnote = db.find_sn_by_key(n['key'])
+
+        if thisnote:
+            # Existig Simple Note
+            thisfile = db.find_nf_by_key(n['key']) # Note File Meta
+            sn_modify = False # Set default status for simplenote
+            nf_modify = False # Set default status for notefile
+
+            if thisfile and n['deleted'] == 0: # Modified S-Notes
+                if os.path.isfile(path + "/" + thisfile['filename']):
+                    file_modifydate = os.path.getmtime(path + "/" + thisfile['filename'])
+
+                    sn_modifyseconds = n['modifydate'].split(".")[0] # Simple Note Modify Time
+                    logger.debug('SN Modified: %s [%s]', sn_modifyseconds, time.ctime(int(sn_modifyseconds)))
+
+                    sncache_modifyseconds = thisnote['modifydate'].split(".")[0] # Last known Simple Note Time
+                    logger.debug('SN (cached) Modified: %s [%s]', sncache_modifyseconds, time.ctime(int(sncache_modifyseconds)))
+
+                    nf_modifyseconds = str(file_modifydate).split(".")[0] # Note File modify Time
+                    logger.debug('NF Modified: %s [%s]', nf_modifyseconds, time.ctime(int(nf_modifyseconds)))
+
+                    if int(sn_modifyseconds) > int(sncache_modifyseconds):
+                        sn_modify = True
+                        logger.debug('SN %s is newer than NF %s', n['key'], thisfile['filename'])
+
+                    if int(nf_modifyseconds) > int(sn_modifyseconds):
+                        nf_modify = True
+                        logger.debug('NF %s is newer than SN %s', thisfile['filename'], n['key'])
+
+                    if nf_modify and sn_modify:
+                        logger.error('DUP! Modified Date Clash %s', thisfile['filename'])
+
+                        old_fqdn = path + "/" + thisfile['filename']
+                        new_fqdn = path + "/DUP_" + filetime + "_" + thisfile['filename']
+                        logger.info('Duplicate File Created %s', new_fqdn)
+
+                        nf_modify = False # Reset this, gonna move the old file
+
+                        logger.debug("DUP | Old: %s New: %s", old_fqdn, new_fqdn)
+                        if not dry_run:
+                            try:
+                                os.rename(old_fqdn, new_fqdn)
+                            except:
+                                logger.error("Failed to move file  %s -> %s", old_fqdn, new_fqdn)
+                                logger.debug("Exception: %s", sys.exc_info()[1])
+
+                    if not nf_modify and not sn_modify:
+                        logger.debug('No changes required for %s [%s]', thisfile['filename'], n['key'])
+
+                    if sn_modify:
+                        logger.info('[SN] > [NF] | %s -> %s', n['key'], thisfile['filename'])
+                        nf_filename = thisfile['filename']
+
+                    if nf_modify:
+                        logger.info('[SN] < [NF] | %s <- %s', n['key'], thisfile['filename'])
+                        nf_filename = thisfile['filename']
+
+                else:
+                    logger.critical("Local File [%s] DELETED but not marked for deletion locally, assuming delete SN -> [%s]", thisfile['filename'], n['key'])
+                    counter_deleted += 1
+
+                    if not dry_run:
+                        trash_note = simplenote.trash_note(n['key'])
+                        logger.debug('API Result: %s', trash_note)
+
+                        if trash_note[1] == 0:
+                            db.sn(trash_note[0])
+                            db.del_nf(n['key'])
+                            logger.info('SN Deleted [%s]', n['key'])
+                        else:
+                            logger.error('Simplenote DELETE Request Failed [%s]', n['key'])
+                            counter_http_errors += 1
+                            counter_deleted -= 1 # giveth and taketh away!
+
+            elif thisfile and n['deleted'] == 1: #  Seen and Deleted SN
+                if os.path.isfile(path + "/" + thisfile['filename']):
+                    logger.info('Deleting File: %s', thisfile['filename'])
+                    counter_deleted += 1
+
+                    if not dry_run:
+                        del thisfile['deleted'] # Remove old deleted meta
+                        thisfile['deleted'] = 1
+                        db.del_nf(n['key']) # delete nofile meta (forget the file)
+                        db.sn(n) # update simplenote cache
+
+                        old_fqdn = path + "/" + thisfile['filename']
+                        new_fqdn = path + "/" + trash_path + "/" + filetime + "_" + thisfile['filename']
+
+                        try:
+                            os.rename(old_fqdn, new_fqdn)
+                            logger.debug("TRASH | Old: %s New: %s", old_fqdn, new_fqdn)
+                        except:
+                            logger.error("Failed to move file  %s -> %s", old_fqdn, new_fqdn)
+                            logger.debug("Exception: %s", sys.exc_info()[1])
+
+            else: # No file meta
+                if n['deleted'] == 0: # Exists in Simple note, but no meta
+                    logger.critical("File Meta AWOL - %s", n['key'])
+                    sn_modify = True # Generate new local file
+                else: # No Meta and Deleted in Simple Note
+                    logger.debug("No file meta for deleted file simplenote, probably never written to disk")
+
+            if sn_modify: # Simplenote has been modified!
+                counter_modified += 1
+                if not dry_run:
+                    thisnote_full = simplenote.get_note(n['key']) # Get the latest note
+                    logger.debug('API Result: %s', thisnote_full)
+
+                    if thisnote_full[1] == 0:
+                        try:
+                            nf_filename
+                        except: # Catch critial AWOL Files
+                            nf_filename = note.get_filename(thisnote_full[0]['content'])
+
+                        # Generate new notefile meta
+                        nf_meta = {}
+                        nf_meta['filename'] = nf_filename
+                        nf_meta['key'] = n['key']
+                        nf_meta['createdate'] = n['createdate']
+                        nf_meta['modifydate'] = n['modifydate']
+                        nf_meta['deleted'] = n['deleted']
+
+                        db.sn(n) # Update simplenote Cache
+                        db.nf(nf_meta) # Update notefile meta
+
+                        thisnote_file = note.update(thisnote_full[0], nf_meta) # Write to file
+                    else:
+                        logger.error('Simplenote DOWNLOAD Request FAILED [%s]', n['key'])
+                        counter_http_errors += 1
+                        counter_modified -= 1 # so far yet so close ;)
+
+            if nf_modify: # Local note has been modified!
+                counter_modified += 1
+                if not dry_run:
+                    notefile_full = note.open(nf_filename)
+
+                    nf = {}
+                    nf['key'] = n['key']
+                    nf['content'] = notefile_full['content']
+                    nf['modifydate'] = notefile_full['modifydate']
+                    nf['version'] = n['version']
+
+                    note_update = simplenote.update_note(nf)
+                    logger.debug('API Result: %s', note_update)
+
+                    if note_update[1] == 0:
+                        nf_meta = {} # update meta
+                        nf_meta['filename'] = nf_filename
+                        nf_meta['key'] = note_update[0]['key']
+                        nf_meta['createdate'] = note_update[0]['createdate']
+                        nf_meta['modifydate'] = note_update[0]['modifydate']
+                        nf_meta['deleted'] = note_update[0]['deleted']
+
+                        db.sn(note_update[0])
+                        db.nf(nf_meta)
+
+                        logger.info('SN Updated [%s] from %s', n['key'], nf_filename)
+                    else:
+                        logger.error('Simplenote UPDATE Request FAILED [%s] <- %s', n['key'], nf_filename)
+                        counter_http_errors += 1
+                        counter_modified -= 1
+
+
+        else:
+            # New Note Added/Found in Simplenote
+            logger.info('Adding SN NOTE: %s to Local DB', n['key'])
+            counter_added += 1
+
+            if dry_run:
+                thisnote = False
+            else:
+                thisnote = db.sn(n)
+
+            if thisnote:
+                if n['deleted'] == 0: # Don't save deleted notes!
+                    thisnote_full = simplenote.get_note(n['key'])
+                    logger.debug('API Result: %s', thisnote_full)
+
+                    if thisnote_full[1] == 0:  # success
+                        thisnote_file = note.new(thisnote_full[0])
+
+                        if thisnote_file:
+                            nf_meta = {}
+                            nf_meta['filename'] = thisnote_file
+                            nf_meta['key'] = n['key']
+                            nf_meta['createdate'] = n['createdate']
+                            nf_meta['modifydate'] = n['modifydate']
+                            nf_meta['deleted'] = n['deleted']
+                            db.nf(nf_meta)
+                        else:
+                            logger.error("Failed to write note: %s", n['key'])
+
+                    else:
+                        logger.error('Simplenote DOWNLOAD Request FAILED [%s]', n['key'])
+                        counter_http_errors += 1
+                        counter_added -= 1
+
+            else:
+                if not dry_run:
+                    logger.error('Failed to updated DB with %s', n['key'])
+
+    if not silent_mode:
+        sys.stdout.write("\n") # New Line for end of progress bar
+
+    # Loop 2
+    if not silent_mode:
+        print("Scanning %s local files" % len(os.listdir(path)))
+        # Progress bar
+        sys.stdout.write("[%s]" % (" " * len(os.listdir(path))))
+        sys.stdout.flush()
+        sys.stdout.write("\b" * (len(os.listdir(path))+1)) # return to start of line, after '['
+
+    for notefile in os.listdir(path): # local search for new files
+
+        if not silent_mode:
+            if not silent_mode:
+                time.sleep(0.05) # print doesn't work if too fast
+                sys.stdout.write("#")
+                sys.stdout.flush()
+
+        if notefile.endswith(file_ext): # only work with .txt file (or whatever!)
+            logger.debug('Checking NF: %s', notefile)
+
+            nf_meta = db.find_nf_by_name(notefile) # Note File Meta
+
+            if not nf_meta: # If there's no meta, this must be a new file
+                logger.info('NEW notefile for upload: %s', notefile)
+                counter_added += 1
+
+                if not dry_run:
+                    nf_meta = note.gen_meta(notefile)
+                    nf_detail = note.open(notefile)
+
+                    new_sn_object = {}
+                    new_sn_object['key'] = nf_meta['key']
+                    new_sn_object['createdate'] = nf_meta['createdate']
+                    new_sn_object['modifydate'] = nf_meta['modifydate']
+                    new_sn_object['content'] = nf_detail['content']
+
+                    new_sn = simplenote.add_note(new_sn_object) # Add the note!
+                    logger.debug('API Result: %s', new_sn)
+
+                    if new_sn[1] == 0:
+                        logger.debug('New Simplenote Created: %s', new_sn)
+                        db.sn(new_sn[0]) # Update simplenote Cache
+                        db.nf(nf_meta) # Update notefile meta
+                    else:
+                        logger.error('Simplenote ADD Request FAILED [%s]', new_sn)
+                        counter_http_errors += 1
+                        counter_added -= 1
+
+    if not silent_mode:
+        sys.stdout.write("\n") # New Line for end of progress bar
+
+    if not dry_run:
+        lastsync = db.update_snsync("sn_last_sync", time.time()) # record last sync
+    db.disconnect() # Saves the sqlite db.
+
+    # end of play report
+    counter_changes = counter_modified + counter_added + counter_deleted
+    logger.info('Changes: %s', counter_changes)
+    if not silent_mode:
+        print('Changes: %s' % counter_changes)
+
+    if counter_modified > 0:
+        logger.info('Modified: %s', counter_modified)
+        if not silent_mode:
+            print('- Modified: %s' % counter_modified)
+
+    if counter_added > 0:
+        logger.info('Added: %s', counter_added)
+        if not silent_mode:
+            print('- Added: %s' % counter_added)
+
+    if counter_deleted > 0:
+        logger.info('Deleted: %s', counter_deleted)
+        if not silent_mode:
+            print('- Deleted: %s' % counter_deleted)
+
+    if counter_http_errors > 0:
+        logger.info('HTTP ERRORS: %s', counter_http_errors)
+        if not silent_mode:
+            print('HTTP ERRORS: %s' % counter_http_errors)
+
+    end_time = time.monotonic() # http://stackoverflow.com/a/26099345
+    logger.info('Time Taken: %s', datetime.timedelta(seconds=end_time - start_time))
+    if not silent_mode:
+        print('Time Taken: %s' % datetime.timedelta(seconds=end_time - start_time))
diff --git a/simplenote_sync/version.py b/simplenote_sync/version.py
new file mode 100644
index 0000000..5e3048b
--- /dev/null
+++ b/simplenote_sync/version.py
@@ -0,0 +1 @@
+__version__ = '0.1'
\ No newline at end of file
diff --git a/snsync b/snsync
new file mode 100755
index 0000000..eeef95d
--- /dev/null
+++ b/snsync
@@ -0,0 +1,10 @@
+#!/usr/bin/env python3
+"""
+    snsync, a rsync client for Simplenote.
+    
+    - This is a wrapper without a file extension to allow you to type "snsync"
+"""
+from simplenote_sync import snsync
+
+if __name__ == '__main__':
+    snsync.main()
\ No newline at end of file