1"""module to send announcements for processed packages 

2 

3@contact: Debian FTP Master <ftpmaster@debian.org> 

4@copyright: 2012, Ansgar Burchardt <ansgar@debian.org> 

5@license: GPL-2+ 

6""" 

7 

8# This program is free software; you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation; either version 2 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the GNU General Public License along 

19# with this program; if not, write to the Free Software Foundation, Inc., 

20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

21 

22import os 

23from typing import Optional, Union 

24 

25from daklib.config import Config 

26from daklib.textutils import fix_maintainer 

27from daklib.utils import mail_addresses_for_upload, TemplateSubst, send_mail 

28 

29 

30class ProcessedUpload: 

31 """Contains data of a processed upload. 

32 """ 

33 # people 

34 maintainer = None #: Maintainer: field contents 

35 changed_by = None #: Changed-By: field contents 

36 fingerprint = None #: Fingerprint of upload signer 

37 

38 # suites 

39 suites = [] #: Destination suites 

40 from_policy_suites = [] #: Policy suites 

41 

42 # package 

43 changes = None #: Contents of .changes file from upload 

44 changes_filename = None #: Changes Filename 

45 sourceful = None #: Did upload contain source 

46 source = None #: Source value from changes 

47 architecture = None #: Architectures from changes 

48 version = None #: Version from changes 

49 bugs = None #: Bugs closed in upload 

50 

51 # program 

52 program = "unknown-program" #: Which dak program was in use 

53 

54 warnings = [] #: Eventual warnings for upload 

55 

56 

57def _subst_for_upload(upload: ProcessedUpload) -> dict: 

58 """ Prepare substitutions used for announce mails. 

59 

60 :param upload: upload to handle 

61 :return: A dict of substition values for use by :func:`daklib.utils.TemplateSubst` 

62 """ 

63 cnf = Config() 

64 

65 maintainer = upload.maintainer or cnf['Dinstall::MyEmailAddress'] 

66 changed_by = upload.changed_by or maintainer 

67 if upload.sourceful: 

68 maintainer_to = mail_addresses_for_upload(maintainer, changed_by, upload.fingerprint) 

69 else: 

70 maintainer_to = mail_addresses_for_upload(maintainer, maintainer, upload.fingerprint) 

71 

72 bcc = 'X-DAK: dak {0}'.format(upload.program) 

73 if 'Dinstall::Bcc' in cnf: 73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true

74 bcc = '{0}\nBcc: {1}'.format(bcc, cnf['Dinstall::Bcc']) 

75 

76 subst = { 

77 '__DISTRO__': cnf['Dinstall::MyDistribution'], 

78 '__BUG_SERVER__': cnf.get('Dinstall::BugServer'), 

79 '__ADMIN_ADDRESS__': cnf['Dinstall::MyAdminAddress'], 

80 '__DAK_ADDRESS__': cnf['Dinstall::MyEmailAddress'], 

81 '__REJECTOR_ADDRESS__': cnf['Dinstall::MyEmailAddress'], 

82 '__MANUAL_REJECT_MESSAGE__': '', 

83 

84 '__BCC__': bcc, 

85 

86 '__MAINTAINER__': changed_by, 

87 '__MAINTAINER_FROM__': fix_maintainer(changed_by)[1], 

88 '__MAINTAINER_TO__': ', '.join(maintainer_to), 

89 '__CHANGES_FILENAME__': upload.changes_filename, 

90 '__FILE_CONTENTS__': upload.changes, 

91 '__SOURCE__': upload.source, 

92 '__VERSION__': upload.version, 

93 '__ARCHITECTURE__': upload.architecture, 

94 '__WARNINGS__': '\n'.join(upload.warnings), 

95 } 

96 

97 override_maintainer = cnf.get('Dinstall::OverrideMaintainer') 

98 if override_maintainer: 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true

99 subst['__MAINTAINER_FROM__'] = subst['__MAINTAINER_TO__'] = override_maintainer 

100 

101 return subst 

102 

103 

104def _whitelists(upload: ProcessedUpload): 

105 return [s.mail_whitelist for s in upload.suites] 

106 

107 

108def announce_reject(upload: ProcessedUpload, reason: str, rejected_by: Optional[str] = None) -> None: 

109 """ Announce a reject. 

110 

111 :param upload: upload to handle 

112 :param reason: Reject reason 

113 :param rejected_by: Who is doing the reject. 

114 """ 

115 cnf = Config() 

116 subst = _subst_for_upload(upload) 

117 whitelists = _whitelists(upload) 

118 

119 automatic = rejected_by is None 

120 

121 subst['__CC__'] = 'X-DAK-Rejection: {0}'.format('automatic' if automatic else 'manual') 

122 subst['__REJECT_MESSAGE__'] = reason 

123 

124 if rejected_by: 

125 subst['__REJECTOR_ADDRESS__'] = rejected_by 

126 

127 if not automatic: 

128 subst['__BCC__'] = '{0}\nBcc: {1}'.format(subst['__BCC__'], subst['__REJECTOR_ADDRESS__']) 

129 

130 message = TemplateSubst(subst, os.path.join(cnf['Dir::Templates'], 'queue.rejected')) 

131 send_mail(message, whitelists=whitelists) 

132 

133 

134def announce_accept(upload: ProcessedUpload) -> None: 

135 """ Announce an upload. 

136 

137 :param upload: upload to handle 

138 """ 

139 

140 cnf = Config() 

141 subst = _subst_for_upload(upload) 

142 whitelists = _whitelists(upload) 

143 

144 accepted_to_real_suite = any(suite.policy_queue is None or suite in upload.from_policy_suites for suite in upload.suites) 

145 subst['__ACTION__'] = 'accept' if accepted_to_real_suite else 'policy' 

146 

147 suite_names = [] 

148 for suite in upload.suites: 

149 if suite.policy_queue and suite not in upload.from_policy_suites: 

150 suite_names.append("{0}->{1}".format(suite.suite_name, suite.policy_queue.queue_name)) 

151 else: 

152 suite_names.append(suite.suite_name) 

153 subst['__SUITE__'] = ', '.join(suite_names) or '(none)' 

154 

155 message = TemplateSubst(subst, os.path.join(cnf['Dir::Templates'], 'process-unchecked.accepted')) 

156 send_mail(message, whitelists=whitelists) 

157 

158 if accepted_to_real_suite and upload.sourceful: 

159 # send mail to announce lists and tracking server 

160 announce = set() 

161 for suite in upload.suites: 

162 if suite.policy_queue is None or suite in upload.from_policy_suites: 162 ↛ 161line 162 didn't jump to line 161, because the condition on line 162 was never false

163 announce.update(suite.announce or []) 

164 

165 announce_list_address = ", ".join(announce) 

166 

167 # according to #890944 this email shall be sent to dispatch@<TrackingServer> to avoid 

168 # bouncing emails 

169 # the package email alias is not yet created shortly after accepting the package 

170 tracker = cnf.get('Dinstall::TrackingServer') 

171 if tracker: 171 ↛ 172line 171 didn't jump to line 172, because the condition on line 171 was never true

172 announce_list_address = "{0}\nBcc: dispatch@{1}".format(announce_list_address, tracker) 

173 

174 if len(announce_list_address) != 0: 174 ↛ 175line 174 didn't jump to line 175, because the condition on line 174 was never true

175 my_subst = subst.copy() 

176 my_subst['__ANNOUNCE_LIST_ADDRESS__'] = announce_list_address 

177 

178 message = TemplateSubst(my_subst, os.path.join(cnf['Dir::Templates'], 'process-unchecked.announce')) 

179 send_mail(message, whitelists=whitelists) 

180 

181 close_bugs_default = cnf.find_b('Dinstall::CloseBugs') 

182 close_bugs = any(s.close_bugs if s.close_bugs is not None else close_bugs_default for s in upload.suites) 

183 if accepted_to_real_suite and upload.sourceful and close_bugs: 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true

184 for bug in upload.bugs: 

185 my_subst = subst.copy() 

186 my_subst['__BUG_NUMBER__'] = str(bug) 

187 

188 message = TemplateSubst(my_subst, os.path.join(cnf['Dir::Templates'], 'process-unchecked.bug-close')) 

189 send_mail(message, whitelists=whitelists) 

190 

191 

192def announce_new(upload: ProcessedUpload) -> None: 

193 """ Announce an upload going to NEW. 

194 

195 :param upload: upload to handle 

196 """ 

197 

198 cnf = Config() 

199 subst = _subst_for_upload(upload) 

200 whitelists = _whitelists(upload) 

201 

202 message = TemplateSubst(subst, os.path.join(cnf['Dir::Templates'], 'process-unchecked.new')) 

203 send_mail(message, whitelists=whitelists)