]> TLD Linux GIT Repositories - tld-builder.git/blob - TLD_Builder/request_handler.py
- more python3 fixes, dropped python2 support
[tld-builder.git] / TLD_Builder / request_handler.py
1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
3 import email
4 import string
5 import time
6 import os
7 import sys
8 import fnmatch
9 import gpg
10 import request
11 import log
12 import path
13 import util
14 import wrap
15 import status
16 from acl import acl
17 from blacklist import blacklist
18 from lock import lock
19 from bqueue import B_Queue
20 from config import config, init_conf
21 from mailer import Message
22 #import messagebus
23
24 def check_double_id(id):
25     id_nl = id + "\n"
26
27     ids = open(path.processed_ids_file)
28     for i in ids:
29         if i == id_nl:
30             # FIXME: security email here?
31             log.alert("request %s already processed" % id)
32             return 1
33     ids.close()
34
35     ids = open(path.processed_ids_file, "a")
36     ids.write(id_nl)
37     ids.close()
38
39     return 0
40
41 def handle_group(r, user):
42     lockf = None
43     def fail_mail(msg):
44         if len(r.batches) >= 1:
45             spec = r.batches[0].spec
46         else:
47             spec = "None.spec"
48         log.error("%s: %s" % (spec, msg))
49         m = Message()
50         m.set_headers(to = r.requester_email, cc = config.builder_list)
51         m.set_headers(subject = "building %s failed" % spec)
52         m.write_line(msg)
53         m.send()
54
55     lockf = lock("request")
56     if check_double_id(r.id):
57         lockf.close()
58         return
59
60     try:
61         if (user.change_requester and r.requester):
62             user = acl.user_by_login(r.requester)
63     except KeyError:
64         r.requester += '/' + user.get_login()
65     else:
66         r.requester = user.get_login()
67         r.requester_email = user.mail_to()
68
69     for batch in r.batches:
70
71         if not user.can_do("src", config.builder, batch.branch):
72             fail_mail("user %s is not allowed to src:%s:%s" \
73                         % (user.get_login(), config.builder, batch.branch))
74             lockf.close()
75             return
76
77         if 'test-build' in r.flags and 'upgrade' in r.flags:
78             fail_mail("it's forbidden to upgrade from a test build")
79             lockf.close()
80             return
81
82         if "upgrade" in r.flags and not user.can_do("upgrade", config.builder, batch.branch):
83             fail_mail("user %s is not allowed to upgrade:%s:%s" \
84                         % (user.get_login(), config.builder, batch.branch))
85             lockf.close()
86             return
87
88         # src builder handles only special commands
89         if batch.is_command() and (batch.command in ["git pull"] or batch.command[:5] == "skip:"  or config.builder in batch.builders):
90             batch.expand_builders(config.binary_builders + [config.src_builder])
91         else:
92             batch.expand_builders(config.binary_builders)
93
94         if not batch.is_command() and config.builder in batch.builders:
95             batch.builders.remove(config.builder)
96
97         for bld in batch.builders:
98             batch.builders_status[bld] = '?'
99             batch.builders_status_time[bld] = time.time()
100             if bld not in config.binary_builders and bld != config.builder:
101                 fail_mail("I (src rpm builder '%s') do not handle binary builder '%s', only '%s'" % \
102                         (config.builder, bld, ' '.join(config.binary_builders)))
103                 lockf.close()
104                 return
105             if batch.is_command():
106                 if "no-chroot" in batch.command_flags:
107                     if not user.can_do("command-no-chroot", bld):
108                         fail_mail("user %s is not allowed to command-no-chroot:%s" \
109                                 % (user.get_login(), bld))
110                         lockf.close()
111                         return
112                 if not user.can_do("command", bld):
113                     fail_mail("user %s is not allowed to command:%s" \
114                                 % (user.get_login(), bld))
115                     lockf.close()
116                     return
117             elif not user.can_do("binary", bld, batch.branch):
118                 pkg = batch.spec
119                 if pkg.endswith(".spec"):
120                     pkg = pkg[:-5]
121                 if not user.can_do("binary-" + pkg, bld, batch.branch):
122                     fail_mail("user %s is not allowed to binary-%s:%s:%s" \
123                                 % (user.get_login(), pkg, bld, batch.branch))
124                     lockf.close()
125                     return
126             if not "test-build" in r.flags and not user.can_do("ready", bld, batch.branch):
127                 fail_mail("user %s is not allowed to send ready builds (ready:%s:%s)" \
128                      % (user.get_login(), bld, batch.branch))
129                 lockf.close()
130                 return
131
132             pkg = batch.spec
133             if pkg.endswith(".spec"):
134                 pkg = pkg[:-5]
135             if not "test-build" in r.flags and blacklist.package(pkg):
136                 fail_mail("package '%s' is blacklisted, only test-builds allowed" % pkg)
137                 lockf.close()
138                 return
139
140     r.priority = user.check_priority(r.priority,config.builder)
141     r.time = time.time()
142     log.notice("queued %s from %s" % (r.id, user.get_login()))
143     q = B_Queue(path.queue_file)
144     q.lock(0)
145     q.read()
146     q.add(r)
147     q.write()
148     q.unlock()
149     lockf.close()
150
151 def handle_notification(r, user):
152     if not user.can_do("notify", r.builder):
153         log.alert("user %s is not allowed to notify:%s" % (user.login, r.builder))
154     q = B_Queue(path.req_queue_file)
155     q.lock(0)
156     q.read()
157     not_fin = list(filter(lambda r: not r.is_done(), q.requests))
158     r.apply_to(q)
159     for r in not_fin:
160         if r.is_done():
161             util.clean_tmp(path.srpms_dir + '/' + r.id)
162     now = time.time()
163     def leave_it(r):
164         # for ,,done'' set timeout to 4d
165         if r.is_done() and r.time + 4 * 24 * 60 * 60 < now:
166             return False
167         # and for not ,,done'' set it to 20d
168         if r.time + 20 * 24 * 60 * 60 < now:
169             util.clean_tmp(path.srpms_dir + '/' + r.id)
170             return False
171         return True
172     q.requests = list(filter(leave_it, q.requests))
173     q.write()
174     q.dump(path.queue_stats_file)
175     q.dump_html(path.queue_html_stats_file)
176     q.write_signed(path.req_queue_signed_file)
177     q.unlock()
178
179 def handle_request(req, filename = None):
180     if req == '':
181         log.alert('Empty body received. Filename: %s' % filename)
182         return False
183
184     keys = gpg.get_keys(req)
185     (em, body) = gpg.verify_sig(req)
186     if not em:
187         log.alert("Invalid signature, missing/untrusted key. Keys in gpg batch: '%s'" % keys)
188         return False
189     user = acl.user_by_email(em)
190     if user == None:
191         # FIXME: security email here
192         log.alert("'%s' not in acl. Keys in gpg batch: '%s'" % (em, keys))
193         return False
194
195     acl.set_current_user(user)
196     status.push("request from %s" % user.login)
197     r = request.parse_request(body)
198     if r.kind == 'group':
199 #        messagebus.notify(topic="request.group", user=user.login, **r.dump_json())
200         handle_group(r, user)
201     elif r.kind == 'notification':
202 #        messagebus.notify(topic="request.notify", user=user.login, **r.dump_json())
203         handle_notification(r, user)
204     else:
205         msg = "%s: don't know how to handle requests of this kind '%s'" \
206                         % (user.get_login(), r.kind)
207         log.alert(msg)
208         m = user.message_to()
209         m.set_headers(subject = "unknown request")
210         m.write_line(msg)
211         m.send()
212     status.pop()
213     return True
214
215 def handle_request_main(req, filename = None):
216     acl.try_reload()
217     blacklist.try_reload()
218     init_conf("src")
219     status.push("handling email request")
220     ret = handle_request(req, filename = filename)
221     status.pop()
222     return ret
223
224 def main():
225     sys.exit(not handle_request_main(sys.stdin.read()))
226
227 if __name__ == '__main__':
228     wrap.wrap(main)