def get_formatted_content(self, pyobj): if isinstance(pyobj, _floattypes) or isinstance(pyobj, _inttypes): pyobj = _gmtime(pyobj) if self.fix_timezone: pyobj = _fix_timezone(pyobj, tz_from=None, tz_to="Z") d = {} pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = [abs(y) for y in pyobj] d['neg'] = '-' else: d['neg'] = '' d = {} for k, i in [('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ('s', 5)]: d[k] = pyobj[i] ms = pyobj[6] if not ms or not hasattr(self, 'format_ms'): return self.format % d if ms > 999: raise ValueError( 'milliseconds must be a integer between 0 and 999') d['ms'] = ms return self.format_ms % d
def get_formatted_content(self, pyobj): if isinstance(pyobj, _floattypes) or isinstance(pyobj, _inttypes): pyobj = _gmtime(pyobj) if self.fix_timezone: pyobj = _fix_timezone(pyobj, tz_from = None, tz_to = "Z") d = {} pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = [abs(y) for y in pyobj] d['neg'] = '-' else: d['neg'] = '' d = {} for k,i in [ ('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ('s', 5) ]: d[k] = pyobj[i] ms = pyobj[6] if not ms or not hasattr(self, 'format_ms'): return self.format % d if ms > 999: raise ValueError('milliseconds must be a integer between 0 and 999') d['ms'] = ms return self.format_ms % d
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) if self.fix_timezone: pyobj = _fix_timezone(pyobj, tz_from = None, tz_to = "Z") d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) d['neg'] = '-' else: d['neg'] = '' d = {} for k,i in [ ('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ('s', 5) ]: d[k] = pyobj[i] ms = pyobj[6] if not ms or not hasattr(self, 'format_ms'): return self.format % d if ms > 999: raise ValueError, 'milliseconds must be a integer between 0 and 999' d['ms'] = ms return self.format_ms % d
def addLog(self): argvs = _sys.argv command = "%s " % _strftime("%B-%d-%Y %H:%M", _gmtime()) for ii in range(len(argvs)): command = command + " " + argvs[ii] # end for self.comments.append(" ") self.comments.append(command)
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) # # handle null time strings # if pyobj == "": return "" # # handle perforce style time strings # elif type(pyobj) == type(""): if "/" in pyobj: tokens = pyobj.split("/") d = {"Y": int(tokens[0]), "M": int(tokens[1])} if not ":" in tokens[2]: d["D"] = int(tokens[2]) return self.format % d else: # this is actually a datetime format, not just a date format timetokens = tokens[2].split(":") d["D"] = int(timetokens[0]) d["h"] = int(timetokens[1]) d["m"] = int(timetokens[2]) d["s"] = int(timetokens[3]) return "%(Y)04d-%(M)02d-%(D)02dT%(h)02d:%(m)02d:%(s)02dZ" % d # # # d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) d["neg"] = "-" else: d["neg"] = "" ms = pyobj[6] if not ms or not hasattr(self, "format_ms"): d = {"Y": pyobj[0], "M": pyobj[1], "D": pyobj[2], "h": pyobj[3], "m": pyobj[4], "s": pyobj[5]} return self.format % d if ms > 999: raise ValueError, "milliseconds must be a integer between 0 and 999" d = {"Y": pyobj[0], "M": pyobj[1], "D": pyobj[2], "h": pyobj[3], "m": pyobj[4], "s": pyobj[5], "ms": ms} return self.format_ms % d
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) neg = "-" else: neg = "" val = "%sP%dY%dM%dDT%dH%dM%dS" % (neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5]) return val
def get_formatted_content(self, pyobj): if isinstance(pyobj, _floattypes) or isinstance(pyobj, _inttypes): pyobj = _gmtime(pyobj) pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = [abs(y) for y in pyobj] neg = '-' else: neg = '' val = '%sP%dY%dM%dDT%dH%dM%dS' % \ ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5]) return val
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = list(map(abs, pyobj)) neg = '-' else: neg = '' val = '%sP%dY%dM%dDT%dH%dM%dS' % \ ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5]) return val
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) neg = '-' else: neg = '' val = '%sP%dY%dM%dDT%dH%dM%dS' % \ ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5]) return val
def convert2to1(self, source, idx=1, idz=[1]): idy = 2 if idx == 2: idy = 1 # end if nx = source.DimSize[idx - 1] ny = source.DimSize[idy - 1] nz = len(idz) self.new(dimno=1, valno=ny * nz, dimsize=[nx]) self.Name = source.Name self.ShotNo = source.ShotNo self.SubNo = source.SubNo self.Date = _strftime("%m/%d/%Y %H:%M", _gmtime()) self.DimName = [source.DimName[idx - 1]] self.DimUnit = [source.DimUnit[idx - 1]] self.ValName = [] self.ValUnit = [] self.comments = source.comments if idx == 1: xx = source.getDimData(dimid=idx - 1) yy = source.getDimData(dimid=idy - 1) for ii in range(nz): self.data[:, ii + 1::nz] = source.getValData(valid=idz[ii] - 1) # end for else: xx = source.getDimData(dimid=idx).transpose() yy = source.getDimData(dimid=idy).transpose() for ii in range(nz): self.data[:, ii + 1::nz] = source.getValData(valid=idz[ii] - 1).transpose() # end for # end if self.data[:, 0] = xx[:, 0] for j in range(ny): for ii in range(nz): valname = source.ValName[idz[ii] - 1] + "@%.6E" % yy[ 0, j] + "(%s)" % source.DimUnit[idy - 1] self.ValName.append(valname) self.ValUnit.append(source.ValUnit[idz[ii] - 1])
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = list(map(abs, pyobj)) d['neg'] = '-' else: d['neg'] = '' ms = pyobj[6] if not ms or not hasattr(self, 'format_ms'): d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], } return self.format % d if ms > 999: raise ValueError( 'milliseconds must be a integer between 0 and 999') d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms': ms, } return self.format_ms % d
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) d['neg'] = '-' else: d['neg'] = '' ms = pyobj[6] if not ms or not hasattr(self, 'format_ms'): d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], } return self.format % d if ms > 999: raise ValueError, 'milliseconds must be a integer between 0 and 999' d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms': ms, } return self.format_ms % d
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in map(lambda x: x < 0, pyobj[0:6]): pyobj = map(abs, pyobj) d['neg'] = '-' else: d['neg'] = '' ms = pyobj[6] if not ms: d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], } return self.format % d if ms > 999: raise ValueError, 'milliseconds must be a integer between 0 and 999' d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms': ms, } return self.format_ms % d
def convert1to2(self, source, ydata, yname="", yunit="", zname="", zunit=""): self.new(dimno=2, valno=1, dimsize=[source.DimSize[0], source.ValNo]) self.Name = source.Name self.ShotNo = source.ShotNo self.SubNo = source.SubNo self.Date = _strftime("%m/%d/%Y %H:%M", _gmtime()) self.DimName.append(source.DimName) self.DimName.append(yname) self.DimUnit.append(source.DimUnit) self.DimUnit.append(yunit) self.ValName.append(zname) self.ValUnit.append(zunit) self.comments = source.comments xx, yy = _np.meshgrid(source.data[:, 0], ydata) self.data[:, 0] = xx.reshape(_np.prod(self.DimSize), order='F') self.data[:, 1] = yy.reshape(_np.prod(self.DimSize), order='F') self.data[:, 2] = source.data[:, 1:].reshape(_np.prod(self.DimSize))
def get_formatted_content(self, pyobj): if type(pyobj) in _floattypes or type(pyobj) in _inttypes: pyobj = _gmtime(pyobj) d = {} pyobj = tuple(pyobj) if 1 in [x < 0 for x in pyobj[0:6]]: pyobj = list(map(abs, pyobj)) d['neg'] = '-' else: d['neg'] = '' ms = pyobj[6] if not ms: d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], } return self.format % d if ms > 999: raise ValueError('milliseconds must be a integer between 0 and 999') d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2], 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms':ms, } return self.format_ms % d
def writeHeader(self, filename=None): self.Date = _strftime("%m/%d/%Y %H:%M", _gmtime()) for ii in range(self.DimNo): if ii == 0: dimname = "'%s'" % self.DimName[ii] dimsize = "%d" % self.DimSize[ii] dimunit = "'%s'" % self.DimUnit[ii] else: dimname = dimname + ", '%s'" % self.DimName[ii] dimsize = dimsize + ", %d" % self.DimSize[ii] dimunit = dimunit + ", '%s'" % self.DimUnit[ii] # end if # end for range dimensions for ii in range(self.ValNo): if ii == 0: valname = "'%s'" % self.ValName[ii] valunit = "'%s'" % self.ValUnit[ii] else: valname = valname + ", '%s'" % self.ValName[ii] valunit = valunit + ", '%s'" % self.ValUnit[ii] # end if # end for in range value number if filename is None: """printing to stdout""" print("# [Parameters]") print("# Name = '%s'" % self.Name) print("# ShotNo = %d" % self.ShotNo) print("# SubNo = %d" % self.SubNo) print("# Date = '%s'" % self.Date) print("#") print("# DimNo = %d" % self.DimNo) print("# DimName = %s" % dimname) print("# DimSize = %s" % dimsize) print("# DimUnit = %s" % dimunit) print("#") print("# ValNo = %d" % self.ValNo) print("# ValName = %s" % valname) print("# ValUnit = %s" % valunit) print("#") print("# [Comments]") for line in self.comments: print("# %s" % line) print("#") print("# [Data]") else: """ printting into a file """ try: with open(filename, 'w') as fp: print("# [Parameters]", file=fp) print("# Name = '%s'" % self.Name, file=fp) print("# ShotNo = %d" % self.ShotNo, file=fp) print("# SubNo = %d" % self.SubNo, file=fp) print("# Date = '%s'" % self.Date, file=fp) print("#", file=fp) print("# DimNo = %d" % self.DimNo, file=fp) print("# DimName = %s" % dimname, file=fp) print("# DimSize = %s" % dimsize, file=fp) print("# DimUnit = %s" % dimunit, file=fp) print("#", file=fp) print("# ValNo = %d" % self.ValNo, file=fp) print("# ValName = %s" % valname, file=fp) print("# ValUnit = %s" % valunit, file=fp) print("#", file=fp) print("# [Comments]", file=fp) for line in self.comments: print("# %s" % line, file=fp) print("# filename = %s (written by egDataFormatIO)" % filename, file=fp) print("#", file=fp) print("# [Data]", file=fp) # end with open filename except: """ python2: when the with_statement fails """ fp = open(filename, 'w') print("# [Parameters]", file=fp) print("# Name = '%s'" % self.Name, file=fp) print("# ShotNo = %d" % self.ShotNo, file=fp) print("# SubNo = %d" % self.SubNo, file=fp) print("# Date = '%s'" % self.Date, file=fp) print("#", file=fp) print("# DimNo = %d" % self.DimNo, file=fp) print("# DimName = %s" % dimname, file=fp) print("# DimSize = %s" % dimsize, file=fp) print("# DimUnit = %s" % dimunit, file=fp) print("#", file=fp) print("# ValNo = %d" % self.ValNo, file=fp) print("# ValName = %s" % valname, file=fp) print("# ValUnit = %s" % valunit, file=fp) print("#", file=fp) print("# [Comments]", file=fp) for line in self.comments: print("# %s" % line, file=fp) print("# filename = %s (written by egDataFormatIO)" % filename, file=fp) print("#", file=fp) print("# [Data]", file=fp) fp.close() finally: try: fp.close() except: pass
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # For any information, bug report, idea, donation, hug, beer, please contact # [email protected] # ############################################################################### from time import gmtime as _gmtime _disclaimer = """ASTROOBS Copyright (C) 2015-%s Guillaume Schworer This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions.""" % _gmtime()[0] print(_disclaimer) """ Provides astronomy ephemeris to plan telescope observations .. note:: * All altitudes, azimuth, hour angle are in degrees * However, ``horizon`` attribute of :class:`Observatory` or :class:`Observation` is in radian * All times are in UT, except for ``Observatory.localnight`` - obviously .. warning:: * it can occur that the Sun, the Moon or a target does not rise or set for an observatory/date combination. In that case, the corresponding attributes will be set to ``None`` Real-life example use: >>> import astroobs as obs
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License, # 'LICENSE.txt', along with this program. If not, see # <http://www.gnu.org/licenses/>. import tosdb from random import choice as _choice, randint as _randint from time import sleep as _sleep, gmtime as _gmtime from argparse import ArgumentParser as _ArgumentParser from platform import system as _system NEXT_YEAR = _gmtime().tm_year + 1 #expires 01/19/2018 SPY_CALL = '.SPY180119C250' T_ITEMS = ['SPY','GOOG','QQQ','XLU','XLI','XLE','XLF','XLB','XLP','XLK', 'MSFT','IWM',SPY_CALL] T_TOPICS = [t.val for t in tosdb.TOPICS] T_TOPICS.remove('NULL_TOPIC') BSIZE = 100 BTIMEOUT = 3000 MAX_ADD = 7 args=None
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License, # 'LICENSE.txt', along with this program. If not, see # <http://www.gnu.org/licenses/>. import tosdb from random import choice as _choice, randint as _randint from time import sleep as _sleep, gmtime as _gmtime from argparse import ArgumentParser as _ArgumentParser from platform import system as _system NEXT_YEAR = _gmtime().tm_year + 1 #expires 01/19/2018 SPY_CALL = '.SPY180119C250' T_ITEMS = [ 'SPY', 'GOOG', 'QQQ', 'XLU', 'XLI', 'XLE', 'XLF', 'XLB', 'XLP', 'XLK', 'MSFT', 'IWM', SPY_CALL ] T_TOPICS = [t.val for t in tosdb.TOPICS] T_TOPICS.remove('NULL_TOPIC') BSIZE = 100 BTIMEOUT = 3000 MAX_ADD = 7
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # For any information, bug report, idea, donation, hug, beer, please contact # [email protected] # ############################################################################### from time import gmtime as _gmtime _disclaimer = """ASTROOBS Copyright (C) 2015-%s Guillaume Schworer This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions.""" % _gmtime()[0] print(_disclaimer) """ Provides astronomy ephemeris to plan telescope observations .. note:: * All altitudes, azimuth, hour angle are in degrees * However, ``horizon`` attribute of :class:`Observatory` or :class:`Observation` is in radian * All times are in UT, except for ``Observatory.localnight`` - obviously .. warning:: * it can occur that the Sun, the Moon or a target does not rise or set for an observatory/date combination. In that case, the corresponding attributes will be set to ``None``
def _db_parse_file_column(bytes_): ''' parse the plist in the file column of the Files table of iOS10+ backups ''' # NOTE: using the plistlib parser failed, not sure why p = _biplist.readPlistFromString(bytes_) # check if we have the same fields as in our reference sample if set(p.keys()) != _known_plist_keys: raise PlistParseError( 'someting in file column of Files table in Manifest.db changed') # we have only seen backups where $version, $archiver and $top have fixed # values, so raise exception when we hit some other value version = p.get('$version') if version != 100000: raise PlistParseError('$version != 100000') archiver = p.get('$archiver') if archiver != 'NSKeyedArchiver': raise PlistParseError('$archiver != NSKeyedArchiver') root_uid = p.get('$top').get('root') if root_uid.integer != 1: raise PlistParseError("$top['root'] != Uid(1)") # the interesting data is in the $objects field objects = p.get('$objects') # first field is expected to be $null if objects[0] != '$null': raise PlistParseError("$objects[0] != $null") # check if we have any new types of of fields #if len(set(objects[1].keys()) - _known_object_keys) != 0: # raise PlistParseError("$objects[1] fields do not match known fields: {:s}".format(str(objects[1].keys()))) uid = objects[1].get('UserID') # contents modification time mtime = _datetime(*list(_gmtime(objects[1].get('LastModified'))[0:7]) + [_UTC]) inode = objects[1].get('InodeNumber') mode = objects[1].get('Mode') # determine filetype and permissions based on mode filetype = FileType(mode & 0xE000) permissions = oct(mode & 0x1FFF) # metadata-change time ctime = _datetime(*list(_gmtime(objects[1].get('LastStatusChange'))[0:7]) + [_UTC]) gid = objects[1].get('GroupID') # birth-time (aka creation time) btime = _datetime(*list(_gmtime(objects[1].get('Birth'))[0:7]) + [_UTC]) size = objects[1].get('Size') # not sure what this is protection = objects[1].get('ProtectionClass') # apparently since iOS11 the plist includes a field 'Flags' in the plist # field as well, but I've only seen value 0 in my backups if objects[1].get('Flags', 0) != 0: raise PearBackError('assumption on plist flags field broken') # the Uid stored in 'RelativePath' seems to point to index in 'objects' # where the actual value is stored. The Uid.integer property gives the # integer value relpath = objects[objects[1].get('RelativePath').integer] # something similar for the '$class', which seems to be 'MBFile' always class_ = objects[objects[1].get('$class').integer].get('$classname') if class_ != 'MBFile': raise PlistParseError('assumption broken: $class is not always MBFile') # extended attributes are not always present, but if they are, they seem to # be pointed to by the UID value in the 'ExtendedAttributes' fields. The # target item then contains a bplist with key-value pairs under the key # 'NS.data' if 'ExtendedAttributes' in objects[1]: ea_idx = objects[1]['ExtendedAttributes'].integer extended_attributes = _biplist.readPlistFromString( objects[ea_idx].get('NS.data')) else: extended_attributes = None # target is only available when type is a symlink and its value indicates # the index of the property in the $objects field if filetype == FileType.Symlink: if 'Target' not in objects[1]: raise PlistParseError('Assumption broken on symlinks') tgt_idx = objects[1]['Target'].integer linktarget = objects[tgt_idx] else: linktarget = None # digest is also not always present, it works similar as above two fields, # let's store hex string instead of bytes if 'Digest' in objects[1]: d_idx = objects[1]['Digest'].integer digest = objects[d_idx] if type(digest) == dict: digest = digest['NS.data'] digest = _hexlify(digest).decode() else: digest = _hexlify(digest).decode() else: digest = None # convert to our named tuple and return object return _plistvals(uid, gid, mtime, ctime, btime, inode, mode, filetype, permissions, size, protection, relpath, extended_attributes, linktarget, digest)
def _parse_mbdb_entry(mbdb, pos): ''' parse a single entry in the mbdb file ''' offset = pos domain, pos = _mbdb_string(mbdb, pos) filename, pos = _mbdb_string(mbdb, pos) linktarget, pos = _mbdb_string(mbdb, pos) # a simple test (N=1, scientific is it not?) shows that what is commonly # called 'datahash' in the scripts I used as basis for this, is stored in a # value that is called 'digest' in the newer (iOS10+) backups. So, we call # this 'digest' here as well. digest, pos = _mbdb_string(mbdb, pos) # this is commonly called enckey in the scripts that I used as source, but # in my backups it is consistently an empty string. So assume that it is, # break if it isn't. unknown, pos = _mbdb_string(mbdb, pos) if unknown != '': raise MbdbParseError( 'assumption broken on empty string in unknown field') mode = int.from_bytes(mbdb[pos:pos + 2], 'big') pos += 2 inode = int.from_bytes(mbdb[pos:pos + 8], 'big') pos += 8 uid = int.from_bytes(mbdb[pos:pos + 4], 'big') pos += 4 gid = int.from_bytes(mbdb[pos:pos + 4], 'big') pos += 4 # some sources that I based this function on had a different # order for these timestamps and in addition instead of a # btime assumed an atime, which I think is incorrect based on some simple # experiments (comparing timestamps on a rooted phone with backup # timestamps). mtime = _datetime( *list(_gmtime(int.from_bytes(mbdb[pos:pos + 4], 'big'))[0:7]) + [_UTC]) pos += 4 ctime = _datetime( *list(_gmtime(int.from_bytes(mbdb[pos:pos + 4], 'big'))[0:7]) + [_UTC]) pos += 4 btime = _datetime( *list(_gmtime(int.from_bytes(mbdb[pos:pos + 4], 'big'))[0:7]) + [_UTC]) pos += 4 size = int.from_bytes(mbdb[pos:pos + 8], 'big') pos += 8 # Based on the different values I've encountered in the field that is # commonly called 'flags' in the scripts that I've used as source it would # seem that this is what is called 'protection' in the newer backups. # Perhaps these values represent some enum value of the protection level. # So, I've called this field 'protection' in contrast to the other scripts # out there. protection = int.from_bytes(mbdb[pos:pos + 1], 'big') pos += 1 numprops = int.from_bytes(mbdb[pos:pos + 1], 'big') pos += 1 # determine filetype and permissions based on mode filetype = FileType(mode & 0xE000) permissions = oct(mode & 0x1FFF) extended_attributes = _OD() for ii in range(numprops): pname, pos = _mbdb_string(mbdb, pos) pval, pos = _mbdb_string(mbdb, pos) extended_attributes[pname] = pval # the fileID was originally stored in a separate mbdx file, but we can also # determine this by combining the domain and filepath and calculating sha1 # hash over it fileID = _sha1('{:s}-{:s}'.format(domain, filename).encode('utf8')).hexdigest() return _file_entry(fileID, domain, filename, uid, gid, mtime, ctime, btime, inode, mode, filetype, permissions, size, protection, extended_attributes, linktarget, digest), pos