mirror of
https://github.com/samsonjs/imapbackup.git
synced 2026-03-25 08:45:53 +00:00
new option -d for download directory
This commit is contained in:
parent
a9d1f9c596
commit
98040d2ad6
1 changed files with 53 additions and 24 deletions
|
|
@ -120,7 +120,6 @@ def string_from_file(value):
|
||||||
will be treated as a path to the file to be read. Precede
|
will be treated as a path to the file to be read. Precede
|
||||||
the '@' with a '\' to treat it as a literal.
|
the '@' with a '\' to treat it as a literal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert isinstance(value, str)
|
assert isinstance(value, str)
|
||||||
|
|
||||||
if not value or value[0] not in ["\\", "@"]:
|
if not value or value[0] not in ["\\", "@"]:
|
||||||
|
|
@ -133,15 +132,17 @@ def string_from_file(value):
|
||||||
return content.read().strip()
|
return content.read().strip()
|
||||||
|
|
||||||
|
|
||||||
def download_messages(server, filename, messages, config):
|
def download_messages(server, filename, messages, overwrite, nospinner, thunderbird, basedir):
|
||||||
"""Download messages from folder and append to mailbox"""
|
"""Download messages from folder and append to mailbox"""
|
||||||
|
|
||||||
if config['overwrite'] and os.path.exists(filename):
|
fullname = os.path.join(basedir,filename)
|
||||||
print ("Deleting", filename)
|
|
||||||
os.remove(filename)
|
if overwrite and os.path.exists(fullname):
|
||||||
|
print ("Deleting mbox: {0} at: {1}".format(filename,fullname))
|
||||||
|
os.remove(fullname)
|
||||||
|
|
||||||
# Open disk file for append in binary mode
|
# Open disk file for append in binary mode
|
||||||
mbox = open(filename, 'ab')
|
mbox = open(fullname, 'ab')
|
||||||
|
|
||||||
# the folder has already been selected by scanFolder()
|
# the folder has already been selected by scanFolder()
|
||||||
|
|
||||||
|
|
@ -152,7 +153,7 @@ def download_messages(server, filename, messages, config):
|
||||||
return
|
return
|
||||||
|
|
||||||
spinner = Spinner("Downloading %s new messages to %s" % (len(messages), filename),
|
spinner = Spinner("Downloading %s new messages to %s" % (len(messages), filename),
|
||||||
config['nospinner'])
|
nospinner)
|
||||||
total = biggest = 0
|
total = biggest = 0
|
||||||
|
|
||||||
# each new message
|
# each new message
|
||||||
|
|
@ -178,7 +179,7 @@ def download_messages(server, filename, messages, config):
|
||||||
assert('OK' == typ)
|
assert('OK' == typ)
|
||||||
data_bytes = data[0][1]
|
data_bytes = data[0][1]
|
||||||
text_bytes = data_bytes.strip().replace(b'\r', b'')
|
text_bytes = data_bytes.strip().replace(b'\r', b'')
|
||||||
if config['thunderbird']:
|
if thunderbird:
|
||||||
# This avoids Thunderbird mistaking a line starting "From " as the start
|
# This avoids Thunderbird mistaking a line starting "From " as the start
|
||||||
# of a new message. _Might_ also apply to other mail lients - unknown
|
# of a new message. _Might_ also apply to other mail lients - unknown
|
||||||
text_bytes = text_bytes.replace(b"\nFrom ", b"\n From ")
|
text_bytes = text_bytes.replace(b"\nFrom ", b"\n From ")
|
||||||
|
|
@ -199,21 +200,23 @@ def download_messages(server, filename, messages, config):
|
||||||
pretty_byte_count(biggest)))
|
pretty_byte_count(biggest)))
|
||||||
|
|
||||||
|
|
||||||
def scan_file(filename, overwrite, nospinner):
|
def scan_file(filename, overwrite, nospinner, basedir):
|
||||||
"""Gets IDs of messages in the specified mbox file"""
|
"""Gets IDs of messages in the specified mbox file"""
|
||||||
# file will be overwritten
|
# file will be overwritten
|
||||||
if overwrite:
|
if overwrite:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
fullname = os.path.join(basedir,filename)
|
||||||
|
|
||||||
# file doesn't exist
|
# file doesn't exist
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(fullname):
|
||||||
print ("File %s: not found" % filename)
|
print ("File %s: not found" % filename)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
spinner = Spinner("File %s" % filename, nospinner)
|
spinner = Spinner("File %s" % filename, nospinner)
|
||||||
|
|
||||||
# open the mailbox file for read
|
# open the mailbox file for read
|
||||||
mbox = mailbox.mbox(filename)
|
mbox = mailbox.mbox(fullname)
|
||||||
|
|
||||||
messages = {}
|
messages = {}
|
||||||
|
|
||||||
|
|
@ -412,11 +415,12 @@ def print_usage():
|
||||||
"""Prints usage, exits"""
|
"""Prints usage, exits"""
|
||||||
# " "
|
# " "
|
||||||
print ("Usage: imapbackup [OPTIONS] -s HOST -u USERNAME [-p PASSWORD]")
|
print ("Usage: imapbackup [OPTIONS] -s HOST -u USERNAME [-p PASSWORD]")
|
||||||
|
print (" -d DIR --mbox-dir=DIR Write mbox files to directory. (defaults to cwd)")
|
||||||
print (" -a --append-to-mboxes Append new messages to mbox files. (default)")
|
print (" -a --append-to-mboxes Append new messages to mbox files. (default)")
|
||||||
print (" -y --yes-overwrite-mboxes Overwite existing mbox files instead of appending.")
|
print (" -y --yes-overwrite-mboxes Overwite existing mbox files instead of appending.")
|
||||||
print (" -f FOLDERS --folders=FOLDERS Specifify which folders use. Comma separated list.")
|
print (" -f FOLDERS --folders=FOLDERS Specifify which folders use. Comma separated list.")
|
||||||
print (" -e --ssl Use SSL. Port defaults to 993.")
|
print (" -e --ssl Use SSL. Port defaults to 993.")
|
||||||
print (" -k KEY --key=KEY PEM private key file for SSL. Specify cert, too.")
|
print (" -k KEY --key=KEY PEM private key file for SSL. Specify cert, too.")
|
||||||
print (" -c CERT --cert=CERT PEM certificate chain for SSL. Specify key, too.")
|
print (" -c CERT --cert=CERT PEM certificate chain for SSL. Specify key, too.")
|
||||||
print (" Python's SSL module doesn't check the cert chain.")
|
print (" Python's SSL module doesn't check the cert chain.")
|
||||||
print (" -s HOST --server=HOST Address of server, port optional, eg. mail.com:143")
|
print (" -s HOST --server=HOST Address of server, port optional, eg. mail.com:143")
|
||||||
|
|
@ -427,7 +431,6 @@ def print_usage():
|
||||||
print (" -t SECS --timeout=SECS Sets socket timeout to SECS seconds.")
|
print (" -t SECS --timeout=SECS Sets socket timeout to SECS seconds.")
|
||||||
print (" --thunderbird Create Mozilla Thunderbird compatible mailbox")
|
print (" --thunderbird Create Mozilla Thunderbird compatible mailbox")
|
||||||
print (" --nospinner Disable spinner (makes output log-friendly)")
|
print (" --nospinner Disable spinner (makes output log-friendly)")
|
||||||
print ("\nNOTE: mbox files are created in the current working directory.")
|
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -435,17 +438,18 @@ def process_cline():
|
||||||
"""Uses getopt to process command line, returns (config, warnings, errors)"""
|
"""Uses getopt to process command line, returns (config, warnings, errors)"""
|
||||||
# read command line
|
# read command line
|
||||||
try:
|
try:
|
||||||
short_args = "aynzbekt:c:s:u:p:f:"
|
short_args = "aynekt:c:s:u:p:f:d:"
|
||||||
long_args = ["append-to-mboxes", "yes-overwrite-mboxes",
|
long_args = ["append-to-mboxes", "yes-overwrite-mboxes",
|
||||||
"ssl", "timeout", "keyfile=", "certfile=", "server=", "user=", "pass=",
|
"ssl", "timeout", "keyfile=", "certfile=", "server=", "user=", "pass=",
|
||||||
"folders=", "thunderbird", "nospinner"]
|
"folders=", "thunderbird", "nospinner", "mbox-dir="]
|
||||||
opts, extraargs = getopt.getopt(sys.argv[1:], short_args, long_args)
|
opts, extraargs = getopt.getopt(sys.argv[1:], short_args, long_args)
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
print_usage()
|
print_usage()
|
||||||
|
|
||||||
warnings = []
|
warnings = []
|
||||||
config = {'overwrite': False, 'usessl': False,
|
config = {'overwrite': False, 'usessl': False,
|
||||||
'thunderbird': False, 'nospinner': False}
|
'thunderbird': False, 'nospinner': False,
|
||||||
|
'basedir': "."}
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# empty command line
|
# empty command line
|
||||||
|
|
@ -454,7 +458,9 @@ def process_cline():
|
||||||
|
|
||||||
# process each command line option, save in config
|
# process each command line option, save in config
|
||||||
for option, value in opts:
|
for option, value in opts:
|
||||||
if option in ("-a", "--append-to-mboxes"):
|
if option in ("-d", "--mbox-dir"):
|
||||||
|
config['basedir'] = value
|
||||||
|
elif option in ("-a", "--append-to-mboxes"):
|
||||||
config['overwrite'] = False
|
config['overwrite'] = False
|
||||||
elif option in ("-y", "--yes-overwrite-mboxes"):
|
elif option in ("-y", "--yes-overwrite-mboxes"):
|
||||||
warnings.append("Existing mbox files will be overwritten!")
|
warnings.append("Existing mbox files will be overwritten!")
|
||||||
|
|
@ -624,14 +630,28 @@ def connect_and_login(config):
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
|
||||||
def create_folder_structure(names):
|
|
||||||
|
def create_basedir(basedir):
|
||||||
|
""" Create the base directory on disk """
|
||||||
|
if os.path.isdir(basedir):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.makedirs(basedir)
|
||||||
|
except OSError as e:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_folder_structure(names,basedir):
|
||||||
""" Create the folder structure on disk """
|
""" Create the folder structure on disk """
|
||||||
for imap_foldername, filename in sorted(names):
|
for imap_foldername, filename in sorted(names):
|
||||||
disk_foldername = os.path.split(filename)[0]
|
disk_foldername = os.path.split(filename)[0]
|
||||||
if disk_foldername:
|
if disk_foldername:
|
||||||
try:
|
try:
|
||||||
# print "*** mkdir:", disk_foldername # *DEBUG
|
# print "*** makedirs:", disk_foldername # *DEBUG
|
||||||
os.mkdir(disk_foldername)
|
disk_path = os.path.join(basedir,disk_foldername)
|
||||||
|
os.makedirs(disk_path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != 17:
|
if e.errno != 17:
|
||||||
raise
|
raise
|
||||||
|
|
@ -650,17 +670,25 @@ def main():
|
||||||
for i in dirs]
|
for i in dirs]
|
||||||
names = list(filter(lambda x: x[0] in dirs, names))
|
names = list(filter(lambda x: x[0] in dirs, names))
|
||||||
|
|
||||||
|
|
||||||
|
basedir = config.get('basedir')
|
||||||
|
if basedir.startswith('~'):
|
||||||
|
basedir = os.path.expanduser(basedir)
|
||||||
|
else:
|
||||||
|
basedir = os.path.abspath(config.get('basedir'))
|
||||||
|
|
||||||
|
create_basedir(basedir)
|
||||||
|
|
||||||
# for n, name in enumerate(names): # *DEBUG
|
# for n, name in enumerate(names): # *DEBUG
|
||||||
# print n, name # *DEBUG
|
# print n, name # *DEBUG
|
||||||
|
create_folder_structure(names,basedir)
|
||||||
create_folder_structure(names)
|
|
||||||
|
|
||||||
for name_pair in names:
|
for name_pair in names:
|
||||||
try:
|
try:
|
||||||
foldername, filename = name_pair
|
foldername, filename = name_pair
|
||||||
fol_messages = scan_folder(
|
fol_messages = scan_folder(
|
||||||
server, foldername, config['nospinner'])
|
server, foldername, config['nospinner'])
|
||||||
fil_messages = scan_file(filename, config['overwrite'], config['nospinner'])
|
fil_messages = scan_file(filename, config['overwrite'], config['nospinner'], basedir)
|
||||||
new_messages = {}
|
new_messages = {}
|
||||||
for msg_id in fol_messages.keys():
|
for msg_id in fol_messages.keys():
|
||||||
if msg_id not in fil_messages:
|
if msg_id not in fil_messages:
|
||||||
|
|
@ -669,7 +697,7 @@ def main():
|
||||||
# for f in new_messages:
|
# for f in new_messages:
|
||||||
# print "%s : %s" % (f, new_messages[f])
|
# print "%s : %s" % (f, new_messages[f])
|
||||||
|
|
||||||
download_messages(server, filename, new_messages, config)
|
download_messages(server, filename, new_messages, config['overwrite'], config['nospinner'], config['thunderbird'], basedir)
|
||||||
|
|
||||||
except SkipFolderException as e:
|
except SkipFolderException as e:
|
||||||
print (e)
|
print (e)
|
||||||
|
|
@ -677,6 +705,7 @@ def main():
|
||||||
print ("Disconnecting")
|
print ("Disconnecting")
|
||||||
server.logout()
|
server.logout()
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
|
|
||||||
print ("ERROR:", e)
|
print ("ERROR:", e)
|
||||||
sys.exit(4)
|
sys.exit(4)
|
||||||
except imaplib.IMAP4.error as e:
|
except imaplib.IMAP4.error as e:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue