commit
ad802737bc
145
buku
145
buku
@ -21,7 +21,6 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import argparse
|
import argparse
|
||||||
import readline
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import html.parser as HTMLParser
|
import html.parser as HTMLParser
|
||||||
from http.client import HTTPConnection, HTTPSConnection
|
from http.client import HTTPConnection, HTTPSConnection
|
||||||
@ -41,7 +40,7 @@ try:
|
|||||||
no_crypto = False
|
no_crypto = False
|
||||||
BLOCKSIZE = 65536
|
BLOCKSIZE = 65536
|
||||||
SALT_SIZE = 32
|
SALT_SIZE = 32
|
||||||
CHUNKSIZE = 0x80000 # Read/write 512 KB chunks
|
CHUNKSIZE = 0x80000 # Read/write 512 KB chunks
|
||||||
except ImportError:
|
except ImportError:
|
||||||
no_crypto = True
|
no_crypto = True
|
||||||
|
|
||||||
@ -87,12 +86,13 @@ class BMHTMLParser(HTMLParser.HTMLParser):
|
|||||||
self.reset() # We have received title data, exit parsing
|
self.reset() # We have received title data, exit parsing
|
||||||
|
|
||||||
def handle_data(self, data):
|
def handle_data(self, data):
|
||||||
if self.lasttag == 'title' and self.inTitle == True:
|
if self.lasttag == 'title' and self.inTitle:
|
||||||
self.data += data
|
self.data += data
|
||||||
|
|
||||||
def error(self, message):
|
def error(self, message):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BukuDb:
|
class BukuDb:
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -145,7 +145,6 @@ class BukuDb:
|
|||||||
|
|
||||||
os.rmdir(olddbpath)
|
os.rmdir(olddbpath)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def initdb():
|
def initdb():
|
||||||
"""Initialize the database connection. Create DB file and/or bookmarks table
|
"""Initialize the database connection. Create DB file and/or bookmarks table
|
||||||
@ -187,12 +186,11 @@ class BukuDb:
|
|||||||
try:
|
try:
|
||||||
cur.execute("ALTER TABLE bookmarks ADD COLUMN desc text default \'\'")
|
cur.execute("ALTER TABLE bookmarks ADD COLUMN desc text default \'\'")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return (conn, cur)
|
return (conn, cur)
|
||||||
|
|
||||||
|
|
||||||
def get_bookmark_index(self, url):
|
def get_bookmark_index(self, url):
|
||||||
"""Check if URL already exists in DB
|
"""Check if URL already exists in DB
|
||||||
|
|
||||||
@ -207,7 +205,6 @@ class BukuDb:
|
|||||||
|
|
||||||
return resultset[0][0]
|
return resultset[0][0]
|
||||||
|
|
||||||
|
|
||||||
def add_bookmark(self, url, title_manual=None, tag_manual=None, desc=None):
|
def add_bookmark(self, url, title_manual=None, tag_manual=None, desc=None):
|
||||||
"""Add a new bookmark
|
"""Add a new bookmark
|
||||||
|
|
||||||
@ -253,7 +250,6 @@ class BukuDb:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('\x1b[1mEXCEPTION\x1b[21m [add_bookmark]: (%s) %s' % (type(e).__name__, e))
|
print('\x1b[1mEXCEPTION\x1b[21m [add_bookmark]: (%s) %s' % (type(e).__name__, e))
|
||||||
|
|
||||||
|
|
||||||
def update_bookmark(self, index, url='', title_manual=None, tag_manual=None, desc=None):
|
def update_bookmark(self, index, url='', title_manual=None, tag_manual=None, desc=None):
|
||||||
""" Update an existing record at index
|
""" Update an existing record at index
|
||||||
|
|
||||||
@ -330,7 +326,6 @@ class BukuDb:
|
|||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
print('URL already exists')
|
print('URL already exists')
|
||||||
|
|
||||||
|
|
||||||
def refreshdb(self, index, title_manual=None):
|
def refreshdb(self, index, title_manual=None):
|
||||||
"""Refresh ALL records in the database. Fetch title for each
|
"""Refresh ALL records in the database. Fetch title for each
|
||||||
bookmark from the web and update the records. Doesn't udpate
|
bookmark from the web and update the records. Doesn't udpate
|
||||||
@ -367,7 +362,6 @@ class BukuDb:
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
print('Index %d updated\n' % row[0])
|
print('Index %d updated\n' % row[0])
|
||||||
|
|
||||||
|
|
||||||
def searchdb(self, keywords, all_keywords=False, json=False):
|
def searchdb(self, keywords, all_keywords=False, json=False):
|
||||||
"""Search the database for an entries with tags or URL
|
"""Search the database for an entries with tags or URL
|
||||||
or title info matching keywords and list those.
|
or title info matching keywords and list those.
|
||||||
@ -381,7 +375,7 @@ class BukuDb:
|
|||||||
placeholder = "'%' || ? || '%'"
|
placeholder = "'%' || ? || '%'"
|
||||||
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE"
|
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE"
|
||||||
|
|
||||||
if all_keywords == True: # Match all keywords in URL or Title
|
if all_keywords: # Match all keywords in URL or Title
|
||||||
for token in keywords:
|
for token in keywords:
|
||||||
query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s)) AND" % (placeholder, placeholder, placeholder, placeholder)
|
query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s)) AND" % (placeholder, placeholder, placeholder, placeholder)
|
||||||
arguments.append(token)
|
arguments.append(token)
|
||||||
@ -389,7 +383,7 @@ class BukuDb:
|
|||||||
arguments.append(token)
|
arguments.append(token)
|
||||||
arguments.append(token)
|
arguments.append(token)
|
||||||
query = query[:-4]
|
query = query[:-4]
|
||||||
else: # Match any keyword in URL or Title
|
else: # Match any keyword in URL or Title
|
||||||
for token in keywords:
|
for token in keywords:
|
||||||
query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s) OR" % (placeholder, placeholder, placeholder, placeholder)
|
query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s) OR" % (placeholder, placeholder, placeholder, placeholder)
|
||||||
arguments.append(token)
|
arguments.append(token)
|
||||||
@ -406,12 +400,11 @@ class BukuDb:
|
|||||||
if len(results) == 0:
|
if len(results) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
if json == False:
|
if not json:
|
||||||
prompt(results, self.noninteractive)
|
prompt(results, self.noninteractive)
|
||||||
else:
|
else:
|
||||||
print(format_json(results))
|
print(format_json(results))
|
||||||
|
|
||||||
|
|
||||||
def search_by_tag(self, tag, json=False):
|
def search_by_tag(self, tag, json=False):
|
||||||
"""Search and list bookmarks with a tag
|
"""Search and list bookmarks with a tag
|
||||||
|
|
||||||
@ -424,12 +417,11 @@ class BukuDb:
|
|||||||
if len(results) == 0:
|
if len(results) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
if json == False:
|
if not json:
|
||||||
prompt(results, self.noninteractive)
|
prompt(results, self.noninteractive)
|
||||||
else:
|
else:
|
||||||
print(format_json(results))
|
print(format_json(results))
|
||||||
|
|
||||||
|
|
||||||
def compactdb(self, index):
|
def compactdb(self, index):
|
||||||
"""When an entry at index is deleted, move the last
|
"""When an entry at index is deleted, move the last
|
||||||
entry in DB to index, if index is lesser.
|
entry in DB to index, if index is lesser.
|
||||||
@ -439,7 +431,7 @@ class BukuDb:
|
|||||||
|
|
||||||
self.cur.execute('SELECT MAX(id) from bookmarks')
|
self.cur.execute('SELECT MAX(id) from bookmarks')
|
||||||
results = self.cur.fetchall()
|
results = self.cur.fetchall()
|
||||||
if len(results) == 1 and results[0][0] is None: # Return if the last index was just deleted
|
if len(results) == 1 and results[0][0] is None: # Return if the last index was just deleted
|
||||||
return
|
return
|
||||||
|
|
||||||
for row in results:
|
for row in results:
|
||||||
@ -453,7 +445,6 @@ class BukuDb:
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
print('Index %d moved to %d' % (row[0], index))
|
print('Index %d moved to %d' % (row[0], index))
|
||||||
|
|
||||||
|
|
||||||
def delete_bookmark(self, index):
|
def delete_bookmark(self, index):
|
||||||
"""Delete a single record or remove the table if index is None
|
"""Delete a single record or remove the table if index is None
|
||||||
|
|
||||||
@ -469,7 +460,7 @@ class BukuDb:
|
|||||||
self.cur.execute('DROP TABLE if exists bookmarks')
|
self.cur.execute('DROP TABLE if exists bookmarks')
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
print('All bookmarks deleted')
|
print('All bookmarks deleted')
|
||||||
else: # Remove a single entry
|
else: # Remove a single entry
|
||||||
try:
|
try:
|
||||||
self.cur.execute('DELETE FROM bookmarks WHERE id = ?', (index,))
|
self.cur.execute('DELETE FROM bookmarks WHERE id = ?', (index,))
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
@ -481,7 +472,6 @@ class BukuDb:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
print('Index out of bound')
|
print('Index out of bound')
|
||||||
|
|
||||||
|
|
||||||
def print_bookmark(self, index, empty=False):
|
def print_bookmark(self, index, empty=False):
|
||||||
"""Print bookmark details at index or all bookmarks if index is None
|
"""Print bookmark details at index or all bookmarks if index is None
|
||||||
Print only bookmarks with blank title or tag if empty is True
|
Print only bookmarks with blank title or tag if empty is True
|
||||||
@ -494,8 +484,8 @@ class BukuDb:
|
|||||||
global jsonOutput
|
global jsonOutput
|
||||||
|
|
||||||
resultset = None
|
resultset = None
|
||||||
if index == 0: # Show all entries
|
if index == 0: # Show all entries
|
||||||
if empty == False:
|
if not empty:
|
||||||
self.cur.execute('SELECT * FROM bookmarks')
|
self.cur.execute('SELECT * FROM bookmarks')
|
||||||
resultset = self.cur.fetchall()
|
resultset = self.cur.fetchall()
|
||||||
else:
|
else:
|
||||||
@ -503,7 +493,7 @@ class BukuDb:
|
|||||||
resultset = self.cur.fetchall()
|
resultset = self.cur.fetchall()
|
||||||
print('\x1b[1m%d records found\x1b[21m\n' % len(resultset))
|
print('\x1b[1m%d records found\x1b[21m\n' % len(resultset))
|
||||||
|
|
||||||
if jsonOutput == False:
|
if not jsonOutput:
|
||||||
if showOpt == 0:
|
if showOpt == 0:
|
||||||
for row in resultset:
|
for row in resultset:
|
||||||
print_record(row)
|
print_record(row)
|
||||||
@ -526,7 +516,7 @@ class BukuDb:
|
|||||||
print('Index out of bound')
|
print('Index out of bound')
|
||||||
return
|
return
|
||||||
|
|
||||||
if jsonOutput == False:
|
if not jsonOutput:
|
||||||
for row in results:
|
for row in results:
|
||||||
if showOpt == 0:
|
if showOpt == 0:
|
||||||
print_record(row)
|
print_record(row)
|
||||||
@ -537,7 +527,6 @@ class BukuDb:
|
|||||||
else:
|
else:
|
||||||
print(format_json(results, True))
|
print(format_json(results, True))
|
||||||
|
|
||||||
|
|
||||||
def list_tags(self):
|
def list_tags(self):
|
||||||
"""Print all unique tags ordered alphabetically
|
"""Print all unique tags ordered alphabetically
|
||||||
"""
|
"""
|
||||||
@ -560,7 +549,6 @@ class BukuDb:
|
|||||||
print('%6d. %s' % (count, tag))
|
print('%6d. %s' % (count, tag))
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
|
|
||||||
def replace_tag(self, orig, new=None):
|
def replace_tag(self, orig, new=None):
|
||||||
"""Replace orig tags with new tags in DB for all records.
|
"""Replace orig tags with new tags in DB for all records.
|
||||||
Remove orig tag is new tag is empty.
|
Remove orig tag is new tag is empty.
|
||||||
@ -588,7 +576,7 @@ class BukuDb:
|
|||||||
results = self.cur.fetchall()
|
results = self.cur.fetchall()
|
||||||
|
|
||||||
for row in results:
|
for row in results:
|
||||||
if delete == False:
|
if not delete:
|
||||||
# Check if tag newtags is already added
|
# Check if tag newtags is already added
|
||||||
if row[1].find(newtags) >= 0:
|
if row[1].find(newtags) >= 0:
|
||||||
newtags = DELIMITER
|
newtags = DELIMITER
|
||||||
@ -601,7 +589,6 @@ class BukuDb:
|
|||||||
if update:
|
if update:
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
def browse_by_index(self, index):
|
def browse_by_index(self, index):
|
||||||
"""Open URL at index in browser
|
"""Open URL at index in browser
|
||||||
|
|
||||||
@ -615,8 +602,7 @@ class BukuDb:
|
|||||||
return
|
return
|
||||||
print('No matching index')
|
print('No matching index')
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print('Index out of bound')
|
print('Index out of bound')
|
||||||
|
|
||||||
|
|
||||||
def close_quit(self, exitval=0):
|
def close_quit(self, exitval=0):
|
||||||
"""Close a DB connection and exit"""
|
"""Close a DB connection and exit"""
|
||||||
@ -625,11 +611,10 @@ class BukuDb:
|
|||||||
try:
|
try:
|
||||||
self.cur.close()
|
self.cur.close()
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
except: # we don't really care about errors, we're closing down anyway
|
except Exception: # we don't really care about errors, we're closing down anyway
|
||||||
pass
|
pass
|
||||||
sys.exit(exitval)
|
sys.exit(exitval)
|
||||||
|
|
||||||
|
|
||||||
def import_bookmark(self, fp):
|
def import_bookmark(self, fp):
|
||||||
"""Import bookmarks from a html file.
|
"""Import bookmarks from a html file.
|
||||||
Supports Firefox, Google Chrome and IE imports
|
Supports Firefox, Google Chrome and IE imports
|
||||||
@ -663,7 +648,6 @@ class BukuDb:
|
|||||||
(DELIMITER + tag['tags'] + DELIMITER) if tag.has_attr('tags') else None,
|
(DELIMITER + tag['tags'] + DELIMITER) if tag.has_attr('tags') else None,
|
||||||
desc)
|
desc)
|
||||||
|
|
||||||
|
|
||||||
def mergedb(self, fp):
|
def mergedb(self, fp):
|
||||||
"""Merge bookmarks from another Buku database file
|
"""Merge bookmarks from another Buku database file
|
||||||
|
|
||||||
@ -690,7 +674,7 @@ class BukuDb:
|
|||||||
try:
|
try:
|
||||||
curfp.close()
|
curfp.close()
|
||||||
connfp.close()
|
connfp.close()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -719,24 +703,24 @@ def connect_server(url, fullurl=False, forced=False):
|
|||||||
if debug:
|
if debug:
|
||||||
print('unquoted: %s' % url)
|
print('unquoted: %s' % url)
|
||||||
|
|
||||||
if url.find('https://') >= 0: # Secure connection
|
if url.find('https://') >= 0: # Secure connection
|
||||||
server = url[8:]
|
server = url[8:]
|
||||||
marker = server.find('/')
|
marker = server.find('/')
|
||||||
if marker > 0:
|
if marker > 0:
|
||||||
if fullurl == False and forced == False:
|
if not fullurl and not forced:
|
||||||
url = server[marker:]
|
url = server[marker:]
|
||||||
server = server[:marker]
|
server = server[:marker]
|
||||||
elif forced == False: # Handle domain name without trailing /
|
elif not forced: # Handle domain name without trailing /
|
||||||
url = '/'
|
url = '/'
|
||||||
urlconn = HTTPSConnection(server, timeout=30)
|
urlconn = HTTPSConnection(server, timeout=30)
|
||||||
elif url.find('http://') >= 0: # Insecure connection
|
elif url.find('http://') >= 0: # Insecure connection
|
||||||
server = url[7:]
|
server = url[7:]
|
||||||
marker = server.find('/')
|
marker = server.find('/')
|
||||||
if marker > 0:
|
if marker > 0:
|
||||||
if fullurl == False and forced == False:
|
if not fullurl and not forced:
|
||||||
url = server[marker:]
|
url = server[marker:]
|
||||||
server = server[:marker]
|
server = server[:marker]
|
||||||
elif forced == False:
|
elif not forced:
|
||||||
url = '/'
|
url = '/'
|
||||||
urlconn = HTTPConnection(server, timeout=30)
|
urlconn = HTTPConnection(server, timeout=30)
|
||||||
else:
|
else:
|
||||||
@ -751,7 +735,7 @@ def connect_server(url, fullurl=False, forced=False):
|
|||||||
# Handle URLs passed with %xx escape
|
# Handle URLs passed with %xx escape
|
||||||
try:
|
try:
|
||||||
url.encode('ascii')
|
url.encode('ascii')
|
||||||
except:
|
except Exception:
|
||||||
url = quote(url)
|
url = quote(url)
|
||||||
|
|
||||||
urlconn.request('GET', url, None, {
|
urlconn.request('GET', url, None, {
|
||||||
@ -776,7 +760,7 @@ def get_page_title(resp):
|
|||||||
else:
|
else:
|
||||||
data = resp.read()
|
data = resp.read()
|
||||||
|
|
||||||
if charset == None:
|
if charset is None:
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
if debug:
|
if debug:
|
||||||
printmsg('Charset missing in response', 'WARNING')
|
printmsg('Charset missing in response', 'WARNING')
|
||||||
@ -811,7 +795,7 @@ def network_handler(url):
|
|||||||
try:
|
try:
|
||||||
urlconn, resp = connect_server(url, False)
|
urlconn, resp = connect_server(url, False)
|
||||||
|
|
||||||
while 1:
|
while True:
|
||||||
if resp is None:
|
if resp is None:
|
||||||
break
|
break
|
||||||
elif resp.status == 200:
|
elif resp.status == 200:
|
||||||
@ -840,7 +824,7 @@ def network_handler(url):
|
|||||||
urlconn.close()
|
urlconn.close()
|
||||||
# Try with complete URL on redirection
|
# Try with complete URL on redirection
|
||||||
urlconn, resp = connect_server(url, True)
|
urlconn, resp = connect_server(url, True)
|
||||||
elif resp.status == 403 and retry == False:
|
elif resp.status == 403 and not retry:
|
||||||
"""Handle URLs of the form https://www.domain.com or
|
"""Handle URLs of the form https://www.domain.com or
|
||||||
https://www.domain.com/ which fails when trying to fetch
|
https://www.domain.com/ which fails when trying to fetch
|
||||||
resource '/', retry with full path.
|
resource '/', retry with full path.
|
||||||
@ -853,7 +837,7 @@ def network_handler(url):
|
|||||||
url = url[:-1]
|
url = url[:-1]
|
||||||
urlconn, resp = connect_server(url, False, True)
|
urlconn, resp = connect_server(url, False, True)
|
||||||
retry = True
|
retry = True
|
||||||
elif resp.status == 500 and retry == False:
|
elif resp.status == 500 and not retry:
|
||||||
"""Retry on status 500 (Internal Server Error) with truncated
|
"""Retry on status 500 (Internal Server Error) with truncated
|
||||||
URL. Some servers support truncated request URL on redirection.
|
URL. Some servers support truncated request URL on redirection.
|
||||||
"""
|
"""
|
||||||
@ -872,12 +856,15 @@ def network_handler(url):
|
|||||||
urlconn.close()
|
urlconn.close()
|
||||||
if titleData is None:
|
if titleData is None:
|
||||||
return ''
|
return ''
|
||||||
return titleData.strip().replace('\n','')
|
return titleData.strip().replace('\n', '')
|
||||||
|
|
||||||
|
|
||||||
def parse_tags(keywords=[]):
|
def parse_tags(keywords=None):
|
||||||
"""Format and get tag string from tokens"""
|
"""Format and get tag string from tokens"""
|
||||||
|
|
||||||
|
if keywords is None:
|
||||||
|
keywords = []
|
||||||
|
|
||||||
tags = DELIMITER
|
tags = DELIMITER
|
||||||
origTags = []
|
origTags = []
|
||||||
uniqueTags = []
|
uniqueTags = []
|
||||||
@ -888,7 +875,7 @@ def parse_tags(keywords=[]):
|
|||||||
|
|
||||||
while marker >= 0:
|
while marker >= 0:
|
||||||
token = tagstr[0:marker]
|
token = tagstr[0:marker]
|
||||||
tagstr = tagstr[marker+1:]
|
tagstr = tagstr[marker + 1:]
|
||||||
marker = tagstr.find(',')
|
marker = tagstr.find(',')
|
||||||
token = token.strip()
|
token = token.strip()
|
||||||
if token == '':
|
if token == '':
|
||||||
@ -927,7 +914,7 @@ def prompt(results, noninteractive=False):
|
|||||||
count += 1
|
count += 1
|
||||||
print_record(row, count)
|
print_record(row, count)
|
||||||
|
|
||||||
if noninteractive == True:
|
if noninteractive:
|
||||||
return
|
return
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -986,15 +973,15 @@ def format_json(resultset, single=False):
|
|||||||
|
|
||||||
global showOpt
|
global showOpt
|
||||||
|
|
||||||
if single == False:
|
if not single:
|
||||||
marks = []
|
marks = []
|
||||||
for row in resultset:
|
for row in resultset:
|
||||||
if showOpt == 1:
|
if showOpt == 1:
|
||||||
record = { 'uri': row[1] }
|
record = {'uri': row[1]}
|
||||||
elif showOpt == 2:
|
elif showOpt == 2:
|
||||||
record = { 'uri': row[1], 'tags': row[3][1:-1] }
|
record = {'uri': row[1], 'tags': row[3][1:-1]}
|
||||||
else:
|
else:
|
||||||
record = { 'uri': row[1], 'title': row[2], 'description': row[4], 'tags': row[3][1:-1]}
|
record = {'uri': row[1], 'title': row[2], 'description': row[4], 'tags': row[3][1:-1]}
|
||||||
|
|
||||||
marks.append(record)
|
marks.append(record)
|
||||||
else:
|
else:
|
||||||
@ -1004,12 +991,12 @@ def format_json(resultset, single=False):
|
|||||||
marks['uri'] = row[1]
|
marks['uri'] = row[1]
|
||||||
elif showOpt == 2:
|
elif showOpt == 2:
|
||||||
marks['uri'] = row[1]
|
marks['uri'] = row[1]
|
||||||
marks['tags'] = row[3][1:-1]
|
marks['tags'] = row[3][1:-1]
|
||||||
else:
|
else:
|
||||||
marks['uri'] = row[1]
|
marks['uri'] = row[1]
|
||||||
marks['title'] = row[2]
|
marks['title'] = row[2]
|
||||||
marks['description'] = row[4]
|
marks['description'] = row[4]
|
||||||
marks['tags'] = row[3][1:-1]
|
marks['tags'] = row[3][1:-1]
|
||||||
|
|
||||||
return json.dumps(marks, sort_keys=True, indent=4)
|
return json.dumps(marks, sort_keys=True, indent=4)
|
||||||
|
|
||||||
@ -1023,7 +1010,7 @@ def is_int(string):
|
|||||||
try:
|
try:
|
||||||
int(string)
|
int(string)
|
||||||
return True
|
return True
|
||||||
except:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -1087,10 +1074,10 @@ def encrypt_file(iterations):
|
|||||||
password = getpass.getpass()
|
password = getpass.getpass()
|
||||||
passconfirm = getpass.getpass()
|
passconfirm = getpass.getpass()
|
||||||
if password == '':
|
if password == '':
|
||||||
print('Empty password');
|
print('Empty password')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if password != passconfirm:
|
if password != passconfirm:
|
||||||
print("Passwords don't match");
|
print("Passwords don't match")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Get SHA256 hash of DB file
|
# Get SHA256 hash of DB file
|
||||||
@ -1099,7 +1086,7 @@ def encrypt_file(iterations):
|
|||||||
# Generate random 256-bit salt and key
|
# Generate random 256-bit salt and key
|
||||||
salt = Random.get_random_bytes(SALT_SIZE)
|
salt = Random.get_random_bytes(SALT_SIZE)
|
||||||
key = (password + salt.decode('utf-8', 'replace')).encode('utf-8')
|
key = (password + salt.decode('utf-8', 'replace')).encode('utf-8')
|
||||||
for i in range(iterations):
|
for _ in range(iterations):
|
||||||
key = hashlib.sha256(key).digest()
|
key = hashlib.sha256(key).digest()
|
||||||
|
|
||||||
iv = Random.get_random_bytes(16)
|
iv = Random.get_random_bytes(16)
|
||||||
@ -1146,7 +1133,7 @@ def decrypt_file(iterations):
|
|||||||
password = ''
|
password = ''
|
||||||
password = getpass.getpass()
|
password = getpass.getpass()
|
||||||
if password == '':
|
if password == '':
|
||||||
printmsg('Decryption failed', 'ERROR');
|
printmsg('Decryption failed', 'ERROR')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
with open(encpath, 'rb') as infile:
|
with open(encpath, 'rb') as infile:
|
||||||
@ -1155,7 +1142,7 @@ def decrypt_file(iterations):
|
|||||||
# Read 256-bit salt and generate key
|
# Read 256-bit salt and generate key
|
||||||
salt = infile.read(32)
|
salt = infile.read(32)
|
||||||
key = (password + salt.decode('utf-8', 'replace')).encode('utf-8')
|
key = (password + salt.decode('utf-8', 'replace')).encode('utf-8')
|
||||||
for i in range(iterations):
|
for _ in range(iterations):
|
||||||
key = hashlib.sha256(key).digest()
|
key = hashlib.sha256(key).digest()
|
||||||
|
|
||||||
iv = infile.read(16)
|
iv = infile.read(16)
|
||||||
@ -1168,7 +1155,7 @@ def decrypt_file(iterations):
|
|||||||
while True:
|
while True:
|
||||||
chunk = infile.read(CHUNKSIZE)
|
chunk = infile.read(CHUNKSIZE)
|
||||||
if len(chunk) == 0:
|
if len(chunk) == 0:
|
||||||
break;
|
break
|
||||||
|
|
||||||
outfile.write(cipher.decrypt(chunk))
|
outfile.write(cipher.decrypt(chunk))
|
||||||
|
|
||||||
@ -1178,7 +1165,7 @@ def decrypt_file(iterations):
|
|||||||
dbhash = get_filehash(dbpath)
|
dbhash = get_filehash(dbpath)
|
||||||
if dbhash != enchash:
|
if dbhash != enchash:
|
||||||
os.remove(dbpath)
|
os.remove(dbpath)
|
||||||
printmsg('Decryption failed', 'ERROR');
|
printmsg('Decryption failed', 'ERROR')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
os.remove(encpath)
|
os.remove(encpath)
|
||||||
@ -1229,7 +1216,7 @@ class CustomTagAction(argparse.Action):
|
|||||||
def __call__(self, parser, args, values, option_string=None):
|
def __call__(self, parser, args, values, option_string=None):
|
||||||
global tagManual
|
global tagManual
|
||||||
|
|
||||||
tagManual = [DELIMITER,]
|
tagManual = [DELIMITER, ]
|
||||||
setattr(args, self.dest, values)
|
setattr(args, self.dest, values)
|
||||||
|
|
||||||
|
|
||||||
@ -1294,10 +1281,11 @@ Webpage: https://github.com/jarun/buku
|
|||||||
|
|
||||||
"""main starts here"""
|
"""main starts here"""
|
||||||
|
|
||||||
|
|
||||||
# Handle piped input
|
# Handle piped input
|
||||||
def main(argv = sys.argv):
|
def main(argv):
|
||||||
if not sys.stdin.isatty():
|
if not sys.stdin.isatty():
|
||||||
pipeargs.extend(sys.argv)
|
pipeargs.extend(argv)
|
||||||
for s in sys.stdin.readlines():
|
for s in sys.stdin.readlines():
|
||||||
pipeargs.extend(s.split())
|
pipeargs.extend(s.split())
|
||||||
|
|
||||||
@ -1324,7 +1312,8 @@ if __name__ == '__main__':
|
|||||||
)
|
)
|
||||||
|
|
||||||
# General options
|
# General options
|
||||||
general_group = argparser.add_argument_group(title='general options',
|
general_group = argparser.add_argument_group(
|
||||||
|
title='general options',
|
||||||
description='''-a, --add URL [tags ...]
|
description='''-a, --add URL [tags ...]
|
||||||
bookmark URL with comma-separated tags
|
bookmark URL with comma-separated tags
|
||||||
-u, --update [N] update fields of bookmark at DB index N
|
-u, --update [N] update fields of bookmark at DB index N
|
||||||
@ -1343,7 +1332,8 @@ if __name__ == '__main__':
|
|||||||
general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS)
|
general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Edit options
|
# Edit options
|
||||||
edit_group=argparser.add_argument_group(title='edit options',
|
edit_group = argparser.add_argument_group(
|
||||||
|
title='edit options',
|
||||||
description='''--url keyword specify url, works with -u only
|
description='''--url keyword specify url, works with -u only
|
||||||
--tag [...] set comma-separated tags, works with -a, -u
|
--tag [...] set comma-separated tags, works with -a, -u
|
||||||
clears tags, if no arguments
|
clears tags, if no arguments
|
||||||
@ -1358,7 +1348,8 @@ if __name__ == '__main__':
|
|||||||
edit_group.add_argument('-c', '--comment', nargs='*', dest='desc', type=str, action=CustomDescAction, metavar='desc', help=argparse.SUPPRESS)
|
edit_group.add_argument('-c', '--comment', nargs='*', dest='desc', type=str, action=CustomDescAction, metavar='desc', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Search options
|
# Search options
|
||||||
search_group=argparser.add_argument_group(title='search options',
|
search_group = argparser.add_argument_group(
|
||||||
|
title='search options',
|
||||||
description='''-s, --sany keyword [...]
|
description='''-s, --sany keyword [...]
|
||||||
search bookmarks for ANY matching keyword
|
search bookmarks for ANY matching keyword
|
||||||
-S, --sall keyword [...]
|
-S, --sall keyword [...]
|
||||||
@ -1372,7 +1363,8 @@ if __name__ == '__main__':
|
|||||||
search_group.add_argument('--st', '--stag', nargs='*', dest='stag', action=CustomTagSearchAction, metavar='keyword', help=argparse.SUPPRESS)
|
search_group.add_argument('--st', '--stag', nargs='*', dest='stag', action=CustomTagSearchAction, metavar='keyword', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Encryption options
|
# Encryption options
|
||||||
crypto_group=argparser.add_argument_group(title='encryption options',
|
crypto_group = argparser.add_argument_group(
|
||||||
|
title='encryption options',
|
||||||
description='''-l, --lock [N] encrypt DB file with N (> 0, default 8)
|
description='''-l, --lock [N] encrypt DB file with N (> 0, default 8)
|
||||||
hash iterations to generate key
|
hash iterations to generate key
|
||||||
-k, --unlock [N] decrypt DB file with N (> 0, default 8)
|
-k, --unlock [N] decrypt DB file with N (> 0, default 8)
|
||||||
@ -1381,7 +1373,8 @@ if __name__ == '__main__':
|
|||||||
crypto_group.add_argument('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help=argparse.SUPPRESS)
|
crypto_group.add_argument('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Power toys
|
# Power toys
|
||||||
power_group=argparser.add_argument_group(title='power toys',
|
power_group = argparser.add_argument_group(
|
||||||
|
title='power toys',
|
||||||
description='''-p, --print [N] show details of bookmark at DB index N
|
description='''-p, --print [N] show details of bookmark at DB index N
|
||||||
show all bookmarks, if no arguments
|
show all bookmarks, if no arguments
|
||||||
-f, --format N modify -p output
|
-f, --format N modify -p output
|
||||||
@ -1412,7 +1405,7 @@ if __name__ == '__main__':
|
|||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
|
|
||||||
# Show help and exit if help requested
|
# Show help and exit if help requested
|
||||||
if args.help == True:
|
if args.help:
|
||||||
argparser.print_help(sys.stderr)
|
argparser.print_help(sys.stderr)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
@ -1453,7 +1446,7 @@ if __name__ == '__main__':
|
|||||||
printmsg('PyCrypto missing', 'ERROR')
|
printmsg('PyCrypto missing', 'ERROR')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if args.decrypt < 1:
|
if args.decrypt < 1:
|
||||||
printmsg('Decryption failed', 'ERROR');
|
printmsg('Decryption failed', 'ERROR')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
decrypt_file(args.decrypt)
|
decrypt_file(args.decrypt)
|
||||||
|
|
||||||
@ -1482,7 +1475,7 @@ if __name__ == '__main__':
|
|||||||
bdb.add_bookmark(args.addurl[0], titleManual, tags, description)
|
bdb.add_bookmark(args.addurl[0], titleManual, tags, description)
|
||||||
|
|
||||||
# Update record
|
# Update record
|
||||||
if update == True:
|
if update:
|
||||||
if len(args.update) == 0:
|
if len(args.update) == 0:
|
||||||
bdb.refreshdb(0, titleManual)
|
bdb.refreshdb(0, titleManual)
|
||||||
elif not args.update[0].isdigit():
|
elif not args.update[0].isdigit():
|
||||||
@ -1522,7 +1515,7 @@ if __name__ == '__main__':
|
|||||||
bdb.searchdb(args.sall, True, jsonOutput)
|
bdb.searchdb(args.sall, True, jsonOutput)
|
||||||
|
|
||||||
# Search bookmarks by tag
|
# Search bookmarks by tag
|
||||||
if tagsearch == True:
|
if tagsearch:
|
||||||
if len(args.stag) > 0:
|
if len(args.stag) > 0:
|
||||||
tag = DELIMITER + ' '.join(args.stag) + DELIMITER
|
tag = DELIMITER + ' '.join(args.stag) + DELIMITER
|
||||||
bdb.search_by_tag(tag, jsonOutput)
|
bdb.search_by_tag(tag, jsonOutput)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user