From 34f903bd7df4387a47f7b83de2975d0c065951d9 Mon Sep 17 00:00:00 2001 From: Kashiko Koibumi Date: Mon, 24 Jun 2024 19:08:16 +0900 Subject: [PATCH 1/5] port to run with Python3; only sending is workable --- bminterface.py | 39 ++++++++++++++++++++------------------- incoming.py | 6 +++--- outgoing.py | 6 +++--- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/bminterface.py b/bminterface.py index 423af94..4f70b2f 100644 --- a/bminterface.py +++ b/bminterface.py @@ -1,11 +1,12 @@ -import ConfigParser -import xmlrpclib +from six.moves import configparser +from six.moves import xmlrpc_client import json import datetime import time import email.utils import os import logging +from codecs import encode purgeList = [] allMessages = [] @@ -17,7 +18,7 @@ def _getKeyLocation(): #make this not suck later def _getConfig(keys): return apiData() #TODO make this work, so the above can be removed - config = ConfigParser.SafeConfigParser() + config = configparser.SafeConfigParser() config.read(keys) try: api_port = config.getint('bitmessagesettings', 'apiport') @@ -30,7 +31,7 @@ def _getConfig(keys): return "http://"+api_uname+":"+api_passwd+"@"+api_iface+":"+str(api_port)+"/" def _makeApi(keys): - return xmlrpclib.ServerProxy(_getConfig(keys)) + return xmlrpc_client.ServerProxy(_getConfig(keys)) def _sendMessage(toAddress, fromAddress, subject, body): api = _makeApi(_getKeyLocation()) @@ -73,8 +74,8 @@ def registerAddress(address): def send(toAddress, fromAddress, subject, body): toAddress = _stripAddress(toAddress) fromAddress = _stripAddress(fromAddress) - subject = subject.encode('base64') - body = body.encode('base64') + subject = encode(subject.encode("utf-8", "replace"), 'base64').decode("utf-8", "replace") + body = encode(body.encode("utf-8", "replace"), 'base64').decode("utf-8", "replace") if toAddress == 'broadcast': return _sendBroadcast(fromAddress, subject, body) else: @@ -170,7 +171,7 @@ def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depe def apiData(): global keysPath - config = ConfigParser.SafeConfigParser() + config = configparser.SafeConfigParser() keysPath = 'keys.dat' config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory @@ -181,30 +182,30 @@ def apiData(): #Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory. appDataFolder = lookupAppdataFolder() keysPath = appDataFolder + 'keys.dat' - config = ConfigParser.SafeConfigParser() + config = configparser.SafeConfigParser() config.read(keysPath) try: config.get('bitmessagesettings','settingsversion') except: #keys.dat was not there either, something is wrong. - print ' ' - print '******************************************************************' - print 'There was a problem trying to access the Bitmessage keys.dat file.' - print 'Make sure that daemon is in the same directory as Bitmessage.' - print '******************************************************************' - print ' ' - print config - print ' ' + print(' ') + print('******************************************************************') + print('There was a problem trying to access the Bitmessage keys.dat file.') + print('Make sure that daemon is in the same directory as Bitmessage.') + print('******************************************************************') + print(' ') + print(config) + print(' ') try: apiConfigured = config.getboolean('bitmessagesettings','apienabled') #Look for 'apienabled' apiEnabled = apiConfigured except: apiConfigured = False #If not found, set to false since it still needs to be configured - print "You need to edit your keys.dat file and enable bitmessage's API" - print "See for more details: https://bitmessage.org/wiki/API" - print "Will now crash..." + print("You need to edit your keys.dat file and enable bitmessage's API") + print("See for more details: https://bitmessage.org/wiki/API") + print("Will now crash...") raise #if (apiConfigured == False):#If the apienabled == false or is not present in the keys.dat file, notify the user and set it up diff --git a/incoming.py b/incoming.py index 7a8987a..5142abe 100644 --- a/incoming.py +++ b/incoming.py @@ -257,7 +257,7 @@ def incomingServer_main(host, port, run_event): sock = None try: conn = ChatterboxConnection(conn) - conn.sendall("+OK server ready") + conn.sendall(b"+OK server ready") while run_event.is_set(): data = conn.recvall() logging.debug("Answering %s" % data) @@ -265,7 +265,7 @@ def incomingServer_main(host, port, run_event): try: cmd = dispatch[command] except KeyError: - conn.sendall("-ERR unknown command") + conn.sendall(b"-ERR unknown command") else: conn.sendall(cmd(data)) if cmd is handleQuit: @@ -275,7 +275,7 @@ def incomingServer_main(host, port, run_event): except (SystemExit, KeyboardInterrupt): pass - except Exception, ex: + except Exception as ex: raise finally: if sock is not None: diff --git a/outgoing.py b/outgoing.py index c2e787d..c50167f 100644 --- a/outgoing.py +++ b/outgoing.py @@ -5,14 +5,14 @@ import logging class outgoingServer(SMTPServer): - def process_message(self, peer, mailfrom, rcpttos, data): + def process_message(self, peer, mailfrom, rcpttos, data, mail_options=[], rcpt_options=[]): parser = email.parser.FeedParser() - parser.feed(data) + parser.feed(data.decode("utf-8", "replace")) msg = parser.close() toAddress = msg['To'] fromAddress = msg['From'] - subject = u' '.join(unicode(t[0], t[1] or 'UTF-8') for t in email.header.decode_header(msg['Subject'])).encode('UTF-8') + subject = ' '.join(t[0] for t in email.header.decode_header(msg['Subject'])) body = self._bmformat(msg) #Make sure we don't send an actually blank subject or body--this can cause problems. From d6197cb0b6a0224ca11c493dde4e065bbf5dc915 Mon Sep 17 00:00:00 2001 From: Kashiko Koibumi Date: Mon, 24 Jun 2024 21:30:49 +0900 Subject: [PATCH 2/5] port to Python3; receiving works now; some errors remained --- bminterface.py | 16 ++++++------ incoming.py | 67 ++++++++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/bminterface.py b/bminterface.py index 4f70b2f..e4c60f9 100644 --- a/bminterface.py +++ b/bminterface.py @@ -1,12 +1,11 @@ from six.moves import configparser from six.moves import xmlrpc_client -import json import datetime import time import email.utils import os import logging -from codecs import encode +from codecs import encode, decode purgeList = [] allMessages = [] @@ -86,12 +85,13 @@ def _getAll(): global currentAddress if not allMessages: api = _makeApi(_getKeyLocation()) - allMessages = json.loads(api.getAllInboxMessages()) + allMessages = api.getAllInboxMessages() logging.debug("current address is %s" % currentAddress) if currentAddress is not None: ret = [] + cur_addr = currentAddress.decode("utf-8", "replace") for msg in allMessages['inboxMessages']: - if msg['toAddress'] == currentAddress: + if msg['toAddress'] == cur_addr: ret.append(msg) return dict(inboxMessages=ret) return allMessages @@ -106,8 +106,8 @@ def get(msgID): #if 'Broadcast' in toAddress: # toAddress = fromAddress - subject = inboxMessages['inboxMessages'][msgID]['subject'].decode('base64') - body = inboxMessages['inboxMessages'][msgID]['message'].decode('base64') + subject = decode(inboxMessages['inboxMessages'][msgID]['subject'].encode("utf-8", "replace"), 'base64') + body = decode(inboxMessages['inboxMessages'][msgID]['message'].encode("utf-8", "replace"), 'base64') return dateTime, toAddress, fromAddress, subject, body def listMsgs(): @@ -136,7 +136,7 @@ def _deleteMessage(msgRef): def getUIDLforAll(): api = _makeApi(_getKeyLocation()) - inboxMessages = json.loads(api.getAllInboxMessages()) + inboxMessages = api.getAllInboxMessages() refdata = [] for msgID in range(len(inboxMessages['inboxMessages'])): msgRef = inboxMessages['inboxMessages'][msgID]['msgid'] #gets the message Ref via the message index number @@ -145,7 +145,7 @@ def getUIDLforAll(): def getUIDLforSingle(msgID): api = _makeApi(_getKeyLocation()) - inboxMessages = json.loads(api.getAllInboxMessages()) + inboxMessages = api.getAllInboxMessages() msgRef = inboxMessages['inboxMessages'][msgID]['msgid'] #gets the message Ref via the message index number return [str(msgRef)] diff --git a/incoming.py b/incoming.py index 5142abe..045a276 100644 --- a/incoming.py +++ b/incoming.py @@ -11,7 +11,7 @@ class ChatterboxConnection(object): - END = "\r\n" + END = b"\r\n" def __init__(self, conn): self.conn = conn def __getattr__(self, name): @@ -33,23 +33,23 @@ def recvall(self, END=END): data[-2] = pair[:pair.index(END)] data.pop() break - return "".join(data) + return b"".join(data) def handleUser(data): d = data.split() logging.debug("data:%s" % d) username = d[-1] - if username[:3] == 'BM-': + if username[:3] == b'BM-': logging.debug("Only showing messages for %s" % username) bminterface.registerAddress(username) else: logging.debug("Showing all messages in the inbox") bminterface.registerAddress(None) - return "+OK user accepted" + return b"+OK user accepted" def handlePass(data): - return "+OK pass accepted" + return b"+OK pass accepted" def _getMsgSizes(): msgCount = bminterface.listMsgs() @@ -66,7 +66,7 @@ def handleStat(data): msgSizeTotal = 0 for msgSize in msgSizes: msgSizeTotal += msgSize - returnData = '+OK %i %i' % (msgCount, msgSizeTotal) + returnData = b'+OK %i %i' % (msgCount, msgSizeTotal) logging.debug("Answering STAT: %i %i" % (msgCount, msgSizeTotal)) return returnData @@ -79,26 +79,26 @@ def handleList(data): # means the server wants a single message response i = int(msgId) - 1 if i >= len(msgSizes): - return "-ERR no such message" + return b"-ERR no such message" else: msgSize = msgSizes[i] - return "+OK %s %s" % (msgId, msgSize) + return b"+OK %i %i" % (msgId, msgSize) msgCount = 0 - returnDataPart2 = '' + returnDataPart2 = b'' msgSizeTotal = 0 for msgSize in msgSizes: msgSizeTotal += msgSize msgCount += 1 - returnDataPart2 += '%i %i\r\n' % (msgCount, msgSize) - returnDataPart2 += '.' - returnDataPart1 = '+OK %i messages (%i octets)\r\n' % (msgCount, msgSizeTotal) + returnDataPart2 += b'%i %i\r\n' % (msgCount, msgSize) + returnDataPart2 += b'.' + returnDataPart1 = b'+OK %i messages (%i octets)\r\n' % (msgCount, msgSizeTotal) returnData = returnDataPart1 + returnDataPart2 logging.debug("Answering LIST: %i %i" % (msgCount, msgSizeTotal)) logging.debug(returnData) return returnData def handleTop(data): - msg = 'test' + msg = b'test' logging.debug(data.split()) cmd, msgID, lines = data.split() msgID = int(msgID)-1 @@ -107,34 +107,37 @@ def handleTop(data): dateTime, toAddress, fromAddress, subject, body = bminterface.get(msgID) logging.debug(subject) msg = makeEmail(dateTime, toAddress, fromAddress, subject, body) - top, bot = msg.split("\n\n", 1) - #text = top + "\r\n\r\n" + "\r\n".join(bot[:lines]) - return "+OK top of message follows\r\n%s\r\n." % top + top, bot = msg.split(b"\n\n", 1) + #text = top + b"\r\n\r\n" + b"\r\n".join(bot[:lines]) + return b"+OK top of message follows\r\n%s\r\n." % top def handleRetr(data): logging.debug(data.split()) msgID = int(data.split()[1])-1 + msgCount = bminterface.listMsgs() + if msgID >= msgCount: + return b"-ERR no such message" dateTime, toAddress, fromAddress, subject, body = bminterface.get(msgID) msg = makeEmail(dateTime, toAddress, fromAddress, subject, body) - return "+OK %i octets\r\n%s\r\n." % (len(msg), msg) + return b"+OK %i octets\r\n%b\r\n." % (len(msg), msg.encode("utf-8", "replace")) def handleDele(data): msgID = int(data.split()[1])-1 bminterface.markForDelete(msgID) - return "+OK I'll try..." + return b"+OK I'll try..." def handleNoop(data): - return "+OK" + return b"+OK" def handleQuit(data): bminterface.cleanup() - return "+OK just pretend I'm gone" + return b"+OK just pretend I'm gone" def handleCapa(data): - returnData = "+OK List of capabilities follows\r\n" + returnData = b"+OK List of capabilities follows\r\n" for k in dispatch: - returnData += "%s\r\n" % k - returnData += "." + returnData += b"%b\r\n" % k.encode("utf-8", "replace") + returnData += b"." return returnData def handleUIDL(data): @@ -143,14 +146,14 @@ def handleUIDL(data): if len(data) == 1: refdata = bminterface.getUIDLforAll() logging.debug(refdata) - returnData = '+OK\r\n' + returnData = b'+OK\r\n' for msgID, d in enumerate(refdata): - returnData += "%s %s\r\n" % (msgID+1, d) - returnData += '.' + returnData += b"%i %b\r\n" % (msgID+1, d.encode("utf-8", "replace")) + returnData += b'.' else: refdata = bminterface.getUIDLforSingle(int(data[1])-1) logging.debug(refdata) - returnData = '+OK ' + data[0] + str(refdata[0]) + returnData = b'+OK ' + data[0] + refdata[0] return returnData def makeEmail(dateTime, toAddress, fromAddress, subject, body): @@ -198,16 +201,16 @@ def makeEmail(dateTime, toAddress, fromAddress, subject, body): def parseBody(body): returnData = [] - text = '' - searchString = ']*' + text = b'' + searchString = b']*' attachment = re.search(searchString, body) while attachment: imageCode = body[attachment.start():attachment.end()] - imageDataRange = re.search('src=[\"\'][^\"\']*[\"\']', imageCode) + imageDataRange = re.search(b'src=[\"\'][^\"\']*[\"\']', imageCode) imageData='' if imageDataRange: try: - imageData = imageCode[imageDataRange.start()+5:imageDataRange.end()-1].lstrip('data:') + imageData = imageCode[imageDataRange.start()+5:imageDataRange.end()-1].lstrip(b'data:') except: pass if imageData: @@ -261,7 +264,7 @@ def incomingServer_main(host, port, run_event): while run_event.is_set(): data = conn.recvall() logging.debug("Answering %s" % data) - command = data.split(None, 1)[0] + command = data.split(None, 1)[0].decode("utf-8", "replace") try: cmd = dispatch[command] except KeyError: From 06c97c848897d430999259e472e93112374ad4d1 Mon Sep 17 00:00:00 2001 From: Kashiko Koibumi Date: Mon, 24 Jun 2024 23:10:17 +0900 Subject: [PATCH 3/5] fix out of bounds problem --- bminterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bminterface.py b/bminterface.py index e4c60f9..c4a9402 100644 --- a/bminterface.py +++ b/bminterface.py @@ -136,7 +136,7 @@ def _deleteMessage(msgRef): def getUIDLforAll(): api = _makeApi(_getKeyLocation()) - inboxMessages = api.getAllInboxMessages() + inboxMessages = _getAll() refdata = [] for msgID in range(len(inboxMessages['inboxMessages'])): msgRef = inboxMessages['inboxMessages'][msgID]['msgid'] #gets the message Ref via the message index number @@ -145,7 +145,7 @@ def getUIDLforAll(): def getUIDLforSingle(msgID): api = _makeApi(_getKeyLocation()) - inboxMessages = api.getAllInboxMessages() + inboxMessages = _getAll() msgRef = inboxMessages['inboxMessages'][msgID]['msgid'] #gets the message Ref via the message index number return [str(msgRef)] From cf58e67a00000edafcbb1f1f9605a549cdbaf299 Mon Sep 17 00:00:00 2001 From: Kashiko Koibumi Date: Tue, 25 Jun 2024 01:07:37 +0900 Subject: [PATCH 4/5] fix for sending images attached --- outgoing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/outgoing.py b/outgoing.py index c50167f..8b24cb6 100644 --- a/outgoing.py +++ b/outgoing.py @@ -49,7 +49,7 @@ def _recurseParse(self, msg): if 'text/plain' in item['Content-Type']: text += item.get_payload() elif 'image' in item['Content-Type']: - [filetype, name] = item['Content-Type'].rstrip().split('\n') + filetype, name = item['Content-Type'].rstrip().split(' ') name = name.replace('name', 'alt') imageraw = item.get_payload().rstrip().split('\n') imagedata = '' From cb5cb25d25f18e78d5b29b3c12571d7c1f6ee030 Mon Sep 17 00:00:00 2001 From: Kashiko Koibumi Date: Tue, 25 Jun 2024 21:41:34 +0900 Subject: [PATCH 5/5] use SafeConfigParser or ConfigParser, which is available --- bminterface.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bminterface.py b/bminterface.py index c4a9402..1560e7c 100644 --- a/bminterface.py +++ b/bminterface.py @@ -7,6 +7,11 @@ import logging from codecs import encode, decode +try: + SafeConfigParser = configparser.SafeConfigParser +except AttributeError: + SafeConfigParser = configparser.ConfigParser + purgeList = [] allMessages = [] currentAddress = None @@ -17,7 +22,7 @@ def _getKeyLocation(): #make this not suck later def _getConfig(keys): return apiData() #TODO make this work, so the above can be removed - config = configparser.SafeConfigParser() + config = SafeConfigParser() config.read(keys) try: api_port = config.getint('bitmessagesettings', 'apiport') @@ -171,7 +176,7 @@ def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depe def apiData(): global keysPath - config = configparser.SafeConfigParser() + config = SafeConfigParser() keysPath = 'keys.dat' config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory @@ -182,7 +187,7 @@ def apiData(): #Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory. appDataFolder = lookupAppdataFolder() keysPath = appDataFolder + 'keys.dat' - config = configparser.SafeConfigParser() + config = SafeConfigParser() config.read(keysPath) try: