Browse Source

moved to github

master
lukas 1 year ago
parent
commit
64a9936ddb
1 changed files with 0 additions and 203 deletions
  1. +0
    -203
      pymap-copy.py

+ 0
- 203
pymap-copy.py View File

@@ -1,203 +0,0 @@
#!/usr/bin/python3
from argparse import ArgumentParser
from imaplib import IMAP4, IMAP4_SSL
from email.header import decode_header
from re import compile
from imap_utf7 import decode as imap_decode


def decode_mime_words(s):
words = []
try:
s = s.decode()
for word, encoding in decode_header(s):
if isinstance(word, bytes):
try:
words.append(word.decode(encoding or 'utf8'))
except UnicodeDecodeError:
try:
words.append(word.decode('cp1250'))
except UnicodeDecodeError:
words.append(str(word))
else:
words.append(word)
except UnicodeDecodeError:
words.append(str(s))
rv = ''.join(''.join(words).splitlines())
return rv


def calc_size(b, factor=1000, precision=1):
units = ['B', 'KB', 'MB', 'GB']
template = '{:.?f} {}'.replace('?', str(precision))
rv = 0

if factor == 1024:
units = ['B', 'KiB', 'MiB', 'GiB']

if factor > b > 0:
rv = '{} {}'.format(b, units[0])

elif b < factor**2:
rv = template.format(b/factor, units[1])

elif b < factor**3:
rv = template.format(b/factor**2, units[2])

elif b < factor**4:
rv = template.format(b/factor**3, units[3])

elif b < factor**5:
rv = template.format(b/factor**4, units[4])

elif b < factor**6:
rv = template.format(b/factor**5, units[5])

return rv


parser = ArgumentParser(description='', epilog='pymap-copy by Lukas Schulte-Tickmann')
parser.add_argument('-d', '--dry-run', help='copy/creating nothing, just feign', action="store_true")

parser.add_argument('--source-user', help='Source mailbox username', nargs='?', required=True)
parser.add_argument('--source-pass', help='Source mailbox password', nargs='?', required=True)
parser.add_argument('--source-server', help='Hostname/IP of the source IMAP-server', nargs='?', required=True)
parser.add_argument('--source-no-ssl', help='Use this option if the destination server does not support TLS/SSL', action="store_true")
parser.add_argument('--source-port', help='The IMAP port of the source server (default: 993)', nargs='?', default=993, type=int)

parser.add_argument('--destination-user', help='Destination mailbox username', nargs='?', required=True)
parser.add_argument('--destination-pass', help='Destination mailbox password', nargs='?', required=True)
parser.add_argument('--destination-server', help='Hostname/IP of the destination server', nargs='?', required=True)
parser.add_argument('--destination-no-ssl', help='Use this option if the destination server does not support TLS/SSL', action="store_true")
parser.add_argument('--destination-port', help='The IMAP port of the destination server (default: 993)', nargs='?', default=993, type=int)

args = parser.parse_args()

regex_folder = compile(b'(\(.*\))\s+?(\".*\")\s+?(.*)')
regex_flags = compile(b'\d+\s+\(FLAGS\s+\((.*)\)\s+.+?\s+\{(\d+)\}')

if __name__ == '__main__':
SOURCE_IMAP = IMAP4_SSL
DESTINATION_IMAP = IMAP4_SSL

if args.source_no_ssl:
SOURCE_IMAP = IMAP4

if args.destination_no_ssl:
DESTINATION_IMAP = IMAP4

source = SOURCE_IMAP(host=args.source_server, port=args.source_port)
destination = DESTINATION_IMAP(host=args.destination_server, port=args.destination_port)

try:
#: Login source
print('Login source...', end='', flush=False)
source.login(args.source_user, args.source_pass)
print('OK')

#: Login destination
print('Login destination...', end='', flush=False)
destination.login(args.destination_user, args.destination_pass)
print('OK')

print()

#: get quota from source
try:
print('Getting source quota...', end='', flush=False)
source_quota = source.getquota('INBOX')
print('OK')
except IMAP4.error:
print('ERROR (maybe not supported, ignoring)')

#: get quota from destination
try:
print('Getting destination quota...', end='', flush=False)
destination_quota = destination.getquota('INBOX')
print('OK')
except IMAP4.error:
print('ERROR (maybe not supported, ignoring)')

print()

#: get source folders
print('Getting source folders...', end='', flush=False)
_, source_folders = source.list()
source_folders = [regex_folder.search(f).group(3) for f in source_folders]
print('OK ({} Folders found)'.format(len(source_folders)))

#: get destination folders
print('Getting destination folders...', end='', flush=False)
_, destination_folders = destination.list()
destination_folders = [regex_folder.search(f).group(3) for f in destination_folders]
print('OK ({} Folders found)'.format(len(destination_folders)))

print('\nStarting mail transfer\n')

for folder in source_folders:
print('Current folder: {} '.format(imap_decode(folder)))

#: creating non-existing folders
if folder not in destination_folders:
print('Creating...', end='', flush=False)

if args.dry_run:
print('DRY-RUN')
else:
status, msg = destination.create(folder)
if status == 'OK':
print('OK')
elif 'ALREADYEXISTS' in msg[0].decode():
print('OK (aleady exists)')
else:
print('ERROR: {}'.format(msg))
continue
#: get list of all mails in the current folder
source.select(mailbox=folder, readonly=True)
_, data = source.search(None, 'ALL')
mails = data[0].split()
source_mailcount = len(mails)

for i, mail_id in enumerate(mails):
#: get info about the mail
_, data = source.fetch(mail_id, '(FLAGS BODY[HEADER.FIELDS (Subject)] RFC822)')
rex = regex_flags.search(data[0][0])
flags = rex.group(1).decode()
size = calc_size(int(rex.group(2)))
mail = data[1][1]
subject = decode_mime_words(data[0][1]).split('Subject:')
if len(subject) != 2:
subject = '(no subject)'
else:
subject = subject[1]

#: copy the mail
print('\r\x1b[2KCopy mail ({}/{}) ({}): {}'.format(i+1, source_mailcount, size, subject), end='')
if not args.dry_run:
status, msg = destination.append(folder, flags, None, mail)
if status != 'OK':
print('ERROR: {}\n'.format(msg))

if mails:
print('\n')
else:
print('No mails.\n')

print('Finish!')
except IMAP4.error as e:
print('ERROR ({})'.format(e))
exit('Abort!')

finally:
try:
source.logout()
except IMAP4.error:
pass

try:
destination.logout()
except IMAP4.error:
pass