From 53ba35da12f4fe8d8a4a1367bea4c631a88ecf3e Mon Sep 17 00:00:00 2001 From: Irkam Date: Thu, 17 Jul 2025 17:57:48 +0200 Subject: [PATCH] Portage py2py3 --- birp.py | 367 +++++++++++++++++++++++++---------------------- py3270wrapper.py | 64 +++++---- requirements.txt | 3 + tn3270.py | 97 ++++++------- 4 files changed, 279 insertions(+), 252 deletions(-) create mode 100644 requirements.txt diff --git a/birp.py b/birp.py index e88891b..023038f 100755 --- a/birp.py +++ b/birp.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from py3270wrapper import WrappedEmulator import tn3270 @@ -8,7 +8,7 @@ import platform from time import sleep from os import path -from colorama import Fore,Back,Style,init +from colorama import Fore, Back, Style, init from IPython import embed from getch import getch import pickle @@ -63,7 +63,8 @@ " #Intensified Fields\t- " + Style.BRIGHT + "Bright text" + Style.RESET_ALL + "\n\ -# Print output that can be surpressed by a CLI opt + +# Print output that can be suppressed by a CLI opt def logger(text, kind='clear', level=0): if results.quiet and (kind == 'warn' or kind == 'info'): return @@ -77,41 +78,44 @@ def logger(text, kind='clear', level=0): if level == 1: lvldisp = "\t" elif level == 2: lvldisp = "\t\t" elif level == 3: lvldisp = "\t\t\t" - print lvldisp+typdisp+text + print(lvldisp+typdisp+text) + # Update a screen object with the latest x3270 screen -def update_screen(em,screen): - screen = tn3270.Screen(em.exec_command('ReadBuffer(Ascii)').data) +def update_screen(em, screen): + screen = tn3270.Screen(em.exec_command(b'ReadBuffer(Ascii)').data) return screen + # Record the current screen, hit enter, and record the response -def exec_trans(em,history,key='enter'): +def exec_trans(em, history, key='enter'): request = tn3270.Screen response = tn3270.Screen check = tn3270.Screen - request = update_screen(em,request) + request = update_screen(em, request) keypress = '' - hostinfo = em.exec_command('Query(Host)').data[0].split(' ') - host = hostinfo[1]+':'+hostinfo[2] + hostinfo = em.exec_command(b'Query(Host)').data[0].split(b' ') + host = hostinfo[1] + b':' + hostinfo[2] data = request.modified_fields if key == 'enter': em.send_enter() keypress = key # PF1=1, PF24=24, PA1=25, PA3=27 elif key > 0 and key < 25: - keypress = 'PF(' + str(key) + ')' + keypress = b'PF(' + str(key).encode('ascii') + b')' em.exec_command(keypress) elif key > 25 and key < 28: - keypress = 'PA(' + str(key - 24) + ')' + keypress = b'PA(' + str(key - 24).encode('ascii') + b')' em.exec_command(keypress) - em.exec_command('Wait(1,3270Mode)') #Capture the whole 3270 screen + em.exec_command(b'Wait(1,3270Mode)') # Capture the whole 3270 screen response = update_screen(em,response) trans = tn3270.Transaction(request,response,data,keypress,host) history.append(trans) return trans + # Compare two screens, allow a "fuzzy" match to account for time/terminal fields -def compare_screen(screen1,screen2,exact=False): +def compare_screen(screen1, screen2, exact=False): diffcount = 0 linecount = 0 for line in screen1.rawbuffer: @@ -120,11 +124,12 @@ def compare_screen(screen1,screen2,exact=False): if exact: return 0 elif diffcount > 2: - return 0 # More than two lines different they're different - return True # screens are the same - + return 0 # More than two lines different they're different + return True # screens are the same + + # Currently unused -def find_first(history,text): +def find_first(history, text): transid = 0 row = 0 rr = 0 @@ -149,6 +154,7 @@ def find_first(history,text): transid += 1 return (-1,-1,-1,-1) # Not found + # Find text within the transaction history def find_all(history,text): result = list() @@ -176,131 +182,133 @@ def find_all(history,text): transid += 1 return result + # Interactive mode, will record transactions, and display hacker view companion -def interactive(em,history): +def interactive(em, history): key = '' trans = '' screen = '' data = '' - logger("Interactive mode started! Hit ESC to exit",kind="info") - logger("Hit Ctrl-h for help. Start typing ...",kind="info") + logger("Interactive mode started! Hit ESC to exit", kind="info") + logger("Hit Ctrl-h for help. Start typing ...", kind="info") while key != getch.KEY_ESC: if not em.is_connected(): - logger(Fore.RED+"Emulator not connected, interactive mode prevented."+Fore.RESET,kind="err") + logger(Fore.RED+"Emulator not connected, interactive mode prevented."+Fore.RESET, kind="err") return key = getch() - if key == getch.KEY_UP: # Up - em.exec_command('Up()') - elif key == getch.KEY_DOWN: # Down - em.exec_command('Down()') - elif key == getch.KEY_LEFT: # Left - em.exec_command('Left()') - elif key == getch.KEY_RIGHT: # Right - em.exec_command('Right()') - elif key == getch.KEY_ENTER: # Enter - trans = exec_trans(em,history,'enter') - print trans.response.colorbuffer - logger('Enter entered',kind='info') - elif key == getch.KEY_CTRLr: # Ctrl-r print screen - screen = update_screen(em,screen) - print screen.colorbuffer - logger('Screen refreshed',kind='info') + if key == getch.KEY_UP: # Up + em.exec_command(b'Up()') + elif key == getch.KEY_DOWN: # Down + em.exec_command(b'Down()') + elif key == getch.KEY_LEFT: # Left + em.exec_command(b'Left()') + elif key == getch.KEY_RIGHT: # Right + em.exec_command(b'Right()') + elif key == getch.KEY_ENTER: # Enter + trans = exec_trans(em, history, 'enter') + print(trans.response.colorbuffer) + logger('Enter entered', kind='info') + elif key == getch.KEY_CTRLr: # Ctrl-r print screen + screen = update_screen(em, screen) + print(screen.colorbuffer) + logger('Screen refreshed', kind='info') elif key == getch.KEY_CTRLu: # Ctrl-u manually push transaction screen = update_screen(em,screen) data = screen.modified_fields - hostinfo = em.exec_command('Query(Host)').data[0].split(' ') - host = hostinfo[1]+':'+hostinfo[2] - trans = tn3270.Transaction(history.last().response,screen,data,'manual',host) + hostinfo = em.exec_command(b'Query(Host)').data[0].split(b' ') + host = hostinfo[1] + b':' + hostinfo[2] + trans = tn3270.Transaction(history.last().response, screen, data, 'manual', host) history.append(trans) - print screen.colorbuffer - logger('Transaction added',kind='info') - elif key == getch.KEY_CTRLh: # Ctrl-h help - print interactive_help - elif key == getch.KEY_CTRLk: # Ctrl-k color key - print color_key - elif key == getch.KEY_CTRLp: # Ctrl-p python shell + print(screen.colorbuffer) + logger('Transaction added', kind='info') + elif key == getch.KEY_CTRLh: # Ctrl-h help + print(interactive_help) + elif key == getch.KEY_CTRLk: # Ctrl-k color key + print(color_key) + elif key == getch.KEY_CTRLp: # Ctrl-p python shell embed() - elif key == getch.KEY_CTRLs: # Ctrl-s screenshot + elif key == getch.KEY_CTRLs: # Ctrl-s screenshot em.save_screen(str(trans.timestamp.date())+'_'+str(trans.timestamp.time())+'.html') - logger('Screenshot saved',kind='info') - elif key == getch.KEY_TAB: # Tab 9 - em.exec_command('Tab()') - elif key == getch.KEY_BACKSPACE: # Backspace - em.exec_command('BackSpace()') - elif key == getch.KEY_DELETE: # Delete - em.exec_command('Delete()') - elif key == getch.KEY_CTRLc: # Ctrl-c Clear - em.exec_command('Clear()') - elif key == getch.KEY_CTRLq: # Ctrl-q PA1 - trans = exec_trans(em,history,25) - print trans.response.colorbuffer - elif key == getch.KEY_CTRLw: # Ctrl-w PA2 - trans = exec_trans(em,history,26) - print trans.response.colorbuffer - elif key == getch.KEY_CTRLe: # Ctrl-e PA3 + logger('Screenshot saved', kind='info') + elif key == getch.KEY_TAB: # Tab 9 + em.exec_command(b'Tab()') + elif key == getch.KEY_BACKSPACE: # Backspace + em.exec_command(b'BackSpace()') + elif key == getch.KEY_DELETE: # Delete + em.exec_command(b'Delete()') + elif key == getch.KEY_CTRLc: # Ctrl-c Clear + em.exec_command(b'Clear()') + elif key == getch.KEY_CTRLq: # Ctrl-q PA1 + trans = exec_trans(em, history, 25) + print(trans.response.colorbuffer) + elif key == getch.KEY_CTRLw: # Ctrl-w PA2 + trans = exec_trans(em, history, 26) + print(trans.response.colorbuffer) + elif key == getch.KEY_CTRLe: # Ctrl-e PA3 trans = exec_trans(em,history,27) - print trans.response.colorbuffer - elif key > 31 and key < 127: # Alphanumeric + print(trans.response.colorbuffer) + elif key > 31 and key < 127: # Alphanumeric em.safe_send(chr(key)) elif key == getch.KEY_F1: - trans = exec_trans(em,history,1) - print trans.response.colorbuffer + trans = exec_trans(em, history, 1) + print(trans.response.colorbuffer) elif key == getch.KEY_F2: - trans = exec_trans(em,history,2) - print trans.response.colorbuffer + trans = exec_trans(em, history, 2) + print(trans.response.colorbuffer) elif key == getch.KEY_F3: - trans = exec_trans(em,history,3) - print trans.response.colorbuffer + trans = exec_trans(em, history, 3) + print(trans.response.colorbuffer) elif key == getch.KEY_F4: - trans = exec_trans(em,history,4) - print trans.response.colorbuffer + trans = exec_trans(em, history, 4) + print(trans.response.colorbuffer) elif key == getch.KEY_F5: - trans = exec_trans(em,history,5) - print trans.response.colorbuffer + trans = exec_trans(em, history, 5) + print(trans.response.colorbuffer) elif key == getch.KEY_F6: trans = exec_trans(em,history,6) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F7: trans = exec_trans(em,history,7) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F8: trans = exec_trans(em,history,8) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F9: trans = exec_trans(em,history,9) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F10: trans = exec_trans(em,history,10) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F11: trans = exec_trans(em,history,11) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_F12: trans = exec_trans(em,history,12) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_AltF8: trans = exec_trans(em,history,13) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_AltF9: trans = exec_trans(em,history,14) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_AltF10: trans = exec_trans(em,history,15) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_AltF11: trans = exec_trans(em,history,16) - print trans.response.colorbuffer + print(trans.response.colorbuffer) elif key == getch.KEY_AltF12: trans = exec_trans(em,history,24) - print trans.response.colorbuffer + print(trans.response.colorbuffer) -def save_history(history,savefile): + +def save_history(history, savefile): if path.exists(savefile): logger('Savefile exists, I won\'t overwrite yet',kind='err') - return False # Don't overwrite existing saves just yet + return False # Don't overwrite existing saves just yet try: - sav = open(savefile,'w') + sav = open(savefile, 'wb') pickle.dump(history, sav) sav.close() except IOError: @@ -308,6 +316,7 @@ def save_history(history,savefile): return False return True + def load_history(loadfile): if not path.exists(loadfile): logger("Couldn't find the history file" + loadfile + " bailing.",kind='err') @@ -321,34 +330,35 @@ def load_history(loadfile): sys.exit(1) return hist -def print_trans(history,num,header): + +def print_trans(history, num, header): trans = history[num] if header: - print "\n",Fore.BLUE,"View Transaction",Fore.RESET - print Fore.BLUE,"================",Fore.RESET - print "\n",Fore.YELLOW,num,Fore.BLUE,trans.timestamp,Fore.CYAN,trans.key,\ - "\t",Fore.BLUE,trans.host,trans.comment,Fore.RESET - print " Req : ",trans.request.stringbuffer[0] + print("\n" + Fore.BLUE + "View Transaction" + Fore.RESET) + print(Fore.BLUE + "================" + Fore.RESET) + print("\n" + Fore.YELLOW + num + Fore.BLUE + trans.timestamp + Fore.CYAN + trans.key + "\t" + Fore.BLUE + trans.host + trans.comment + Fore.RESET) + print(" Req : " + trans.request.stringbuffer[0]) for field in trans.data: - print " Data: row:",field.row,"col:",field.col,"str:",Fore.RED,field.contents,Fore.RESET - print " Resp: ",trans.response.stringbuffer[0],'\n' + print(" Data: row:" + field.row + "col:" + field.col + "str:" + Fore.RED + field.contents + Fore.RESET) + print(" Resp: " + trans.response.stringbuffer[0] + '\n') + # todo have print_history call print_trans rather def print_history(history): - print "\n",Fore.BLUE,"Transaction List",Fore.RESET - print Fore.BLUE,"================",Fore.RESET,"\n" + print("\n" + Fore.BLUE + "Transaction List" + Fore.RESET) + print(Fore.BLUE + "================" + Fore.RESET + "\n") count = 0 for trans in history: - print Fore.YELLOW,count,Fore.BLUE,trans.timestamp,Fore.CYAN,trans.key,\ - "\t",Fore.BLUE,trans.host,trans.comment,Fore.RESET - print " Req : ",trans.request.stringbuffer[0] + print(Fore.YELLOW + count + Fore.BLUE + trans.timestamp + Fore.CYAN + trans.key + "\t" + Fore.BLUE + trans.host + trans.comment + Fore.RESET) + print(" Req : " + trans.request.stringbuffer[0]) for field in trans.data: fieldtxt = field.contents.strip() if len(fieldtxt) > 0: - print " Data: row:",field.row,"col:",field.col,"str:",Fore.RED,fieldtxt,Fore.RESET - print " Resp: ",trans.response.stringbuffer[0],"\n" + print(" Data: row:" + field.row + "col:" + field.col + "str:" + Fore.RED + fieldtxt + Fore.RESET) + print(" Resp: " + trans.response.stringbuffer[0] + "\n") count += 1 + def menu_save(history): savefile = '' logger(''.join([Fore.CYAN,'What file should I save to (must not exist): ',Fore.RESET]),kind='info') @@ -356,61 +366,71 @@ def menu_save(history): if save_history(history,savefile): logger(''.join([Fore.CYAN,'History saved to ',savefile,Fore.RESET]),kind='info') -def print_seq(history,start,stop): - print '\n' * 100 + +def print_seq(history, start, stop): + print('\n' * 100 ) for trans in history[start:stop+1]: - print '\n' * 101 - print trans.request + print('\n' * 101) + print(trans.request) sleep(1) - print '\n' * 100 - print Fore.RED,trans.key,Fore.RESET - print trans.response + print('\n' * 100) + print(Fore.RED + trans.key + Fore.RESET) + print(trans.response) sleep(1) + # this will print two copies of the individual screen -def screentofile(screen,path): - f = open(path+'.emu','w') - g = open(path+'.brp','w') +def screentofile(screen, file_path): + f = open(file_path + '.emu', 'wb') + g = open(file_path + '.brp', 'wb') f.write(utf_8.encode(screen.emubuffer)[0]) g.write(utf_8.encode(screen.colorbuffer)[0]) f.close() g.close() + def menu_screen(transaction, reqres): - #If reqres is True, we show the request, if False, we show the response - if reqres: screen = transaction.request - else: screen = transaction.response + # If reqres is True, we show the request, if False, we show the response + if reqres: + screen = transaction.request + else: + screen = transaction.response key = '' - print screen.colorbuffer + print(screen.colorbuffer) while key != getch.KEY_x: - logger(''.join([Fore.CYAN,"Type 'f' to view the screen's fields or 'p' to view the un-markedup screen, 'e' to view the screen as an emulator would, 'r' to switch between the Request/Response, or 's' to export a copy to a text file (.brp/.emu). Type 'x' to go back.",Fore.RESET]),kind='info') + logger(''.join([Fore.CYAN, + "Type 'f' to view the screen's fields or 'p' to view the un-markedup screen, 'e' to view the screen as an emulator would, 'r' to switch between the Request/Response, or 's' to export a copy to a text file (.brp/.emu). Type 'x' to go back.", + Fore.RESET]), kind='info') key = getch() if key == getch.KEY_f or key == getch.KEY_F: - print Fore.BLUE,"View Fields",Fore.RESET - print Fore.BLUE,"===========",Fore.RESET,"\n" + print(Fore.BLUE + "View Fields" + Fore.RESET) + print(Fore.BLUE + "===========" + Fore.RESET + "\n") pprint(screen.fields) - logger(''.join([Fore.RED,"Dropping into shell, check the",Fore.BLUE," screen ",Fore.RED,"object. Type quit() to return here.",Fore.RESET,"\n\n"]),kind='info') + logger(''.join([Fore.RED, + "Dropping into shell, check the", Fore.BLUE, " screen ", Fore.RED, "object. Type quit() to return here.", + Fore.RESET, "\n\n"]), kind='info') embed() elif key == getch.KEY_p or key == getch.KEY_p: - print '\n',screen + print('\n' + screen) elif key == getch.KEY_e or key == getch.KEY_e: - print '\n',screen.emubuffer + print('\n' + screen.emubuffer) elif key == getch.KEY_r or key == getch.KEY_r: reqres = not reqres if reqres: screen = transaction.request - print Fore.BLUE,'REQUEST',Fore.RESET + print(Fore.BLUE + 'REQUEST' + Fore.RESET) else: screen = transaction.response - print Fore.BLUE,'RESPONSE',Fore.RESET - print screen.colorbuffer + print(Fore.BLUE + 'RESPONSE' + Fore.RESET) + print(screen.colorbuffer) elif key == getch.KEY_s or key == getch.KEY_s: filename = transaction.host+'_'+str(transaction.timestamp.date())+'_'+str(transaction.timestamp.time()) screentofile(screen, filename) -def menu_trans(history,num): + +def menu_trans(history, num): if num >= len(history) or num < 0: - logger(''.join([Fore.CYAN,"Whoops, that transaction doesn't exist.",Fore.RESET]),kind='info') + logger(''.join([Fore.CYAN, "Whoops, that transaction doesn't exist.", Fore.RESET]), kind='info') return 1 trans = history[num] key = '' @@ -424,11 +444,12 @@ def menu_trans(history,num): elif key == getch.KEY_2: menu_screen(trans, reqres=False) elif key == getch.KEY_j: - return menu_trans(history,num+1) + return menu_trans(history, num+1) elif key == getch.KEY_k: - return menu_trans(history,num-1) + return menu_trans(history, num-1) return 0 + def menu_history(history): choice = '' while choice.lower() != 'x': @@ -442,6 +463,7 @@ def menu_history(history): if key >= 0 and key < len(history): menu_trans(history,key) + def menu_find(history): choice = '' while choice.lower() != 'x': @@ -460,14 +482,15 @@ def menu_find(history): if key >= 0 and key < len(history): return menu_trans(history,key) + def menu(em, history): key = '' while key != getch.KEY_CTRLc: - print menu_list + print(menu_list) key = getch() if key == getch.KEY_1: - interactive(em,history) + interactive(em, history) elif key == getch.KEY_2: menu_history(history) elif key == getch.KEY_3: @@ -477,30 +500,34 @@ def menu(em, history): elif key == getch.KEY_5: menu_save(history) elif key == getch.KEY_X or key == getch.KEY_x: - logger('Do big irons dream of electric paddocks? Goodnight.',kind='info') + logger('Do big irons dream of electric paddocks? Goodnight.', kind='info') sys.exit(0) + # Just an excuse to wrap this away in a fold def prestartup(): - init() # initialise coloured output from colorama + init() # initialise coloured output from colorama # Define and fetch commandline arguments - parser = argparse.ArgumentParser(\ - description = 'Big Iron Recon & Pwnage (BIRP) by @singe',\ - epilog = "It's easier than you think" ) - parser.add_argument('-t', '--target',\ - help='Target IP address or hostname & port: TARGET[:PORT]. The default port is 23. If you don\'t specify a target, you can manually specify it in the emulator',\ - required = False, dest = 'target', default = "") - parser.add_argument('-s', '--sleep',\ - help='Seconds to sleep between actions (increase on slower systems). The default is 0 seconds.',\ + parser = argparse.ArgumentParser( + description='Big Iron Recon & Pwnage (BIRP) by @singe', + epilog="It's easier than you think") + parser.add_argument('-t', '--target', + help='Target IP address or hostname & port: TARGET[:PORT]. ' + 'The default port is 23. ' + 'If you don\'t specify a target, you can manually specify it in the emulator', + required=False, dest='target', default="") + parser.add_argument('-s', '--sleep', + help='Seconds to sleep between actions (increase on slower systems). The default is 0 seconds.', default = 0, type = float, dest = 'sleep') - parser.add_argument('-l', '--load', help='Load a previously saved history file', default='',\ - dest = 'loadfile', type = str) - parser.add_argument('-q', '--quiet', help="Ssssh! Don't print info text.",\ - default = False, dest = 'quiet', action = 'store_true') + parser.add_argument('-l', '--load', help='Load a previously saved history file', default='', + dest='loadfile', type=str) + parser.add_argument('-q', '--quiet', help="Ssssh! Don't print info text.", + default=False, dest='quiet', action='store_true') results = parser.parse_args() return results + # Just an excuse to wrap this away in a fold def startup(): # Parse commandline arguments @@ -513,7 +540,7 @@ def startup(): logger('Attack platform\t\t: ' + platform.system(),kind='info') if not platform.system() == 'Windows': - em = WrappedEmulator(visible=True,delay=results.sleep) + em = WrappedEmulator(visible=True, delay=results.sleep) elif platform.system() == 'Windows': logger('x3270 not supported on Windows',kind='err') sys.exit(1) @@ -524,25 +551,27 @@ def startup(): logger('Load history from\t\t: ' + results.loadfile,kind='info') history = load_history(results.loadfile) - return (em,history) + return (em, history) -results = prestartup() -(em,history) = startup() -if results.target: - logger('Connecting to ' + results.target,kind='info') - try: - em.connect(results.target) - except: - logger('Connection failure',kind='err') - sys.exit(1) - if not em.is_connected(): - logger('Could not connect to ' + results.target + '. Aborting.',kind='err') - sys.exit(1) +if __name__ == '__main__': + results = prestartup() + (em, history) = startup() + + if results.target: + logger('Connecting to ' + results.target, kind='info') + try: + em.connect(results.target) + except: + logger('Connection failure', kind='err') + sys.exit(1) + if not em.is_connected(): + logger('Could not connect to ' + results.target + '. Aborting.',kind='err') + sys.exit(1) - hostinfo = em.get_hostinfo() - host = hostinfo[1]+':'+hostinfo[2] -menu(em, history) + hostinfo = em.get_hostinfo() + host = hostinfo[1] + b':' + hostinfo[2] + menu(em, history) -# And we're done. Close the connection -em.terminate() + # And we're done. Close the connection + em.terminate() diff --git a/py3270wrapper.py b/py3270wrapper.py index 5818e5f..2dbb37c 100755 --- a/py3270wrapper.py +++ b/py3270wrapper.py @@ -1,10 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from py3270 import Emulator,CommandError,FieldTruncateError,TerminatedError,WaitError,KeyboardStateError,FieldTruncateError,x3270App,s3270App +from py3270 import Emulator, CommandError, FieldTruncateError, TerminatedError, WaitError, KeyboardStateError, FieldTruncateError, X3270App, S3270App import platform from time import sleep from sys import exit from os import path +import logging + # Override some behaviour of py3270 library class EmulatorIntermediate(Emulator): @@ -12,26 +14,26 @@ def __init__(self, visible=True, delay=0): try: Emulator.__init__(self, visible) self.delay = delay - except OSError, e: + except OSError as e: print("Can't run x3270, are you sure it's in the right place? Actual error: "+str(e)) exit(1) - def send_enter(self): # Allow a delay to be configured - self.exec_command('Enter') + def send_enter(self): # Allow a delay to be configured + self.exec_command(b'Enter') if self.delay > 0: sleep(self.delay) def screen_get(self): - response = self.exec_command('Ascii()') + response = self.exec_command(b'Ascii()') return response.data # Send text without triggering field protection def safe_send(self, text): - for i in xrange(0,len(text)): + for i in range(0, len(text)): self.send_string(text[i]) if self.status.field_protection == 'P': - return False # We triggered field protection, stop - return True # Safe + return False # We triggered field protection, stop + return True # Safe # Fill fields in carefully, checking for triggering field protections def safe_fieldfill(self, ypos, xpos, tosend, length): @@ -41,57 +43,57 @@ def safe_fieldfill(self, ypos, xpos, tosend, length): self.move_to(ypos, xpos) try: self.delete_field() - if safe_send(self, tosend): - return True # Hah, we win, take that mainframe + if self.safe_send(tosend): + return True # Hah, we win, take that mainframe else: - return False # we entered what we could, bailing - except CommandError, e: + return False # we entered what we could, bailing + except CommandError as e: # We hit an error, get mad return False # if str(e) == 'Keyboard locked': # Search the screen for text when we don't know exactly where it is, checking for read errors def find_response(self, response): - for rows in xrange(1,int(self.status.row_number)+1): - for cols in xrange(1,int(self.status.col_number)+1-len(response)): + for rows in range(1, int(self.status.row_number)+1): + for cols in range(1, int(self.status.col_number)+1-len(response)): try: if self.string_found(rows, cols, response): return True - except CommandError, e: + except CommandError as e: # We hit a read error, usually because the screen hasn't returned # increasing the delay works sleep(self.delay) self.delay += 1 - whine('Read error encountered, assuming host is slow, increasing delay by 1s to: ' + str(self.delay),kind='warn') + logging.warning('Read error encountered, assuming host is slow, increasing delay by 1s to: ' + str(self.delay)) return False return False # Get the current x3270 cursor position def get_pos(self): - results = self.exec_command('Query(Cursor)') + results = self.exec_command(b'Query(Cursor)') row = int(results.data[0].split(' ')[0]) col = int(results.data[0].split(' ')[1]) - return (row,col) + return (row, col) def get_hostinfo(self): - return self.exec_command('Query(Host)').data[0].split(' ') + return self.exec_command(b'Query(Host)').data[0].split(b' ') + # Set the emulator intelligently based on your platform if platform.system() == 'Darwin': class WrappedEmulator(EmulatorIntermediate): - x3270App.executable = './x3270' - s3270App.executable = 's3270' + X3270App.executable = './x3270' + S3270App.executable = 's3270' elif platform.system() == 'Linux': class WrappedEmulator(EmulatorIntermediate): - x3270App.executable = './x3270' - s3270App.executable = 's3270' + X3270App.executable = '/usr/bin/x3270' + S3270App.executable = 's3270' elif platform.system() == 'Windows': class WrappedEmulator(EmulatorIntermediate): - #x3270_executable = 'Windows_Binaries/wc3270.exe' - x3270App.executable = 'wc3270.exe' + X3270App.executable = 'wc3270.exe' else: - logger('Your Platform:', platform.system(), 'is not supported at this time.',kind='err') - sys.exit(1) -if not path.isfile(x3270App.executable): - print("Can't find the x3270 executable at "+x3270App.executable+" You can configure the location at the bottom of py3270wrapper.py") - exit(1) + logging.error('Your Platform: {} is not supported at this time.'.format(platform.system())) + exit(1) +if not path.isfile(X3270App.executable): + print("Can't find the x3270 executable at {}. You can configure the location at the bottom of py3270wrapper.py".format(X3270App.executable)) + exit(1) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b6b4b0d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +py3270==0.3.5 +colorama==0.4.6 +ipython==8.17.2 diff --git a/tn3270.py b/tn3270.py index 92d94bb..912b6d9 100644 --- a/tn3270.py +++ b/tn3270.py @@ -1,5 +1,6 @@ from datetime import datetime -from colorama import Fore,Back,Style,init +from colorama import Fore, Back, Style, init + # Object to hold field details class Field: @@ -20,7 +21,7 @@ def __init__(self, contents, row, col, rawstatus, printable=0, protected=0, nume self.modify = modify def __repr__(self): - a = "" + a = "" return ''.join(a) def __str__(self): @@ -29,10 +30,11 @@ def __str__(self): def __len__(self): return len(self.contents) + # Object to hold a screen from x3270 class Screen: - def __init__(self, rawbuff): - self.rawbuffer = rawbuff + def __init__(self, rawbuffer): + self.rawbuffer = rawbuffer self.rows = len(self.rawbuffer) self.cols = len(self.rawbuffer[0]) #From x3270 defines @@ -52,13 +54,13 @@ def __init__(self, rawbuff): def plainbuffer(self): plnbuf = list() for line in self.rawbuffer: - splitline = line.split(' ') + splitline = line.split(b' ') plainline = [] for i in splitline: - if len(i) == 2: #Not a field marker + if len(i) == 2: # Not a field marker plainline.append(i) else: - plainline.append('00') #Use a NULL instead of a field marker + plainline.append(b'00') # Use a NULL instead of a field marker plnbuf.append(plainline) return plnbuf @@ -67,7 +69,7 @@ def plainbuffer(self): def stringbuffer(self): strbuf = list() for line in self.plainbuffer: - newstr = ''.join(line).decode("hex") + newstr = b''.join(line).decode("hex") strbuf.append(newstr) return strbuf @@ -76,7 +78,7 @@ def __str__(self): return '\n'.join(self.stringbuffer).replace('\x00',' ') def __repr__(self): - a = "" + a = "" return ''.join(a) @property @@ -84,17 +86,17 @@ def __repr__(self): # This looks at field markers only and ignores colours asked for by the host def colorbuffer(self): colbuf = list() - colbuf.append(Fore.RED) #Highlight unfield'ed text - counter = 0 #for line numbers + colbuf.append(Fore.RED) # Highlight unfield'ed text + counter = 0 # for line numbers for line in self.rawbuffer: newline = list() - newline.append('{:>2}'.format(counter)+' ') #line no + newline.append('{:>2} '.format(counter)) # line no counter += 1 - for i in line.split(' '): + for i in line.split(b' '): # SF(c0=c8) is example of StartField markup - if len(i) > 3 and i.find('SF(') >= 0: - attrib = int(i[3:5],16) - val = int(i[6:8],16) + if len(i) > 3 and i.find(b'SF(') >= 0: + attrib = int(i[3:5], 16) + val = int(i[6:8], 16) foreflag = False backflag = False @@ -120,14 +122,15 @@ def colorbuffer(self): if not styleflag: newline.append(Style.NORMAL) - newline.append(u'\u2219') #Field marker + newline.append(u'\u2219') # Field marker elif len(i) == 2: - if i == '00': - newline.append(u"\u2400") + if i == b'00': + newline.append('\x00') else: - newline.append(i.decode("hex")) + newline.append(bytes.fromhex(i.decode('ascii')).decode('ascii')) #newline.append(Fore.RESET+Back.RESET+Style.RESET_ALL) + colbuf.append(''.join(newline)) strcolbuf = '\n'.join(colbuf) + Fore.RESET + Back.RESET return strcolbuf @@ -140,13 +143,13 @@ def emubuffer(self): colbuf = list() for line in self.rawbuffer: newline = list() - for i in line.split(' '): + for i in line.split(b' '): # SF(c0=c8) is example of StartField markup - if len(i) > 3 and i.find('SF(') >= 0: - attrib = int(i[3:5],16) - val = int(i[6:8],16) + if len(i) > 3 and i.find(b'SF(') >= 0: + attrib = int(i[3:5], 16) + val = int(i[6:8], 16) - newline.append(u' ') #Field marker + newline.append(b' ') #Field marker modflag = False hideflag = False @@ -161,10 +164,13 @@ def emubuffer(self): newline.append(Back.RESET) elif len(i) == 2: - if i == '00': - newline.append(u' ') + print(i) + print(i.decode('ascii')) + print(bytes.fromhex(i.decode('ascii'))) + if i == b'00': + newline.append(b' ') elif hideflag: - newline.append(u' ') + newline.append(b' ') else: newline.append(i.decode("hex")) colbuf.append(''.join(newline)) @@ -178,9 +184,9 @@ def fields(self): row = 0 for line in self.rawbuffer: col = 0 - for i in line.split(' '): + for i in line.split(b' '): # SF(c0=c8) is example of StartField markup - if len(i) > 3 and i.find('SF(') >= 0: + if len(i) > 3 and i.find(b'SF(') >= 0: attrib = int(i[3:5],16) val = int(i[6:8],16) @@ -194,7 +200,7 @@ def fields(self): zeronsel = 0 reserved = 0 modify = 0 - rawstatus = i #store the raw status for later implementation of SA() + rawstatus = i # store the raw status for later implementation of SA() if (val | self.__FA_PRINTABLE) == val: printable = 1 if (val | self.__FA_PROTECT ) == val: @@ -221,7 +227,8 @@ def fields(self): # Add the character to the last field entity added elif len(i) == 2: - contents = i.decode("hex") + # contents = i.decode("hex") + contents = i.decode('ascii') if len(field_list) > 0: field_list[len(field_list)-1].contents += contents @@ -231,35 +238,20 @@ def fields(self): @property def protected_fields(self): - try: - res = filter(lambda x: x.protected == 1, self.fields) - except IndexError: - res = [] - return res + return [field for field in self.fields if field.protected == 1] @property def input_fields(self): - try: - res = filter(lambda x: x.protected == 0, self.fields) - except IndexError: - res = [] - return res + return [field for field in self.fields if field.protected == 0] @property def hidden_fields(self): - try: - res = filter(lambda x: x.hidden == 1, self.fields) - except IndexError: - res = [] - return res + return [field for field in self.fields if field.hidden == 1] @property def modified_fields(self): - try: - res = filter(lambda x: x.modify == 1, self.fields) - except IndexError: - res = [] - return res + return [field for field in self.fields if field.modify == 1] + # Object to hold an single tn3270 "transaction" i.e. request/response & timestamp class Transaction: @@ -283,6 +275,7 @@ def __repr__(self): " Resp: ",repr(self.response),">" return ''.join(a) + class History: def __init__(self): self.timeline = list()