2009-08-04 13:43:31 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# mail.py - methods used by requestsync when used in "mail" mode
|
|
|
|
#
|
2011-01-15 20:54:45 +02:00
|
|
|
# Copyright © 2009 Michael Bienia <geser@ubuntu.com>,
|
|
|
|
# 2011 Stefano Rivera <stefanor@ubuntu.com>
|
2009-08-04 13:43:31 +02:00
|
|
|
#
|
2009-08-05 23:10:05 +02:00
|
|
|
# This module may contain code written by other authors/contributors to
|
|
|
|
# the main requestsync script. See there for their names.
|
|
|
|
#
|
2009-08-04 13:43:31 +02:00
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
2009-08-05 23:10:05 +02:00
|
|
|
# as published by the Free Software Foundation; version 2
|
2010-12-03 00:06:43 +01:00
|
|
|
#
|
2009-08-04 13:43:31 +02:00
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# 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.
|
|
|
|
#
|
2009-08-05 23:10:05 +02:00
|
|
|
# Please see the /usr/share/common-licenses/GPL-2 file for the full text
|
2009-08-04 13:43:31 +02:00
|
|
|
# of the GNU General Public License license.
|
|
|
|
|
2009-08-07 12:53:33 +02:00
|
|
|
import os
|
2011-12-03 18:09:49 +01:00
|
|
|
import re
|
2009-08-07 12:53:33 +02:00
|
|
|
import sys
|
2009-08-22 17:12:55 +02:00
|
|
|
import smtplib
|
|
|
|
import socket
|
2019-09-04 19:17:00 -03:00
|
|
|
import subprocess
|
2011-12-03 21:57:11 +02:00
|
|
|
import tempfile
|
2011-06-25 17:53:44 +02:00
|
|
|
|
2017-03-23 06:43:28 -04:00
|
|
|
from debian.changelog import Changelog
|
2012-05-06 10:15:54 +02:00
|
|
|
from distro_info import DebianDistroInfo, DistroDataOutdated
|
2011-06-25 17:53:44 +02:00
|
|
|
|
2017-03-31 11:26:26 -04:00
|
|
|
from ubuntutools.archive import DebianSourcePackage, UbuntuSourcePackage
|
2013-03-19 00:18:02 +01:00
|
|
|
from ubuntutools.lp.udtexceptions import PackageNotFoundException
|
|
|
|
from ubuntutools.logger import Logger
|
2011-11-13 20:15:19 +02:00
|
|
|
from ubuntutools.question import confirmation_prompt, YesNoQuestion
|
2017-05-01 00:20:03 +02:00
|
|
|
|
|
|
|
|
2009-08-22 17:39:38 +02:00
|
|
|
__all__ = [
|
2011-09-10 15:42:40 +02:00
|
|
|
'get_debian_srcpkg',
|
|
|
|
'get_ubuntu_srcpkg',
|
|
|
|
'need_sponsorship',
|
|
|
|
'check_existing_reports',
|
|
|
|
'get_ubuntu_delta_changelog',
|
|
|
|
'mail_bug',
|
2010-12-23 00:01:39 +02:00
|
|
|
]
|
2009-08-06 00:23:58 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def get_debian_srcpkg(name, release):
|
2017-03-31 11:26:26 -04:00
|
|
|
# Canonicalise release:
|
|
|
|
debian_info = DebianDistroInfo()
|
|
|
|
try:
|
|
|
|
codename = debian_info.codename(release, default=release)
|
|
|
|
return DebianSourcePackage(package=name, series=codename).lp_spph
|
|
|
|
except DistroDataOutdated as e:
|
|
|
|
Logger.warn(e)
|
|
|
|
except PackageNotFoundException:
|
|
|
|
pass
|
|
|
|
return DebianSourcePackage(package=name, series=release).lp_spph
|
2009-08-06 00:23:58 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def get_ubuntu_srcpkg(name, release):
|
2017-03-31 11:26:26 -04:00
|
|
|
return UbuntuSourcePackage(package=name, series=release).lp_spph
|
2009-08-07 12:53:33 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def need_sponsorship(name, component, release):
|
2010-12-22 23:04:29 +02:00
|
|
|
'''
|
|
|
|
Ask the user if he has upload permissions for the package or the
|
|
|
|
component.
|
|
|
|
'''
|
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
val = YesNoQuestion().ask("Do you have upload permissions for the '%s' component or "
|
|
|
|
"the package '%s' in Ubuntu %s?\nIf in doubt answer 'n'." %
|
|
|
|
(component, name, release), 'no')
|
2011-11-13 20:15:19 +02:00
|
|
|
return val == 'no'
|
2009-08-22 17:12:55 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def check_existing_reports(srcpkg):
|
2010-12-22 23:04:29 +02:00
|
|
|
'''
|
|
|
|
Point the user to the URL to manually check for duplicate bug reports.
|
|
|
|
'''
|
2014-12-18 20:48:28 +00:00
|
|
|
print('Please check on '
|
|
|
|
'https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n'
|
|
|
|
'for duplicate sync requests before continuing.' % srcpkg)
|
2011-11-13 20:15:19 +02:00
|
|
|
confirmation_prompt()
|
2009-08-22 17:39:38 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def get_ubuntu_delta_changelog(srcpkg):
|
2011-09-10 10:28:41 +02:00
|
|
|
'''
|
|
|
|
Download the Ubuntu changelog and extract the entries since the last sync
|
|
|
|
from Debian.
|
|
|
|
'''
|
2011-11-13 22:50:34 +02:00
|
|
|
changelog = Changelog(srcpkg.getChangelog())
|
2011-09-10 10:28:41 +02:00
|
|
|
if changelog is None:
|
2019-09-05 16:10:26 -03:00
|
|
|
return ''
|
2011-09-10 10:28:41 +02:00
|
|
|
delta = []
|
|
|
|
debian_info = DebianDistroInfo()
|
|
|
|
for block in changelog:
|
|
|
|
distribution = block.distributions.split()[0].split('-')[0]
|
|
|
|
if debian_info.valid(distribution):
|
|
|
|
break
|
2019-09-04 19:17:00 -03:00
|
|
|
delta += [str(change) for change in block.changes()
|
2011-09-10 10:28:41 +02:00
|
|
|
if change.strip()]
|
|
|
|
|
2019-09-05 16:10:26 -03:00
|
|
|
return '\n'.join(delta)
|
2011-09-10 10:28:41 +02:00
|
|
|
|
2017-05-01 00:20:03 +02:00
|
|
|
|
2011-09-10 15:42:40 +02:00
|
|
|
def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain,
|
|
|
|
keyid, myemailaddr, mailserver_host, mailserver_port,
|
|
|
|
mailserver_user, mailserver_pass):
|
2010-12-22 23:04:29 +02:00
|
|
|
'''
|
|
|
|
Submit the sync request per email.
|
|
|
|
'''
|
|
|
|
|
2011-02-01 10:10:31 +02:00
|
|
|
to = 'new@' + bug_mail_domain
|
2010-12-22 23:04:29 +02:00
|
|
|
|
|
|
|
# generate mailbody
|
|
|
|
if srcpkg:
|
|
|
|
mailbody = ' affects ubuntu/%s\n' % srcpkg
|
|
|
|
else:
|
|
|
|
mailbody = ' affects ubuntu\n'
|
|
|
|
mailbody += '''\
|
2009-08-22 17:12:55 +02:00
|
|
|
status %s
|
|
|
|
importance wishlist
|
|
|
|
subscribe %s
|
|
|
|
done
|
|
|
|
|
|
|
|
%s''' % (status, subscribe, bugtext)
|
2010-12-03 00:10:41 +01:00
|
|
|
|
2010-12-22 23:04:29 +02:00
|
|
|
# prepare sign command
|
|
|
|
gpg_command = None
|
2011-09-09 20:03:47 +02:00
|
|
|
for cmd in ('gnome-gpg', 'gpg2', 'gpg'):
|
2010-12-22 23:04:29 +02:00
|
|
|
if os.access('/usr/bin/%s' % cmd, os.X_OK):
|
|
|
|
gpg_command = [cmd]
|
2011-09-09 20:03:47 +02:00
|
|
|
break
|
|
|
|
|
|
|
|
if not gpg_command:
|
2011-09-10 00:50:48 +02:00
|
|
|
Logger.error("Cannot locate gpg, please install the 'gnupg' package!")
|
2011-09-09 20:03:47 +02:00
|
|
|
sys.exit(1)
|
2009-08-22 17:12:55 +02:00
|
|
|
|
2010-12-22 23:04:29 +02:00
|
|
|
gpg_command.append('--clearsign')
|
|
|
|
if keyid:
|
|
|
|
gpg_command.extend(('-u', keyid))
|
2009-08-22 17:12:55 +02:00
|
|
|
|
2010-12-22 23:04:29 +02:00
|
|
|
# sign the mail body
|
2019-09-04 12:47:03 -03:00
|
|
|
gpg = subprocess.Popen(
|
|
|
|
gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
|
|
encoding='utf-8')
|
|
|
|
signed_report = gpg.communicate(mailbody)[0]
|
2011-09-09 20:03:47 +02:00
|
|
|
if gpg.returncode != 0:
|
2011-09-10 00:50:48 +02:00
|
|
|
Logger.error("%s failed.", gpg_command[0])
|
2011-09-09 20:03:47 +02:00
|
|
|
sys.exit(1)
|
2009-08-22 17:12:55 +02:00
|
|
|
|
2010-12-22 23:04:29 +02:00
|
|
|
# generate email
|
2019-09-05 16:10:26 -03:00
|
|
|
mail = '''\
|
2009-08-22 17:12:55 +02:00
|
|
|
From: %s
|
|
|
|
To: %s
|
|
|
|
Subject: %s
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
|
|
|
|
%s''' % (myemailaddr, to, bugtitle, signed_report)
|
|
|
|
|
2014-12-18 20:48:28 +00:00
|
|
|
print('The final report is:\n%s' % mail)
|
2011-11-13 20:15:19 +02:00
|
|
|
confirmation_prompt()
|
2010-12-22 23:04:29 +02:00
|
|
|
|
2011-11-28 20:50:28 +01:00
|
|
|
# save mail in temporary file
|
2017-05-01 00:20:03 +02:00
|
|
|
backup = tempfile.NamedTemporaryFile(
|
|
|
|
mode='w',
|
|
|
|
delete=False,
|
|
|
|
prefix='requestsync-' + re.sub(r'[^a-zA-Z0-9_-]', '', bugtitle.replace(' ', '_'))
|
|
|
|
)
|
2011-12-03 21:57:11 +02:00
|
|
|
with backup:
|
|
|
|
backup.write(mail)
|
2011-11-28 20:50:28 +01:00
|
|
|
|
2011-11-29 20:20:25 +01:00
|
|
|
Logger.normal('The e-mail has been saved in %s and will be deleted '
|
2011-12-03 21:57:11 +02:00
|
|
|
'after succesful transmission', backup.name)
|
2011-11-28 20:50:28 +01:00
|
|
|
|
2010-12-22 23:04:29 +02:00
|
|
|
# connect to the server
|
2011-11-27 22:14:05 +01:00
|
|
|
while True:
|
|
|
|
try:
|
2011-12-03 22:07:18 +02:00
|
|
|
Logger.normal('Connecting to %s:%s ...', mailserver_host,
|
|
|
|
mailserver_port)
|
2011-11-27 22:14:05 +01:00
|
|
|
s = smtplib.SMTP(mailserver_host, mailserver_port)
|
|
|
|
break
|
2014-12-18 20:48:28 +00:00
|
|
|
except smtplib.SMTPConnectError as s:
|
2017-05-30 11:21:52 +01:00
|
|
|
try:
|
|
|
|
# py2 path
|
|
|
|
# pylint: disable=unsubscriptable-object
|
|
|
|
Logger.error('Could not connect to %s:%s: %s (%i)',
|
|
|
|
mailserver_host, mailserver_port, s[1], s[0])
|
|
|
|
except TypeError:
|
|
|
|
# pylint: disable=no-member
|
|
|
|
Logger.error('Could not connect to %s:%s: %s (%i)',
|
|
|
|
mailserver_host, mailserver_port, s.strerror, s.errno)
|
2011-11-28 19:53:23 +01:00
|
|
|
if s.smtp_code == 421:
|
2017-05-01 00:20:03 +02:00
|
|
|
confirmation_prompt(message='This is a temporary error, press [Enter] '
|
|
|
|
'to retry. Press [Ctrl-C] to abort now.')
|
2017-05-30 11:21:52 +01:00
|
|
|
except socket.error as s:
|
|
|
|
try:
|
|
|
|
# py2 path
|
|
|
|
# pylint: disable=unsubscriptable-object
|
|
|
|
Logger.error('Could not connect to %s:%s: %s (%i)',
|
|
|
|
mailserver_host, mailserver_port, s[1], s[0])
|
|
|
|
except TypeError:
|
|
|
|
# pylint: disable=no-member
|
|
|
|
Logger.error('Could not connect to %s:%s: %s (%i)',
|
|
|
|
mailserver_host, mailserver_port, s.strerror, s.errno)
|
|
|
|
return
|
2010-12-22 23:04:29 +02:00
|
|
|
|
|
|
|
if mailserver_user and mailserver_pass:
|
|
|
|
try:
|
|
|
|
s.login(mailserver_user, mailserver_pass)
|
|
|
|
except smtplib.SMTPAuthenticationError:
|
2011-09-09 20:03:47 +02:00
|
|
|
Logger.error('Error authenticating to the server: '
|
|
|
|
'invalid username and password.')
|
2010-12-22 23:04:29 +02:00
|
|
|
s.quit()
|
|
|
|
return
|
2018-03-08 12:56:36 +01:00
|
|
|
except smtplib.SMTPException:
|
2011-09-09 20:03:47 +02:00
|
|
|
Logger.error('Unknown SMTP error.')
|
2010-12-22 23:04:29 +02:00
|
|
|
s.quit()
|
|
|
|
return
|
|
|
|
|
2011-12-03 11:57:18 +01:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
s.sendmail(myemailaddr, to, mail.encode('utf-8'))
|
|
|
|
s.quit()
|
2011-12-03 21:57:11 +02:00
|
|
|
os.remove(backup.name)
|
2011-12-03 11:57:18 +01:00
|
|
|
Logger.normal('Sync request mailed.')
|
|
|
|
break
|
2014-12-18 20:48:28 +00:00
|
|
|
except smtplib.SMTPRecipientsRefused as smtperror:
|
2011-12-03 11:57:18 +01:00
|
|
|
smtp_code, smtp_message = smtperror.recipients[to]
|
|
|
|
Logger.error('Error while sending: %i, %s', smtp_code, smtp_message)
|
|
|
|
if smtp_code == 450:
|
2017-05-01 00:20:03 +02:00
|
|
|
confirmation_prompt(message='This is a temporary error, press [Enter] '
|
|
|
|
'to retry. Press [Ctrl-C] to abort now.')
|
2011-12-03 11:57:18 +01:00
|
|
|
else:
|
|
|
|
return
|
2014-12-18 20:48:28 +00:00
|
|
|
except smtplib.SMTPResponseException as e:
|
2011-12-03 22:06:49 +02:00
|
|
|
Logger.error('Error while sending: %i, %s',
|
|
|
|
e.smtp_code, e.smtp_error)
|
|
|
|
return
|
2011-12-03 18:01:55 +01:00
|
|
|
except smtplib.SMTPServerDisconnected:
|
|
|
|
Logger.error('Server disconnected while sending the mail.')
|
|
|
|
return
|