From 425eded1a2b810f27dc5c3b176f4e3b1e6796789 Mon Sep 17 00:00:00 2001
From: peter-tank <30540412+peter-tank@users.noreply.github.com>
Date: Tue, 17 Jul 2018 20:01:02 +0800
Subject: [PATCH] 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>
---
src/bitmessagecli.py | 3519 +++++++++++++++++++++++++-----------------
src/bmsettings.py | 373 +++++
2 files changed, 2471 insertions(+), 1421 deletions(-)
create mode 100644 src/bmsettings.py
diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py
index 02fed7e9..faa8c75a 100644
--- a/src/bitmessagecli.py
+++ b/src/bitmessagecli.py
@@ -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 ' 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 = """
-
+
-Filename:%s
-Filesize:%sKB
-Encoding:base64
+Filename: %s
+Filesize: %sKB
+Encoding: base64
-
+
""" % (fileName, invSize, fileName, filetype, data)
else: # Else it is not an image so do not include the embedded image code.
theAttachment = """
-
+
Filename:%s
Filesize:%sKB
Encoding:base64
-""" % (fileName, invSize, fileName, fileName, data)
+""" % (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 .\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 .\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, | = (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=" | = (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 '' 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] + '~~' + 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] + '~~' + 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 .\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()
diff --git a/src/bmsettings.py b/src/bmsettings.py
new file mode 100644
index 00000000..3ffbe8b4
--- /dev/null
+++ b/src/bmsettings.py
@@ -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()