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