pwsafe.py (revelation-0.5.3.tar.xz) | : | pwsafe.py (revelation-0.5.4.tar.xz) | ||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | # GNU General Public License for more details. | |||
# | # | |||
# You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | # along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||
# | # | |||
from . import base | from . import base | |||
from revelation import data, entry, util | from revelation import data, entry | |||
import locale, re, struct | import locale, re, struct | |||
from Cryptodome.Cipher import Blowfish | from Cryptodome.Cipher import Blowfish | |||
import Cryptodome.Random as Random | ||||
FIELDTYPE_NAME = 0x00 | FIELDTYPE_NAME = 0x00 | |||
FIELDTYPE_UUID = 0x01 | FIELDTYPE_UUID = 0x01 | |||
FIELDTYPE_GROUP = 0x02 | FIELDTYPE_GROUP = 0x02 | |||
FIELDTYPE_TITLE = 0x03 | FIELDTYPE_TITLE = 0x03 | |||
FIELDTYPE_USER = 0x04 | FIELDTYPE_USER = 0x04 | |||
FIELDTYPE_NOTES = 0x05 | FIELDTYPE_NOTES = 0x05 | |||
FIELDTYPE_PASSWORD = 0x06 | FIELDTYPE_PASSWORD = 0x06 | |||
FIELDTYPE_END = 0xff | FIELDTYPE_END = 0xff | |||
skipping to change at line 62 | skipping to change at line 63 | |||
] | ] | |||
def __init__(self, input = None): | def __init__(self, input = None): | |||
self.count = [0, 0] | self.count = [0, 0] | |||
self.init() | self.init() | |||
if input != None: | if input != None: | |||
self.update(input) | self.update(input) | |||
def __bytelist2longBigEndian(self, list): | def __bytelist2longBigEndian(self, list): | |||
imax = len(list)/4 | imax = len(list)//4 | |||
hl = [0] * imax | hl = [0] * imax | |||
j = 0 | j = 0 | |||
i = 0 | i = 0 | |||
while i < imax: | while i < imax: | |||
b0 = long(ord(list[j])) << 24 | b0 = list[j] << 24 | |||
b1 = long(ord(list[j+1])) << 16 | b1 = list[j+1] << 16 | |||
b2 = long(ord(list[j+2])) << 8 | b2 = list[j+2] << 8 | |||
b3 = long(ord(list[j+3])) | b3 = list[j+3] | |||
hl[i] = b0 | b1 | b2 | b3 | hl[i] = b0 | b1 | b2 | b3 | |||
i = i+1 | i = i+1 | |||
j = j+4 | j = j+4 | |||
return hl | return hl | |||
def __long2bytesBigEndian(self, n, blocksize=0): | def __long2bytesBigEndian(self, n, blocksize=0): | |||
s = '' | s = b'' | |||
pack = struct.pack | pack = struct.pack | |||
while n > 0: | while n > 0: | |||
s = pack('>I', n & 0xffffffff) + s | s = pack('>I', n & 0xffffffff) + s | |||
n = n >> 32 | n = n >> 32 | |||
for i in range(len(s)): | for i in range(len(s)): | |||
if s[i] != '\000': | if s[i] != b'\000': | |||
break | break | |||
else: | else: | |||
s = '\000' | s = b'\000' | |||
i = 0 | i = 0 | |||
s = s[i:] | s = s[i:] | |||
if blocksize > 0 and len(s) % blocksize: | if blocksize > 0 and len(s) % blocksize: | |||
s = (blocksize - len(s) % blocksize) * '\000' + s | s = (blocksize - len(s) % blocksize) * b'\000' + s | |||
return s | return s | |||
def __rotateLeft(self, x, n): | def __rotateLeft(self, x, n): | |||
return (x << n) | (x >> (32-n)) | return (x << n) | (x >> (32-n)) | |||
def __transform(self, W): | def __transform(self, W): | |||
for t in range(16, 80): | for t in range(16, 80): | |||
W.append(self.__rotateLeft( | W.append(self.__rotateLeft( | |||
W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffff) | W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffff) | |||
skipping to change at line 167 | skipping to change at line 168 | |||
input = [] + self.input | input = [] + self.input | |||
count = [] + self.count | count = [] + self.count | |||
index = (self.count[1] >> 3) & 0x3f | index = (self.count[1] >> 3) & 0x3f | |||
if index < 56: | if index < 56: | |||
padLen = 56 - index | padLen = 56 - index | |||
else: | else: | |||
padLen = 120 - index | padLen = 120 - index | |||
padding = ['\200'] + ['\000'] * 63 | padding = [128] + [0] * 63 | |||
self.update(padding[:padLen]) | self.update(padding[:padLen]) | |||
bits = self.__bytelist2longBigEndian(self.input[:56]) + count | bits = self.__bytelist2longBigEndian(self.input[:56]) + count | |||
self.__transform(bits) | self.__transform(bits) | |||
digest = self.__long2bytesBigEndian(self.H0, 4) + \ | digest = self.__long2bytesBigEndian(self.H0, 4) + \ | |||
self.__long2bytesBigEndian(self.H1, 4) + \ | self.__long2bytesBigEndian(self.H1, 4) + \ | |||
self.__long2bytesBigEndian(self.H2, 4) + \ | self.__long2bytesBigEndian(self.H2, 4) + \ | |||
self.__long2bytesBigEndian(self.H3, 4) + \ | self.__long2bytesBigEndian(self.H3, 4) + \ | |||
self.__long2bytesBigEndian(self.H4, 4) | self.__long2bytesBigEndian(self.H4, 4) | |||
skipping to change at line 204 | skipping to change at line 204 | |||
self.length = 0 | self.length = 0 | |||
self.input = [] | self.input = [] | |||
self.H0 = H0 | self.H0 = H0 | |||
self.H1 = H1 | self.H1 = H1 | |||
self.H2 = H2 | self.H2 = H2 | |||
self.H3 = H3 | self.H3 = H3 | |||
self.H4 = H4 | self.H4 = H4 | |||
def update(self, inBuf): | def update(self, inBuf): | |||
leninBuf = long(len(inBuf)) | leninBuf = len(inBuf) | |||
index = (self.count[1] >> 3) & 0x3F | index = (self.count[1] >> 3) & 0x3F | |||
self.count[1] = self.count[1] + (leninBuf << 3) | self.count[1] = self.count[1] + (leninBuf << 3) | |||
if self.count[1] < (leninBuf << 3): | if self.count[1] < (leninBuf << 3): | |||
self.count[0] = self.count[0] + 1 | self.count[0] = self.count[0] + 1 | |||
self.count[0] = self.count[0] + (leninBuf >> 29) | self.count[0] = self.count[0] + (leninBuf >> 29) | |||
partLen = 64 - index | partLen = 64 - index | |||
skipping to change at line 235 | skipping to change at line 235 | |||
i = 0 | i = 0 | |||
self.input = self.input + list(inBuf) | self.input = self.input + list(inBuf) | |||
# misc functions common to both datahandlers | # misc functions common to both datahandlers | |||
def decrypt(key, ciphertext, iv = None): | def decrypt(key, ciphertext, iv = None): | |||
"Decrypts data" | "Decrypts data" | |||
if len(ciphertext) % 8 != 0: | if len(ciphertext) % 8 != 0: | |||
raise base.FormatError | raise base.FormatError | |||
cipher = Blowfish.new(key) | cipher = Blowfish.new(key, Blowfish.MODE_ECB) | |||
cbc = iv | cbc = iv | |||
plaintext = "" | plaintext = b"" | |||
for cipherblock in [ ciphertext[i * 8 : (i + 1) * 8] for i in range(len(ciph ertext) / 8) ]: | for cipherblock in [ ciphertext[i * 8 : (i + 1) * 8] for i in range(len(ciph ertext) // 8) ]: | |||
plainblock = decrypt_block(cipher, cipherblock) | plainblock = decrypt_block(cipher, cipherblock) | |||
if cbc != None: | if cbc != None: | |||
plainblock = "".join([ chr(ord(plainblock[i]) ^ ord(cbc[i])) for i i n range(len(plainblock)) ]) | plainblock = bytes([ plainblock[i] ^ cbc[i] for i in range(len(plain block)) ]) | |||
cbc = cipherblock | cbc = cipherblock | |||
plaintext += plainblock | plaintext += plainblock | |||
return plaintext | return plaintext | |||
def decrypt_block(cipher, block): | def decrypt_block(cipher, block): | |||
"Decrypts a block with the given cipher" | "Decrypts a block with the given cipher" | |||
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] | block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4])) | |||
block = cipher.decrypt(block) | block = cipher.decrypt(block) | |||
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] | block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4])) | |||
return block | return block | |||
def encrypt(key, plaintext, iv = None): | def encrypt(key, plaintext, iv = None): | |||
"Encrypts data" | "Encrypts data" | |||
if len(plaintext) % 8 != 0: | if len(plaintext) % 8 != 0: | |||
raise base.FormatError | raise base.FormatError | |||
cipher = Blowfish.new(key) | cipher = Blowfish.new(key, Blowfish.MODE_ECB) | |||
cbc = iv | cbc = iv | |||
ciphertext = "" | ciphertext = b"" | |||
for plainblock in [ plaintext[i * 8 : (i + 1) * 8] for i in range(len(plaint ext) / 8) ]: | for plainblock in [ plaintext[i * 8 : (i + 1) * 8] for i in range(len(plaint ext) // 8) ]: | |||
if cbc != None: | if cbc != None: | |||
plainblock = "".join([ chr(ord(plainblock[i]) ^ ord(cbc[i])) for i i n range(len(plainblock)) ]) | plainblock = bytes([ plainblock[i] ^ cbc[i] for i in range(len(plain block)) ]) | |||
cipherblock = encrypt_block(cipher, plainblock) | cipherblock = encrypt_block(cipher, plainblock) | |||
ciphertext += cipherblock | ciphertext += cipherblock | |||
if cbc != None: | if cbc != None: | |||
cbc = cipherblock | cbc = cipherblock | |||
return ciphertext | return ciphertext | |||
def encrypt_block(cipher, block): | def encrypt_block(cipher, block): | |||
"Encrypts a block with the given cipher" | "Encrypts a block with the given cipher" | |||
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] | block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4])) | |||
block = cipher.encrypt(block) | block = cipher.encrypt(block) | |||
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] | block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4])) | |||
return block | return block | |||
def generate_testhash(password, random): | def generate_testhash(password, random): | |||
"Generates a testhash based on a password and a random string" | "Generates a testhash based on a password and a random string" | |||
key = SHA(random + "\x00\x00" + password).digest() | key = SHA(random + b"\x00\x00" + password.encode()).digest() | |||
cipher = Blowfish.new(key) | cipher = Blowfish.new(key, Blowfish.MODE_ECB) | |||
for i in range(1000): | for i in range(1000): | |||
random = encrypt_block(cipher, random) | random = encrypt_block(cipher, random) | |||
h = SHA() | h = SHA() | |||
h.init(0, 0, 0, 0, 0) | h.init(0, 0, 0, 0, 0) | |||
h.update(random) | h.update(random) | |||
h.update("\x00\x00") | h.update(b"\x00\x00") | |||
testhash = h.digest() | testhash = h.digest() | |||
return testhash | return testhash | |||
def create_field(value, type = FIELDTYPE_NAME): | def create_field(value, type = FIELDTYPE_NAME): | |||
"Creates a field" | "Creates a field" | |||
if isinstance(value, str): | ||||
value = value.encode() | ||||
field = struct.pack("ii", len(value), type) + value | field = struct.pack("ii", len(value), type) + value | |||
if len(value) == 0 or len(value) % 8 != 0: | if len(value) == 0 or len(value) % 8 != 0: | |||
field += "\x00" * (8 - len(value) % 8) | field += b"\x00" * (8 - len(value) % 8) | |||
return field | return field | |||
def normalize_field(field): | def normalize_field(field): | |||
"Normalizes a field value" | "Normalizes a field value" | |||
enc = locale.getpreferredencoding() | enc = locale.getpreferredencoding() | |||
field = field.replace("\x00", "") | field = field.replace(b"\x00", b"") | |||
field = re.sub("\s+", " ", field) | field = re.sub(b"\s+", b" ", field) | |||
field = field.strip() | field = field.strip() | |||
field = field.decode(enc, "replace") | field = field.decode(enc, "replace") | |||
field = field.encode("utf-8", "replace") | ||||
return field | return field | |||
def parse_field_header(header): | def parse_field_header(header): | |||
"Parses field data, returns the length and type" | "Parses field data, returns the length and type" | |||
if len(header) < 8: | if len(header) < 8: | |||
raise base.FormatError | raise base.FormatError | |||
length, type = struct.unpack("ii", header[:8]) | length, type = struct.unpack("ii", header[:8]) | |||
skipping to change at line 373 | skipping to change at line 374 | |||
raise base.FormatError | raise base.FormatError | |||
if (len(input) - 56) % 8 != 0: | if (len(input) - 56) % 8 != 0: | |||
raise base.FormatError | raise base.FormatError | |||
def export_data(self, entrystore, password): | def export_data(self, entrystore, password): | |||
"Exports data from an entrystore" | "Exports data from an entrystore" | |||
# serialize data | # serialize data | |||
enc = locale.getpreferredencoding() | enc = locale.getpreferredencoding() | |||
db = "" | db = b"" | |||
iter = entrystore.iter_children(None) | iter = entrystore.iter_children(None) | |||
while iter is not None: | while iter is not None: | |||
e = entrystore.get_entry(iter) | e = entrystore.get_entry(iter) | |||
if type(e) != entry.FolderEntry: | if type(e) != entry.FolderEntry: | |||
e = e.convert_generic() | e = e.convert_generic() | |||
edata = "" | edata = b"" | |||
edata += create_field(e.name.encode(enc, "replace") + "\xAD" + e | edata += create_field(e.name.encode(enc, "replace") + b"\xAD" + | |||
[entry.UsernameField].encode("iso-8859-1")) | e[entry.UsernameField].encode("iso-8859-1")) | |||
edata += create_field(e[entry.PasswordField].encode(enc, "replac e")) | edata += create_field(e[entry.PasswordField].encode(enc, "replac e")) | |||
edata += create_field(e.description.encode(enc, "replace")) | edata += create_field(e.description.encode(enc, "replace")) | |||
db += edata | db += edata | |||
iter = entrystore.iter_traverse_next(iter) | iter = entrystore.iter_traverse_next(iter) | |||
# encrypt data | # encrypt data | |||
random = util.random_string(8) | rand = Random.new() | |||
salt = util.random_string(20) | random = rand.read(8) | |||
iv = util.random_string(8) | salt = rand.read(20) | |||
iv = rand.read(8) | ||||
testhash = generate_testhash(password, random) | testhash = generate_testhash(password, random) | |||
ciphertext = encrypt(SHA(password + salt).digest(), db, iv) | ciphertext = encrypt(SHA(password.encode() + salt).digest(), db, iv) | |||
return random + testhash + salt + iv + ciphertext | return random + testhash + salt + iv + ciphertext | |||
def import_data(self, input, password): | def import_data(self, input, password): | |||
"Imports data into an entrystore" | "Imports data into an entrystore" | |||
# read header and test password | # read header and test password | |||
if password is None: | if password is None: | |||
raise base.PasswordError | raise base.PasswordError | |||
random = input[0:8] | random = input[0:8] | |||
testhash = input[8:28] | testhash = input[8:28] | |||
salt = input[28:48] | salt = input[28:48] | |||
iv = input[48:56] | iv = input[48:56] | |||
if testhash != generate_testhash(password, random): | if testhash != generate_testhash(password, random): | |||
raise base.PasswordError | raise base.PasswordError | |||
# load data | # load data | |||
db = decrypt(SHA(password + salt).digest(), input[56:], iv) | db = decrypt(SHA(password.encode() + salt).digest(), input[56:], iv ) | |||
entrystore = data.EntryStore() | entrystore = data.EntryStore() | |||
while len(db) > 0: | while len(db) > 0: | |||
dbentry = { "name" : "", "username" : "", "password" : "", "note" : "" } | dbentry = { "name" : "", "username" : "", "password" : "", "note" : "" } | |||
for f in ( "name", "password", "note" ): | for f in ( "name", "password", "note" ): | |||
flen, ftype = parse_field_header(db[:8]) | flen, ftype = parse_field_header(db[:8]) | |||
value = db[8:8 + flen] | value = db[8:8 + flen] | |||
if f == "name" and "\xAD" in value: | if f == "name" and b"\xAD" in value: | |||
value, dbentry["username"] = value.split("\xAD", 1) | value, dbentry["username"] = value.split(b"\xAD", 1) | |||
dbentry[f] = value | dbentry[f] = value | |||
db = db[8 + flen:] | db = db[8 + flen:] | |||
e = entry.GenericEntry() | e = entry.GenericEntry() | |||
e.name = normalize_field(dbentry["name"]) | e.name = normalize_field(dbentry["name"]) | |||
e.description = normalize_field(dbentry["note"]) | e.description = normalize_field(dbentry["note"]) | |||
e[entry.UsernameField] = normalize_field(dbentry["username"]) | e[entry.UsernameField] = normalize_field(dbentry["username"]) | |||
e[entry.PasswordField] = normalize_field(dbentry["password"]) | e[entry.PasswordField] = normalize_field(dbentry["password"]) | |||
skipping to change at line 511 | skipping to change at line 513 | |||
if len(input) < 56: | if len(input) < 56: | |||
raise base.FormatError | raise base.FormatError | |||
if (len(input) - 56) % 8 != 0: | if (len(input) - 56) % 8 != 0: | |||
raise base.FormatError | raise base.FormatError | |||
def export_data(self, entrystore, password): | def export_data(self, entrystore, password): | |||
"Exports data from an entrystore" | "Exports data from an entrystore" | |||
# set up magic entry at start of database | # set up magic entry at start of database | |||
db = "" | db = b"" | |||
db += "\x48\x00\x00\x00\x00\x00\x00\x00" | db += b"\x48\x00\x00\x00\x00\x00\x00\x00" | |||
db += " !!!Version 2 File Format!!! Please upgrade to PasswordSafe 2.0 o | db += b" !!!Version 2 File Format!!! Please upgrade to PasswordSafe 2.0 | |||
r later" | or later" | |||
db += "\x03\x00\x00\x00\x06\x00\x00\x00" | db += b"\x03\x00\x00\x00\x06\x00\x00\x00" | |||
db += "2.0\x00\x00\x00\x00\x00" | db += b"2.0\x00\x00\x00\x00\x00" | |||
db += "\x00\x00\x00\x00\x06\x00\x00\x00" | db += b"\x00\x00\x00\x00\x06\x00\x00\x00" | |||
db += "\x00\x00\x00\x00\x00\x00\x00\x00" | db += b"\x00\x00\x00\x00\x00\x00\x00\x00" | |||
# serialize data | # serialize data | |||
uuids = [] | uuids = [] | |||
iter = entrystore.iter_children(None) | iter = entrystore.iter_children(None) | |||
enc = locale.getpreferredencoding() | enc = locale.getpreferredencoding() | |||
rand = Random.new() | ||||
while iter is not None: | while iter is not None: | |||
e = entrystore.get_entry(iter) | e = entrystore.get_entry(iter) | |||
if type(e) != entry.FolderEntry: | if type(e) != entry.FolderEntry: | |||
e = e.convert_generic() | e = e.convert_generic() | |||
uuid = util.random_string(16) | uuid = rand.read(16) | |||
while uuid in uuids: | while uuid in uuids: | |||
uuid = util.random_string(16) | uuid = rand.read(16) | |||
edata = "" | edata = b"" | |||
edata += create_field(uuid, FIELDTYPE_UUID) | edata += create_field(uuid, FIELDTYPE_UUID) | |||
edata += create_field(self.__get_group(entrystore, iter), FIELDT YPE_GROUP) | edata += create_field(self.__get_group(entrystore, iter), FIELDT YPE_GROUP) | |||
edata += create_field(e.name.encode(enc, "replace"), FIELDTYPE_T ITLE) | edata += create_field(e.name.encode(enc, "replace"), FIELDTYPE_T ITLE) | |||
s = e[entry.UsernameField] | s = e[entry.UsernameField] | |||
if s is None: | if s is None: | |||
s = "" | s = "" | |||
edata += create_field(s.encode(enc, "replace"), FIELDTYPE_USER) | edata += create_field(s.encode(enc, "replace"), FIELDTYPE_USER) | |||
edata += create_field(e[entry.PasswordField].encode(enc, "replac e"), FIELDTYPE_PASSWORD) | edata += create_field(e[entry.PasswordField].encode(enc, "replac e"), FIELDTYPE_PASSWORD) | |||
edata += create_field(e.description.encode(enc, "replace"), FIEL DTYPE_NOTES) | edata += create_field(e.description.encode(enc, "replace"), FIEL DTYPE_NOTES) | |||
edata += create_field("", FIELDTYPE_END) | edata += create_field("", FIELDTYPE_END) | |||
db += edata | db += edata | |||
iter = entrystore.iter_traverse_next(iter) | iter = entrystore.iter_traverse_next(iter) | |||
# encrypt data | # encrypt data | |||
random = util.random_string(8) | rand = Random.new() | |||
salt = util.random_string(20) | random = rand.read(8) | |||
iv = util.random_string(8) | salt = rand.read(20) | |||
iv = rand.read(8) | ||||
testhash = generate_testhash(password, random) | testhash = generate_testhash(password, random) | |||
ciphertext = encrypt(SHA(password + salt).digest(), db, iv) | ciphertext = encrypt(SHA(password.encode() + salt).digest(), db, iv) | |||
return random + testhash + salt + iv + ciphertext | return random + testhash + salt + iv + ciphertext | |||
def import_data(self, input, password): | def import_data(self, input, password): | |||
"Imports data into an entrystore" | "Imports data into an entrystore" | |||
# read header and test password | # read header and test password | |||
if password is None: | if password is None: | |||
raise base.PasswordError | raise base.PasswordError | |||
random = input[0:8] | random = input[0:8] | |||
testhash = input[8:28] | testhash = input[8:28] | |||
salt = input[28:48] | salt = input[28:48] | |||
iv = input[48:56] | iv = input[48:56] | |||
if testhash != generate_testhash(password, random): | if testhash != generate_testhash(password, random): | |||
raise base.PasswordError | raise base.PasswordError | |||
# load data | # load data | |||
db = decrypt(SHA(password + salt).digest(), input[56:], iv) | db = decrypt(SHA(password.encode() + salt).digest(), input[56:], iv ) | |||
entrystore = data.EntryStore() | entrystore = data.EntryStore() | |||
# read magic entry | # read magic entry | |||
for f in "magic", "version", "prefs": | for f in "magic", "version", "prefs": | |||
flen, ftype = parse_field_header(db) | flen, ftype = parse_field_header(db) | |||
value = db[8:8 + flen] | value = db[8:8 + flen] | |||
if f == "magic" and value != " !!!Version 2 File Format!!! Please up grade to PasswordSafe 2.0 or later": | if f == "magic" and value != b" !!!Version 2 File Format!!! Please u pgrade to PasswordSafe 2.0 or later": | |||
raise base.FormatError | raise base.FormatError | |||
db = db[8 + flen:] | db = db[8 + flen:] | |||
# import entries | # import entries | |||
e = entry.GenericEntry() | e = entry.GenericEntry() | |||
group = None | group = None | |||
groupmap = {} | groupmap = {} | |||
while len(db) > 0: | while len(db) > 0: | |||
flen, ftype = parse_field_header(db) | flen, ftype = parse_field_header(db) | |||
value = normalize_field(db[8:8 + flen]) | value = normalize_field(db[8:8 + flen]) | |||
if ftype == FIELDTYPE_NAME: | if ftype == FIELDTYPE_NAME: | |||
if "\xAD" not in value: | if b"\xAD" not in value: | |||
e.name = value | e.name = value | |||
else: | else: | |||
n, u = value.split("\xAD", 1) | n, u = value.split(b"\xAD", 1) | |||
e.name = normalize_field(n) | e.name = normalize_field(n) | |||
e[entry.UsernameField] = normalize_field(u) | e[entry.UsernameField] = normalize_field(u) | |||
elif ftype == FIELDTYPE_TITLE: | elif ftype == FIELDTYPE_TITLE: | |||
e.name = value | e.name = value | |||
elif ftype == FIELDTYPE_USER: | elif ftype == FIELDTYPE_USER: | |||
e[entry.UsernameField] = value | e[entry.UsernameField] = value | |||
End of changes. 46 change blocks. | ||||
62 lines changed or deleted | 66 lines changed or added |