def reload_changelog(self): """Reloads debian/changelog and updates the version. Returns true if the changelog was reloaded successfully. Returns false if the user wants to correct a broken changelog. """ # Check the changelog self._changelog = debian.changelog.Changelog() try: self._changelog.parse_changelog(open("debian/changelog"), max_blocks=1, strict=True) except debian.changelog.ChangelogParseError as error: Logger.error("The changelog entry doesn't validate: %s", str(error)) ask_for_manual_fixing() return False # Get new version of package try: self._version = self._changelog.get_version() except IndexError: Logger.error("Debian package version could not be determined. " "debian/changelog is probably malformed.") ask_for_manual_fixing() return False return True
def apply(self, task): """Applies the patch in the current directory.""" assert self._changed_files is not None, "You forgot to download the patch." edit = False if self.is_debdiff(): cmd = [ "patch", "--merge", "--force", "-p", str(self.get_strip_level()), "-i", self._full_path ] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Failed to apply debdiff %s to %s %s.", self._patch_file, task.package, task.get_version()) if not edit: ask_for_manual_fixing() edit = True else: cmd = ["add-patch", self._full_path] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Failed to apply diff %s to %s %s.", self._patch_file, task.package, task.get_version()) if not edit: ask_for_manual_fixing() edit = True return edit
def _check_dsc(self, verify_signature=False): "Check that the dsc matches what we are expecting" assert self._dsc is not None self.source = self.dsc['Source'] self.version = debian.debian_support.Version(self.dsc['Version']) valid = False message = None gpg_info = None try: gpg_info = self.dsc.get_gpg_info() valid = gpg_info.valid() except IOError: message = ('Signature on %s could not be verified, install ' 'debian-keyring' % self.dsc_name) if message is None: if valid: message = 'Valid signature' else: message = ('Signature on %s could not be verified' % self.dsc_name) if gpg_info is not None: if 'GOODSIG' in gpg_info: message = ('Good signature by %s (0x%s)' % (gpg_info['GOODSIG'][1], gpg_info['GOODSIG'][0])) elif 'VALIDSIG' in gpg_info: message = 'Valid signature by 0x%s' % gpg_info['VALIDSIG'][0] if verify_signature: if valid: Logger.normal(message) else: Logger.error(message) raise DownloadError(message) else: Logger.info(message)
def build_source(self, keyid, upload, previous_version): """Tries to build the source package. Returns true if the source package was built successfully. Returns false if the user wants to change something. """ if self._branch: cmd = ['bzr', 'builddeb', '--builder=debuild', '-S', '--', '--no-lintian', '-nc'] else: cmd = ['debuild', '--no-lintian', '-nc', '-S'] cmd.append("-v" + previous_version.full_version) if previous_version.upstream_version == \ self._changelog.upstream_version and upload == "ubuntu": # FIXME: Add proper check that catches cases like changed # compression (.tar.gz -> tar.bz2) and multiple orig source tarballs cmd.append("-sd") else: cmd.append("-sa") if keyid is not None: cmd += ["-k" + keyid] env = os.environ if upload == 'ubuntu': env['DEB_VENDOR'] = 'Ubuntu' Logger.command(cmd) if subprocess.call(cmd, env=env) != 0: Logger.error("Failed to build source tarball.") # TODO: Add a "retry" option ask_for_manual_fixing() return False return True
def get_patch_or_branch(bug): patch = None branch = None if not is_sync(bug): attached_patches = [a for a in bug.attachments if a.type == "Patch"] linked_branches = [b.branch for b in bug.linked_branches] if len(attached_patches) == 0 and len(linked_branches) == 0: if len(bug.attachments) == 0: Logger.error( "No attachment and no linked branch found on " "bug #%i. Add the tag sync to the bug if it is " "a sync request.", bug.id) else: Logger.error( "No attached patch and no linked branch found. " "Go to https://launchpad.net/bugs/%i and mark an " "attachment as patch.", bug.id) sys.exit(1) elif len(attached_patches) == 1 and len(linked_branches) == 0: patch = Patch(attached_patches[0]) elif len(attached_patches) == 0 and len(linked_branches) == 1: branch = linked_branches[0].bzr_identity else: patch, branch = ask_for_patch_or_branch(bug, attached_patches, linked_branches) return (patch, branch)
def rmadison(url, package, suite=None, arch=None): "Call rmadison and parse the result" cmd = ['rmadison', '-u', url] if suite: cmd += ['-s', suite] if arch: cmd += ['-a', arch] cmd.append(package) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) output, error_output = process.communicate() if process.wait() != 0: if error_output: Logger.error('rmadison failed with: %s', error_output) else: Logger.error('rmadison failed') sys.exit(1) # rmadison uses some shorthand if suite: suite = suite.replace('-proposed-updates', '-p-u') # pylint bug: http://www.logilab.org/ticket/46273 # pylint: disable=E1103 for line in output.strip().splitlines(): # pylint: enable=E1103 pkg, ver, dist, archs = [x.strip() for x in line.split('|')] comp = 'main' if '/' in dist: dist, comp = dist.split('/') archs = set(x.strip() for x in archs.split(',')) # rmadison returns some results outside the requested set. # It'll include backports, and when given an unknown suite, # it ignores that argument # # some versions (2.14.1ubuntu0.1) of rmadison return 'sid' when # asked about 'unstable'. Others return 'unstable'. Accept either. if (suite and dist != suite and not (suite == 'sid' and dist == 'unstable')): continue if 'source' in archs: yield { 'source': pkg, 'version': ver, 'suite': dist, 'component': comp, } archs.discard('source') if archs: yield { 'binary': pkg, 'version': ver, 'suite': dist, 'component': comp, 'architectures': archs, }
def snapshot_list(self): "Return a filename -> hash dictionary from snapshot.debian.org" if self._snapshot_list is None: try: import json except ImportError: import simplejson as json except ImportError: Logger.error("Please install python-simplejson.") raise DownloadError("Unable to dowload from " "snapshot.debian.org without " "python-simplejson") try: data = self.url_opener.open( 'http://snapshot.debian.org/mr/package/%s/%s/srcfiles?fileinfo=1' % (self.source, self.version.full_version)) reader = codecs.getreader('utf-8') srcfiles = json.load(reader(data)) except HTTPError: Logger.error( 'Version %s of %s not found on ' 'snapshot.debian.org', self.version.full_version, self.source) self._snapshot_list = False return False self._snapshot_list = dict( (info[0]['name'], hash_) for hash_, info in srcfiles['fileinfo'].items()) return self._snapshot_list
def extract_source(dsc_file, verbose=False): cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file] if not verbose: cmd.insert(1, "-q") Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file))) sys.exit(1)
def _update_maintainer_field(): """Update the Maintainer field in debian/control.""" Logger.command(["update-maintainer"]) try: update_maintainer("debian", Logger.verbose) except MaintainerUpdateException as e: Logger.error("update-maintainer failed: %s", str(e)) sys.exit(1)
def update_maintainer(debian_directory, verbose=False): """updates the Maintainer field of an Ubuntu package * No modifications are made if the Maintainer field contains an ubuntu.com email address. Otherwise, the Maintainer field will be set to Ubuntu Developers <*****@*****.**> * The old value will be saved in a field named XSBC-Original-Maintainer if the Maintainer field is modified. Policy: https://wiki.ubuntu.com/DebianMaintainerField """ try: changelog_file, control_files = _find_files(debian_directory, verbose) except MaintainerUpdateException as e: Logger.error(str(e)) raise distribution = _get_distribution(changelog_file) for control_file in control_files: control = Control(control_file) original_maintainer = control.get_maintainer() if original_maintainer is None: Logger.error("No Maintainer field found in %s.", control_file) raise MaintainerUpdateException("No Maintainer field found") if original_maintainer.strip().lower() in _PREVIOUS_UBUNTU_MAINTAINER: if verbose: print("The old maintainer was: %s" % original_maintainer) print("Resetting as: %s" % _UBUNTU_MAINTAINER) control.set_maintainer(_UBUNTU_MAINTAINER) control.save() continue if original_maintainer.strip().endswith("ubuntu.com>"): if verbose: print( "The Maintainer email is set to an ubuntu.com address. Doing nothing." ) continue if distribution in ("stable", "testing", "unstable", "experimental"): if verbose: print("The package targets Debian. Doing nothing.") return if control.get_original_maintainer() is not None: Logger.warn("Overwriting original maintainer: %s", control.get_original_maintainer()) if verbose: print("The original maintainer is: %s" % original_maintainer) print("Resetting as: %s" % _UBUNTU_MAINTAINER) control.set_original_maintainer(original_maintainer) control.set_maintainer(_UBUNTU_MAINTAINER) control.save() return
def merge_branch(branch): edit = False cmd = ["bzr", "merge", branch] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Failed to merge branch %s." % (branch)) ask_for_manual_fixing() edit = True return edit
def unpack(self, destdir=None): "Unpack in workdir" cmd = ['dpkg-source', '-x', self.dsc_name] if destdir: cmd.append(destdir) Logger.command(cmd) if subprocess.call(cmd, cwd=self.workdir): Logger.error('Source unpack failed.') sys.exit(1)
def download_branch(branch): dir_name = os.path.basename(branch) if os.path.isdir(dir_name): shutil.rmtree(dir_name) cmd = ["bzr", "branch", branch] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Failed to download branch %s." % (branch)) sys.exit(1) return dir_name
def check_version(self, previous_version): """Check if the version of the package is greater than the given one. Return true if the version of the package is newer. Returns false if the user wants to change something. """ if self._version <= previous_version: Logger.error("The version %s is not greater than the already " "available %s.", self._version, previous_version) return ask_for_ignoring_or_fixing() return True
def ack_sync(self, upload, task, launchpad): """Acknowledge a sync request and subscribe ubuntu-archive.""" if upload == "ubuntu": self._print_logs() question = Question(["yes", "edit", "no"]) answer = question.ask("Do you want to acknowledge the sync request", "no") if answer == "edit": return False elif answer == "no": user_abort() bug = task.bug task.status = "Confirmed" if task.importance == "Undecided": task.importance = "Wishlist" task.lp_save() Logger.info("Set bug #%i status to Confirmed.", bug.id) msg = "Sync request ACK'd." if self._build_log: msg = ("%s %s builds on %s. " + msg) % \ (self._package, self._version, self._builder.get_architecture()) bug.newMessage(content=msg, subject="sponsor-patch") Logger.info("Acknowledged sync request bug #%i.", bug.id) bug.subscribe(person=launchpad.people['ubuntu-archive']) Logger.info("Subscribed ubuntu-archive to bug #%i.", bug.id) bug.subscribe(person=launchpad.me) Logger.info("Subscribed me to bug #%i.", bug.id) sponsorsteam = launchpad.people['ubuntu-sponsors'] for sub in bug.subscriptions: if sub.person == sponsorsteam and sub.canBeUnsubscribedByUser(): bug.unsubscribe(person=launchpad.people['ubuntu-sponsors']) Logger.info("Unsubscribed ubuntu-sponsors from bug #%i.", bug.id) elif sub.person == sponsorsteam: Logger.info("Couldn't unsubscribe ubuntu-sponsors from " "bug #%i.", bug.id) Logger.normal("Successfully acknowledged sync request bug #%i.", bug.id) else: Logger.error("Sync requests can only be acknowledged when the " "upload target is Ubuntu.") sys.exit(1) return True
def get_open_ubuntu_bug_task(launchpad, bug, branch=None): """Returns an open Ubuntu bug task for a given Launchpad bug. The bug task needs to be open (not complete) and target Ubuntu. The user will be ask to select one if multiple open Ubuntu bug task exits for the bug. """ bug_tasks = [BugTask(x, launchpad) for x in bug.bug_tasks] ubuntu_tasks = [x for x in bug_tasks if x.is_ubuntu_task()] bug_id = bug.id if branch: branch = branch.split('/') # Non-production LP? if len(branch) > 5: branch = branch[3:] if len(ubuntu_tasks) == 0: Logger.error("No Ubuntu bug task found on bug #%i." % (bug_id)) sys.exit(1) elif len(ubuntu_tasks) == 1: task = ubuntu_tasks[0] if len(ubuntu_tasks) > 1 and branch and branch[1] == 'ubuntu': tasks = [ t for t in ubuntu_tasks if t.get_series() == branch[2] and t.package == branch[3] ] if len(tasks) > 1: # A bug targeted to the development series? tasks = [t for t in tasks if t.series is not None] assert len(tasks) == 1 task = tasks[0] elif len(ubuntu_tasks) > 1: task_list = [t.get_short_info() for t in ubuntu_tasks] Logger.info("%i Ubuntu tasks exist for bug #%i.\n%s", len(ubuntu_tasks), bug_id, "\n".join(task_list)) open_ubuntu_tasks = [x for x in ubuntu_tasks if not x.is_complete()] if len(open_ubuntu_tasks) == 1: task = open_ubuntu_tasks[0] else: Logger.normal( "https://launchpad.net/bugs/%i has %i Ubuntu tasks:" % (bug_id, len(ubuntu_tasks))) for i in range(len(ubuntu_tasks)): print("%i) %s" % (i + 1, ubuntu_tasks[i].get_package_and_series())) selected = input_number( "To which Ubuntu task does the patch belong", 1, len(ubuntu_tasks)) task = ubuntu_tasks[selected - 1] Logger.info("Selected Ubuntu task: %s" % (task.get_short_info())) return task
def _create_and_change_into(workdir): """Create (if it does not exits) and change into given working directory.""" if not os.path.isdir(workdir): try: os.makedirs(workdir) except os.error as error: Logger.error( "Failed to create the working directory %s [Errno %i]: %s." % (workdir, error.errno, error.strerror)) sys.exit(1) if workdir != os.getcwd(): Logger.command(["cd", workdir]) os.chdir(workdir)
def edit_source(): # Spawn shell to allow modifications cmd = [get_user_shell()] Logger.command(cmd) print("""An interactive shell was launched in file://%s Edit your files. When you are done, exit the shell. If you wish to abort the process, exit the shell such that it returns an exit code other than zero. """ % (os.getcwd()), end=' ') returncode = subprocess.call(cmd) if returncode != 0: Logger.error("Shell exited with exit value %i." % (returncode)) sys.exit(1)
def _download_file(self, url, filename): "Download url to filename in workdir." pathname = os.path.join(self.workdir, filename) if self.dsc.verify_file(pathname): Logger.debug('Using existing %s', filename) return True size = [ entry['size'] for entry in self.dsc['Files'] if entry['name'] == filename ] assert len(size) == 1 size = int(size[0]) parsed = urlparse(url) if not self.quiet: Logger.normal('Downloading %s from %s (%0.3f MiB)', filename, parsed.hostname, size / 1024.0 / 1024) if parsed.scheme == 'file': in_ = open(parsed.path, 'rb') else: try: in_ = self.url_opener.open(url) except URLError: return False downloaded = 0 bar_width = 60 try: with open(pathname, 'wb') as out: while True: block = in_.read(10240) if block == b'': break downloaded += len(block) out.write(block) if not self.quiet: percent = downloaded * 100 // size bar = '=' * int(round(downloaded * bar_width / size)) bar = (bar + '>' + ' ' * bar_width)[:bar_width] Logger.stdout.write('[%s] %#3i%%\r' % (bar, percent)) Logger.stdout.flush() in_.close() finally: if not self.quiet: Logger.stdout.write(' ' * (bar_width + 7) + '\r') Logger.stdout.flush() if not self.dsc.verify_file(pathname): Logger.error('Checksum for %s does not match.', filename) return False return True
def restore_maintainer(debian_directory, verbose=False): """Restore the original maintainer""" try: changelog_file, control_files = _find_files(debian_directory, verbose) except MaintainerUpdateException as e: Logger.error(str(e)) raise for control_file in control_files: control = Control(control_file) orig_maintainer = control.get_original_maintainer() if not orig_maintainer: continue if verbose: print("Restoring original maintainer: %s" % orig_maintainer) control.set_maintainer(orig_maintainer) control.remove_original_maintainer() control.save()
def sync(self, upload, series, bug_number, requester): """Does a sync of the source package.""" if upload == "ubuntu": cmd = ["syncpackage", self._package, "-b", str(bug_number), "-f", "-s", requester, "-V", str(self._version), "-d", series] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Syncing of %s %s failed.", self._package, str(self._version)) sys.exit(1) else: # FIXME: Support this use case! Logger.error("Uploading a synced package other than to Ubuntu " "is not supported yet!") sys.exit(1) return True
def get_ubuntu_delta_changelog(srcpkg): ''' Download the Ubuntu changelog and extract the entries since the last sync from Debian. ''' archive = Distribution('ubuntu').getArchive() spph = archive.getPublishedSources(source_name=srcpkg.getPackageName(), exact_match=True, pocket='Release') debian_info = DebianDistroInfo() topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)' r'((\s+%(name_chars)s+)+)\;' % {'name_chars': '[-+0-9a-z.]'}, re.IGNORECASE) delta = [] for record in spph: changes_url = record.changesFileUrl() if changes_url is None: # Native sync break try: response, body = Http().request(changes_url) except HttpLib2Error as e: Logger.error(str(e)) break if response.status != 200: Logger.error("%s: %s %s", changes_url, response.status, response.reason) break changes = Changes(Http().request(changes_url)[1]) for line in changes['Changes'].splitlines(): line = line[1:] m = topline.match(line) if m: distribution = m.group(3).split()[0].split('-')[0] if debian_info.valid(distribution): break if line.startswith(u' '): delta.append(line) else: continue break return '\n'.join(delta)
def debdiff(self, newpkg, diffstat=False): """Write a debdiff comparing this src pkg to a newer one. Optionally print diffstat. Return the debdiff filename. """ cmd = ['debdiff', self.dsc_name, newpkg.dsc_name] difffn = newpkg.dsc_name[:-3] + 'debdiff' Logger.command(cmd + ['> %s' % difffn]) with open(difffn, 'w') as f: if subprocess.call(cmd, stdout=f, cwd=self.workdir) > 2: Logger.error('Debdiff failed.') sys.exit(1) if diffstat: cmd = ('diffstat', '-p1', difffn) Logger.command(cmd) if subprocess.call(cmd): Logger.error('diffstat failed.') sys.exit(1) return os.path.abspath(difffn)
def is_fixed(self, lp_bug): """Make sure that the given Launchpad bug is closed. Returns true if the bug is closed. Returns false if the user wants to change something. """ assert os.path.isfile(self._changes_file), "%s does not exist." % (self._changes_file) changes = debian.deb822.Changes(open(self._changes_file)) fixed_bugs = [] if "Launchpad-Bugs-Fixed" in changes: fixed_bugs = changes["Launchpad-Bugs-Fixed"].split(" ") fixed_bugs = [int(bug) for bug in fixed_bugs] # Check if the given bug is marked as duplicate. In this case get the # master bug. while lp_bug.duplicate_of: lp_bug = lp_bug.duplicate_of if lp_bug.id not in fixed_bugs: Logger.error("Launchpad bug #%i is not closed by new version." % (lp_bug.id)) return ask_for_ignoring_or_fixing() return True
def check_target(self, upload, launchpad): """Make sure that the target is correct. Returns true if the target is correct. Returns false if the user wants to change something. """ (devel_series, supported_series) = _get_series(launchpad) if upload == "ubuntu": allowed = supported_series + \ [s + "-proposed" for s in supported_series] + \ [devel_series] if self._changelog.distributions not in allowed: Logger.error("%s is not an allowed series. It needs to be one of %s." % (self._changelog.distributions, ", ".join(allowed))) return ask_for_ignoring_or_fixing() elif upload and upload.startswith("ppa/"): allowed = supported_series + [devel_series] if self._changelog.distributions not in allowed: Logger.error("%s is not an allowed series. It needs to be one of %s." % (self._changelog.distributions, ", ".join(allowed))) return ask_for_ignoring_or_fixing() return True
def get_builder(name): if name in _SUPPORTED_BUILDERS: builder = _SUPPORTED_BUILDERS[name]() if builder.exists_in_path(): return builder Logger.error("Builder doesn't appear to be installed: %s", name) else: Logger.error("Unsupported builder specified: %s.", name) Logger.error("Supported builders: %s", ", ".join(sorted(_SUPPORTED_BUILDERS.keys())))
def ask_and_upload(self, upload): """Ask the user before uploading the source package. Returns true if the source package is uploaded successfully. Returns false if the user wants to change something. """ # Upload package if upload: self._print_logs() if upload == "ubuntu": target = "the official Ubuntu archive" else: target = upload question = Question(["yes", "edit", "no"]) answer = question.ask("Do you want to upload the package to %s" % target, "no") if answer == "edit": return False elif answer == "no": user_abort() cmd = ["dput", "--force", upload, self._changes_file] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error("Upload of %s to %s failed." % (os.path.basename(self._changes_file), upload)) sys.exit(1) # Push the branch if the package is uploaded to the Ubuntu archive. if upload == "ubuntu" and self._branch: cmd = ['debcommit'] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error('Bzr commit failed.') sys.exit(1) cmd = ['bzr', 'mark-uploaded'] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error('Bzr tagging failed.') sys.exit(1) cmd = ['bzr', 'push', ':parent'] Logger.command(cmd) if subprocess.call(cmd) != 0: Logger.error('Bzr push failed.') sys.exit(1) return True
def _update_failure(self, returncode, dist): if returncode != 0: Logger.error("Failed to update %s chroot for %s." % (dist, self.name)) return returncode
def _build_failure(self, returncode, dsc_file): if returncode != 0: Logger.error("Failed to build %s from source with %s." % (os.path.basename(dsc_file), self.name)) return returncode
def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain, keyid, myemailaddr, mailserver_host, mailserver_port, mailserver_user, mailserver_pass): ''' Submit the sync request per email. ''' to = 'new@' + bug_mail_domain # generate mailbody if srcpkg: mailbody = ' affects ubuntu/%s\n' % srcpkg else: mailbody = ' affects ubuntu\n' mailbody += '''\ status %s importance wishlist subscribe %s done %s''' % (status, subscribe, bugtext) # prepare sign command gpg_command = None for cmd in ('gnome-gpg', 'gpg2', 'gpg'): if os.access('/usr/bin/%s' % cmd, os.X_OK): gpg_command = [cmd] break if not gpg_command: Logger.error("Cannot locate gpg, please install the 'gnupg' package!") sys.exit(1) gpg_command.append('--clearsign') if keyid: gpg_command.extend(('-u', keyid)) # sign the mail body gpg = subprocess.Popen(gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) signed_report = gpg.communicate( mailbody.encode('utf-8'))[0].decode('utf-8') if gpg.returncode != 0: Logger.error("%s failed.", gpg_command[0]) sys.exit(1) # generate email mail = u'''\ From: %s To: %s Subject: %s Content-Type: text/plain; charset=UTF-8 %s''' % (myemailaddr, to, bugtitle, signed_report) print('The final report is:\n%s' % mail) confirmation_prompt() # save mail in temporary file backup = tempfile.NamedTemporaryFile( mode='w', delete=False, prefix='requestsync-' + re.sub(r'[^a-zA-Z0-9_-]', '', bugtitle.replace(' ', '_'))) with backup: backup.write(mail) Logger.normal( 'The e-mail has been saved in %s and will be deleted ' 'after succesful transmission', backup.name) # connect to the server while True: try: Logger.normal('Connecting to %s:%s ...', mailserver_host, mailserver_port) s = smtplib.SMTP(mailserver_host, mailserver_port) break except smtplib.SMTPConnectError 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) if s.smtp_code == 421: confirmation_prompt( message='This is a temporary error, press [Enter] ' 'to retry. Press [Ctrl-C] to abort now.') 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 if mailserver_user and mailserver_pass: try: s.login(mailserver_user, mailserver_pass) except smtplib.SMTPAuthenticationError: Logger.error('Error authenticating to the server: ' 'invalid username and password.') s.quit() return except smtplib.SMTPException: Logger.error('Unknown SMTP error.') s.quit() return while True: try: s.sendmail(myemailaddr, to, mail.encode('utf-8')) s.quit() os.remove(backup.name) Logger.normal('Sync request mailed.') break except smtplib.SMTPRecipientsRefused as smtperror: smtp_code, smtp_message = smtperror.recipients[to] Logger.error('Error while sending: %i, %s', smtp_code, smtp_message) if smtp_code == 450: confirmation_prompt( message='This is a temporary error, press [Enter] ' 'to retry. Press [Ctrl-C] to abort now.') else: return except smtplib.SMTPResponseException as e: Logger.error('Error while sending: %i, %s', e.smtp_code, e.smtp_error) return except smtplib.SMTPServerDisconnected: Logger.error('Server disconnected while sending the mail.') return