From 8198e1922ad010da6ea4367da317b35f51c04562 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 19 Jan 2015 18:53:07 +0100 Subject: [PATCH] Added a CLI interface to the daemon from https://github.com/Dokument/PyBitmessage-Daemon/raw/master/daemon.py --- src/bitmessagecli.py | 1710 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1710 insertions(+) create mode 100644 src/bitmessagecli.py diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py new file mode 100644 index 00000000..cfe892b6 --- /dev/null +++ b/src/bitmessagecli.py @@ -0,0 +1,1710 @@ +#!/usr/bin/env python2.7.x +# 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.4.2, by .dok (Version 0.3.0) + + +import ConfigParser +import xmlrpclib +import datetime +import hashlib +import getopt +import imghdr +import ntpath +import json +import time +import sys +import os + +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() + +def userInput(message): #Checks input for exit or quit. Also formats for input, etc + global usrPrompt + print '\n' + message + uInput = raw_input('> ') + + if (uInput.lower() == 'exit'): #Returns the user to the main menu + usrPrompt = 1 + main() + + elif (uInput.lower() == 'quit'): #Quits the program + print '\n Bye\n' + sys.exit() + os.exit() + else: + return uInput + +def restartBmNotify(): #Prompts the user to restart Bitmessage. + print '\n *******************************************************************' + print ' WARNING: If Bitmessage is running locally, you must restart it now.' + print ' *******************************************************************\n' + +def safeConfigGetBoolean(section,field): + global keysPath + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + try: + return config.getboolean(section,field) + except: + return False + +#Begin keys.dat interactions +def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py + APPNAME = "PyBitmessage" + from os import path, environ + if sys.platform == 'darwin': + if "HOME" in environ: + dataFolder = 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.' + os.exit() + + elif 'win32' in sys.platform or 'win64' in sys.platform: + dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\' + else: + dataFolder = path.expanduser(path.join("~", ".config/" + APPNAME + "/")) + return dataFolder + +def configInit(): + global keysName + config = ConfigParser.SafeConfigParser() + + config.add_section('bitmessagesettings') + config.set('bitmessagesettings', 'port', '8444') #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. + config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat + + with open(keysName, 'wb') as configfile: + config.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): + global keysPath + global usrPrompt + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + + + if (apiEnabled == 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": # + config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat + with open(keysPath, 'wb') as configfile: + config.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 == True): #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") + apiInterface = userInput("API Interface. (127.0.0.1)") + 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' + + config.set('bitmessagesettings', 'port', '8444') #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. + config.set('bitmessagesettings','apienabled','true') + config.set('bitmessagesettings', 'apiport', apiPort) + config.set('bitmessagesettings', 'apiinterface', '127.0.0.1') + config.set('bitmessagesettings', 'apiusername', apiUsr) + config.set('bitmessagesettings', 'apipassword', apiPwd) + config.set('bitmessagesettings', 'daemon', daemon) + with open(keysPath, 'wb') as configfile: + config.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(): + global keysName + global keysPath + global usrPrompt + + config = ConfigParser.SafeConfigParser() + config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory + + try: + config.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 + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + try: + config.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 + config.get('bitmessagesettings', 'apiport') + config.get('bitmessagesettings', 'apiinterface') + config.get('bitmessagesettings', 'apiusername') + config.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(safeConfigGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true + + config.read(keysPath)#read again since changes have been made + apiPort = int(config.get('bitmessagesettings', 'apiport')) + apiInterface = config.get('bitmessagesettings', 'apiinterface') + apiUsername = config.get('bitmessagesettings', 'apiusername') + apiPassword = config.get('bitmessagesettings', 'apipassword') + + print '\n API data successfully imported.\n' + + return "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface+ ":" + str(apiPort) + "/" #Build the api credentials + +#End keys.dat interactions + + +def apiTest(): #Tests the API connection to bitmessage. Returns true if it is connected. + + try: + result = api.add(2,3) + except: + return False + + if (result == 5): + return True + else: + return False + +def bmSettings(): #Allows the viewing and modification of keys.dat settings. + global keysPath + global usrPrompt + config = ConfigParser.SafeConfigParser() + keysPath = 'keys.dat' + + config.read(keysPath)#Read the keys.dat + try: + port = config.get('bitmessagesettings', 'port') + except: + print '\n File not found.\n' + usrPrompt = 0 + main() + + startonlogon = safeConfigGetBoolean('bitmessagesettings', 'startonlogon') + minimizetotray = safeConfigGetBoolean('bitmessagesettings', 'minimizetotray') + showtraynotifications = safeConfigGetBoolean('bitmessagesettings', 'showtraynotifications') + startintray = safeConfigGetBoolean('bitmessagesettings', 'startintray') + defaultnoncetrialsperbyte = config.get('bitmessagesettings', 'defaultnoncetrialsperbyte') + defaultpayloadlengthextrabytes = config.get('bitmessagesettings', 'defaultpayloadlengthextrabytes') + daemon = safeConfigGetBoolean('bitmessagesettings', 'daemon') + + socksproxytype = config.get('bitmessagesettings', 'socksproxytype') + sockshostname = config.get('bitmessagesettings', 'sockshostname') + socksport = config.get('bitmessagesettings', 'socksport') + socksauthentication = safeConfigGetBoolean('bitmessagesettings', 'socksauthentication') + socksusername = config.get('bitmessagesettings', 'socksusername') + sockspassword = config.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.") + config.set('bitmessagesettings', 'port', str(uInput)) + elif uInput == "startonlogon": + print ' Current status: ' + str(startonlogon) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'startonlogon', str(uInput)) + elif uInput == "minimizetotray": + print ' Current status: ' + str(minimizetotray) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'minimizetotray', str(uInput)) + elif uInput == "showtraynotifications": + print ' Current status: ' + str(showtraynotifications) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'showtraynotifications', str(uInput)) + elif uInput == "startintray": + print ' Current status: ' + str(startintray) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'startintray', str(uInput)) + elif uInput == "defaultnoncetrialsperbyte": + print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte + uInput = userInput("Enter the new defaultnoncetrialsperbyte.") + config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput)) + elif uInput == "defaultpayloadlengthextrabytes": + print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes + uInput = userInput("Enter the new defaultpayloadlengthextrabytes.") + config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput)) + elif uInput == "daemon": + print ' Current status: ' + str(daemon) + uInput = userInput("Enter the new status.").lower() + config.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.") + config.set('bitmessagesettings', 'socksproxytype', str(uInput)) + elif uInput == "sockshostname": + print ' Current socks host name: ' + sockshostname + uInput = userInput("Enter the new sockshostname.") + config.set('bitmessagesettings', 'sockshostname', str(uInput)) + elif uInput == "socksport": + print ' Current socks port number: ' + socksport + uInput = userInput("Enter the new socksport.") + config.set('bitmessagesettings', 'socksport', str(uInput)) + elif uInput == "socksauthentication": + print ' Current status: ' + str(socksauthentication) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'socksauthentication', str(uInput)) + elif uInput == "socksusername": + print ' Current socks username: ' + socksusername + uInput = userInput("Enter the new socksusername.") + config.set('bitmessagesettings', 'socksusername', str(uInput)) + elif uInput == "sockspassword": + print ' Current socks password: ' + sockspassword + uInput = userInput("Enter the new password.") + config.set('bitmessagesettings', 'sockspassword', str(uInput)) + else: + print "\n Invalid input. Please try again.\n" + invalidInput = True + + if invalidInput != 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: + config.write(configfile) + restartBmNotify() + break + + + elif uInput == "n": + usrPrompt = 1 + main() + else: + print "Invalid input." + usrPrompt = 1 + main() + +def validAddress(address): + address_information = api.decodeAddress(address) + address_information = eval(address_information) + + if 'success' in str(address_information.get('status')).lower(): + return True + else: + return False + +def getAddress(passphrase,vNumber,sNumber): + passphrase = passphrase.encode('base64')#passphrase must be encoded + + return api.getDeterministicAddress(passphrase,vNumber,sNumber) + +def subscribe(): + global usrPrompt + + while True: + address = userInput("What address would you like to subscribe to?") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== 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') + + api.addSubscription(address,label) + print ('\n You are now subscribed to: ' + address + '\n') + +def unsubscribe(): + global usrPrompt + + while True: + address = userInput("What address would you like to unsubscribe from?") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + + uInput = userInput("Are you sure, (Y)es or (N)o?").lower() + + api.deleteSubscription(address) + print ('\n You are now unsubscribed from: ' + address + '\n') + +def listSubscriptions(): + global usrPrompt + #jsonAddresses = json.loads(api.listSubscriptions()) + #numAddresses = len(jsonAddresses['addresses']) #Number of addresses + print '\nLabel, Address, Enabled\n' + try: + print api.listSubscriptions() + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + '''for addNum in range (0, numAddresses): #processes all of the addresses and lists them out + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + enabled = jsonAddresses['addresses'][addNum]['enabled'] + + print label, address, enabled + ''' + print ' ' + +def createChan(): + 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() + + +def joinChan(): + global usrPrompt + while True: + address = userInput("Enter channel address") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + password = userInput("Enter channel name") + password = password.encode('base64') + try: + print api.joinChan(password,address) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def leaveChan(): + global usrPrompt + while True: + address = userInput("Enter channel address") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== 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() + + +def listAdd(): #Lists 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 ' |---|-------------------|-------------------------------------|--|-------|' + for addNum in range (0, numAddresses): #processes all of the addresses and lists them out + label = str(jsonAddresses['addresses'][addNum]['label']) + address = str(jsonAddresses['addresses'][addNum]['address']) + stream = str(jsonAddresses['addresses'][addNum]['stream']) + enabled = str(jsonAddresses['addresses'][addNum]['enabled']) + + if (len(label) > 19): + label = label[:16] + '...' + + print ' |' + str(addNum).ljust(3) + '|' + label.ljust(19) + '|' + address.ljust(37) + '|' + stream.ljust(1), '|' + enabled.ljust(7) + '|' + + print ' --------------------------------------------------------------------------\n' + +def genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe): #Generate address + global usrPrompt + if deterministic == 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() + + return generatedAddress + + elif deterministic == True: #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 + else: + return 'Entry Error' + +def saveFile(fileName, fileData): #Allows attachments and messages/broadcats to be saved + + #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("|", "~") + + directory = 'attachments' + + if not os.path.exists(directory): + os.makedirs(directory) + + filePath = directory +'/'+ fileName + + '''try: #Checks if file already exists + with open(filePath): + print 'File Already Exists' + return + except IOError: pass''' + + + f = open(filePath, 'wb+') #Begin saving to file + f.write(fileData.decode("base64")) + f.close + + print '\n Successfully saved '+ filePath + '\n' + +def attachment(): #Allows users to attach a file to their message or broadcast + theAttachmentS = '' + + while True: + + 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.') + + try: + with open(filePath): break + except IOError: + print '\n %s was not found on your filesystem or can not be opened.\n' % filePath + pass + + #print filesize, and encoding estimate with confirmation if file is over X size (1mb?) + invSize = os.path.getsize(filePath) + invSize = (invSize / 1024) #Converts to kilobytes + round(invSize,2) #Rounds to two decimal places + + if (invSize > 500.0):#If over 500KB + print '\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() + + 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() + + 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 ' Attachment detected as an Image.' + print ' tags will automatically be included,' + print ' allowing the recipient to view the image' + print ' using the "View HTML code..." option in Bitmessage.' + print ' ---------------------------------------------------\n' + isImage = True + time.sleep(2) + + print '\n Encoding Attachment, Please Wait ...\n' #Alert the user that the encoding process may take some time. + + with open(filePath, 'rb') as f: #Begin the actual encoding + data = f.read(188743680) #Reads files up to 180MB, the maximum size for Bitmessage. + data = data.encode("base64") + + if (isImage == True): #If it is an image, include image tags in the message + theAttachment = """ + + + +Filename:%s +Filesize:%sKB +Encoding:base64 + +
+
+ %s +
+
""" % (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) + + 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 + theAttachmentS = str(theAttachmentS) + str(theAttachment)+ '\n\n' + elif (uInput == 'n' or uInput == 'no'): + break + + theAttachmentS = theAttachmentS + theAttachment + return theAttachmentS + +def sendMsg(toAddress, fromAddress, subject, message): #With no arguments sent, sendMsg fills in the blanks. subject and message must be encoded before they are passed. + global usrPrompt + if (validAddress(toAddress)== False): + while True: + toAddress = userInput("What is the To Address?") + + if (toAddress == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(toAddress)== False): + print '\n Invalid Address. "c" to cancel. Please try again.\n' + else: + break + + + if (validAddress(fromAddress)== False): + try: + jsonAddresses = json.loads(api.listAddresses()) + numAddresses = len(jsonAddresses['addresses']) #Number of addresses + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + 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() + + for addNum in range (0, numAddresses): #processes all of the addresses + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == label): #address entered was a label and is found + fromAddress = address + found = True + break + + if (found == False): + if(validAddress(fromAddress)== False): + print '\n Invalid Address. Please try again.\n' + + else: + for addNum in range (0, numAddresses): #processes all of the addresses + #label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == address): #address entered was a found in our addressbook. + found = True + break + + if (found == False): + print '\n The address entered is not one of yours. Please try again.\n' + + if (found == True): + 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'] + + if (subject == ''): + 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": + message = message + '\n\n' + attachment() + + message = message.encode('base64') + + try: + ackData = api.sendMessage(toAddress, fromAddress, subject, message) + print '\n Message Status:', api.getStatus(ackData), '\n' + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + +def sendBrd(fromAddress, subject, message): #sends a broadcast + global usrPrompt + if (fromAddress == ''): + + try: + jsonAddresses = json.loads(api.listAddresses()) + numAddresses = len(jsonAddresses['addresses']) #Number of addresses + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + 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() + + for addNum in range (0, numAddresses): #processes all of the addresses + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == label): #address entered was a label and is found + fromAddress = address + found = True + break + + if (found == False): + if(validAddress(fromAddress)== False): + print '\n Invalid Address. Please try again.\n' + + else: + for addNum in range (0, numAddresses): #processes all of the addresses + #label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == address): #address entered was a found in our addressbook. + found = True + break + + if (found == False): + print '\n The address entered is not one of yours. Please try again.\n' + + if (found == True): + 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'] + + if (subject == ''): + 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": + message = message + '\n\n' + attachment() + + message = message.encode('base64') + + try: + ackData = api.sendBroadcast(fromAddress, subject, message) + print '\n Message Status:', api.getStatus(ackData), '\n' + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def inbox(unreadOnly = False): #Lists the messages by: Message Number, 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() + + 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 ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') + messagesPrinted += 1 + if not message['read']: messagesUnread += 1 + + if (messagesPrinted%20 == 0 and messagesPrinted != 0): + uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + + print '\n -----------------------------------' + print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages) + print ' -----------------------------------\n' + +def outbox(): + global usrPrompt + try: + outboxMessages = json.loads(api.getAllSentMessages()) + numMessages = len(outboxMessages['sentMessages']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + 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 + print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject + + print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') + + if (msgNum%20 == 0 and msgNum != 0): + uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + + print '\n -----------------------------------' + print ' There are ',numMessages,' messages in the outbox.' + print ' -----------------------------------\n' + +def readSentMsg(msgNum): #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 ' ' + + if (msgNum >= numMessages): + print '\n Invalid Message Number.\n' + main() + + #Begin attachment detection + message = outboxMessages['sentMessages'][msgNum]['message'].decode('base64') + + 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'): + + attachment = message[attPos+9:attEndPos] + saveFile(fileName,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 ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject + print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') + print ' Message:\n' + print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + print ' ' + +def readMsg(msgNum): #Opens 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() + + #Begin attachment detection + message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + + 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'): + + attachment = message[attPos+9:attEndPos] + saveFile(fileName,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 ' From:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Received:',datetime.datetime.fromtimestamp(float(inboxMessages['inboxMessages'][msgNum]['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') + print ' Message:\n' + print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + print ' ' + return inboxMessages['inboxMessages'][msgNum]['msgid'] + +def replyMsg(msgNum,forwardORreply): #Allows you to reply to the message you are currently on. Saves typing in the addresses and subject. + global usrPrompt + forwardORreply = forwardORreply.lower() #makes it lowercase + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + 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. + + subject = inboxMessages['inboxMessages'][msgNum]['subject'] + subject = subject.decode('base64') + + if (forwardORreply == 'reply'): + toAdd = inboxMessages['inboxMessages'][msgNum]['fromAddress'] #Address it was From, now the To address + subject = "Re: " + subject + + elif (forwardORreply == 'forward'): + subject = "Fwd: " + subject + + while True: + toAdd = userInput("What is the To Address?") + + if (toAdd == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(toAdd)== 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() + + subject = subject.encode('base64') + + newMessage = userInput("Enter your Message.") + + uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() + if uInput == "y": + newMessage = newMessage + '\n\n' + attachment() + + newMessage = newMessage + '\n\n------------------------------------------------------\n' + newMessage = newMessage + message + newMessage = newMessage.encode('base64') + + sendMsg(toAdd, fromAdd, subject, newMessage) + + main() + +def delMsg(msgNum): #Deletes a specified message from the inbox + global usrPrompt + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + msgId = inboxMessages['inboxMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number + + msgAck = api.trashMessage(msgId) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + return msgAck + +def delSentMsg(msgNum): #Deletes a specified message from the outbox + global usrPrompt + try: + outboxMessages = json.loads(api.getAllSentMessages()) + msgId = outboxMessages['sentMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number + msgAck = api.trashSentMessage(msgId) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + return msgAck + +def getLabelForAddress(address): + global usrPrompt + + if address in knownAddresses: + return knownAddresses[address] + else: + buildKnownAddresses() + if address in knownAddresses: + return knownAddresses[address] + + return address + +def buildKnownAddresses(): + # 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() + + # 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() + +def listAddressBookEntries(): + try: + 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 + + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def addAddressToAddressBook(address, label): + 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() + +def deleteAddressFromAddressBook(address): + try: + response = api.deleteAddressBookEntry(address) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def getAPIErrorCode(response): + if "API Error" in response: + # if we got an API error return the number by getting the number + # after the second space and removing the trailing colon + return int(response.split()[2][:-1]) + +def markMessageRead(messageID): + try: + response = api.getInboxMessageByID(messageID, True) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def markMessageUnread(messageID): + try: + response = api.getInboxMessageByID(messageID, False) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def markAllMessagesRead(): + 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']) + +def markAllMessagesUnread(): + 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']) + + +def UI(usrInput): #Main user menu + global usrPrompt + + 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 ' | listSubscriptions | Lists all of the subscriptions. |' + 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() + + elif usrInput == "apitest": #tests the API Connection. + if (apiTest() == True): + print '\n API connection test has: PASSED\n' + else: + print '\n API connection test has: FAILED\n' + main() + + elif usrInput == "addinfo": + tmp_address = userInput('\nEnter the Bitmessage Address.') + address_information = api.decodeAddress(tmp_address) + address_information = eval(address_information) + + print '\n------------------------------' + + if 'success' in str(address_information.get('status')).lower(): + print ' Valid Address' + print ' Address Version: %s' % str(address_information.get('addressVersion')) + print ' Stream Number: %s' % str(address_information.get('streamNumber')) + else: + print ' Invalid Address !' + + 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() + os.exit() + + 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 == "d" or uInput == "determinstic": #Creates a deterministic address + deterministic = True + + #lbl = raw_input('Label the new address:') #currently not possible via the api + lbl = '' + passphrase = userInput('Enter the Passphrase.')#.encode('base64') + numOfAdd = int(userInput('How many addresses would you like to generate?')) + #addVNum = int(raw_input('Address version number (default "0"):')) + #streamNum = int(raw_input('Stream number (default "0"):')) + addVNum = 3 + streamNum = 1 + isRipe = userInput('Shorten the address, (Y)es or (N)o?').lower() + + if isRipe == "y": + ripe = True + print genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe) + main() + elif isRipe == "n": + 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() + + + elif uInput == "r" or uInput == "random": #Creates a random address with user-defined label + deterministic = False + null = '' + lbl = userInput('Enter the label for the new address.') + + print genAdd(lbl,deterministic, null,null, null, null, null) + main() + + else: + print '\n Invalid input\n' + main() + + elif usrInput == "getaddress": #Gets the address for/from a passphrase + phrase = userInput("Enter the address passphrase.") + print '\n Working...\n' + #vNumber = int(raw_input("Enter the address version number:")) + #sNumber = int(raw_input("Enter the address stream number:")) + + address = getAddress(phrase,4,1)#,vNumber,sNumber) + print ('\n Address: ' + address + '\n') + + usrPrompt = 1 + main() + + elif usrInput == "subscribe": #Subsribe to an address + subscribe() + usrPrompt = 1 + main() + 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() + userPrompt = 1 + main() + + elif usrInput == "join": + joinChan() + userPrompt = 1 + main() + + elif usrInput == "leave": + leaveChan() + userPrompt = 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 == 'r' or uInput == '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 == "d" or uInput == '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) + + uInput = userInput("Would you like to (D)elete, or (Exit) this message?").lower() #Gives the user the option to delete the message + + 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 != 'i' and uInput == 'inbox' and uInput != 'o' and uInput == 'outbox'): + print '\n Invalid Input.\n' + usrPrompt = 1 + main() + + if (uInput == 'i' or uInput == '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') + message = inboxMessages['inboxMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function + + 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') + message = outboxMessages['sentMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function + + 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 == 'i' or uInput == '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 == 'a' or msgNum == '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 == 'o' or uInput == '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 == '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 == 'a' or msgNum == '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' + userPrompt = 1 + main() + + elif usrInput == "exit": + print '\n You are already at the main menu. Use "quit" to quit.\n' + usrPrompt = 1 + main() + + elif usrInput == "listaddressbookentries": + res = listAddressBookEntries() + if res == 20: print '\n Error: API function not supported.\n' + usrPrompt = 1 + main() + + 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() + + elif usrInput == "deleteaddressbookentry": + address = userInput('Enter address') + res = deleteAddressFromAddressBook(address) + if res == 20: print '\n Error: API function not supported.\n' + usrPrompt = 1 + main() + + elif usrInput == "markallmessagesread": + markAllMessagesRead() + usrPrompt = 1 + main() + + elif usrInput == "markallmessagesunread": + markAllMessagesUnread() + usrPrompt = 1 + main() + + else: + print '\n "',usrInput,'" is not a command.\n' + usrPrompt = 1 + main() + +def main(): + global api + global usrPrompt + + if (usrPrompt == 0): + print '\n ------------------------------' + print ' | Bitmessage Daemon by .dok |' + print ' | Version 0.2.6 for BM 0.3.5 |' + print ' ------------------------------' + api = xmlrpclib.ServerProxy(apiData()) #Connect to BitMessage using these api credentials + + if (apiTest() == 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 + + #if (apiTest() == False):#Preform a connection test #taken out until I get the error handler working + # print '*************************************' + # print 'WARNING: No connection to Bitmessage.' + # print '*************************************' + # print ' ' + elif (usrPrompt == 1): + print '\nType (H)elp for a list of commands.' #Startup message + usrPrompt = 2 + + try: + UI((raw_input('>').lower()).replace(" ", "")) + except EOFError: + UI("quit") + +if __name__ == "__main__": + main()