Massive changes to src/bitmessagecli.py

* Mainly changes:
  * Message attachments faults detecting, and allow to dump whole embeded message alone to files.
  * Multi-line message acceptable in sending, (End with `Ctrl+D`)and allow resent on 'Connection Error.'
  * Print out detailed API Error returned.
  * Update Contacts list cmd, bcz contact list API returns unEncoded lable.
  * Message pull routing refine, mainlly access inbox messages by IDs, to reduce bandwidth usages.
  * API connections `SOCKS5` `HTTP` proxied.
* UIs
  * Shorten the user cmds inputs.
  * Try to remember user last choices.
  * Refine user input checkings.(rest to default selection by input a blank string)
  * A comprehensive command line parser, override configurations read from file `client.dat` in current working directory.
  * Message review limited to 380 characters in default.

default settings `client.dat`
```
[global]
start_daemon = http://127.0.0.1:8888
start_daemon = http://127.0.0.1:8445

[api]
path = 127.0.0.1:8445
type = HTTP

[proxy]
path = 127.0.0.1:1080
type = none
timeout = 30
remotedns = True
```

Signed-off-by: peter-tank <30540412+peter-tank@users.noreply.github.com>
This commit is contained in:
peter-tank 2018-07-17 20:01:02 +08:00
parent d0c2759c41
commit 425eded1a2
No known key found for this signature in database
GPG Key ID: 5C422EFEA3A30F52
2 changed files with 2471 additions and 1421 deletions

View File

@ -7,646 +7,1153 @@
Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation
Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php.
This is an example of a daemon client for PyBitmessage 0.6.2, by .dok (Version 0.3.1) , modified
This is an example of a daemon client for PyBitmessage 0.6.2, original by .dok (Version 0.3.1)
Modified by .pt (Version 0.4.0), for PyBitmessage 0.6.3
TODO: fix the following (currently ignored) violations:
"""
import xmlrpclib
import datetime
import argparse
import ConfigParser
import imghdr
import ntpath
import json
import socket
import time
import sys
import os
from bmconfigparser import BMConfigParser
import base64
import ssl
import socket
# python3 maybe
try:
import httplib
import xmlrpclib
from urlparse import urlparse
from urllib import unquote
except ImportError:
import http.client as httplib
import xmlrpc.client as xmlrpclib
from urllib.parse import urlparse, unquote
import traceback
import time
import datetime
import inspect
import re
import subprocess
from collections import OrderedDict
api = ''
keysName = 'keys.dat'
keysPath = 'keys.dat'
usrPrompt = 0 # 0 = First Start, 1 = prompt, 2 = no prompt if the program is starting up
knownAddresses = dict()
api = ''
knownAddresses = dict({'addresses': []})
cmdStr = 'getLabel'.lower()
# menu by this order
cmdTbl = OrderedDict()
cmdTbl['Command'] = 'Description'
cmdTbl['s1'] = '-'
cmdTbl['help'] = 'This help'
cmdTbl['daemon'] = 'Try to start PyBitmessage daemon locally'
cmdTbl['apiTest'] = 'Daemon API connection tests'
cmdTbl['status'] = 'Get the summary of running daemon'
cmdTbl['addInfo'] = 'Request detailed info to a address'
cmdTbl['bmSettings'] = 'PyBitmessage settings "keys.dat"'
cmdTbl['exit'] = 'Use anytime to return to main menu'
cmdTbl['quit'] = 'Quit this CLI'
cmdTbl['shutdown'] = 'Shutdown the connectable daemon via. API'
cmdTbl['s2'] = '-'
cmdTbl['listAddresses'] = 'List user\'s addresse(s) (Senders)'
cmdTbl['newAddress'] = 'Generate a new sender address'
cmdTbl['getAddress'] = 'Get determinist address from passphrase'
cmdTbl['s3'] = '-'
cmdTbl['listAddressBK'] = 'List the "Address Book" entry (Contacts)'
cmdTbl['addAddressBK'] = 'Add a address to the "Address Book"'
cmdTbl['delAddressBK'] = 'Delete a address from the "Address Book"'
cmdTbl['s4'] = '-'
cmdTbl['listsubscrips'] = 'List subscriped addresses'
cmdTbl['subscribe'] = 'Subscribes to an address'
cmdTbl['unsubscribe'] = 'Unsubscribe from an address'
cmdTbl['s5'] = '-'
cmdTbl['create'] = 'Create a channel'
cmdTbl['join'] = 'Join to a channel'
cmdTbl['leave'] = 'Leave from a channel'
cmdTbl['s6'] = '-'
cmdTbl['getLabel'] = 'Retrieve addresse(s) label for message heads'
cmdTbl['s7'] = '-'
cmdTbl['inbox'] = 'List all inbox message heads'
cmdTbl['outbox'] = 'List all outbox message heads heads'
cmdTbl['news'] = 'List all "unread" inbox message heads'
cmdTbl['send'] = 'Send out new message or broadcast'
cmdTbl['s8'] = '-'
cmdTbl['read'] = 'Read a message from in(out)box'
cmdTbl['readAll'] = 'Mard "read" for all inbox message(s)'
cmdTbl['unreadAll'] = 'Mark "unread" for all inbox message(s)'
cmdTbl['s9'] = '-'
cmdTbl['save'] = 'Save(Dump) a in(out)box message to disk'
cmdTbl['delete'] = 'Delete a(ll) in(out)box messages from remote'
cmdShorts = dict()
retStrings = dict({
'none': '',
'usercancel': '\n User canceled.\n',
'invalidinput': '\n Invalid input.\n',
'invalidindex': '\n Invalid message index.\n',
'invalidaddr': '\n Invalid address.\n',
'indexoutofbound': '\n Reach end of index.\n',
'bmsnotallow': '\n Daemon configure command not allowed.\n',
'nomain': '\n Cannot locate "bitmessagemain.py", daemon start failed.\n',
})
inputShorts = dict({
'yes': ['y', 'yes'],
'no': ['n', 'no'],
'exit': ['e', 'ex', 'exit'],
'save': ['save', 's', 'sv'],
'deterministic': ['d', 'dt'],
'random': ['r', 'rd', 'random'],
'message': ['m', 'msg', 'message'],
'broadcast': ['b', 'br', 'brd', 'broadcast'],
'inbox': ['i', 'in', 'ib', 'inbox'],
'outbox': ['o', 'ou', 'out', 'ob', 'outbox'],
'dump': ['d', 'dp', 'dump'],
'save': ['s', 'sa', 'save'],
'reply': ['r', 'rp', 'reply'],
'forward': ['f', 'fw', 'forward'],
'delete': ['d', 'del', 'delete'],
'all': ['a', 'all'],
})
inputs = dict()
def duplicated(out):
global cmdShorts
seen = dict()
dups = list()
dcmds = dict()
for x in out:
if x not in seen:
seen[x] = 1
else:
if seen[x] == 1:
dups.append(x)
seen[x] += 1
for x in dups:
for cmd in cmdShorts:
if x in cmdShorts[cmd]:
dcmds[cmd] = cmdShorts[cmd]
return dcmds
def cmdGuess():
global cmdTbl, cmdShorts
fullWords = [
'api', 'test', 'info', 'settings', 'quit', 'exit', 'set', 'list',
'add', 'addresses', 'subscrips', 'label', 'all', 'delete', 'join',
'scribe', 'build', 'in', 'out', 'box', 'new', 'create', 'end', 'shut',
'read', 'down', 'get', 'del', 'address',
'ubs', 'un', 'addressb', 'tatus', 'ave'
]
halfWords = [
'rate', 'lete', 'oin', 'lea', 'ead', 'eave', 'tatus', 'bke', 'un', 've',
'fo', 'dress', 'boo', 'lete', 'reate', 'dae', 'mon', 'reate', 'sa',
'inbox', 'outbox', 'send', 'in', 'add', 'news', 'all',
]
fullWords.sort(key=lambda item: (-len(item), item))
halfWords.sort(key=lambda item: (-len(item), item))
out = list()
# shorten
wordscounter = 0
for guessWords in [fullWords, halfWords]:
wordscounter += 1
for cmd in cmdTbl:
lcmd = cmd.lower()
if not any(cmdShorts.get(cmd, [])): # keep full command name
cmdShorts[cmd] = [lcmd]
for words in guessWords:
lwords = words.lower()
lcmd = lcmd.replace(lwords, lwords[0], 1)
if lcmd == lwords:
break
counter = len(cmdShorts[cmd])
if lcmd not in cmdShorts[cmd]:
if counter > 1 and len(lcmd) < len(cmdShorts[cmd][1]):
cmdShorts[cmd].insert(1, lcmd)
else:
cmdShorts[cmd].append(lcmd)
out.append(lcmd)
dcmds = duplicated(out)
if any(dcmds):
print '\n cmdGuess() Fail!'
print ' duplicated =', dcmds
print ' Change your "guessWords" list(%d).\n' % wordscounter
return False
cmdShorts['Command'] = ''
cmdShorts['exit'] = ''
cmdShorts['help'] = list(['help', 'h', '?'])
return True
def showCmdTbl():
global cmdTbl, cmdShorts
url = 'https://github.com/BitMessage/PyBitmessage'
print
print ''.join([5 * ' ', 73 * '-'])
print ''.join([5 * ' ', '|', url[:int(len(url)/2)].rjust(35), url[int(len(url)/2):].ljust(36), '|'])
print ''.join([5 * ' ', 73 * '-'])
for cmd in cmdTbl:
lcmd = ('' if len(cmd) > 18 else cmd + ' ') + str(cmdShorts[cmd][1:])
if len(lcmd) > 23:
lcmd = lcmd[:20] + '...'
des = cmdTbl[cmd]
if len(des) > 45:
des = des[:42] + '...'
if des == '-':
print '|'.join([5 * ' ', 24 * '-', 46 * '-', ''])
else:
print '| '.join([5 * ' ', lcmd.ljust(23), des.ljust(45), ''])
print ''.join([5 * ' ', 73 * '-'])
class Config(object):
def __init__(self, argv):
self.version = "0.4.0"
self.argv = argv
self.action = None
self.config_file = "client.dat" # for initial default value
self.conn = 'HTTP://127.0.0.1:8445/' # default API uri
self.createParser()
self.createArguments()
def createParser(self):
# Create parser
self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
self.parser.register('type', 'bool', self.strToBool)
self.subparsers = self.parser.add_subparsers(title="Actions", dest="action")
def __str__(self):
return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
def strToBool(self, v):
return v.lower() in ("yes", "true", "t", "1")
# Create command line arguments
def createArguments(self):
# this_file = os.path.abspath(__file__).replace("\\", "/").rstrip("cd")
# if this_file.endswith("/src/bitmessagecli.py"):
# main
action = self.subparsers.add_parser("main", help='Start this CLI (default)')
action = self.parser
self.parser.add_argument('--version', action='version', version='BitMessageCLI %s' % (self.version))
self.parser.add_argument('--start_daemon', help='Start BMs API daemon locally', default=None, metavar='BMs_host_uri')
# api settings
# action = self.subparsers.add_parser('api', help='Set API settings.')
action.add_argument('--api_username', help='BMs API basic auth user name.', default=None, metavar='username')
action.add_argument('--api_password', help='BMs API basic auth password.', default=None, metavar='password')
action.add_argument('--api_path', help='BMs API host address.', default=self.conn, metavar='ip:port')
action.add_argument('--api_type', help='BMs API hosts type.', default='HTTP', choices=["HTTP", "HTTPS"])
# proxy settings
# action = self.subparsers.add_parser('proxy', help='Use proxy for connections.')
action.add_argument('--proxy_username', help='Username to authenticate to the proxy server.', default=None, metavar='username')
action.add_argument('--proxy_password', help='Password to authenticate to the proxy server.', default=None, metavar='password')
action.add_argument('--proxy_path', help='Address of the proxy server.', default='127.0.0.1:1080', metavar='ip:port')
action.add_argument('--proxy_type', help='Proxy type.', default='none', choices=['none', 'SOCKS4', 'SOCKS5', 'HTTP'])
action.add_argument('--proxy_remotedns', help='Send DNS request to remote(socks proxied).', type='bool', choices=[True, False], default=True)
action.add_argument('--proxy_timeout', help='Network connection timeout.', default=30, type=int, metavar='seconds')
self.parser.add_argument('--config_file', help='Path of config file.', default=self.config_file, metavar='path')
self.parser.add_argument('--end', help='Stop multi value argument parsing(inner_use).', action='store_true')
return self.parser
# Find arguments specified for current action
def getActionArguments(self):
back = {}
arguments = self.parser._subparsers._group_actions[0].choices[self.action]._actions[0:] # First is --version
# for argument in arguments:
# if argument.dest != 'help':
# back[argument.dest] = getattr(self, argument.dest)
return back
# Try to find action from argv
def getAction(self, argv):
actions = [action.choices.keys() for action in self.parser._actions if action.dest == "action"][0] # Valid actions
found_action = False
for action in actions: # See if any in argv
if action in argv:
found_action = action
break
return found_action
# Move unknown parameters to end of argument list
def moveUnknownToEnd(self, argv, default_action):
valid_actions = sum([action.option_strings for action in self.parser._actions], [])
valid_parameters = []
unkown_parameters = []
unkown = False
for arg in argv:
if arg.startswith("--"):
if arg not in valid_actions:
unkown = True
else:
unkown = False
elif arg == default_action:
unkown = False
if unkown:
unkown_parameters.append(arg)
else:
valid_parameters.append(arg)
return valid_parameters + unkown_parameters
# Parse arguments from config file and command line
def parse(self, parse_config=True):
argv = self.argv[:] # Copy command line arguments
self.parseCommandline(argv) # Parse argv
self.setAttributes()
if parse_config:
argv = self.parseConfig(argv) # Add arguments from config file
self.parseCommandline(argv) # Parse argv
self.setAttributes()
# Parse command line arguments
def parseCommandline(self, argv):
# Find out if action is specificed on start
action = self.getAction(argv)
if not action:
argv.append("--end")
argv.append("main")
action = "main"
argv = self.moveUnknownToEnd(argv, action)
self.arguments = self.parser.parse_args(argv[1:])
# Parse config file
def parseConfig(self, argv):
# Find config file path from parameters
if "--config_file" in argv:
self.config_file = argv[argv.index("--config_file") + 1]
print '- Configuration loading . (%s)' % os.path.realpath(self.config_file)
if os.path.isfile(self.config_file):
config = ConfigParser.ConfigParser(allow_no_value=True)
config.read(self.config_file)
for section in config.sections():
for key, val in config.items(section):
if section != "global": # If not global prefix key with section
key = section + "_" + key
to_end = key == "start_daemon" # Prefer config value over argument
argv_extend = ["--%s" % key]
if val:
argv_extend.append(val)
if to_end:
argv = argv[:-1] + argv_extend + argv[-1:]
else:
argv = argv[:1] + argv_extend + argv[1:]
return argv
# Expose arguments as class attributes
def setAttributes(self):
# Set attributes from arguments
if self.arguments:
args = vars(self.arguments)
for key, val in args.items():
if type(val) is list:
val = val[:]
setattr(self, key, val)
def saveValue(self, key, value):
if not os.path.isfile(self.config_file):
content = ""
else:
content = open(self.config_file).read()
lines = content.splitlines()
global_line_i = None
key_line_i = None
i = 0
for line in lines:
if line.strip() == "[global]":
global_line_i = i
if line.startswith(key + " ="):
key_line_i = i
i += 1
if key_line_i and len(lines) > key_line_i + 1:
while True: # Delete previous multiline values
is_value_line = lines[key_line_i + 1].startswith(" ") or lines[key_line_i + 1].startswith("\t")
if not is_value_line:
break
del lines[key_line_i + 1]
if value is None: # Delete line
if key_line_i:
del lines[key_line_i]
else: # Add / update
if type(value) is list:
value_lines = [""] + [str(line).replace("\n", "").replace("\r", "") for line in value]
else:
value_lines = [str(value).replace("\n", "").replace("\r", "")]
new_line = "%s = %s" % (key, "\n ".join(value_lines))
if key_line_i: # Already in the config, change the line
lines[key_line_i] = new_line
elif global_line_i is None: # No global section yet, append to end of file
lines.append("[global]")
lines.append(new_line)
else: # Has global section, append the line after it
lines.insert(global_line_i + 1, new_line)
open(self.config_file, "w").write("\n".join(lines))
class Actions(object):
def call(self, function_name, kwargs):
print '- Original by .dok (Version 0.3.1) https://github.com/Dokument/PyBitmessage-Daemon'
print '- Modified by .pt (Version 0.4.0) https://github.com/BitMessage/PyBitmessage'
print '- Version: %s, Python %s' % (config.version, sys.version)
func = getattr(self, function_name, None)
back = func(**kwargs)
if back:
print back
# Default action: Start CLI only
def main(self, *argv):
while True:
try:
CLI()
except InputException as err:
print retStrings.get(err.resKey, '\n Not defined error raised: %s.\n' % err.resKey)
def api(self, api_username, api_password, api_path, api_type):
print 'action api: api_username:', api_username
def proxy(self, proxy_username, proxy_password, proxy_path, proxy_type, proxy_timeout):
print 'action proxy: proxy_username:', proxy_username
def start():
''' Call actions '''
# action_kwargs = config.getActionArguments()
actions.call(config.action, {})
# proxied start
# original https://github.com/benhengx/xmlrpclibex
# add basic auth support for top level host while none/HTTP proxied
class ProxyError(Exception): pass
class GeneralProxyError(ProxyError): pass
class Socks5AuthError(ProxyError): pass
class Socks5Error(ProxyError): pass
class Socks4Error(ProxyError): pass
class HTTPError(ProxyError): pass
def init_socks(proxy, timeout):
'''init a socks proxy socket.'''
import urllib
map_to_type = {
'SOCKS4': socks.PROXY_TYPE_SOCKS4,
'SOCKS5': socks.PROXY_TYPE_SOCKS5,
'HTTP': socks.PROXY_TYPE_HTTP
}
address_family = socket.AF_INET
ssock = socks.socksocket(address_family, socket.SOCK_STREAM)
ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# ssock.setsockopt()
if isinstance(timeout, (int, float)):
ssock.settimeout(timeout)
proxytype = map_to_type[proxy['proxy_type']]
rdns = proxy['proxy_remotedns']
addr, port = proxy['proxy_path'].split(':', 1)
port = int(port)
username = proxy['proxy_username']
password = proxy['proxy_password']
isauth = username and password
if isauth is True:
ssock.setproxy(proxytype, addr, port, username, password, rdns)
socks.setdefaultproxy(proxytype, addr, port, username, password, rdns)
else:
ssock.setproxy(proxytype, addr, port, rdns)
socks.setdefaultproxy(proxytype, addr, port, rdns)
socket.socket = socks.socksocket
# ssock.connect(("www.google.com", 443))
# urllib.urlopen("https://www.google.com/")
return ssock
class SocksProxiedHTTPConnection(httplib.HTTPConnection):
'''Proxy the http connection through a socks proxy.'''
def init_socks(self, proxy):
self.ssock = init_socks(proxy, self.timeout)
def connect(self):
self.ssock.connect((self.host, self.port))
self.sock = self.ssock
class SocksProxiedHTTPSConnection(httplib.HTTPSConnection):
'''Proxy the https connection through a socks proxy.'''
def init_socks(self, proxy):
self.ssock = init_socks(proxy, self.timeout)
def connect(self):
self.ssock.connect((self.host, self.port))
self.sock = ssl.wrap_socket(self.ssock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1)
def close(self):
httplib.HTTPSConnection.close(self)
if self.ssock:
self.ssock.close()
self.ssock = None
class TransportWithTo(xmlrpclib.Transport):
'''Transport support timeout'''
cls_http_conn = httplib.HTTPConnection
cls_https_conn = httplib.HTTPSConnection
def __init__(self, use_datetime=0, is_https=False, timeout=None):
xmlrpclib.Transport.__init__(self, use_datetime)
self.is_https = is_https
if timeout is None:
timeout = socket._GLOBAL_DEFAULT_TIMEOUT
self.timeout = timeout
def make_connection(self, host):
self.realhost = host
if self._connection and host == self._connection[0]:
return self._connection[1]
# create a HTTP/HTTPS connection object from a host descriptor
# host may be a string, or a (host, x509-dict) tuple
# no basic auth head returns here
chost, self._extra_headers, x509 = self.get_host_info(host)
# store the host argument along with the connection object
if self.is_https: # xmlrpclib.SafeTransport + timeout
self._connection = host, self.cls_https_conn(chost, None, timeout=self.timeout, **(x509 or {}))
else: # xmlrpclib.Transport + timeout
self._connection = host, self.cls_http_conn(chost, timeout=self.timeout)
return self._connection[1]
# def send_request(self, connection, handler, request_body):
# connection.putrequest('POST', '%s://%s' % (self.realhost, handler))
# def send_host(self, connection, host):
# connection.putheader('Host', self.realhost)
# def send_user_agent(self, connection):
# connection.putheader("User-Agent", self.send_user_agent)
class ProxiedTransportWithTo(TransportWithTo):
'''Transport supports timeout and http proxy'''
def __init__(self, proxy, use_datetime=0, timeout=None, api_cred=None):
TransportWithTo.__init__(self, use_datetime, False, timeout)
self.api_cred = api_cred
self.proxy_path = proxy['proxy_path']
if proxy['proxy_username'] and proxy['proxy_password']:
self.proxy_cred = base64.encodestring('%s:%s' % (unquote(proxy['proxy_username']), unquote(proxy['proxy_password']))).strip()
else:
self.proxy_cred = None
def request(self, host, handler, request_body, verbose=False):
realhandler = 'HTTP://%s%s' % (host, handler)
return TransportWithTo.request(self, host, realhandler, request_body, verbose)
def make_connection(self, host):
return TransportWithTo.make_connection(self, self.proxy_path)
def send_content(self, connection, request_body):
if self.proxy_cred:
connection.putheader('Proxy-Authorization', 'Basic ' + self.proxy_cred)
if self.api_cred:
connection.putheader('Authorization', 'Basic ' + self.api_cred)
return TransportWithTo.send_content(self, connection, request_body)
class SocksProxiedTransportWithTo(TransportWithTo):
'''Transport supports timeout and socks SOCKS4/SOCKS5 and http connect tunnel'''
cls_http_conn = SocksProxiedHTTPConnection
cls_https_conn = SocksProxiedHTTPSConnection
def __init__(self, proxy, use_datetime=0, is_https=False, timeout=None):
TransportWithTo.__init__(self, use_datetime, is_https, timeout)
self.proxy = proxy
def make_connection(self, host):
conn = TransportWithTo.make_connection(self, host)
conn.init_socks(self.proxy)
return conn
class Proxiedxmlrpclib(xmlrpclib.ServerProxy):
"""New added keyword arguments
timeout: seconds waiting for the socket
proxy: a dict specify the proxy settings, it supports the following fields:
proxy_path: the address of the proxy server. default: 127.0.0.1:1080
proxy_username: username to authenticate to the server. default None
proxy_password: password to authenticate to the server, only relevant when
username is set. default None
proxy_type: string, 'SOCKS4', 'SOCKS5', 'HTTP' (HTTP connect tunnel), only
relevant when is_socks is True. default 'SOCKS5'
"""
def __init__(self, uri, transport=None, encoding=None, verbose=0,
allow_none=0, use_datetime=0, timeout=None, proxy=None):
scheme, netloc, path, x, xx, xxx = urlparse(uri)
api_username = unquote(urlparse(uri).username)
api_password = unquote(urlparse(uri).password)
api_cred = None
self.uri = uri
if api_username and api_password:
api_cred = base64.encodestring('%s:%s' % (api_username, api_password)).strip()
netloc = netloc.split('@')[1]
if transport is None and (timeout or proxy):
is_https = scheme == 'https'
if proxy.get('proxy_type', 'none') == 'none':
transport = TransportWithTo(use_datetime, is_https, timeout)
else:
timeout = proxy.get('timeout', timeout) # overide default timeout from proxy dict
is_socks = 'SOCKS' in proxy['proxy_type']
if is_https and not is_socks: # set default HTTP type for https uri
# https must be tunnelled through http connect
is_socks = True
proxy['proxy_type'] = 'HTTP'
if not is_socks: # http proxy
self.uri = '%s://%s%s' % (scheme, netloc, path)
transport = ProxiedTransportWithTo(proxy, use_datetime, timeout, api_cred)
else: # http connect and socksx
transport = SocksProxiedTransportWithTo(proxy, use_datetime, is_https, timeout)
xmlrpclib.ServerProxy.__init__(self, self.uri, transport, encoding, verbose, allow_none, use_datetime)
# proxied end
class BMAPIWrapper(object):
def set_proxy(self, proxy=None):
self.proxy = proxy
self.__init__(self.conn, self.proxy)
def __init__(self, uri=None, proxy=None):
self.proxy = proxy
self.conn = uri
proxied = 'non-proxied'
if proxy:
proxied = proxy['proxy_type'] + ' | ' + proxy['proxy_path']
try:
self.xmlrpc = Proxiedxmlrpclib(uri, verbose=False, allow_none=True, use_datetime=True, timeout=30, proxy=self.proxy)
print '\n XML-RPC initialed on: "%s" (%s)' % (self.conn, proxied)
except Exception as err: # IOError, unsupported XML-RPC protocol/
self.xmlrpc = None
print '\n XML-RPC initial failed on: "%s" - {%s}\n' % (self.conn, err)
# traceback.print_exc()
def __getattr__(self, apiname):
attr = getattr(self.xmlrpc, apiname, None)
def wrapper(*args, **kwargs):
error = 0
result = ''
errormsg = ''
try:
if attr is None:
error = 1
errormsg = ' Not prepared for calling API methods. (%s)' % apiname
return {'error': error, 'result': result, 'errormsg': errormsg}
response = attr(*args, **kwargs)
if type(response) is str and ("API Error" in response or 'RPC ' in response): # API Error, Authorization Error, Proxy Error
error = 2
if "API Error" in response:
error = getAPIErrorCode(response)
if error in [20, 21]: # programing error, Invalid method/Unexpected API Failure
print '\n Maybe no such API method:', apiname
print ' Try helping:', self.xmlrpc.system.listMethods() if error == 20 else self.xmlrpc.system.methodHelp(apiname)
errormsg = '\n ' + response + '\n'
return {'error': error, 'result': result, 'errormsg': errormsg}
if apiname in [
'add',
'helloWorld',
'statusBar',
]:
result = response
else: # pre-checking for API returns
try:
if apiname in [
'getAllInboxMessageIDs',
'getAllInboxMessageIds',
]:
result = json.loads(response)['inboxMessageIds']
elif apiname in [
'getInboxMessageByID',
'getInboxMessageById',
]:
result = json.loads(response)['inboxMessage']
elif apiname in [
'GetAllInboxMessages',
'getInboxMessagesByReceiver',
'getInboxMessagesByAddress',
]:
result = json.loads(response)['inboxMessages']
elif apiname in [
'getAllSentMessageIDs',
'getAllSentMessageIds',
]:
result = json.loads(response)['sentMessageIds']
elif apiname in [
'getAllSentMessages',
'getSentMessagesByAddress',
'getSentMessagesBySender',
'getSentMessageByAckData',
]:
result = json.loads(response)['sentMessages']
elif apiname in [
'getSentMessageByID',
'getSentMessageById',
]:
result = json.loads(response)['sentMessage']
elif apiname in [
'listAddressBookEntries',
'listAddressbook',
'listAddresses',
'listAddressbook',
'createDeterministicAddresses',
]:
result = json.loads(response)['addresses']
elif apiname in [
'listSubscriptions',
]:
result = json.loads(response)['subscriptions']
elif apiname in [
'decodeAddress',
'clientStatus',
'getMessageDataByDestinationHash',
'getMessageDataByDestinationTag',
]:
result = json.loads(response)
elif apiname in [
'addSubscription',
'deleteSubscription',
'createChan',
'joinChan',
'leaveChan',
'sendMessage',
'sendBroadcast',
'getStatus',
'trashMessage',
'trashInboxMessage',
'trashSentMessageByAckData',
'trashSentMessage',
'addAddressBK',
'addAddressbook',
'delAddressBK',
'deleteAddressbook',
'createRandomAddress',
'getDeterministicAddress',
'deleteAddress',
'disseminatePreEncryptedMsg',
'disseminatePubkey',
'deleteAndVacuum',
'shutdown',
]:
result = response
else:
error = 99
errormsg = '\n BMAPIWrapper error: unexpected api. <%s>\n' % apiname
except ValueError as err: # json.loads error
error = 3
result = response
errormsg = '\n Server returns unexpected data, maybe a network problem there? (%s)\n' % err
except TypeError as err: # unsupported XML-RPC protocol
error = -1
errormsg = '\n XML-RPC not initialed correctly: %s.\n' % str(err)
# traceback.print_exc()
except (ProxyError, GeneralProxyError, Socks5AuthError, Socks5Error, Socks4Error, HTTPError, socket.error, xmlrpclib.ProtocolError, xmlrpclib.Fault) as err: # (xmlrpclib.Error, ConnectionError, socks.GeneralProxyError)
error = -2
errormsg = '\n Connection error: %s.\n' % str(err)
# traceback.print_exc()
except Exception: # /httplib.BadStatusLine: connection close immediatly
error = -99
errormsg = '\n Unexpected error: %s.\n' % sys.exc_info()[0]
# traceback.print_exc()
# print json.dumps({'error': error, 'result': result, 'errormsg': errormsg})
return {'error': error, 'result': result, 'errormsg': errormsg}
return wrapper
class InputException(Exception):
def __init__(self, resKey):
Exception.__init__(self, resKey)
self.resKey = resKey
def __str__(self):
return self.resKey
def inputAddress(prompt='What is the address?'):
global retStrings
src = retStrings['invalidaddr']
while True:
address = userInput(prompt + '\nTry again or')
if not validAddress(address):
print src
continue
else:
break
return address
def inputIndex(prompt='Input a index: ', maximum=-1, alter=[]):
global retStrings
while True:
cinput = userInput(prompt + '\nTry again, (c) or').lower()
try:
if cinput == "c":
cinput = '-1'
raise InputException('usercancel')
elif cinput in alter:
break
elif int(cinput) < 0 or (maximum >= 0 and int(cinput) > maximum):
src = retStrings['invalidindex']
print src
else:
break
except (InputException, KeyboardInterrupt) as err:
raise
except ValueError:
src = retStrings['invalidinput']
print src
return cinput
def userInput(message):
"""Checks input for exit or quit. Also formats for input, etc"""
global usrPrompt
global cmdStr, retStrings
print '\n' + message
uInput = raw_input('> ')
stack = list(inspect.stack())
where = ''.join([
str(stack[3][2]),
stack[3][3],
str(stack[2][2]),
stack[1][3],
str(stack[1][2]),
stack[3][3],
cmdStr
])
print ('\n%s (exit) to cancel.\nPress Enter to input default [%s]: ' % (message, inputs.get(where, '')))
uInput = raw_input('>')
if uInput.lower() == 'exit': # Returns the user to the main menu
usrPrompt = 1
main()
raise InputException('usercancel')
elif uInput.lower() == 'quit': # Quits the program
print '\n Bye\n'
sys.exit(0)
elif uInput == '': # Return last value.
return inputs.get(where, '')
else:
return uInput
inputs[where] = uInput
def restartBmNotify():
"""Prompt the user to restart Bitmessage"""
print '\n *******************************************************************'
print ' WARNING: If Bitmessage is running locally, you must restart it now.'
print ' *******************************************************************\n'
# Begin keys.dat interactions
def lookupAppdataFolder():
"""gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py"""
APPNAME = "PyBitmessage"
if sys.platform == 'darwin':
if "HOME" in os.environ:
dataFolder = os.path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/'
else:
print(
' Could not find home folder, please report '
'this message and your OS X version to the Daemon Github.')
sys.exit(1)
elif 'win32' in sys.platform or 'win64' in sys.platform:
dataFolder = os.path.join(os.environ['APPDATA'], APPNAME) + '\\'
else:
dataFolder = os.path.expanduser(os.path.join("~", ".config/" + APPNAME + "/"))
return dataFolder
def configInit():
"""Initialised the configuration"""
BMConfigParser().add_section('bitmessagesettings')
# Sets the bitmessage port to stop the warning about the api not properly
# being setup. This is in the event that the keys.dat is in a different
# directory or is created locally to connect to a machine remotely.
BMConfigParser().set('bitmessagesettings', 'port', '8444')
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true') # Sets apienabled to true in keys.dat
with open(keysName, 'wb') as configfile:
BMConfigParser().write(configfile)
print '\n ' + str(keysName) + ' Initalized in the same directory as daemon.py'
print ' You will now need to configure the ' + str(keysName) + ' file.\n'
def apiInit(apiEnabled):
"""Initialise the API"""
global usrPrompt
BMConfigParser().read(keysPath)
if apiEnabled is False: # API information there but the api is disabled.
uInput = userInput("The API is not enabled. Would you like to do that now, (Y)es or (N)o?").lower()
if uInput == "y":
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true') # Sets apienabled to true in keys.dat
with open(keysPath, 'wb') as configfile:
BMConfigParser().write(configfile)
print 'Done'
restartBmNotify()
return True
elif uInput == "n":
print ' \n************************************************************'
print ' Daemon will not work when the API is disabled. '
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ************************************************************\n'
usrPrompt = 1
main()
else:
print '\n Invalid Entry\n'
usrPrompt = 1
main()
elif apiEnabled: # API correctly setup
# Everything is as it should be
return True
else: # API information was not present.
print '\n ' + str(keysPath) + ' not properly configured!\n'
uInput = userInput("Would you like to do this now, (Y)es or (N)o?").lower()
if uInput == "y": # User said yes, initalize the api by writing these values to the keys.dat file
print ' '
apiUsr = userInput("API Username")
apiPwd = userInput("API Password")
apiPort = userInput("API Port")
apiEnabled = userInput("API Enabled? (True) or (False)").lower()
daemon = userInput("Daemon mode Enabled? (True) or (False)").lower()
if (daemon != 'true' and daemon != 'false'):
print '\n Invalid Entry for Daemon.\n'
uInput = 1
main()
print ' -----------------------------------\n'
# sets the bitmessage port to stop the warning about the api not properly
# being setup. This is in the event that the keys.dat is in a different
# directory or is created locally to connect to a machine remotely.
BMConfigParser().set('bitmessagesettings', 'port', '8444')
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true')
BMConfigParser().set('bitmessagesettings', 'apiport', apiPort)
BMConfigParser().set('bitmessagesettings', 'apiinterface', '127.0.0.1')
BMConfigParser().set('bitmessagesettings', 'apiusername', apiUsr)
BMConfigParser().set('bitmessagesettings', 'apipassword', apiPwd)
BMConfigParser().set('bitmessagesettings', 'daemon', daemon)
with open(keysPath, 'wb') as configfile:
BMConfigParser().write(configfile)
print '\n Finished configuring the keys.dat file with API information.\n'
restartBmNotify()
return True
elif uInput == "n":
print '\n ***********************************************************'
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ***********************************************************\n'
usrPrompt = 1
main()
else:
print ' \nInvalid entry\n'
usrPrompt = 1
main()
def apiData():
"""TBC"""
global keysName
global keysPath
global usrPrompt
BMConfigParser().read(keysPath) # First try to load the config file (the keys.dat file) from the program directory
try:
BMConfigParser().get('bitmessagesettings', 'port')
appDataFolder = ''
except:
# Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory.
appDataFolder = lookupAppdataFolder()
keysPath = appDataFolder + keysPath
BMConfigParser().read(keysPath)
try:
BMConfigParser().get('bitmessagesettings', 'port')
except:
# keys.dat was not there either, something is wrong.
print '\n ******************************************************************'
print ' There was a problem trying to access the Bitmessage keys.dat file'
print ' or keys.dat is not set up correctly'
print ' Make sure that daemon is in the same directory as Bitmessage. '
print ' ******************************************************************\n'
uInput = userInput("Would you like to create a keys.dat in the local directory, (Y)es or (N)o?").lower()
if (uInput == "y" or uInput == "yes"):
configInit()
keysPath = keysName
usrPrompt = 0
main()
elif (uInput == "n" or uInput == "no"):
print '\n Trying Again.\n'
usrPrompt = 0
main()
else:
print '\n Invalid Input.\n'
usrPrompt = 1
main()
try: # checks to make sure that everyting is configured correctly. Excluding apiEnabled, it is checked after
BMConfigParser().get('bitmessagesettings', 'apiport')
BMConfigParser().get('bitmessagesettings', 'apiinterface')
BMConfigParser().get('bitmessagesettings', 'apiusername')
BMConfigParser().get('bitmessagesettings', 'apipassword')
except:
apiInit("") # Initalize the keys.dat file with API information
# keys.dat file was found or appropriately configured, allow information retrieval
# apiEnabled =
# apiInit(BMConfigParser().safeGetBoolean('bitmessagesettings','apienabled'))
# #if false it will prompt the user, if true it will return true
BMConfigParser().read(keysPath) # read again since changes have been made
apiPort = int(BMConfigParser().get('bitmessagesettings', 'apiport'))
apiInterface = BMConfigParser().get('bitmessagesettings', 'apiinterface')
apiUsername = BMConfigParser().get('bitmessagesettings', 'apiusername')
apiPassword = BMConfigParser().get('bitmessagesettings', 'apipassword')
print '\n API data successfully imported.\n'
# Build the api credentials
return "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface + ":" + str(apiPort) + "/"
# End keys.dat interactions
return uInput
def apiTest():
"""Tests the API connection to bitmessage. Returns true if it is connected."""
try:
result = api.add(2, 3)
except:
return False
return result == 5
def bmSettings():
"""Allows the viewing and modification of keys.dat settings."""
global keysPath
global usrPrompt
keysPath = 'keys.dat'
BMConfigParser().read(keysPath) # Read the keys.dat
try:
port = BMConfigParser().get('bitmessagesettings', 'port')
except:
print '\n File not found.\n'
usrPrompt = 0
main()
startonlogon = BMConfigParser().safeGetBoolean('bitmessagesettings', 'startonlogon')
minimizetotray = BMConfigParser().safeGetBoolean('bitmessagesettings', 'minimizetotray')
showtraynotifications = BMConfigParser().safeGetBoolean('bitmessagesettings', 'showtraynotifications')
startintray = BMConfigParser().safeGetBoolean('bitmessagesettings', 'startintray')
defaultnoncetrialsperbyte = BMConfigParser().get('bitmessagesettings', 'defaultnoncetrialsperbyte')
defaultpayloadlengthextrabytes = BMConfigParser().get('bitmessagesettings', 'defaultpayloadlengthextrabytes')
daemon = BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon')
socksproxytype = BMConfigParser().get('bitmessagesettings', 'socksproxytype')
sockshostname = BMConfigParser().get('bitmessagesettings', 'sockshostname')
socksport = BMConfigParser().get('bitmessagesettings', 'socksport')
socksauthentication = BMConfigParser().safeGetBoolean('bitmessagesettings', 'socksauthentication')
socksusername = BMConfigParser().get('bitmessagesettings', 'socksusername')
sockspassword = BMConfigParser().get('bitmessagesettings', 'sockspassword')
print '\n -----------------------------------'
print ' | Current Bitmessage Settings |'
print ' -----------------------------------'
print ' port = ' + port
print ' startonlogon = ' + str(startonlogon)
print ' minimizetotray = ' + str(minimizetotray)
print ' showtraynotifications = ' + str(showtraynotifications)
print ' startintray = ' + str(startintray)
print ' defaultnoncetrialsperbyte = ' + defaultnoncetrialsperbyte
print ' defaultpayloadlengthextrabytes = ' + defaultpayloadlengthextrabytes
print ' daemon = ' + str(daemon)
print '\n ------------------------------------'
print ' | Current Connection Settings |'
print ' -----------------------------------'
print ' socksproxytype = ' + socksproxytype
print ' sockshostname = ' + sockshostname
print ' socksport = ' + socksport
print ' socksauthentication = ' + str(socksauthentication)
print ' socksusername = ' + socksusername
print ' sockspassword = ' + sockspassword
print ' '
uInput = userInput("Would you like to modify any of these settings, (Y)es or (N)o?").lower()
if uInput == "y":
while True: # loops if they mistype the setting name, they can exit the loop with 'exit'
invalidInput = False
uInput = userInput("What setting would you like to modify?").lower()
print ' '
if uInput == "port":
print ' Current port number: ' + port
uInput = userInput("Enter the new port number.")
BMConfigParser().set('bitmessagesettings', 'port', str(uInput))
elif uInput == "startonlogon":
print ' Current status: ' + str(startonlogon)
uInput = userInput("Enter the new status.")
BMConfigParser().set('bitmessagesettings', 'startonlogon', str(uInput))
elif uInput == "minimizetotray":
print ' Current status: ' + str(minimizetotray)
uInput = userInput("Enter the new status.")
BMConfigParser().set('bitmessagesettings', 'minimizetotray', str(uInput))
elif uInput == "showtraynotifications":
print ' Current status: ' + str(showtraynotifications)
uInput = userInput("Enter the new status.")
BMConfigParser().set('bitmessagesettings', 'showtraynotifications', str(uInput))
elif uInput == "startintray":
print ' Current status: ' + str(startintray)
uInput = userInput("Enter the new status.")
BMConfigParser().set('bitmessagesettings', 'startintray', str(uInput))
elif uInput == "defaultnoncetrialsperbyte":
print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte
uInput = userInput("Enter the new defaultnoncetrialsperbyte.")
BMConfigParser().set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput))
elif uInput == "defaultpayloadlengthextrabytes":
print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes
uInput = userInput("Enter the new defaultpayloadlengthextrabytes.")
BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput))
elif uInput == "daemon":
print ' Current status: ' + str(daemon)
uInput = userInput("Enter the new status.").lower()
BMConfigParser().set('bitmessagesettings', 'daemon', str(uInput))
elif uInput == "socksproxytype":
print ' Current socks proxy type: ' + socksproxytype
print "Possibilities: 'none', 'SOCKS4a', 'SOCKS5'."
uInput = userInput("Enter the new socksproxytype.")
BMConfigParser().set('bitmessagesettings', 'socksproxytype', str(uInput))
elif uInput == "sockshostname":
print ' Current socks host name: ' + sockshostname
uInput = userInput("Enter the new sockshostname.")
BMConfigParser().set('bitmessagesettings', 'sockshostname', str(uInput))
elif uInput == "socksport":
print ' Current socks port number: ' + socksport
uInput = userInput("Enter the new socksport.")
BMConfigParser().set('bitmessagesettings', 'socksport', str(uInput))
elif uInput == "socksauthentication":
print ' Current status: ' + str(socksauthentication)
uInput = userInput("Enter the new status.")
BMConfigParser().set('bitmessagesettings', 'socksauthentication', str(uInput))
elif uInput == "socksusername":
print ' Current socks username: ' + socksusername
uInput = userInput("Enter the new socksusername.")
BMConfigParser().set('bitmessagesettings', 'socksusername', str(uInput))
elif uInput == "sockspassword":
print ' Current socks password: ' + sockspassword
uInput = userInput("Enter the new password.")
BMConfigParser().set('bitmessagesettings', 'sockspassword', str(uInput))
else:
print "\n Invalid input. Please try again.\n"
invalidInput = True
if invalidInput is not True: # don't prompt if they made a mistake.
uInput = userInput("Would you like to change another setting, (Y)es or (N)o?").lower()
if uInput != "y":
print '\n Changes Made.\n'
with open(keysPath, 'wb') as configfile:
BMConfigParser().write(configfile)
restartBmNotify()
break
elif uInput == "n":
usrPrompt = 1
main()
else:
print "Invalid input."
usrPrompt = 1
main()
response = api.add(2, 3)
return response['result'] == 5 if response['error'] == 0 else False
def validAddress(address):
"""Predicate to test address validity"""
address_information = json.loads(api.decodeAddress(address))
return 'success' in str(address_information['status']).lower()
print ' Validating...', address
response = api.decodeAddress(address)
if response['error'] != 0:
print response['errormsg']
return False
return 'success' in response['result']['status'].lower()
def getAddress(passphrase, vNumber, sNumber):
"""Get a deterministic address"""
passphrase = passphrase.encode('base64') # passphrase must be encoded
return api.getDeterministicAddress(passphrase, vNumber, sNumber)
passPhrase = passphrase.encode('base64') # passphrase must be encoded
print ' Getting address:', passphrase
response = api.getDeterministicAddress(passPhrase, vNumber, sNumber)
if response['error'] != 0:
return response['errormsg']
print ' Address:', response['result']
def subscribe():
def subscribe(address, label):
"""Subscribe to an address"""
global usrPrompt
while True:
address = userInput("What address would you like to subscribe to?")
if address == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(address) is False:
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
label = userInput("Enter a label for this address.")
label = label.encode('base64')
print ' Subscribing address:', label
response = api.addSubscription(address, label)
if response['error'] != 0:
return response['errormsg']
api.addSubscription(address, label)
print '\n You are now subscribed to: ' + address + '\n'
return '\n ' + response['result']
def unsubscribe():
def unsubscribe(address):
"""Unsusbcribe from an address"""
global usrPrompt
while True:
address = userInput("What address would you like to unsubscribe from?")
print ' unSubscribing address:', address
response = api.deleteSubscription(address)
if response['error'] != 0:
return response['errormsg']
if address == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(address) is False:
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
userInput("Are you sure, (Y)es or (N)o?").lower() # uInput =
api.deleteSubscription(address)
print '\n You are now unsubscribed from: ' + address + '\n'
return '\n ' + response['result']
def listSubscriptions():
"""List subscriptions"""
global usrPrompt
print '\nLabel, Address, Enabled\n'
try:
print api.listSubscriptions()
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' '
print ' Subscribed list retrieving...'
response = api.listSubscriptions()
if response['error'] != 0:
return response['errormsg']
jsonAddresses = response['result']
numAddresses = len(jsonAddresses)
print
print ' ------------------------------------------------------------------------'
print ' | # | Label | Address |Enabled|'
print ' |----|--------------------|------------------------------------|-------|'
for addNum in range(0, numAddresses): # processes all of the addresses and lists them out
label = (jsonAddresses[addNum]['label'].decode('base64')).encode(
'utf-8') # may still misdiplay in some consoles
address = str(jsonAddresses[addNum]['address'])
enabled = str(jsonAddresses[addNum]['enabled'])
if len(label) > 19:
label = label[:16] + '...'
print '| '.join([' ', str(addNum).ljust(3), label.ljust(19), address.ljust(35), enabled.ljust(6), '', ])
print ''.join([' ', 72 * '-', '\n', ])
return ''
def createChan():
def createChan(password):
"""Create a channel"""
global usrPrompt
password = userInput("Enter channel name")
password = password.encode('base64')
try:
print api.createChan(password)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
base64 = password.encode('base64')
print ' Channel creating...', password
response = api.createChan(base64)
if response['error'] != 0:
return response['errormsg']
return '\n ' + response['result']
def joinChan():
"""Join a channel"""
global usrPrompt
while True:
address = userInput("Enter channel address")
uInput = ''
address = inputAddress('Enter channel address')
while uInput == '':
uInput = userInput('Enter channel name[1~]')
password = uInput.encode('base64')
if address == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(address) is False:
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
print ' Channel joining...', uInput
response = api.joinChan(password, address)
if response['error'] != 0:
return response['errormsg']
password = userInput("Enter channel name")
password = password.encode('base64')
try:
print api.joinChan(password, address)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return '\n ' + response['result']
def leaveChan():
"""Leave a channel"""
global usrPrompt
while True:
address = userInput("Enter channel address")
address = inputAddress("Enter channel address")
print ' Channel leaving...', 'address'
response = api.leaveChan(address)
if response['error'] != 0:
return response['errormsg']
if address == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(address) is False:
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
try:
print api.leaveChan(address)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return '\n ' + response['result']
def listAdd():
"""List all of the addresses and their info"""
global usrPrompt
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) # Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
# print '\nAddress Number,Label,Address,Stream,Enabled\n'
print '\n --------------------------------------------------------------------------'
print ' | # | Label | Address |S#|Enabled|'
print ' |---|-------------------|-------------------------------------|--|-------|'
print ' Retrieving...', 'Senders'
response = api.listAddresses()
if response['error'] != 0:
return response['errormsg']
jsonAddresses = response['result']
numAddresses = len(jsonAddresses) # Number of addresses
# print '\nAddress Index,Label,Address,Stream,Enabled\n'
print
print ' ------------------------------------------------------------------------------'
print ' | # | Label | Address |S# |Enabled|'
print ' |----|--------------------|--------------------------------------|---|-------|'
for addNum in range(0, numAddresses): # processes all of the addresses and lists them out
label = (jsonAddresses['addresses'][addNum]['label']).encode(
'utf') # may still misdiplay in some consoles
address = str(jsonAddresses['addresses'][addNum]['address'])
stream = str(jsonAddresses['addresses'][addNum]['stream'])
enabled = str(jsonAddresses['addresses'][addNum]['enabled'])
label = jsonAddresses[addNum]['label'].encode(
'utf-8') # may still misdiplay in some consoles
address = str(jsonAddresses[addNum]['address'])
stream = str(jsonAddresses[addNum]['stream'])
enabled = str(jsonAddresses[addNum]['enabled'])
if len(label) > 19:
label = label[:16] + '...'
print ''.join([
' |',
str(addNum).ljust(3),
'|',
label.ljust(19),
'|',
address.ljust(37),
'|',
stream.ljust(1),
'|',
enabled.ljust(7),
'|',
])
print '| '.join([' ', str(addNum).ljust(3), label.ljust(19), address.ljust(37), stream.ljust(2), enabled.ljust(6), '', ])
print ''.join([
' ',
74 * '-',
'\n',
])
print ''.join([' ', 78 * '-', '\n', ])
return ''
def genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe):
"""Generate address"""
global usrPrompt
if deterministic is False: # Generates a new address with the user defined label. non-deterministic
addressLabel = lbl.encode('base64')
try:
generatedAddress = api.createRandomAddress(addressLabel)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' Address requesting...', lbl
response = api.createRandomAddress(addressLabel)
if response['error'] != 0:
return response['errormsg']
return generatedAddress
else: # Generates a new deterministic address with the user inputs.
passPhrase = passphrase.encode('base64')
print ' Address deterministic...', passphrase
response = api.createDeterministicAddresses(passPhrase, numOfAdd, addVNum, streamNum, ripe)
if response['error'] != 0:
return response['errormsg']
elif deterministic: # Generates a new deterministic address with the user inputs.
passphrase = passphrase.encode('base64')
try:
generatedAddress = api.createDeterministicAddresses(passphrase, numOfAdd, addVNum, streamNum, ripe)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return generatedAddress
return 'Entry Error'
return '\n Address:', response['result']
def saveFile(fileName, fileData):
def getBase64Len(x=''):
return int(len(x)*(3/4)) - 2 if x[-2:] == '==' else 1 if x[-1] == '=' else 0
def dump2File(fileName, fileData, deCoded):
"""Allows attachments and messages/broadcats to be saved"""
global inputShorts
# This section finds all invalid characters and replaces them with ~
fileName = fileName.replace(" ", "")
fileName = fileName.replace("/", "~")
# fileName = fileName.replace("\\", "~") How do I get this to work...?
fileName = fileName.replace(":", "~")
fileName = fileName.replace("*", "~")
fileName = fileName.replace("?", "~")
fileName = fileName.replace('"', "~")
fileName = fileName.replace("<", "~")
fileName = fileName.replace(">", "~")
fileName = fileName.replace("|", "~")
for s in ' /\\:*?"<>|':
fileName = fileName.replace(s, '~')
directory = os.path.abspath('attachments')
if not os.path.exists(directory):
os.makedirs(directory)
try:
os.makedirs(directory)
except OSError as err:
return '\n %s.\n' % str(err)
# return '\n Failed creating ' + directory + '\n'
except Exception:
return '\n Unexpected error: %s.\n' % sys.exc_info()[0]
filePath = os.path.join(directory, fileName)
with open(filePath, 'wb+') as path_to_file:
path_to_file.write(fileData.decode("base64"))
print '\n Successfully saved ' + filePath + '\n'
if not deCoded:
x = filter(lambda z: not re.match(r'^\s*$', z), fileData)
trydecode = False
if len(x) % 4 == 0: # check by length before decode.
trydecode = True
else:
print '\n'.join([
' -----------------------------------',
' Contents seems not "BASE64" encoded. (base on length check)',
' Start[%d] ~ Ends[%d].' % (x[:3], x[-3:]),
' About: %d(bytes).' % getBase64Len(x),
' FileName: "%s"' % fileName,
])
uInput = userInput('Try to decode it anyway, (n)o or (Y)es?')
if uInput not in inputShorts['no']:
trydecode = True
if trydecode is True:
try:
y = x.decode('base64', 'strict')
if x == y.encode('base64').replace('\n', ''): # double check decoded string.
fileData = y
else:
print '\n Failed on "BASE64" re-encode checking.\n'
except ValueError:
return '\n Failed on "BASE64" decoding.\n'
else:
print '\n Not "BASE64" contents, dump to file directly.'
try:
with open(filePath, 'wb+') as path_to_file:
path_to_file.write(fileData)
except IOError as err:
return '\n %s.\n' % str(err)
# return '\n Failed on operating: "' + filePath + '"\n'
except Exception:
return '\n Unexpected error: %s.\n' % sys.exc_info()[0]
return ' Successfully saved to: "' + filePath + '"'
def attachment():
@ -654,20 +1161,21 @@ def attachment():
theAttachmentS = ''
while True:
global inputShorts
for counter in range(1, 3): # maximum 3 of attachments
isImage = False
theAttachment = ''
while True: # loops until valid path is entered
filePath = userInput(
'\nPlease enter the path to the attachment or just the attachment name if in this folder.')
'\nPlease enter the path to the attachment or just the attachment name if in this folder[Max:180MB], %d/3 allowed.' % counter)
try:
with open(filePath):
break
except IOError:
print '\n %s was not found on your filesystem or can not be opened.\n' % filePath
print '\n Failed open file on: ', filePath + '\n'
# print filesize, and encoding estimate with confirmation if file is over X size (1mb?)
invSize = os.path.getsize(filePath)
@ -675,26 +1183,22 @@ def attachment():
round(invSize, 2) # Rounds to two decimal places
if invSize > 500.0: # If over 500KB
print ''.join([
'\n WARNING:The file that you are trying to attach is ',
invSize,
'KB and will take considerable time to send.\n'
])
uInput = userInput('Are you sure you still want to attach it, (Y)es or (N)o?').lower()
print '\n WARNING:The file that you are trying to attach is %d(KB) and will take considerable time to send.\n' % invSize
uInput = userInput('Are you sure you still want to attach it, (y)es or (N)o?').lower()
if uInput not in inputShorts['yes']:
return '\n Attachment discarded.'
if uInput != "y":
print '\n Attachment discarded.\n'
return ''
elif invSize > 184320.0: # If larger than 180MB, discard.
print '\n Attachment too big, maximum allowed size:180MB\n'
main()
return '\n Attachment too big, maximum allowed size:180MB\n'
pathLen = len(str(ntpath.basename(filePath))) # Gets the length of the filepath excluding the filename
fileName = filePath[(len(str(filePath)) - pathLen):] # reads the filename
filetype = imghdr.what(filePath) # Tests if it is an image file
if filetype is not None:
print '\n ---------------------------------------------------'
print
print ' ---------------------------------------------------'
print ' Attachment detected as an Image.'
print ' <img> tags will automatically be included,'
print ' allowing the recipient to view the image'
@ -704,7 +1208,7 @@ def attachment():
time.sleep(2)
# Alert the user that the encoding process may take some time.
print '\n Encoding Attachment, Please Wait ...\n'
print ' Encoding Attachment, Please Wait ...'
with open(filePath, 'rb') as f: # Begin the actual encoding
data = f.read(188743680) # Reads files up to 180MB, the maximum size for Bitmessage.
@ -713,81 +1217,67 @@ def attachment():
if isImage: # If it is an image, include image tags in the message
theAttachment = """
<!-- Note: Image attachment below. Please use the right click "View HTML code ..." option to view it. -->
<!-- Sent using Bitmessage Daemon. https://github.com/Dokument/PyBitmessage-Daemon -->
<!-- Sent using Bitmessage Daemon. https://github.com/BitMessage/PyBitmessage -->
Filename:%s
Filesize:%sKB
Encoding:base64
Filename: %s
Filesize: %sKB
Encoding: base64
<center>
<div id="image">
<img alt = "%s" src='data:image/%s;base64, %s' />
<img alt="%s" src="data:image/%s;base64, %s"/>
</div>
</center>""" % (fileName, invSize, fileName, filetype, data)
else: # Else it is not an image so do not include the embedded image code.
theAttachment = """
<!-- Note: File attachment below. Please use a base64 decoder, or Daemon, to save it. -->
<!-- Sent using Bitmessage Daemon. https://github.com/Dokument/PyBitmessage-Daemon -->
<!-- Sent using Bitmessage Daemon. https://github.com/BitMessage/PyBitmessage -->
Filename:%s
Filesize:%sKB
Encoding:base64
<attachment alt = "%s" src='data:file/%s;base64, %s' />""" % (fileName, invSize, fileName, fileName, data)
<attachment alt="%s" src="data:file/%s;base64, %s"/>""" % (fileName, invSize, fileName, fileName, data)
uInput = userInput('Would you like to add another attachment, (Y)es or (N)o?').lower()
uInput = userInput('Would you like to add another attachment, (y)es or (N)o?').lower()
if uInput == 'y' or uInput == 'yes': # Allows multiple attachments to be added to one message
if uInput in inputShorts['yes']: # Allows multiple attachments to be added to one message
theAttachmentS = str(theAttachmentS) + str(theAttachment) + '\n\n'
elif uInput == 'n' or uInput == 'no':
else:
break
theAttachmentS = theAttachmentS + theAttachment
return theAttachmentS
def sendMsg(toAddress, fromAddress, subject, message):
def sendMsg(toAddress, fromAddress, subject=None, message=None):
"""
With no arguments sent, sendMsg fills in the blanks.
subject and message must be encoded before they are passed.
"""
global usrPrompt
if validAddress(toAddress) is False:
while True:
toAddress = userInput("What is the To Address?")
global retStrings, inputShorts
if toAddress == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(toAddress) is False:
print '\n Invalid Address. "c" to cancel. Please try again.\n'
else:
break
if validAddress(toAddress) is False:
toAddress = inputAddress("What is the To Address indeed?")
if validAddress(fromAddress) is False:
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) # Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' Sender retrieving...', fromAddress
response = api.listAddresses()
if response['error'] != 0:
return response['errormsg']
jsonAddresses = response['result']
numAddresses = len(jsonAddresses) # Number of addresses
if numAddresses > 1: # Ask what address to send from if multiple addresses
found = False
while True:
print ' '
fromAddress = userInput("Enter an Address or Address Label to send from.")
if fromAddress == "exit":
usrPrompt = 1
main()
fromAddress = userInput('Enter an Address or Address Label to send from.')
for addNum in range(0, numAddresses): # processes all of the addresses
label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
label = jsonAddresses[addNum]['label']
address = jsonAddresses[addNum]['address']
if fromAddress == label: # address entered was a label and is found
fromAddress = address
found = True
@ -799,7 +1289,7 @@ def sendMsg(toAddress, fromAddress, subject, message):
else:
for addNum in range(0, numAddresses): # processes all of the addresses
address = jsonAddresses['addresses'][addNum]['address']
address = jsonAddresses[addNum]['address']
if fromAddress == address: # address entered was a found in our addressbook.
found = True
break
@ -812,55 +1302,70 @@ def sendMsg(toAddress, fromAddress, subject, message):
else: # Only one address in address book
print '\n Using the only address in the addressbook to send from.\n'
fromAddress = jsonAddresses['addresses'][0]['address']
fromAddress = jsonAddresses[0]['address']
if subject == '':
subject = userInput("Enter your Subject.")
if subject is None:
subject = userInput('Enter your Subject.')
subject = subject.encode('base64')
if message == '':
message = userInput("Enter your Message.")
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
if message is None:
while True:
try:
message += '\n' + raw_input('Continue enter your message line by line, end with <CTL-D>.\n>')
except EOFError:
break
uInput = userInput('Would you like to add an attachment, (y)es or (N)o?').lower()
if uInput in inputShorts['yes']:
message = message + '\n\n' + attachment()
message = message.encode('base64')
try:
while True:
print ' Message sending...', subject.decode('base64')
ackData = api.sendMessage(toAddress, fromAddress, subject, message)
print '\n Message Status:', api.getStatus(ackData), '\n'
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if ackData['error'] == 1:
return ackData['errormsg']
elif ackData['error'] != 0:
print ackData['errormsg']
uInput = userInput('Would you like to try again, (n)o or (Y)es?').lower()
if uInput in inputShorts['no']:
break
if ackData['error'] == 0:
print ' Fetching send status...'
status = api.getStatus(ackData['result'])
if status['error'] == 1:
return status['errormsg']
elif status['error'] != 0:
print status['errormsg']
else:
return ' Message Status:' + status['result']
return ''
def sendBrd(fromAddress, subject, message):
def sendBrd(fromAddress=None, subject=None, message=None):
"""Send a broadcast"""
global usrPrompt
if fromAddress == '':
global inputShorts
if fromAddress is None:
print ' Retrieving...', 'Senders'
response = api.listAddresses()
if response['error'] != 0:
return response['errormsg']
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) # Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
jsonAddresses = response['result']
numAddresses = len(jsonAddresses) # Number of addresses
if numAddresses > 1: # Ask what address to send from if multiple addresses
found = False
while True:
fromAddress = userInput("\nEnter an Address or Address Label to send from.")
if fromAddress == "exit":
usrPrompt = 1
main()
fromAddress = userInput('Enter an Address or Address Label to send from.')
for addNum in range(0, numAddresses): # processes all of the addresses
label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
label = jsonAddresses[addNum]['label']
address = jsonAddresses[addNum]['address']
if fromAddress == label: # address entered was a label and is found
fromAddress = address
found = True
@ -872,7 +1377,7 @@ def sendBrd(fromAddress, subject, message):
else:
for addNum in range(0, numAddresses): # processes all of the addresses
address = jsonAddresses['addresses'][addNum]['address']
address = jsonAddresses[addNum]['address']
if fromAddress == address: # address entered was a found in our addressbook.
found = True
break
@ -884,432 +1389,744 @@ def sendBrd(fromAddress, subject, message):
break # Address was found
else: # Only one address in address book
print '\n Using the only address in the addressbook to send from.\n'
fromAddress = jsonAddresses['addresses'][0]['address']
print ' Using the only address in the addressbook to send from.\n'
fromAddress = jsonAddresses[0]['address']
if subject == '':
subject = userInput("Enter your Subject.")
subject = subject.encode('base64')
if message == '':
message = userInput("Enter your Message.")
if subject is None:
subject = userInput('Enter your Subject.')
subject = subject.encode('base64')
if message is None:
while True:
try:
message += '\n' + raw_input('Continue enter your message line by line, end with <CTL-D>.\n>')
except EOFError:
break
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
uInput = userInput('Would you like to add an attachment, (y)es or (N)o?').lower()
if uInput in inputShorts['yes']:
message = message + '\n\n' + attachment()
message = message.encode('base64')
try:
while True:
print ' Broadcast message sending...'
ackData = api.sendBroadcast(fromAddress, subject, message)
print '\n Message Status:', api.getStatus(ackData), '\n'
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if ackData['error'] == 1:
return ackData['errormsg']
elif status['error'] != 0:
print '\n %s.\n' % str(err)
uInput = userInput('Would you like to try again, no or (Y)es?').lower()
if uInput in inputShorts['no']:
break
if ackData['error'] == 0:
print ' Fetching send status...'
status = api.getStatus(ackData['result'])
if status['error'] == 1:
return status['errormsg']
elif status['error'] != 0:
print status['errormsg']
else:
return ' Message Status:' + status['result']
return ''
def inbox(unreadOnly=False):
"""Lists the messages by: Message Number, To Address Label, From Address Label, Subject, Received Time)"""
def inbox(unreadOnly=False, pageNum=20):
"""Lists the messages by: message index, To Address Label, From Address Label, Subject, Received Time)"""
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' Inbox index fetching...'
response = api.getAllInboxMessageIDs()
if response['error'] != 0:
return response['errormsg']
messageIds = response['result']
numMessages = len(messageIds)
messagesPrinted = 0
messagesUnread = 0
for msgNum in range(0, numMessages): # processes all of the messages in the inbox
message = inboxMessages['inboxMessages'][msgNum]
# if we are displaying all messages or if this message is unread then display it
if not unreadOnly or not message['read']:
print ' -----------------------------------\n'
print ' Message Number:', msgNum # Message Number
print ' To:', getLabelForAddress(message['toAddress']) # Get the to address
print ' From:', getLabelForAddress(message['fromAddress']) # Get the from address
print ' Subject:', message['subject'].decode('base64') # Get the subject
print ''.join([
' Received:',
datetime.datetime.fromtimestamp(
float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S'),
])
messagesPrinted += 1
if not message['read']:
messagesUnread += 1
for msgNum in range(numMessages - 1, -1, -1): # processes all of the messages in the inbox
messageID = messageIds[msgNum]['msgid']
print ' -----------------------------------'
print ' Inbox message retrieving...', messageID
response = api.getInboxMessageByID(messageID)
if response['error'] == 1:
return response['errormsg']
elif response['error'] != 0:
print '\n Retrieve failed on:', msgNum, messageID
print response['errormsg']
else:
message = response['result'][0]
# if we are displaying all messages or if this message is unread then display it
if not unreadOnly or not message['read']:
print ' -----------------------------------'
print ' Inbox index: %d/%d' % (msgNum, numMessages - 1) # message index
print ' Message ID:', message['msgid']
print ' Read:', message['read']
print ' To:', getLabelForAddress(message['toAddress']) # Get the to address
print ' From:', getLabelForAddress(message['fromAddress']) # Get the from address
print ' Subject:', message['subject'].decode('base64') # Get the subject
print ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Base64Len:', str(len(message['message']))
messagesPrinted += 1
if not message['read']:
messagesUnread += 1
if messagesPrinted % 20 == 0 and messagesPrinted != 0:
userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() # uInput =
if messagesPrinted % pageNum == 0 and messagesPrinted != 0:
try:
uInput = userInput('Paused on %d/%d, next [%d],' % (msgNum, numMessages - 1, msgNum - pageNum if msgNum > pageNum else 0))
print '\n -----------------------------------'
except InputException as err:
raise InputException(str(err))
print ' -----------------------------------'
print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages)
print ' -----------------------------------\n'
print ' -----------------------------------'
return ''
def outbox():
def outbox(pageNum=20):
"""TBC"""
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' All outbox messages downloading...'
response = api.getAllSentMessages()
if response['error'] != 0:
return response['errormsg']
for msgNum in range(0, numMessages): # processes all of the messages in the outbox
print '\n -----------------------------------\n'
print ' Message Number:', msgNum # Message Number
# print ' Message ID:', outboxMessages['sentMessages'][msgNum]['msgid']
print ' To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) # Get the to address
outboxMessages = response['result']
numMessages = len(outboxMessages)
for msgNum in range(numMessages - 1, -1, -1): # processes all of the messages in the outbox
message = outboxMessages[msgNum]
print ' -----------------------------------'
print ' Outbox index: %d/%d' % (msgNum, numMessages - 1) # message index
print ' Message ID:', message['msgid']
print ' To:', getLabelForAddress(message['toAddress']) # Get the to address
# Get the from address
print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress'])
print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') # Get the subject
print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] # Get the subject
print ' From:', getLabelForAddress(message['fromAddress'])
print ' Subject:', message['subject'].decode('base64') # Get the subject
print ' Status:', message['status'] # Get the status
print ' Ack:', message['ackData'] # Get the ackData
print ' Last Action Time:', datetime.datetime.fromtimestamp(float(message['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Base64Len:', str(len(message['message']))
print ''.join([
' Last Action Time:',
datetime.datetime.fromtimestamp(
float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S'),
])
if msgNum % pageNum == 0 and msgNum != 0:
uInput = userInput('Paused on %d/%d, next [%d],' % (msgNum, numMessages - 1, msgNum - pageNum if msgNum > pageNum else 0))
if msgNum % 20 == 0 and msgNum != 0:
userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() # uInput =
print '\n -----------------------------------'
print ' -----------------------------------'
print ' There are ', numMessages, ' messages in the outbox.'
print ' -----------------------------------\n'
return ''
def readSentMsg(msgNum):
def attDetect(content=None, textmsg=None, attPrefix=None, askSave=True):
global inputShorts
attPos = msgPos = 0
# Hard way search attachments
for counter in range(1, 3): # Allows maximum 3 of attachments to be downloaded/saved
try:
attPos = content.index(';base64,', attPos) + 9 # Finds the attachment position
attEndPos = content.index('/>', attPos) - 1 # Finds the end of the attachment
attPre = attPos - 9 # back for ;base64, | <img src=";base64, | <attachment src=";base64,
# try remove prefix <xxxx | <xxxxxxxxxxx
pBack = 0
if attPre >= (msgPos + 12):
if '<' == content[attPre - 12]:
pBack = 12
elif attPre - msgPos + 5 >= 0:
if '<' == content[attPre - 5]:
pBack = 5
attPre = attPre - pBack
try:
fnPos = content.index('alt="', msgPos, attPos) + 5 # Finds position of the filename
fnEndPos = content.index('" src=', msgPos, attPos) # Finds the end position
# fnLen = fnEndPos - fnPos #Finds the length of the filename
fn = content[fnPos:fnEndPos]
attPre = fnPos - 5 # back for alt=" | <img alt=" | <attachment alt="
# try remove prefix <xxxx | <xxxxxxxxxxx
pBack = 0
if attPre >= (msgPos + 12):
if '<' == content[attPre - 12]:
pBack = 12
elif attPre - msgPos + 5 >= 0:
if '<' == content[attPre - 5]:
pBack = 5
attPre = attPre - pBack
except ValueError:
fn = 'notdetected'
fn = '%d_attachment_%d_%s' % (attPrefix, attPos, fn)
this_attachment = content[attPos:attEndPos]
x = filter(lambda z: not re.match(r'^\s*$', z), this_attachment)
# x = x.replace('\n', '').strip()
trydecode = False
if len(x) % 4 == 0: # check by length before decode.
trydecode = True
else:
print '\n'.join([
' -----------------------------------',
' Embeded mesaage seems not "BASE64" encoded. (base on length check)',
' Offset: %d, about: %d(bytes).' % (attPos, (int(len(x)*3/4)) - 2 if x[-2:] == '==' else 1 if x[-1] == '=' else 0),
' Start[%d] ~ Ends[%d].' % (x[:3], x[-3:]),
' FileName: "%s"' % fn,
])
uInput = userInput('Try to decode anyway, (n)o or (Y)es?')
if uInput not in inputShorts['no']:
trydecode = True
if trydecode is True:
try:
y = x.decode('base64', 'strict')
if x == y.encode('base64').replace('\n', ''): # double check decoded string.
print ' This embeded message decoded successfully:', fn
if askSave is True:
uInput = userInput('Download the "decoded" attachment, (y)es or (No)?\nName(%d): %s,' % (counter, fn)).lower()
if uInput in inputShorts['yes']:
src = dump2File(fn, y, True)
else:
src = dump2File(fn, y, True)
print src
attmsg = '\n'.join([
' -----------------------------------',
' Attachment: "%s"' % fn,
' Size: %d(bytes)' % getBase64Len(x),
' -----------------------------------',
])
# remove base64 and '<att' prefix and suffix '/>' stuff
textmsg = textmsg + content[msgPos:attPre] + attmsg
attEndPos += 3
msgPos = attEndPos
else:
print '\n Failed on decode this embeded "BASE64" like message on re-encode check.\n'
except ValueError:
pass
print '\n Failed on decode this emdeded "BASE64" encoded like message.\n'
except Exception:
print '\n Unexpected error: %s.\n' % sys.exc_info()[0]
else:
print '\n Skiped a embeded "BASE64" encoded like message.'
if attEndPos != msgPos:
textmsg = textmsg + content[msgPos:attEndPos]
msgPos = attEndPos
except ValueError:
textmsg = textmsg + content[msgPos:]
break
except Exception:
print '\n Unexpected error: %s.\n' % sys.exc_info()[0]
return textmsg
def readSentMsg(cmd='read', msgNum=-1, messageID=None, trunck=380, withAtta=False):
"""Opens a sent message for reading"""
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' All outbox messages downloading...', str(msgNum)
if messageID is None:
response = api.getAllSentMessages()
if response['error'] != 0:
return response['errormsg']
print ' '
message = response['result'][msgNum]
else:
response = api.getSentMessageByID(messageID)
if response['error'] != 0:
return response['errormsg']
if msgNum >= numMessages:
print '\n Invalid Message Number.\n'
main()
message = response['result'][0]
# Begin attachment detection
message = outboxMessages['sentMessages'][msgNum]['message'].decode('base64')
subject = message['subject'].decode('base64')
content = message['message'].decode('base64')
full = len(content)
textmsg = ''
textmsg = content if withAtta else attDetect(content, textmsg, 'outbox_' + subject, cmd != 'save')
while True: # Allows multiple messages to be downloaded/saved
if ';base64,' in message: # Found this text in the message, there is probably an attachment.
attPos = message.index(";base64,") # Finds the attachment position
attEndPos = message.index("' />") # Finds the end of the attachment
# attLen = attEndPos - attPos #Finds the length of the message
if 'alt = "' in message: # We can get the filename too
fnPos = message.index('alt = "') # Finds position of the filename
fnEndPos = message.index('" src=') # Finds the end position
# fnLen = fnEndPos - fnPos #Finds the length of the filename
fileName = message[fnPos + 7:fnEndPos]
else:
fnPos = attPos
fileName = 'Attachment'
uInput = userInput(
'\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower()
if uInput == "y" or uInput == 'yes':
this_attachment = message[attPos + 9:attEndPos]
saveFile(fileName, this_attachment)
message = message[:fnPos] + '~<Attachment data removed for easier viewing>~' + message[(attEndPos + 4):]
else:
break
# End attachment Detection
print '\n To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) # Get the to address
print ' ', 74 * '-'
print ' Message index:', str(msgNum) # message outdex
print ' Message ID:', message['msgid']
print ' To:', getLabelForAddress(message['toAddress']) # Get the to address
# Get the from address
print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress'])
print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') # Get the subject
print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] # Get the subject
print ''.join([
' Last Action Time:',
datetime.datetime.fromtimestamp(
float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S'),
])
print ' From:', getLabelForAddress(message['fromAddress'])
print ' Subject:', subject # Get the subject
print ' Status:', message['status'] # Get the status
print ' Ack:', message['ackData'] # Get the ackData
print ' Last Action Time:', datetime.datetime.fromtimestamp(float(message['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Length: %d/%d' % (trunck if trunck <= full else full, full)
print ' Message:\n'
print message # inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
print ' '
print textmsg if trunck < 0 or len(textmsg) <= trunck else textmsg[:trunck] + '\n\n ~< MESSAGE TOO LONG TRUNCKED TO SHOW >~'
print ' ', 74 * '-'
if cmd == 'save':
ret = dump2File('outbox_' + subject, textmsg, withAtta)
print ret
return ''
def readMsg(msgNum):
def readMsg(cmd='read', msgNum=-1, messageID=None, trunck=380, withAtta=False):
"""Open a message for reading"""
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if msgNum >= numMessages:
print '\n Invalid Message Number.\n'
main()
print ' Inbox message reading...', str(msgNum)
response = api.getInboxMessageByID(messageID, True)
if response['error'] != 0:
return response['errormsg']
# Begin attachment detection
message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
message = response['result'][0]
subject = message['subject'].decode('base64')
content = message['message'].decode('base64') # Message that you are replying too.
full = len(content)
textmsg = ''
textmsg = content if withAtta else attDetect(content, textmsg, 'inbox_' + subject, cmd != 'save')
while True: # Allows multiple messages to be downloaded/saved
if ';base64,' in message: # Found this text in the message, there is probably an attachment.
attPos = message.index(";base64,") # Finds the attachment position
attEndPos = message.index("' />") # Finds the end of the attachment
# attLen = attEndPos - attPos #Finds the length of the message
if 'alt = "' in message: # We can get the filename too
fnPos = message.index('alt = "') # Finds position of the filename
fnEndPos = message.index('" src=') # Finds the end position
# fnLen = fnEndPos - fnPos #Finds the length of the filename
fileName = message[fnPos + 7:fnEndPos]
else:
fnPos = attPos
fileName = 'Attachment'
uInput = userInput(
'\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower()
if uInput == "y" or uInput == 'yes':
this_attachment = message[attPos + 9:attEndPos]
saveFile(fileName, this_attachment)
message = message[:fnPos] + '~<Attachment data removed for easier viewing>~' + message[attEndPos + 4:]
else:
break
# End attachment Detection
print '\n To:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['toAddress']) # Get the to address
print ' ', 74 * '-'
print ' Inbox index :', msgNum # message index
print ' Message ID:', message['msgid']
print ' Read:', message['read']
print ' To:', getLabelForAddress(message['toAddress']) # Get the to address
# Get the from address
print ' From:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['fromAddress'])
print ' Subject:', inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') # Get the subject
print ''.join([
' Received:', datetime.datetime.fromtimestamp(
float(inboxMessages['inboxMessages'][msgNum]['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S'),
])
print ' From:', getLabelForAddress(message['fromAddress'])
print ' Subject:', subject # Get the subject
print ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Length: %d/%d' % (trunck if trunck <= full else full, full)
print ' Message:\n'
print message # inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
print ' '
return inboxMessages['inboxMessages'][msgNum]['msgid']
print textmsg if trunck < 0 or len(textmsg) <= trunck else textmsg[:trunck] + '\n\n ~< MESSAGE TOO LONG TRUNCKED TO SHOW >~'
print ' ', 74 * '-'
if cmd == 'save':
ret = dump2File('inbox_' + subject + str(full), textmsg, withAtta)
print ret
return ''
def replyMsg(msgNum, forwardORreply):
def replyMsg(msgNum=-1, messageID=None, forwardORreply=None):
"""Allows you to reply to the message you are currently on. Saves typing in the addresses and subject."""
global usrPrompt
global inputShorts, retStrings
forwardORreply = forwardORreply.lower() # makes it lowercase
try:
inboxMessages = json.loads(api.getAllInboxMessages())
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' Inbox message %s... %d' % (forwardORreply, msgNum)
response = api.getInboxMessageByID(messageID, True)
if response['error'] != 0:
return response['errormsg']
fromAdd = inboxMessages['inboxMessages'][msgNum]['toAddress'] # Address it was sent To, now the From address
message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') # Message that you are replying too.
message = response['result'][0]
subject = message['subject'].decode('base64')
content = message['message'].decode('base64') # Message that you are replying too.
full = len(content)
textmsg = ''
textmsg = attDetect(content, textmsg, subject, True)
subject = inboxMessages['inboxMessages'][msgNum]['subject']
subject = subject.decode('base64')
if forwardORreply == 'forward':
attachMessage = '\n'.join([
'> To: ', fwdFrom,
'> From: ', fromAdd,
'> Subject: ', subject,
'> Received: ', recvTime,
'> Message:',
])
else:
attachMessage = ''
for line in textmsg.splitlines():
attachMessage = attachMessage + '> ' + line + '\n'
fromAdd = message['toAddress'] # Address it was sent To, now the From address
fwdFrom = message['fromAddress'] # Address it was sent To, will attached to fwd
recvTime = datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S')
if forwardORreply == 'reply':
toAdd = inboxMessages['inboxMessages'][msgNum]['fromAddress'] # Address it was From, now the To address
subject = "Re: " + subject
toAdd = message['fromAddress'] # Address it was From, now the To address
subject = "Re: " + re.sub('^Re: *', '', subject)
elif forwardORreply == 'forward':
subject = "Fwd: " + subject
subject = "Fwd: " + re.sub('^Fwd: *', '', subject)
toAdd = inputAddress("What is the To Address?")
while True:
toAdd = userInput("What is the To Address?")
if toAdd == "c":
usrPrompt = 1
print ' '
main()
elif validAddress(toAdd) is False:
print '\n Invalid Address. "c" to cancel. Please try again.\n'
else:
break
else:
print '\n Invalid Selection. Reply or Forward only'
usrPrompt = 0
main()
return '\n Invalid Selection. Reply or Forward only'
subject = subject.encode('base64')
newMessage = userInput("Enter your Message.")
message = ''
while True:
try:
print '\n'.join([
' Drafting:',
message,
' -----------------------------------',
])
message += '\n' + raw_input('Continue enter your message line by line, end with <CTL-D>.\n>')
except EOFError:
break
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
newMessage = newMessage + '\n\n' + attachment()
src = retStrings['usercancel']
uInput = userInput('Would you like to add an attachment, (y)es or (N)o?').lower()
if uInput in inputShorts['yes']:
message = message + '\n\n' + attachment()
newMessage = newMessage + '\n\n------------------------------------------------------\n'
newMessage = newMessage + message
newMessage = newMessage.encode('base64')
message = message + '\n\n------------------------------------------------------\n'
message = message + attachMessage
message = message.encode('base64')
sendMsg(toAdd, fromAdd, subject, newMessage)
print message
uInput = userInput('Realy want to send upper message, (n)o or (Y)es?').lower()
if uInput not in inputShorts['no']:
src = sendMsg(toAdd, fromAdd, subject, message)
main()
return src
def delMsg(msgNum):
def delMsg(msgNum=-1, messageID=None):
"""Deletes a specified message from the inbox"""
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
# gets the message ID via the message index number
msgId = inboxMessages['inboxMessages'][int(msgNum)]['msgid']
print ' Inbox message deleting...', messageID
response = api.trashMessage(messageID)
if response['error'] != 0:
return response['errormsg']
msgAck = api.trashMessage(msgId)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return msgAck
return '\n ' + response['result']
def delSentMsg(msgNum):
def delSentMsg(msgNum=-1, messageID=None):
"""Deletes a specified message from the outbox"""
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
# gets the message ID via the message index number
msgId = outboxMessages['sentMessages'][int(msgNum)]['msgid']
msgAck = api.trashSentMessage(msgId)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if messageID is None:
print ' All outbox messages downloading...', str(msgNum)
response = api.getAllSentMessages()
if response['error'] != 0:
return response['errormsg']
return msgAck
outboxMessages = response['result']
# gets the message ackData via the message index number
ackData = outboxMessages[msgNum]['ackData']
print ' Outbox message deleting...', ackData
response = api.trashSentMessageByAckData(ackData)
if response['error'] != 0:
return response['errormsg']
else:
print ' Outbox message deleting...', messageID
response = api.trashSentMessage(messageID)
if response['error'] != 0:
return response['errormsg']
return '\n ' + response['result']
def toReadInbox(cmd='read', trunck=380, withAtta=False):
global inputShorts, retStrings
numMessages = 0
print ' Inbox index fetching...'
response = api.getAllInboxMessageIDs()
if response['error'] != 0:
return response['errormsg']
messageIds = response['result']
numMessages = len(messageIds)
if numMessages < 1:
return ' Zero message found.\n'
src = retStrings['usercancel']
if cmd != 'delete':
msgNum = int(inputIndex('Input the index of the message to %s [0-%d]: ' % (cmd, numMessages - 1), numMessages - 1))
nextNum = msgNum
ret = ''
while msgNum >= 0: # save, read
nextNum += 1
messageID = messageIds[msgNum]['msgid']
if cmd == 'save':
ret = readMsg(cmd, msgNum, messageID, trunck, withAtta)
return ret
else:
ret = readMsg(cmd, msgNum, messageID)
print ret
uInput = userInput('Would you like to set this message to unread, (y)es or (N)o?').lower()
if uInput in inputShorts['yes']:
ret = markMessageReadbit(msgNum, messageID, False)
else:
uInput = userInput('Would you like to (f)orward, (r)eply, (s)ave, (d)ump or Delete this message?').lower()
if uInput in inputShorts['reply']:
ret = replyMsg(msgNum, messageID, 'reply')
elif uInput in inputShorts['forward']:
ret = replyMsg(msgNum, messageID, 'forward')
elif uInput in inputShorts['save']:
ret = readMsg('save', msgNum, messageID, withAtta=False)
elif uInput in inputShorts['dump']:
ret = readMsg('save', msgNum, messageID, withAtta=True)
else:
uInput = userInput('Are you sure to delete, (y)es or (N)o?').lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
# nextNum -= 1
# numMessages -= 1
ret = delMsg(msgNum, messageID)
print ret
if nextNum < numMessages:
uInput = userInput('Next message, (n)o or (Y)es?').lower() # Prevent
msgNum = nextNum if uInput not in inputShorts['no'] else -1
else:
msgNum = -1
src = retStrings['indexoutofbound']
else:
uInput = inputIndex('Input the index of the message you wish to delete or (A)ll to empty the inbox [0-%d]: ' % (numMessages - 1), numMessages - 1, inputShorts['all'][0]).lower()
if uInput in inputShorts['all']:
ret = inbox(False)
print ret
uInput = userInput('Are you sure to delete all this %d message(s), (y)es or (N)o?' % numMessages).lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
for msgNum in range(0, numMessages): # processes all of the messages in the outbox
ret = delMsg(msgNum, messageIds[msgNum]['msgid'])
print ret
src = ''
else:
nextNum = msgNum = int(uInput)
while msgNum >= 0: # save, read
nextNum += 1
messageID = messageIds[msgNum]['msgid']
ret = readMsg(cmd, msgNum, messageID)
print ret
uInput = userInput('Are you sure to delete, (y)es or (N)o?').lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
# nextNum -= 1
# numMessages -= 1
ret = delMsg(msgNum, messageID)
print ret
if nextNum < numMessages:
uInput = userInput('Next message, (n)o or (Y)es?').lower() # Prevent
msgNum = nextNum if uInput not in inputShorts['no'] else -1
else:
msgNum = -1
src = retStrings['indexoutofbound']
return src
def toReadOutbox(cmd='read', trunck=380, withAtta=False):
global inputShorts, retStrings
print ' Outbox index fetching...'
response = api.getAllSentMessageIDs()
if response['error'] != 0:
return response['errormsg']
messageIds = response['result']
numMessages = len(messageIds)
if numMessages < 1:
return ' Zero message found.\n'
src = retStrings['usercancel']
if cmd != 'delete':
msgNum = int(inputIndex('Input the index of the message open [0-%d]: ' % (numMessages - 1), numMessages - 1))
nextNum = msgNum
ret = ''
while msgNum >= 0: # save, read
nextNum += 1
messageID = messageIds[msgNum]['msgid']
if cmd == 'save':
ret = readSentMsg(cmd, msgNum, messageID, trunck, withAtta)
return ret
else:
ret = readSentMsg(cmd, msgNum, messageID)
print ret
# Gives the user the option to delete the message
uInput = userInput('Would you like to (s)ave, (d)ump or Delete this message directly?').lower()
if uInput in inputShorts['save']:
ret = readSentMsg('save', msgNum, messageID, withAtta=False)
elif uInput in inputShorts['dump']:
ret = readSentMsg('save', msgNum, messageID, withAtta=True)
else:
uInput = userInput('Are you sure to delete, (y)es or (N)o?').lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
nextNum -= 1
numMessages -= 1
ret = delSentMsg(msgNum, messageID)
print ret
if nextNum < numMessages:
uInput = userInput('Next message, (n)o or (Y)es?').lower() # Prevent
msgNum = nextNum if uInput not in inputShorts['no'] else -1
else:
msgNum = -1
src = retStrings['indexoutofbound']
else:
uInput = inputIndex('Input the index of the message you wish to delete or (A)ll to empty the outbox [0-%d]: ' % (numMessages - 1), numMessages - 1, inputShorts['all'][0]).lower()
if uInput in inputShorts['all']:
ret = outbox()
print ret
uInput = userInput('Are you sure to delete all this %d message(s), (y)es or (N)o?' % numMessages).lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
for msgNum in range(0, numMessages): # processes all of the messages in the outbox
ret = delSentMsg(msgNum, messageIds[msgNum]['msgid'])
print ret
src = ''
else:
nextNum = msgNum = int(uInput)
while msgNum >= 0: # save, read
nextNum += 1
messageID = messageIds[msgNum]['msgid']
ret = readSentMsg(cmd, msgNum, messageID)
print ret
uInput = userInput('Are you sure to delete this message, (y)es or (N)o?').lower() # Prevent accidental deletion
if uInput in inputShorts['yes']:
nextNum -= 1
numMessages -= 1
ret = delSentMsg(msgNum, messageID)
print ret
if nextNum < numMessages:
uInput = userInput('Next message, (n)o or (Y)es?').lower() # Prevent
msgNum = nextNum if uInput not in inputShorts['no'] else -1
else:
msgNum = -1
src = retStrings['indexoutofbound']
return src
def getLabelForAddress(address):
"""Get label for an address"""
if address in knownAddresses:
return knownAddresses[address]
else:
buildKnownAddresses()
if address in knownAddresses:
return knownAddresses[address]
for entry in knownAddresses['addresses']:
if entry['address'] == address:
return "%s (%s)" % (entry['label'], entry['address'])
return address
def buildKnownAddresses():
def getLabel():
"""Build known addresses"""
global usrPrompt
# add from address book
try:
response = api.listAddressBookEntries()
# if api is too old then fail
if "API Error 0020" in response:
return
addressBook = json.loads(response)
for entry in addressBook['addresses']:
if entry['address'] not in knownAddresses:
knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
errors = ''
newentry = []
print ' Retrieving...', 'Contacts'
addresses = api.listAddressBookEntries()
if addresses['error'] != 0:
print addresses['errormsg']
else:
for entry in addresses['result']:
isnew = True
for old in knownAddresses['addresses']:
if entry['address'] == old['address']:
isnew = False
break
if isnew is True:
newentry.append({'label': entry['label'].decode('base64').encode('utf-8'), 'address': entry['address']})
if any(newentry):
for new in newentry:
knownAddresses['addresses'].append(new)
# add from my addresses
try:
response = api.listAddresses2()
# if api is too old just return then fail
if "API Error 0020" in response:
return
addresses = json.loads(response)
for entry in addresses['addresses']:
if entry['address'] not in knownAddresses:
knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
newentry = []
print ' Retrieving...', 'Senders'
addresses = api.listAddresses()
if addresses['error'] != 0:
print addresses['errormsg']
else:
for entry in addresses['result']:
isnew = True
for old in knownAddresses['addresses']:
if entry['address'] == old['address']:
isnew = False
break
if isnew is True:
newentry.append({'label': entry['label'].encode('utf-8'), 'address': entry['address']})
if any(newentry):
for new in newentry:
knownAddresses['addresses'].append(new)
return ''
def listAddressBookEntries():
def listAddressBK(printKnown=False):
"""List addressbook entries"""
global usrPrompt
try:
if not printKnown:
print ' Retrieving...', 'Contacts'
response = api.listAddressBookEntries()
if "API Error" in response:
return getAPIErrorCode(response)
addressBook = json.loads(response)
print
print ' --------------------------------------------------------------'
print ' | Label | Address |'
print ' |--------------------|---------------------------------------|'
for entry in addressBook['addresses']:
label = entry['label'].decode('base64')
address = entry['address']
if len(label) > 19:
label = label[:16] + '...'
print ' | ' + label.ljust(19) + '| ' + address.ljust(37) + ' |'
print ' --------------------------------------------------------------'
print
if response['error'] != 0:
return response['errormsg']
addressBook = response['result']
else:
addressBook = knownAddresses['addresses']
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
numAddresses = len(addressBook)
print
print ' ------------------------------------------------------------------'
print ' | # | Label | Address |'
print ' |----|--------------------|--------------------------------------|'
for addNum in range(0, numAddresses): # processes all of the addresses and lists them out
entry = addressBook[addNum]
label = entry['label'].decode('base64').encode('utf-8') if not printKnown else entry['label']
address = entry['address']
if len(label) > 19:
label = label[:16] + '...'
print '| '.join([' ', str(addNum).ljust(3), label.ljust(19), address.ljust(37), '', ])
print ''.join([' ', 66 * '-', '\n', ])
return ''
def addAddressToAddressBook(address, label):
"""Add an address to an addressbook"""
global usrPrompt
print ' Adding...', label
response = api.addAddressBK(address, label.encode('base64'))
if response['error'] != 0:
return response['errormsg']
try:
response = api.addAddressBookEntry(address, label.encode('base64'))
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return '\n ' + response['result']
def deleteAddressFromAddressBook(address):
"""Delete an address from an addressbook"""
global usrPrompt
print ' Deleting...', address
response = api.delAddressBK(address)
if response['error'] != 0:
return response['errormsg']
try:
response = api.deleteAddressBookEntry(address)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return '\n ' + response['result']
def getAPIErrorCode(response):
@ -1321,563 +2138,423 @@ def getAPIErrorCode(response):
return int(response.split()[2][:-1])
def markMessageRead(messageID):
"""Mark a message as read"""
def markMessageReadbit(msgNum=-1, messageID=None, read=False):
"""Mark a mesasge as unread/read"""
global usrPrompt
print ' Marking...', str(msgNum),
response = api.getInboxMessageByID(messageID, read)
if response['error'] != 0:
print 'Failed.'
return response['errormsg']
try:
response = api.getInboxMessageByID(messageID, True)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print 'OK.'
return ''
def markMessageUnread(messageID):
"""Mark a mesasge as unread"""
def markAllMessagesReadbit(read=False):
"""Mark all messages as unread/read"""
global usrPrompt
print ' Inbox index fetching...', 'mark'
response = api.getAllInboxMessageIDs()
if response['error'] != 0:
return response['errormsg']
try:
response = api.getInboxMessageByID(messageID, False)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
messageIds = response['result']
numMessages = len(messageIds)
if numMessages < 1:
return ' Zero message found.\n'
for msgNum in range(0, numMessages): # processes all of the messages in the inbox
src = markMessageReadbit(msgNum, messageIds[msgNum]['msgid'], read)
print src
return ''
def markAllMessagesRead():
"""Mark all messages as read"""
def addInfo(address):
global usrPrompt
print ' Address decoding...', address
response = api.decodeAddress(address)
if response['error'] != 0:
return response['errormsg']
try:
inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages']
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
for message in inboxMessages:
if not message['read']:
markMessageRead(message['msgid'])
addinfo = response['result']
print ' ------------------------------'
if 'success' in addinfo['status'].lower():
print ' Valid Address'
print ' Address Version: %s' % str(addinfo['addressVersion'])
print ' Stream Number: %s\n' % str(addinfo['streamNumber'])
else:
print '\n Invalid Address !\n'
def markAllMessagesUnread():
"""Mark all messages as unread"""
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages']
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
for message in inboxMessages:
if message['read']:
markMessageUnread(message['msgid'])
return ''
def clientStatus():
"""Print the client status"""
global usrPrompt
print ' Client status fetching...'
print ' ------------------------------'
try:
client_status = json.loads(api.clientStatus())
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
response = api.clientStatus()
if response['error'] == 0:
client_status = response['result']
for key in client_status.keys():
print ' ', key, ':', str(client_status[key])
else:
print response['errormsg']
print "\nnetworkStatus: " + client_status['networkStatus'] + "\n"
print "\nnetworkConnections: " + str(client_status['networkConnections']) + "\n"
print "\nnumberOfPubkeysProcessed: " + str(client_status['numberOfPubkeysProcessed']) + "\n"
print "\nnumberOfMessagesProcessed: " + str(client_status['numberOfMessagesProcessed']) + "\n"
print "\nnumberOfBroadcastsProcessed: " + str(client_status['numberOfBroadcastsProcessed']) + "\n"
response = api.getAllInboxMessageIDs()
if response['error'] == 0:
inboxMessageIds = response['result']
inumMessages = len(inboxMessageIds)
print ' InboxMessages:', str(inumMessages)
else:
print response['errormsg']
response = api.getAllSentMessageIDs()
if response['error'] == 0:
outboxMessageIds = response['result']
onumMessages = len(outboxMessageIds)
print ' OutboxMessages:', str(onumMessages)
else:
print response['errormsg']
# print ' Message.dat:', str(boxSize)
# print ' knownNodes.dat:', str(knownNodes)
# print ' debug.log:', str(debugSize)
print ' ------------------------------'
return ''
def shutdown():
"""Shutdown the API"""
try:
api.shutdown()
except socket.error:
pass
print "\nShutdown command relayed\n"
print ' Shutdown command sending...'
response = api.shutdown()
if response['error'] != 0:
return response['errormsg']
return '\n ' + response['result']
def UI(usrInput):
def start_daemon(uri=''):
start_dir = os.path.dirname(os.path.abspath(__file__))
mainfile = os.path.join(start_dir, 'bitmessagemain.py')
print " Try to start daemon: %s..." % uri
if os.path.isfile(mainfile):
cmd = ' '.join([sys.executable, mainfile, '--daemon'])
print '\n Exec:', cmd, uri
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
out, err = p.communicate()
result = out.split('\n')
for line in result:
print ' ' + line
else:
print ' ' + retStrings['nomain']
def UI(cmdInput=None):
"""Main user menu"""
global usrPrompt
global usrPrompt, inputShorts, cmdShorts, retStrings, bms_allow
if usrInput == "help" or usrInput == "h" or usrInput == "?":
print ' '
print ' -------------------------------------------------------------------------'
print ' | https://github.com/Dokument/PyBitmessage-Daemon |'
print ' |-----------------------------------------------------------------------|'
print ' | Command | Description |'
print ' |------------------------|----------------------------------------------|'
print ' | help | This help file. |'
print ' | apiTest | Tests the API |'
print ' | addInfo | Returns address information (If valid) |'
print ' | bmSettings | BitMessage settings |'
print ' | exit | Use anytime to return to main menu |'
print ' | quit | Quits the program |'
print ' |------------------------|----------------------------------------------|'
print ' | listAddresses | Lists all of the users addresses |'
print ' | generateAddress | Generates a new address |'
print ' | getAddress | Get determinist address from passphrase |'
print ' |------------------------|----------------------------------------------|'
print ' | listAddressBookEntries | Lists entries from the Address Book |'
print ' | addAddressBookEntry | Add address to the Address Book |'
print ' | deleteAddressBookEntry | Deletes address from the Address Book |'
print ' |------------------------|----------------------------------------------|'
print ' | subscribe | Subscribes to an address |'
print ' | unsubscribe | Unsubscribes from an address |'
print ' |------------------------|----------------------------------------------|'
print ' | create | Creates a channel |'
print ' | join | Joins a channel |'
print ' | leave | Leaves a channel |'
print ' |------------------------|----------------------------------------------|'
print ' | inbox | Lists the message information for the inbox |'
print ' | outbox | Lists the message information for the outbox |'
print ' | send | Send a new message or broadcast |'
print ' | unread | Lists all unread inbox messages |'
print ' | read | Reads a message from the inbox or outbox |'
print ' | save | Saves message to text file |'
print ' | delete | Deletes a message or all messages |'
print ' -------------------------------------------------------------------------'
print ' '
main()
src = 'MUST WRONG'
uInput = ''
elif usrInput == "apitest": # tests the API Connection.
if apiTest():
print '\n API connection test has: PASSED\n'
if not any(cmdShorts):
if not cmdGuess():
raise SystemExit('\n Bye\n')
if cmdInput in cmdShorts['help']:
src = showCmdTbl()
elif cmdInput in cmdShorts['daemon']:
src = start_daemon(getattr(config, 'start_daemon', ''))
elif cmdInput in cmdShorts['apiTest']: # tests the API Connection.
print ' API connection test has:',
print 'PASSED' if apiTest() else 'FAILED\n'
src = ''
elif cmdInput in cmdShorts['addInfo']:
while uInput == '':
uInput = userInput('Input the Bitmessage Address.')
src = addInfo(uInput)
elif cmdInput in cmdShorts['bmSettings']: # tests the API Connection.
if bms_allow is True:
if hasattr(conninit, 'main') and hasattr(conninit, 'bmSettings'):
src = conninit.bmSettings()
else:
src = '\n Depends moudle changed, calling dismissed.'
else:
print '\n API connection test has: FAILED\n'
main()
src = ' ' + retStrings['bmsnotallow']
elif usrInput == "addinfo":
tmp_address = userInput('\nEnter the Bitmessage Address.')
address_information = json.loads(api.decodeAddress(tmp_address))
elif cmdInput in cmdShorts['quit']: # Quits the application
raise SystemExit('\n Bye\n')
print '\n------------------------------'
elif cmdInput in cmdShorts['listAddresses']: # Lists all of the identities in the addressbook
src = listAdd()
if 'success' in str(address_information['status']).lower():
print ' Valid Address'
print ' Address Version: %s' % str(address_information['addressVersion'])
print ' Stream Number: %s' % str(address_information['streamNumber'])
else:
print ' Invalid Address !'
elif cmdInput in cmdShorts['newAddress']: # Generates a new address
uInput = userInput('Would you like to create a (d)eterministic or (R)andom address?').lower()
print '------------------------------\n'
main()
elif usrInput == "bmsettings": # tests the API Connection.
bmSettings()
print ' '
main()
elif usrInput == "quit": # Quits the application
print '\n Bye\n'
sys.exit(0)
elif usrInput == "listaddresses": # Lists all of the identities in the addressbook
listAdd()
main()
elif usrInput == "generateaddress": # Generates a new address
uInput = userInput('\nWould you like to create a (D)eterministic or (R)andom address?').lower()
if uInput in ["d", "deterministic"]: # Creates a deterministic address
if uInput in inputShorts['deterministic']: # Creates a deterministic address
deterministic = True
lbl = ''
passphrase = userInput('Enter the Passphrase.') # .encode('base64')
passphrase = userInput('Input the Passphrase.') # .encode('base64')
numOfAdd = int(userInput('How many addresses would you like to generate?'))
addVNum = 3
streamNum = 1
isRipe = userInput('Shorten the address, (Y)es or (N)o?').lower()
isRipe = userInput('Shorten the address, (Y)es or no?').lower()
if isRipe == "y":
if isRipe in inputShorts['yes']:
ripe = True
print genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
main()
elif isRipe == "n":
src = genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
else:
ripe = False
print genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
main()
elif isRipe == "exit":
usrPrompt = 1
main()
else:
print '\n Invalid input\n'
main()
src = genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
elif uInput == "r" or uInput == "random": # Creates a random address with user-defined label
else: # Creates a random address with user-defined label
deterministic = False
null = ''
lbl = userInput('Enter the label for the new address.')
lbl = null = None
while lbl is None:
lbl = userInput('Input the label for the new address.')
src = genAdd(lbl, deterministic, null, null, null, null, null)
print genAdd(lbl, deterministic, null, null, null, null, null)
main()
elif cmdInput in cmdShorts['getAddress']: # Gets the address for/from a passphrase
while len(uInput) < 6:
uInput = userInput('Input a strong address passphrase.[6-]')
src = getAddress(uInput, 4, 1)
elif cmdInput in cmdShorts['subscribe']: # Subsribe to an address
address = inputAddress('What address would you like to subscribe?')
while uInput == '':
uInput = userInput('Enter a label for this address.')
src = subscribe(address, uInput)
elif cmdInput in cmdShorts['unsubscribe']: # Unsubscribe from an address
address = inputAddress("What address would you like to unsubscribe from?")
uInput = userInput('Are you sure to unsubscribe: [%s]?' % address)
if uInput in inputShorts['yes']:
src = unsubscribe(address)
elif cmdInput in cmdShorts['listsubscrips']: # Unsubscribe from an address
src = listSubscriptions()
elif cmdInput in cmdShorts['create']:
while uInput == '':
uInput = userInput('Enter channel name')
src = createChan(uInput)
elif cmdInput in cmdShorts['join']:
src = joinChan()
elif cmdInput in cmdShorts['leave']:
src = leaveChan()
elif cmdInput in cmdShorts['getLabel']: # Retrieve all of the addressbooks
src = getLabel()
print src,
src = listAddressBK(True)
elif cmdInput in cmdShorts['inbox']:
src = inbox(False)
elif cmdInput in cmdShorts['news']:
src = inbox(True)
elif cmdInput in cmdShorts['outbox']:
src = outbox()
elif cmdInput in cmdShorts['send']: # Sends a message or broadcast
uInput = userInput('Would you like to send a (b)roadcast or (M)essage?').lower()
null = ''
if uInput in inputShorts['broadcast']:
src = sendBrd(null, null, null)
else:
src = sendMsg(null, null, null, null)
elif cmdInput in cmdShorts['delete']:
withAtta = True
uInput = userInput('Would you like to delete message(s) from the (i)nbox or (O)utbox?').lower()
if uInput in inputShorts['inbox']:
src = toReadInbox(cmd='delete', withAtta=withAtta)
else:
print '\n Invalid input\n'
main()
src = toReadOutbox(cmd='delete', withAtta=withAtta)
elif usrInput == "getaddress": # Gets the address for/from a passphrase
phrase = userInput("Enter the address passphrase.")
print '\n Working...\n'
address = getAddress(phrase, 4, 1) # ,vNumber,sNumber)
print '\n Address: ' + address + '\n'
usrPrompt = 1
main()
elif cmdInput in cmdShorts['read']: # Opens a message from the inbox for viewing.
withAtta = False
uInput = userInput('Would you like to read a message from the (i)nbox or (O)utbox?').lower()
elif usrInput == "subscribe": # Subsribe to an address
subscribe()
usrPrompt = 1
main()
if uInput in inputShorts['inbox']:
src = toReadInbox(cmd='read', withAtta=withAtta)
elif usrInput == "unsubscribe": # Unsubscribe from an address
unsubscribe()
usrPrompt = 1
main()
elif usrInput == "listsubscriptions": # Unsubscribe from an address
listSubscriptions()
usrPrompt = 1
main()
elif usrInput == "create":
createChan()
usrPrompt = 1
main()
elif usrInput == "join":
joinChan()
usrPrompt = 1
main()
elif usrInput == "leave":
leaveChan()
usrPrompt = 1
main()
elif usrInput == "inbox":
print '\n Loading...\n'
inbox()
main()
elif usrInput == "unread":
print '\n Loading...\n'
inbox(True)
main()
elif usrInput == "outbox":
print '\n Loading...\n'
outbox()
main()
elif usrInput == 'send': # Sends a message or broadcast
uInput = userInput('Would you like to send a (M)essage or (B)roadcast?').lower()
if (uInput == 'm' or uInput == 'message'):
null = ''
sendMsg(null, null, null, null)
main()
elif (uInput == 'b' or uInput == 'broadcast'):
null = ''
sendBrd(null, null, null)
main()
elif usrInput == "read": # Opens a message from the inbox for viewing.
uInput = userInput("Would you like to read a message from the (I)nbox or (O)utbox?").lower()
if (uInput != 'i' and uInput != 'inbox' and uInput != 'o' and uInput != 'outbox'):
print '\n Invalid Input.\n'
usrPrompt = 1
main()
msgNum = int(userInput("What is the number of the message you wish to open?"))
if (uInput == 'i' or uInput == 'inbox'):
print '\n Loading...\n'
messageID = readMsg(msgNum)
uInput = userInput("\nWould you like to keep this message unread, (Y)es or (N)o?").lower()
if not (uInput == 'y' or uInput == 'yes'):
markMessageRead(messageID)
usrPrompt = 1
uInput = userInput("\nWould you like to (D)elete, (F)orward, (R)eply to, or (Exit) this message?").lower()
if uInput in ['r', 'reply']:
print '\n Loading...\n'
print ' '
replyMsg(msgNum, 'reply')
usrPrompt = 1
elif uInput == 'f' or uInput == 'forward':
print '\n Loading...\n'
print ' '
replyMsg(msgNum, 'forward')
usrPrompt = 1
elif uInput in ["d", 'delete']:
uInput = userInput("Are you sure, (Y)es or (N)o?").lower() # Prevent accidental deletion
if uInput == "y":
delMsg(msgNum)
print '\n Message Deleted.\n'
usrPrompt = 1
else:
usrPrompt = 1
else:
print '\n Invalid entry\n'
usrPrompt = 1
elif (uInput == 'o' or uInput == 'outbox'):
readSentMsg(msgNum)
# Gives the user the option to delete the message
uInput = userInput("Would you like to (D)elete, or (Exit) this message?").lower()
if (uInput == "d" or uInput == 'delete'):
uInput = userInput('Are you sure, (Y)es or (N)o?').lower() # Prevent accidental deletion
if uInput == "y":
delSentMsg(msgNum)
print '\n Message Deleted.\n'
usrPrompt = 1
else:
usrPrompt = 1
else:
print '\n Invalid Entry\n'
usrPrompt = 1
main()
elif usrInput == "save":
uInput = userInput("Would you like to save a message from the (I)nbox or (O)utbox?").lower()
if uInput not in ['i', 'inbox', 'o', 'outbox']:
print '\n Invalid Input.\n'
usrPrompt = 1
main()
if uInput in ['i', 'inbox']:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
while True:
msgNum = int(userInput("What is the number of the message you wish to save?"))
if msgNum >= numMessages:
print '\n Invalid Message Number.\n'
else:
break
subject = inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64')
# Don't decode since it is done in the saveFile function
message = inboxMessages['inboxMessages'][msgNum]['message']
elif uInput == 'o' or uInput == 'outbox':
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
while True:
msgNum = int(userInput("What is the number of the message you wish to save?"))
if msgNum >= numMessages:
print '\n Invalid Message Number.\n'
else:
break
subject = outboxMessages['sentMessages'][msgNum]['subject'].decode('base64')
# Don't decode since it is done in the saveFile function
message = outboxMessages['sentMessages'][msgNum]['message']
subject = subject + '.txt'
saveFile(subject, message)
usrPrompt = 1
main()
elif usrInput == "delete": # will delete a message from the system, not reflected on the UI.
uInput = userInput("Would you like to delete a message from the (I)nbox or (O)utbox?").lower()
if uInput in ['i', 'inbox']:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
while True:
msgNum = userInput(
'Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower()
if msgNum == 'a' or msgNum == 'all':
break
elif int(msgNum) >= numMessages:
print '\n Invalid Message Number.\n'
else:
break
uInput = userInput("Are you sure, (Y)es or (N)o?").lower() # Prevent accidental deletion
if uInput == "y":
if msgNum in ['a', 'all']:
print ' '
for msgNum in range(0, numMessages): # processes all of the messages in the inbox
print ' Deleting message ', msgNum + 1, ' of ', numMessages
delMsg(0)
print '\n Inbox is empty.'
usrPrompt = 1
else:
delMsg(int(msgNum))
print '\n Notice: Message numbers may have changed.\n'
main()
else:
usrPrompt = 1
elif uInput in ['o', 'outbox']:
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
while True:
msgNum = userInput(
'Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower()
if msgNum in ['a', 'all']:
break
elif int(msgNum) >= numMessages:
print '\n Invalid Message Number.\n'
else:
break
uInput = userInput("Are you sure, (Y)es or (N)o?").lower() # Prevent accidental deletion
if uInput == "y":
if msgNum in ['a', 'all']:
print ' '
for msgNum in range(0, numMessages): # processes all of the messages in the outbox
print ' Deleting message ', msgNum + 1, ' of ', numMessages
delSentMsg(0)
print '\n Outbox is empty.'
usrPrompt = 1
else:
delSentMsg(int(msgNum))
print '\n Notice: Message numbers may have changed.\n'
main()
else:
usrPrompt = 1
else:
print '\n Invalid Entry.\n'
usrPrompt = 1
main()
src = toReadOutbox(cmd='read', withAtta=withAtta)
elif usrInput == "exit":
print '\n You are already at the main menu. Use "quit" to quit.\n'
usrPrompt = 1
main()
elif cmdInput in cmdShorts['save']:
uInput = userInput('Would you like to save a message from the (i)nbox or (O)utbox?').lower()
elif usrInput == "listaddressbookentries":
res = listAddressBookEntries()
if res == 20:
print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
if uInput in inputShorts['inbox']:
withAtta = True
uInput = userInput('Would you like to decode and (s)ave or (D)ump directly?').lower()
if uInput in inputShorts['save']:
withAtta = False
src = toReadInbox(cmd='save', trunck=-1, withAtta=withAtta)
elif usrInput == "addaddressbookentry":
address = userInput('Enter address')
label = userInput('Enter label')
res = addAddressToAddressBook(address, label)
if res == 16:
print '\n Error: Address already exists in Address Book.\n'
if res == 20:
print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
else:
withAtta = True
uInput = userInput('Would you like to decode and (s)ave or (D)ump directly?').lower()
if uInput in inputShorts['save']:
withAtta = False
elif usrInput == "deleteaddressbookentry":
address = userInput('Enter address')
res = deleteAddressFromAddressBook(address)
if res == 20:
print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
src = toReadOutbox(cmd='save', trunck=-1, withAtta=withAtta)
elif usrInput == "markallmessagesread":
markAllMessagesRead()
usrPrompt = 1
main()
elif cmdInput in cmdShorts['quit']:
src = '\n You are already at the main menu. Use "quit" to quit.\n'
elif usrInput == "markallmessagesunread":
markAllMessagesUnread()
usrPrompt = 1
main()
elif cmdInput in cmdShorts['listAddressBK']:
src = listAddressBK()
elif usrInput == "status":
clientStatus()
usrPrompt = 1
main()
elif cmdInput in cmdShorts['addAddressBK']:
label = ''
while uInput == '':
uInput = userInput('Enter address to add.')
while label == '':
label = userInput('Enter label')
src = addAddressToAddressBook(uInput, label)
elif usrInput == "shutdown":
shutdown()
usrPrompt = 1
main()
elif cmdInput in cmdShorts['delAddressBK']:
while uInput == '':
uInput = userInput('Enter address to delete.')
src = deleteAddressFromAddressBook(uInput)
elif cmdInput in cmdShorts['readAll']:
src = markAllMessagesReadbit(True)
elif cmdInput in cmdShorts['unreadAll']:
src = markAllMessagesReadbit(False)
elif cmdInput in cmdShorts['status']:
src = clientStatus()
elif cmdInput in cmdShorts['shutdown']:
src = shutdown()
else:
print '\n "', usrInput, '" is not a command.\n'
src = '\n "' + cmdInput + '" is not a command.\n'
if src is None or src == '':
src = retStrings['none']
usrPrompt = 1
main()
elif 'Connection' in src or 'ProtocolError' in src:
usrPrompt = 0
else:
usrPrompt = 1
print src
def main():
def CLI():
"""Entrypoint for the CLI app"""
global api
global usrPrompt
global usrPrompt, config, api, cmdStr
if usrPrompt == 0:
print '\n ------------------------------'
print ' | Bitmessage Daemon by .dok |'
print ' | Version 0.3.1 for BM 0.6.2 |'
print ' ------------------------------'
api = xmlrpclib.ServerProxy(apiData()) # Connect to BitMessage using these api credentials
if config.conn:
api = BMAPIWrapper(config.conn, config.proxy)
# api.set_proxy(config.proxy)
if apiTest() is False:
print
print ' ****************************************************************'
print ' WARNING: You are not connected to the Bitmessage API service.'
print ' Either Bitmessage is not running or your settings are incorrect.'
print ' Use the command "apiTest" or "bmSettings" to resolve this issue.'
print ' ****************************************************************\n'
if apiTest() is False:
print '\n ****************************************************************'
print ' WARNING: You are not connected to the Bitmessage client.'
print ' Either Bitmessage is not running or your settings are incorrect.'
print ' Use the command "apiTest" or "bmSettings" to resolve this issue.'
print ' ****************************************************************\n'
print 'Type (H)elp for a list of commands.' # Startup message
usrPrompt = 2
print '\nType (H)elp for a list of commands.\nPress Enter for default cmd [%s]: ' % cmdStr # Startup message
usrPrompt = 2
else:
print
print ' *****************************************************'
print ' WARNING: API not functionable till you finish the'
print ' configuration.'
print ' *****************************************************\n'
print '\nType (H)elp for a list of commands.\nPress Enter for default cmd [%s]: ' % cmdStr # Startup message
usrPrompt = 0
elif usrPrompt == 1:
print '\nType (H)elp for a list of commands.' # Startup message
print '\nType (H)elp for a list of commands.\nPress Enter for default cmd [%s]: ' % cmdStr # Startup message
usrPrompt = 2
try:
UI((raw_input('>').lower()).replace(" ", ""))
cmdInput = raw_input('>').lower().replace(" ", "")
if cmdInput != '':
cmdStr = cmdInput # save as last cmd for replay
UI(cmdStr)
except EOFError:
UI("quit")
if __name__ == "__main__":
main()
config = Config(sys.argv)
config.parse()
bms_allow = False
socks_allow = False
if not config.arguments: # Config parse failed, show the help screen and exit
config.parse()
if config.action == 'main':
try:
print '- Try to get API connect uri from BMs setting file "keys.dat"...'
import bmsettings as conninit
bms_allow = True
if hasattr(conninit, 'apiData'):
config.conn = conninit.apiData()
print ' BMs host uri:', config.conn, '\n'
except Exception as err:
print ' Depends check failed, command "bmSettings" disabled. (%s)' % err
pass
try:
print '- Try to get socks module for proxied...'
from socks import ProxyError, GeneralProxyError, Socks5AuthError, Socks5Error, Socks4Error, HTTPError
import socks
if hasattr(socks, 'setdefaultproxy'):
socks_allow = True
else:
print ' Not the correct "socks" module imported.'
except ImportError as err:
print ' Depends check failed, "SOCKS" type proxy disabled. (%s)' % err
if getattr(config, 'start_daemon'):
start_daemon(getattr(config, 'start_daemon'))
socks_type = getattr(config, 'proxy_type', 'none')
proxy = dict()
if socks_type != 'none':
if socks_allow is False and 'SOCKS' in socks_type:
print ' Socks type proxy disabled.'
else:
for key in ['proxy_type', 'proxy_path', 'proxy_username', 'proxy_password', 'proxy_remotedns', 'proxy_timeout']:
proxy[key] = getattr(config, key)
config.proxy = proxy
if getattr(config, 'api_path', None):
if getattr(config, 'api_username', None) and getattr(config, 'api_password', None):
config.conn = '%s://%s:%s@%s/' % (config.api_type, config.api_username, config.api_password, config.api_path)
else:
config.conn = '%s://%s/' % (config.api_type, config.api_path)
actions = Actions()
start()
print 'bye'
sys.exit()

373
src/bmsettings.py Normal file
View File

@ -0,0 +1,373 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# pylint: disable=too-many-lines,global-statement,too-many-branches,too-many-statements,inconsistent-return-statements
# pylint: disable=too-many-nested-blocks,too-many-locals,protected-access,too-many-arguments,too-many-function-args
# pylint: disable=no-member
"""
Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation
Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php.
This is an example of a daemon client for PyBitmessage 0.6.2, by .dok (Version 0.3.1) , modified
TODO: fix the following (currently ignored) violations:
"""
import sys
import os
from bmconfigparser import BMConfigParser
import ConfigParser
keysName = 'keys.dat'
keysPath = 'keys.dat'
def userInput(message):
"""Checks input for exit or quit. Also formats for input, etc"""
print message
uInput = raw_input('> ')
return uInput
def lookupAppdataFolder():
"""gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py"""
APPNAME = "PyBitmessage"
if sys.platform == 'darwin':
if "HOME" in os.environ:
dataFolder = os.path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/'
else:
print(
' Could not find home folder, please report '
'this message and your OS X version to the Daemon Github.')
sys.exit(1)
elif 'win32' in sys.platform or 'win64' in sys.platform:
dataFolder = os.path.join(os.environ['APPDATA'], APPNAME) + '\\'
else:
dataFolder = os.path.expanduser(os.path.join("~", ".config/" + APPNAME + "/"))
return dataFolder
def configInit():
"""Initialised the configuration"""
BMConfigParser().add_section('bitmessagesettings')
# Sets the bitmessage port to stop the warning about the api not properly
# being setup. This is in the event that the keys.dat is in a different
# directory or is created locally to connect to a machine remotely.
BMConfigParser().set('bitmessagesettings', 'port', '8444')
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true') # Sets apienabled to true in keys.dat
with open(keysName, 'wb') as configfile:
BMConfigParser().write(configfile)
print ' {0} Initalized in the same directory as this CLI.\n' \
' You will now need to configure the {0} file.\n'.format(keysName)
def restartBmNotify():
"""Prompt the user to restart Bitmessage"""
print
print ' *******************************************************************'
print ' WARNING: If Bitmessage is running locally, you must restart it now.'
print ' *******************************************************************\n'
def apiInit(apiEnabled):
"""Initialise the API"""
global keysPath
BMConfigParser().read(keysPath)
isValid = False
if apiEnabled is False: # API information there but the api is disabled.
uInput = userInput('The API is not enabled. Would you like to do that now, (Y)es or (N)o?').lower()
if uInput == "y":
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true') # Sets apienabled to true in keys.dat
with open(keysPath, 'wb') as configfile:
BMConfigParser().write(configfile)
print 'Done'
restartBmNotify()
isValid = True
elif uInput == "n":
print
print ' ************************************************************'
print ' Daemon will not work when the API is disabled. '
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ************************************************************\n'
else:
print '\n Invalid Entry\n'
elif apiEnabled: # API correctly setup
# Everything is as it should be
isValid = True
else: # API information was not present.
print '\n ' + str(keysPath) + ' not properly configured!\n'
uInput = userInput('Would you like to do this now, (Y)es or (N)o?').lower()
if uInput == "y": # User said yes, initalize the api by writing these values to the keys.dat file
print ' '
apiUsr = userInput("API Username")
apiPwd = userInput("API Password")
apiPort = userInput("API Port")
apiEnabled = userInput("API Enabled? (True) or (False)").lower()
daemon = userInput("Daemon mode Enabled? (True) or (False)").lower()
if (daemon != 'true' and daemon != 'false'):
print '\n Invalid Entry for Daemon.\n'
# sets the bitmessage port to stop the warning about the api not properly
# being setup. This is in the event that the keys.dat is in a different
# directory or is created locally to connect to a machine remotely.
BMConfigParser().set('bitmessagesettings', 'port', '8444')
BMConfigParser().set('bitmessagesettings', 'apienabled', 'true')
BMConfigParser().set('bitmessagesettings', 'apiport', apiPort)
BMConfigParser().set('bitmessagesettings', 'apiinterface', '127.0.0.1')
BMConfigParser().set('bitmessagesettings', 'apiusername', apiUsr)
BMConfigParser().set('bitmessagesettings', 'apipassword', apiPwd)
BMConfigParser().set('bitmessagesettings', 'daemon', daemon)
with open(keysPath, 'wb') as configfile:
BMConfigParser().write(configfile)
print ' Finished configuring the keys.dat file with API information.\n'
restartBmNotify()
isValid = True
elif uInput == "n":
print
print ' ***********************************************************'
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ***********************************************************\n'
else:
print ' \nInvalid entry\n'
return isValid
def apiData():
"""TBC"""
global keysName
global keysPath
print '\n Configure file searching:', os.path.realpath(keysPath)
BMConfigParser().read(keysPath) # First try to load the config file (the keys.dat file) from the program directory
try:
BMConfigParser().get('bitmessagesettings', 'port')
appDataFolder = ''
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as err:
pass
# Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory.
appDataFolder = lookupAppdataFolder()
keysPath = appDataFolder + keysPath
print '\n Configure file searching:', os.path.realpath(keysPath)
BMConfigParser().read(keysPath)
try:
BMConfigParser().get('bitmessagesettings', 'port')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as err:
# keys.dat was not there either, something is wrong.
print
print ' *************************************************************'
print ' WARNING: There was a problem on accessing congfigure file.'
print ' Make sure that daemon is in the same directory as Bitmessage. '
print ' *************************************************************\n'
finalCheck = False
uInput = userInput('Would you like to create a "keys.dat" file in current working directory, (Y)es or (N)o?').lower()
if (uInput == "y" or uInput == "yes"):
configInit()
keysPath = keysName
f = True
elif (uInput == "n" or uInput == "no"):
print '\n Trying Again.\n'
else:
print '\n Invalid Input.\n'
if not finalCheck:
return ''
try: # checks to make sure that everyting is configured correctly. Excluding apiEnabled, it is checked after
BMConfigParser().get('bitmessagesettings', 'apiport')
BMConfigParser().get('bitmessagesettings', 'apiinterface')
BMConfigParser().get('bitmessagesettings', 'apiusername')
BMConfigParser().get('bitmessagesettings', 'apipassword')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as err:
isValid = apiInit("") # Initalize the keys.dat file with API information
if not isValid:
print '\n Config file not valid.\n'
return ''
# keys.dat file was found or appropriately configured, allow information retrieval
# apiEnabled =
# apiInit(BMConfigParser().safeGetBoolean('bitmessagesettings','apienabled'))
# #if false it will prompt the user, if true it will return true
BMConfigParser().read(keysPath) # read again since changes have been made
apiPort = int(BMConfigParser().get('bitmessagesettings', 'apiport'))
apiInterface = BMConfigParser().get('bitmessagesettings', 'apiinterface')
apiUsername = BMConfigParser().get('bitmessagesettings', 'apiusername')
apiPassword = BMConfigParser().get('bitmessagesettings', 'apipassword')
ret = "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface + ":" + str(apiPort) + "/"
print '\n API data successfully imported.'
# Build the api credentials
return ret
def bmSettings():
"""Allows the viewing and modification of keys.dat settings."""
global keysPath
global usrPrompt
BMConfigParser().read(keysPath) # Read the keys.dat
try:
print ' Configure file loading:', os.path.realpath(keysPath)
port = BMConfigParser().get('bitmessagesettings', 'port')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as err:
print '\n File not found.\n'
return ''
startonlogon = BMConfigParser().safeGetBoolean('bitmessagesettings', 'startonlogon')
minimizetotray = BMConfigParser().safeGetBoolean('bitmessagesettings', 'minimizetotray')
showtraynotifications = BMConfigParser().safeGetBoolean('bitmessagesettings', 'showtraynotifications')
startintray = BMConfigParser().safeGetBoolean('bitmessagesettings', 'startintray')
defaultnoncetrialsperbyte = BMConfigParser().get('bitmessagesettings', 'defaultnoncetrialsperbyte')
defaultpayloadlengthextrabytes = BMConfigParser().get('bitmessagesettings', 'defaultpayloadlengthextrabytes')
daemon = BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon')
socksproxytype = BMConfigParser().get('bitmessagesettings', 'socksproxytype')
sockshostname = BMConfigParser().get('bitmessagesettings', 'sockshostname')
socksport = BMConfigParser().get('bitmessagesettings', 'socksport')
socksauthentication = BMConfigParser().safeGetBoolean('bitmessagesettings', 'socksauthentication')
socksusername = BMConfigParser().get('bitmessagesettings', 'socksusername')
sockspassword = BMConfigParser().get('bitmessagesettings', 'sockspassword')
print
print ' -----------------------------------'
print ' | Current Bitmessage Settings |'
print ' -----------------------------------'
print ' port = ' + port
print ' startonlogon = ' + str(startonlogon)
print ' minimizetotray = ' + str(minimizetotray)
print ' showtraynotifications = ' + str(showtraynotifications)
print ' startintray = ' + str(startintray)
print ' defaultnoncetrialsperbyte = ' + defaultnoncetrialsperbyte
print ' defaultpayloadlengthextrabytes = ' + defaultpayloadlengthextrabytes
print ' daemon = ' + str(daemon)
print ' ------------------------------------'
print ' | Current Connection Settings |'
print ' -----------------------------------'
print ' socksproxytype = ' + socksproxytype
print ' sockshostname = ' + sockshostname
print ' socksport = ' + socksport
print ' socksauthentication = ' + str(socksauthentication)
print ' socksusername = ' + socksusername
print ' sockspassword = ' + sockspassword
print ' '
uInput = userInput('Would you like to modify any of these settings, (n)o or (Y)es?').lower()
if uInput in ['y', 'yes']:
while True: # loops if they mistype the setting name, they can exit the loop with 'exit'
invalidInput = False
uInput = userInput('What setting would you like to modify?').lower()
print ' '
if uInput == "port":
print ' Current port number: ' + port
uInput = userInput("Input the new port number.")
BMConfigParser().set('bitmessagesettings', 'port', str(uInput))
elif uInput == "startonlogon":
print ' Current status: ' + str(startonlogon)
uInput = userInput("Input the new status.")
BMConfigParser().set('bitmessagesettings', 'startonlogon', str(uInput))
elif uInput == "minimizetotray":
print ' Current status: ' + str(minimizetotray)
uInput = userInput("Input the new status.")
BMConfigParser().set('bitmessagesettings', 'minimizetotray', str(uInput))
elif uInput == "showtraynotifications":
print ' Current status: ' + str(showtraynotifications)
uInput = userInput("Input the new status.")
BMConfigParser().set('bitmessagesettings', 'showtraynotifications', str(uInput))
elif uInput == "startintray":
print ' Current status: ' + str(startintray)
uInput = userInput("Input the new status.")
BMConfigParser().set('bitmessagesettings', 'startintray', str(uInput))
elif uInput == "defaultnoncetrialsperbyte":
print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte
uInput = userInput("Input the new defaultnoncetrialsperbyte.")
BMConfigParser().set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput))
elif uInput == "defaultpayloadlengthextrabytes":
print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes
uInput = userInput("Input the new defaultpayloadlengthextrabytes.")
BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput))
elif uInput == "daemon":
print ' Current status: ' + str(daemon)
uInput = userInput("Input the new status.").lower()
BMConfigParser().set('bitmessagesettings', 'daemon', str(uInput))
elif uInput == "socksproxytype":
print ' Current socks proxy type: ' + socksproxytype
print "Possibilities: 'none', 'SOCKS4a', 'SOCKS5'."
uInput = userInput("Input the new socksproxytype.")
BMConfigParser().set('bitmessagesettings', 'socksproxytype', str(uInput))
elif uInput == "sockshostname":
print ' Current socks host name: ' + sockshostname
uInput = userInput("Input the new sockshostname.")
BMConfigParser().set('bitmessagesettings', 'sockshostname', str(uInput))
elif uInput == "socksport":
print ' Current socks port number: ' + socksport
uInput = userInput("Input the new socksport.")
BMConfigParser().set('bitmessagesettings', 'socksport', str(uInput))
elif uInput == "socksauthentication":
print ' Current status: ' + str(socksauthentication)
uInput = userInput("Input the new status.")
BMConfigParser().set('bitmessagesettings', 'socksauthentication', str(uInput))
elif uInput == "socksusername":
print ' Current socks username: ' + socksusername
uInput = userInput("Input the new socksusername.")
BMConfigParser().set('bitmessagesettings', 'socksusername', str(uInput))
elif uInput == "sockspassword":
print ' Current socks password: ' + sockspassword
uInput = userInput("Input the new password.")
BMConfigParser().set('bitmessagesettings', 'sockspassword', str(uInput))
else:
print "\n Invalid field. Please try again.\n"
invalidInput = True
if invalidInput is not True: # don't prompt if they made a mistake.
uInput = userInput("Would you like to change another setting, (n)o or (Y)es?").lower()
if uInput in ['n', 'no']:
with open(keysPath, 'wb') as configfile:
src = BMConfigParser().write(configfile)
restartBmNotify()
break
def main():
apiData() # configuration file "keys.dat" searching
bmSettings()
if __name__ == "__main__":
main()