Skip to content

Commit c47d317

Browse files
committed
Update MailList.py to use new pickle utility functions
1 parent 6294a39 commit c47d317

1 file changed

Lines changed: 25 additions & 95 deletions

File tree

Mailman/MailList.py

Lines changed: 25 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -654,53 +654,22 @@ def Create(self, name, admin, crypted_password,
654654
#
655655
# Database and filesystem I/O
656656
#
657-
def __save(self, data_dict):
658-
# Save the file as a binary pickle, and rotate the old version to a
659-
# backup file. We must guarantee that config.pck is always valid so
660-
# we never rotate unless the we've successfully written the temp file.
661-
# We use pickle now because marshal is not guaranteed to be compatible
662-
# between Python versions.
657+
def __save(self, dbfile, dict):
658+
# Save the dictionary to the specified database file. We always save
659+
# using pickle, even if the file was originally a marshal file. This
660+
# is because pickle is guaranteed to be compatible across Python
661+
# versions, while marshal is not.
663662
#
664-
# We use protocol 4 for Python 2/3 compatibility because:
665-
# 1. It supports large objects (>4GB)
666-
# 2. It's compatible between Python 2.7 and Python 3.x
667-
# 3. It handles Unicode strings properly
668-
# 4. It's the highest protocol version supported by both Python 2.7 and 3.x
669-
fname = os.path.join(self.fullpath(), 'config.pck')
670-
fname_tmp = fname + '.tmp.%s.%d' % (socket.gethostname(), os.getpid())
671-
fname_last = fname + '.last'
672-
fp = None
663+
# On success return None. On error, return the error object.
673664
try:
674-
fp = open(fname_tmp, 'wb')
675-
# Use protocol 4 for Python 2/3 compatibility, with fix_imports for backward compatibility
676-
pickle.dump(data_dict, fp, protocol=4, fix_imports=True)
677-
fp.flush()
678-
if mm_cfg.SYNC_AFTER_WRITE:
679-
os.fsync(fp.fileno())
680-
fp.close()
681-
except IOError as e:
682-
syslog('error',
683-
'Failed config.pck write, retaining old state.\n%s', e)
684-
if fp is not None:
685-
os.unlink(fname_tmp)
686-
raise
687-
# Now do config.pck.tmp.xxx -> config.pck -> config.pck.last rotation
688-
# as safely as possible.
689-
try:
690-
# Remove existing backup file if it exists
691-
try:
692-
os.unlink(fname_last)
693-
except OSError as e:
694-
if e.errno != errno.ENOENT:
695-
raise
696-
# Create new backup file
697-
os.link(fname, fname_last)
698-
except OSError as e:
699-
if e.errno != errno.ENOENT:
700-
raise
701-
os.rename(fname_tmp, fname)
702-
# Reset the timestamp
703-
self.__timestamp = os.path.getmtime(fname)
665+
# Save using the utility function with protocol 4
666+
save_pickle_file(dbfile, dict)
667+
# Update the timestamp
668+
self.__timestamp = os.path.getmtime(dbfile)
669+
return None
670+
except Exception as e:
671+
syslog('error', 'Failed to save database file %s: %s', dbfile, str(e))
672+
return e
704673

705674
def Save(self):
706675
"""Save the mailing list's configuration to disk.
@@ -731,7 +700,7 @@ def Save(self):
731700
# list members' passwords (in clear text).
732701
omask = os.umask(0o007)
733702
try:
734-
self.__save(dict)
703+
self.__save(os.path.join(self.fullpath(), 'config.pck'), dict)
735704
finally:
736705
os.umask(omask)
737706
self.SaveRequestsDb()
@@ -753,55 +722,16 @@ def __load(self, dbfile):
753722
elif dbfile.endswith('.pck') or dbfile.endswith('.pck.last'):
754723
def loadfunc(fp):
755724
try:
756-
# Read the first byte to determine protocol version
757-
protocol = ord(fp.read(1))
758-
print(C_('List %(listname)s %(dbfile)s uses pickle protocol %(protocol)d') % {
759-
'listname': self.internal_name(),
760-
'dbfile': os.path.basename(dbfile),
761-
'protocol': protocol
762-
})
763-
# Reset file pointer to beginning
764-
fp.seek(0)
765-
766-
# For protocol 2 files (Python 2.x), try loading with different encodings
767-
if protocol == 2:
768-
try:
769-
# First try with latin1 (most common for Python 2.x)
770-
return pickle.load(fp, fix_imports=True, encoding='latin1')
771-
except (UnicodeDecodeError, pickle.UnpicklingError) as e:
772-
syslog('error', 'Failed to load with latin1: %s', str(e))
773-
fp.seek(0)
774-
try:
775-
# Then try with UTF-8
776-
return pickle.load(fp, fix_imports=True, encoding='utf-8')
777-
except (UnicodeDecodeError, pickle.UnpicklingError) as e:
778-
syslog('error', 'Failed to load with UTF-8: %s', str(e))
779-
fp.seek(0)
780-
# Finally try without encoding
781-
return pickle.load(fp, fix_imports=True)
782-
# For protocol 4 files (Python 3.x), try loading with different encodings
783-
elif protocol == 4:
784-
try:
785-
# First try with UTF-8
786-
return pickle.load(fp, fix_imports=True, encoding='utf-8')
787-
except (UnicodeDecodeError, pickle.UnpicklingError) as e:
788-
syslog('error', 'Failed to load with UTF-8: %s', str(e))
789-
fp.seek(0)
790-
try:
791-
# Then try with latin1
792-
return pickle.load(fp, fix_imports=True, encoding='latin1')
793-
except (UnicodeDecodeError, pickle.UnpicklingError) as e:
794-
syslog('error', 'Failed to load with latin1: %s', str(e))
795-
fp.seek(0)
796-
# Finally try without encoding
797-
return pickle.load(fp, fix_imports=True)
798-
else:
799-
# For other protocols, try without encoding first
800-
try:
801-
return pickle.load(fp, fix_imports=True)
802-
except (UnicodeDecodeError, pickle.UnpicklingError):
803-
fp.seek(0)
804-
return pickle.load(fp, fix_imports=True, encoding='latin1')
725+
# Get the protocol version
726+
protocol = get_pickle_protocol(fp.name)
727+
if protocol is not None:
728+
print(C_('List %(listname)s %(dbfile)s uses pickle protocol %(protocol)d') % {
729+
'listname': self.internal_name(),
730+
'dbfile': os.path.basename(dbfile),
731+
'protocol': protocol
732+
})
733+
# Use the utility function to load the pickle
734+
return load_pickle_file(fp.name)
805735
except Exception as e:
806736
syslog('error', 'Failed to load pickle file %s: %s', dbfile, str(e))
807737
raise

0 commit comments

Comments
 (0)