Copy Link
Add to Bookmark
Report

NULL mag Issue 06 20 Access a jam base with python

eZine's profile picture
Published in 
null magazine
 · 26 Dec 2020

  




as i am learning python, i made this simple lib/program to read a JAM
base. it can read the header file and also read info/text from a
message. for sure i will evolve it and make it even better, but for
the purposes of the mag, is enough for now. just change the file name
of the jam base and test it yourself.

you can use it freely. u can read message text and make all sort of
things with it, in your projects.

python is a "universal" programming language now days, so expect more
things written in python also.

--- cut here ---------------------------------------------------------------
#!/usr/bin/python3
import struct
from datetime import datetime
import os

jam_local = int('00000001',16)
jam_intransit = int('00000002',16)
jam_priv = int('00000004',16)
jam_rcvd = int('00000008',16)
jam_sent = int('00000010',16)
jam_killsent = int('00000020',16)
jam_achvsent = int('00000040',16)
jam_hold = int('00000080',16)
jam_crash = int('00000100',16)
jam_imm = int('00000200',16)
jam_direct = int('00000400',16)
jam_gate = int('00000800',16)
jam_freq = int('00001000',16)
jam_fattch = int('00002000',16)
jam_truncfile = int('00004000',16)
jam_killfile = int('00008000',16)
jam_rcptreq = int('00010000',16)
jam_confmreq = int('00020000',16)
jam_orphan = int('00040000',16)
jam_encrypt = int('00080000',16)
jam_compress = int('00100000',16)
jam_escaped = int('00200000',16)
jam_fpu = int('00400000',16)
jam_typelocal = int('00800000',16)
jam_typeecho = int('01000000',16)
jam_typenet = int('02000000',16)
jam_nodisp = int('20000000',16)
jam_locked = int('40000000',16)
jam_deleted = int('80000000',16)
base_filename = ''
mbase_header=dict()
msg_header=dict()
# mbase_header["signature"]
# mbase_header["created"]
# mbase_header["modcounter"]
# mbase_header["activemsgs"]
# mbase_header["passwordcrc"]
# mbase_header["basemsgnum"]
#functions for msg attributes
def isdeleted():
global msg_header
if (jam_deleted & msg_header["attr1"] ) != 0:
return True
else:
return False
def islocal():
global msg_header
if (jam_local & msg_header["attr1"] ) != 0:
return True
else:
return False

def isreceived():
global msg_header
if (jam_rcvd & msg_header["attr1"] ) != 0:
return True
else:
return False
# read the header file and get info
# returns 0 if is succesful, -1 if file doesn't exist and -2 if file
# is not valid jam base
def get_msg_base_hdr(filename):
global mbase_header
global base_filename
base_filename = filename
filename = filename + '.jhr'
if os.path.exists(filename) == False:
return -1
jamf = "ssssIIIII"
with open(filename, mode='rb') as file:
fileMsgHdr = file.read(struct.calcsize(jamf))
jam_hdr = struct.unpack(jamf, fileMsgHdr[0:24])
mbase_header["signature"] = jam_hdr[:4]
mbase_header["created"] = \
datetime.fromtimestamp(jam_hdr[4]).strftime('%Y-%m-%d %H:%M:%S')
mbase_header["modcounter"] = jam_hdr[5]
mbase_header["activemsgs"] = jam_hdr[6]
mbase_header["passwordcrc"] = jam_hdr[7]
mbase_header["basemsgnum"] = jam_hdr[8]
if mbase_header["signature"] == (b'J', b'A', b'M', b'\x00'):
return 0
else:
return -2
# read the message header
def read_msg_header(filename,num):
global msg_header
global base_filename
base_filename = filename
jdx_name = filename + '.jdx' # get header offset from index file
if os.path.exists(jdx_name) == False:
return -1
jdx = open(jdx_name,"rb")
jdx_size = os.path.getsize(jdx_name)
offset = (num - mbase_header["basemsgnum"])*8
if offset>jdx_size:
jdx.close()
return -2
jdx.seek((num - mbase_header["basemsgnum"])*8)
jdxf = "<II"
jdxvalue = jdx.read(8)
jdxuser,jdxoffset = struct.unpack(jdxf,jdxvalue)
jdx.close()

jhr_name = filename + '.jhr'
jhr = open(jhr_name,"rb") # open header file
jhr.seek(jdxoffset) # seek header of specific msg

#debug string
#print('header offset: '+str(jdxoffset))

msgheaderf = "<4sHHIIIIIIIIIIIiIIIII"
msgheader = jhr.read(struct.calcsize (msgheaderf)) # read header
msg = struct.unpack(msgheaderf,msgheader)

#debug string
#print("header size: "+str(struct.calcsize (msgheaderf)))

msg_header["signature"] = msg[0]
msg_header["rev"] = msg[1]
msg_header["resvd"] = msg[2]
msg_header["subfieldlen"] = msg[3]
msg_header["timesread"] = msg[4]
msg_header["msgidcrc"] = msg[5]
msg_header["replycrc"] = msg[6]
msg_header["replyto"] = msg[7]
msg_header["replyfirst"] = msg[8]
msg_header["replynext"] = msg[9]
msg_header["datewritten"] = msg[10]
msg_header["datercvd"] = msg[11]
msg_header["datearrived"] = msg[12]
msg_header["msgnum"] = msg[13]
msg_header["attr1"] = msg[14]
msg_header["attr2"] = msg[15]
msg_header["textofs"] = msg[16]
msg_header["textlen"] = msg[17]
msg_header["pwdcrc"] = msg[18]
msg_header["cost"] = msg[19]

msg_header["origin"] = ''
msg_header["destination"] = ''
msg_header["sender"] = ''
msg_header["receiver"] = ''
msg_header["msgid"] = ''
msg_header["replyid"] = ''
msg_header["subject"] = ''
msg_header["pid"] = ''
msg_header["trace"] = ''
msg_header["kludge"] = ''
msg_header["seenby"] = ''
msg_header["path2d"] = ''
msg_header["flags"] = ''
msg_header["tzutc"] = ''
if msg_header["subfieldlen"] != 0:
#print(jdxoffset + struct.calcsize (msgheaderf) + msg_header["subfieldlen"]) while jhr.tell() <= jdxoffset + struct.calcsize (msgheaderf) + \
msg_header["subfieldlen"]:
loid = jhr.read(2)
loid = struct.unpack('<H',loid)
loid = loid[0]
hiid = jhr.read(2)
hiid = struct.unpack('<H',hiid)
hiid = hiid[0]
sflen = struct.unpack('<I',jhr.read(4))
buf = jhr.read(sflen[0])
#debug string
#print("tell: "+str(jhr.tell()))


if loid == 0:
msg_header["origin"] = ''.join(str(buf)).strip("b'")
elif loid == 1:
msg_header["destination"] = ''.join(str(buf)).strip("b'")
elif loid == 2:
msg_header["sender"] = ''.join(str(buf)).strip("b'")
elif loid == 3:
msg_header["receiver"] = ''.join(str(buf)).strip("b'")
elif loid == 4:
msg_header["msgid"] = ''.join(str(buf)).strip("b'")
elif loid == 5:
msg_header["replyid"] = ''.join(str(buf)).strip("b'")
elif loid == 6:
msg_header["subject"] = ''.join(str(buf)).strip("b'")
elif loid == 7:
msg_header["pid"] = ''.join(str(buf)).strip("b'")
elif loid == 8:
msg_header["trace"] = ''.join(str(buf)).strip("b'")
elif loid == 2000:
msg_header["kludge"] = ''.join(str(buf)).strip("b'")
elif loid == 2001:
msg_header["seenby"] = ''.join(str(buf)).strip("b'")
elif loid == 2002:
msg_header["path2d"] = ''.join(str(buf)).strip("b'")
elif loid == 2003:
msg_header["flags"] = buf
elif loid == 2004:
msg_header["tzutc"] = ''.join(str(buf)).strip("b'")
jhr.close()
return 0
# get the text of a message if msg is not deleted
def msg_read_text():
global base_filename
global msg_header
if not os.path.exists(base_filename+'.jdt'):
return -2
if isdeleted():
return -3
jdt = open(base_filename+'.jdt','rb')
print(msg_header["textofs"])
print(msg_header["textlen"])
jdt.seek(msg_header["textofs"])
res = jdt.read(msg_header["textlen"])
jdt.close()
res = ''.join(str(res,"cp437"))
res = "\n".join(res.splitlines())
return res

# convert unix time to string
def unix2str(dt):
return datetime.fromtimestamp(dt).strftime('%Y-%m-%d %H:%M:%S')
# example
# get the header of a base
if get_msg_base_hdr("./fsx_bbs") != 0:
print('file not found')
exit
# read header of message 1
read_msg_header("./fsx_bbs",1)
# print some info
print (msg_header["sender"])
print (msg_header["subject"])
print (msg_header["destination"])
print (msg_header["origin"])
print (msg_header["tzutc"])
# print text of msg
print(msg_read_text())
print("")

--- stop cut here ----------------------------------------------------------
--- admire here --------------------------------------------------------:P--



← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT