]> git.decadent.org.uk Git - dak.git/blob - dak/process_new.py
17cf4e8f0bd1ac810fbb392abe7a6fff5f223b71
[dak.git] / dak / process_new.py
1 #!/usr/bin/env python
2 # vim:set et ts=4 sw=4:
3
4 """ Handles NEW and BYHAND packages
5
6 @contact: Debian FTP Master <ftpmaster@debian.org>
7 @copyright: 2001, 2002, 2003, 2004, 2005, 2006  James Troup <james@nocrew.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Frank Lichtenheld <djpig@debian.org>
10 @license: GNU General Public License version 2 or later
11 """
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
16
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
26 ################################################################################
27
28 # 23:12|<aj> I will not hush!
29 # 23:12|<elmo> :>
30 # 23:12|<aj> Where there is injustice in the world, I shall be there!
31 # 23:13|<aj> I shall not be silenced!
32 # 23:13|<aj> The world shall know!
33 # 23:13|<aj> The world *must* know!
34 # 23:13|<elmo> oh dear, he's gone back to powerpuff girls... ;-)
35 # 23:13|<aj> yay powerpuff girls!!
36 # 23:13|<aj> buttercup's my favourite, who's yours?
37 # 23:14|<aj> you're backing away from the keyboard right now aren't you?
38 # 23:14|<aj> *AREN'T YOU*?!
39 # 23:15|<aj> I will not be treated like this.
40 # 23:15|<aj> I shall have my revenge.
41 # 23:15|<aj> I SHALL!!!
42
43 ################################################################################
44
45 from __future__ import with_statement
46
47 import copy
48 import errno
49 import os
50 import readline
51 import stat
52 import sys
53 import time
54 import contextlib
55 import pwd
56 import apt_pkg, apt_inst
57 import examine_package
58
59 from daklib.dbconn import *
60 from daklib.queue import *
61 from daklib import daklog
62 from daklib import utils
63 from daklib.regexes import re_no_epoch, re_default_answer, re_isanum, re_package
64 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError, CantGetLockError
65 from daklib.summarystats import SummaryStats
66 from daklib.config import Config
67 from daklib.changesutils import *
68
69 # Globals
70 Options = None
71 Logger = None
72
73 Priorities = None
74 Sections = None
75
76 ################################################################################
77 ################################################################################
78 ################################################################################
79
80 def recheck(upload, session):
81 # STU: I'm not sure, but I don't thin kthis is necessary any longer:    upload.recheck(session)
82     if len(upload.rejects) > 0:
83         answer = "XXX"
84         if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
85             answer = 'S'
86
87         print "REJECT\n%s" % '\n'.join(upload.rejects)
88         prompt = "[R]eject, Skip, Quit ?"
89
90         while prompt.find(answer) == -1:
91             answer = utils.our_raw_input(prompt)
92             m = re_default_answer.match(prompt)
93             if answer == "":
94                 answer = m.group(1)
95             answer = answer[:1].upper()
96
97         if answer == 'R':
98             upload.do_reject(manual=0, reject_message='\n'.join(upload.rejects))
99             upload.pkg.remove_known_changes(session=session)
100             session.commit()
101             return 0
102         elif answer == 'S':
103             return 0
104         elif answer == 'Q':
105             end()
106             sys.exit(0)
107
108     return 1
109
110 ################################################################################
111
112 class Section_Completer:
113     def __init__ (self, session):
114         self.sections = []
115         self.matches = []
116         for s, in session.query(Section.section):
117             self.sections.append(s)
118
119     def complete(self, text, state):
120         if state == 0:
121             self.matches = []
122             n = len(text)
123             for word in self.sections:
124                 if word[:n] == text:
125                     self.matches.append(word)
126         try:
127             return self.matches[state]
128         except IndexError:
129             return None
130
131 ############################################################
132
133 class Priority_Completer:
134     def __init__ (self, session):
135         self.priorities = []
136         self.matches = []
137         for p, in session.query(Priority.priority):
138             self.priorities.append(p)
139
140     def complete(self, text, state):
141         if state == 0:
142             self.matches = []
143             n = len(text)
144             for word in self.priorities:
145                 if word[:n] == text:
146                     self.matches.append(word)
147         try:
148             return self.matches[state]
149         except IndexError:
150             return None
151
152 ################################################################################
153
154 def print_new (new, upload, indexed, file=sys.stdout):
155     check_valid(new)
156     broken = False
157     index = 0
158     for pkg in new.keys():
159         index += 1
160         section = new[pkg]["section"]
161         priority = new[pkg]["priority"]
162         if new[pkg]["section id"] == -1:
163             section += "[!]"
164             broken = True
165         if new[pkg]["priority id"] == -1:
166             priority += "[!]"
167             broken = True
168         if indexed:
169             line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
170         else:
171             line = "%-20s %-20s %-20s" % (pkg, priority, section)
172         line = line.strip()+'\n'
173         file.write(line)
174     notes = get_new_comments(upload.pkg.changes.get("source"))
175     for note in notes:
176         print "\nAuthor: %s\nVersion: %s\nTimestamp: %s\n\n%s" \
177               % (note.author, note.version, note.notedate, note.comment)
178         print "-" * 72
179     return broken, len(notes) > 0
180
181 ################################################################################
182
183 def index_range (index):
184     if index == 1:
185         return "1"
186     else:
187         return "1-%s" % (index)
188
189 ################################################################################
190 ################################################################################
191
192 def edit_new (new, upload):
193     # Write the current data to a temporary file
194     (fd, temp_filename) = utils.temp_filename()
195     temp_file = os.fdopen(fd, 'w')
196     print_new (new, upload, indexed=0, file=temp_file)
197     temp_file.close()
198     # Spawn an editor on that file
199     editor = os.environ.get("EDITOR","vi")
200     result = os.system("%s %s" % (editor, temp_filename))
201     if result != 0:
202         utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
203     # Read the edited data back in
204     temp_file = utils.open_file(temp_filename)
205     lines = temp_file.readlines()
206     temp_file.close()
207     os.unlink(temp_filename)
208     # Parse the new data
209     for line in lines:
210         line = line.strip()
211         if line == "":
212             continue
213         s = line.split()
214         # Pad the list if necessary
215         s[len(s):3] = [None] * (3-len(s))
216         (pkg, priority, section) = s[:3]
217         if not new.has_key(pkg):
218             utils.warn("Ignoring unknown package '%s'" % (pkg))
219         else:
220             # Strip off any invalid markers, print_new will readd them.
221             if section.endswith("[!]"):
222                 section = section[:-3]
223             if priority.endswith("[!]"):
224                 priority = priority[:-3]
225             for f in new[pkg]["files"]:
226                 upload.pkg.files[f]["section"] = section
227                 upload.pkg.files[f]["priority"] = priority
228             new[pkg]["section"] = section
229             new[pkg]["priority"] = priority
230
231 ################################################################################
232
233 def edit_index (new, upload, index):
234     priority = new[index]["priority"]
235     section = new[index]["section"]
236     ftype = new[index]["type"]
237     done = 0
238     while not done:
239         print "\t".join([index, priority, section])
240
241         answer = "XXX"
242         if ftype != "dsc":
243             prompt = "[B]oth, Priority, Section, Done ? "
244         else:
245             prompt = "[S]ection, Done ? "
246         edit_priority = edit_section = 0
247
248         while prompt.find(answer) == -1:
249             answer = utils.our_raw_input(prompt)
250             m = re_default_answer.match(prompt)
251             if answer == "":
252                 answer = m.group(1)
253             answer = answer[:1].upper()
254
255         if answer == 'P':
256             edit_priority = 1
257         elif answer == 'S':
258             edit_section = 1
259         elif answer == 'B':
260             edit_priority = edit_section = 1
261         elif answer == 'D':
262             done = 1
263
264         # Edit the priority
265         if edit_priority:
266             readline.set_completer(Priorities.complete)
267             got_priority = 0
268             while not got_priority:
269                 new_priority = utils.our_raw_input("New priority: ").strip()
270                 if new_priority not in Priorities.priorities:
271                     print "E: '%s' is not a valid priority, try again." % (new_priority)
272                 else:
273                     got_priority = 1
274                     priority = new_priority
275
276         # Edit the section
277         if edit_section:
278             readline.set_completer(Sections.complete)
279             got_section = 0
280             while not got_section:
281                 new_section = utils.our_raw_input("New section: ").strip()
282                 if new_section not in Sections.sections:
283                     print "E: '%s' is not a valid section, try again." % (new_section)
284                 else:
285                     got_section = 1
286                     section = new_section
287
288         # Reset the readline completer
289         readline.set_completer(None)
290
291     for f in new[index]["files"]:
292         upload.pkg.files[f]["section"] = section
293         upload.pkg.files[f]["priority"] = priority
294     new[index]["priority"] = priority
295     new[index]["section"] = section
296     return new
297
298 ################################################################################
299
300 def edit_overrides (new, upload, session):
301     print
302     done = 0
303     while not done:
304         print_new (new, upload, indexed=1)
305         new_index = {}
306         index = 0
307         for i in new.keys():
308             index += 1
309             new_index[index] = i
310
311         prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
312
313         got_answer = 0
314         while not got_answer:
315             answer = utils.our_raw_input(prompt)
316             if not answer.isdigit():
317                 answer = answer[:1].upper()
318             if answer == "E" or answer == "D":
319                 got_answer = 1
320             elif re_isanum.match (answer):
321                 answer = int(answer)
322                 if (answer < 1) or (answer > index):
323                     print "%s is not a valid index (%s).  Please retry." % (answer, index_range(index))
324                 else:
325                     got_answer = 1
326
327         if answer == 'E':
328             edit_new(new, upload)
329         elif answer == 'D':
330             done = 1
331         else:
332             edit_index (new, upload, new_index[answer])
333
334     return new
335
336 ################################################################################
337
338 def edit_note(note, upload, session):
339     # Write the current data to a temporary file
340     (fd, temp_filename) = utils.temp_filename()
341     editor = os.environ.get("EDITOR","vi")
342     answer = 'E'
343     while answer == 'E':
344         os.system("%s %s" % (editor, temp_filename))
345         temp_file = utils.open_file(temp_filename)
346         newnote = temp_file.read().rstrip()
347         temp_file.close()
348         print "New Note:"
349         print utils.prefix_multi_line_string(newnote,"  ")
350         prompt = "[D]one, Edit, Abandon, Quit ?"
351         answer = "XXX"
352         while prompt.find(answer) == -1:
353             answer = utils.our_raw_input(prompt)
354             m = re_default_answer.search(prompt)
355             if answer == "":
356                 answer = m.group(1)
357             answer = answer[:1].upper()
358     os.unlink(temp_filename)
359     if answer == 'A':
360         return
361     elif answer == 'Q':
362         end()
363         sys.exit(0)
364
365     comment = NewComment()
366     comment.package = upload.pkg.changes["source"]
367     comment.version = upload.pkg.changes["version"]
368     comment.comment = newnote
369     comment.author  = utils.whoami()
370     comment.trainee = bool(Options["Trainee"])
371     session.add(comment)
372     session.commit()
373
374 ################################################################################
375
376 def check_pkg (upload):
377     try:
378         less_fd = os.popen("less -R -", 'w', 0)
379         stdout_fd = sys.stdout
380         try:
381             sys.stdout = less_fd
382             changes = utils.parse_changes (upload.pkg.changes_file)
383             examine_package.display_changes(changes['distribution'], upload.pkg.changes_file)
384             files = upload.pkg.files
385             for f in files.keys():
386                 if files[f].has_key("new"):
387                     ftype = files[f]["type"]
388                     if ftype == "deb":
389                         examine_package.check_deb(changes['distribution'], f)
390                     elif ftype == "dsc":
391                         examine_package.check_dsc(changes['distribution'], f)
392         finally:
393             examine_package.output_package_relations()
394             sys.stdout = stdout_fd
395     except IOError, e:
396         if e.errno == errno.EPIPE:
397             utils.warn("[examine_package] Caught EPIPE; skipping.")
398             pass
399         else:
400             raise
401     except KeyboardInterrupt:
402         utils.warn("[examine_package] Caught C-c; skipping.")
403         pass
404
405 ################################################################################
406
407 ## FIXME: horribly Debian specific
408
409 def do_bxa_notification(upload):
410     files = upload.pkg.files
411     summary = ""
412     for f in files.keys():
413         if files[f]["type"] == "deb":
414             control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
415             summary += "\n"
416             summary += "Package: %s\n" % (control.Find("Package"))
417             summary += "Description: %s\n" % (control.Find("Description"))
418     upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
419     bxa_mail = utils.TemplateSubst(upload.Subst,Config()["Dir::Templates"]+"/process-new.bxa_notification")
420     utils.send_mail(bxa_mail)
421
422 ################################################################################
423
424 def add_overrides (new, upload, session):
425     changes = upload.pkg.changes
426     files = upload.pkg.files
427     srcpkg = changes.get("source")
428
429     for suite in changes["suite"].keys():
430         suite_id = get_suite(suite).suite_id
431         for pkg in new.keys():
432             component_id = get_component(new[pkg]["component"]).component_id
433             type_id = get_override_type(new[pkg]["type"]).overridetype_id
434             priority_id = new[pkg]["priority id"]
435             section_id = new[pkg]["section id"]
436             Logger.log(["%s overrides" % (srcpkg), suite, new[pkg]["component"], new[pkg]["type"], new[pkg]["priority"], new[pkg]["section"]])
437             session.execute("INSERT INTO override (suite, component, type, package, priority, section, maintainer) VALUES (:sid, :cid, :tid, :pkg, :pid, :sectid, '')",
438                             { 'sid': suite_id, 'cid': component_id, 'tid':type_id, 'pkg': pkg, 'pid': priority_id, 'sectid': section_id})
439             for f in new[pkg]["files"]:
440                 if files[f].has_key("new"):
441                     del files[f]["new"]
442             del new[pkg]
443
444     session.commit()
445
446     if Config().FindB("Dinstall::BXANotify"):
447         do_bxa_notification(upload)
448
449 ################################################################################
450
451 def prod_maintainer (note, upload):
452     cnf = Config()
453     # Here we prepare an editor and get them ready to prod...
454     (fd, temp_filename) = utils.temp_filename()
455     temp_file = os.fdopen(fd, 'w')
456     if len(note) > 0:
457         for line in note:
458             temp_file.write(line)
459     temp_file.close()
460     editor = os.environ.get("EDITOR","vi")
461     answer = 'E'
462     while answer == 'E':
463         os.system("%s %s" % (editor, temp_filename))
464         temp_fh = utils.open_file(temp_filename)
465         prod_message = "".join(temp_fh.readlines())
466         temp_fh.close()
467         print "Prod message:"
468         print utils.prefix_multi_line_string(prod_message,"  ",include_blank_lines=1)
469         prompt = "[P]rod, Edit, Abandon, Quit ?"
470         answer = "XXX"
471         while prompt.find(answer) == -1:
472             answer = utils.our_raw_input(prompt)
473             m = re_default_answer.search(prompt)
474             if answer == "":
475                 answer = m.group(1)
476             answer = answer[:1].upper()
477     os.unlink(temp_filename)
478     if answer == 'A':
479         return
480     elif answer == 'Q':
481         end()
482         sys.exit(0)
483     # Otherwise, do the proding...
484     user_email_address = utils.whoami() + " <%s>" % (
485         cnf["Dinstall::MyAdminAddress"])
486
487     Subst = upload.Subst
488
489     Subst["__FROM_ADDRESS__"] = user_email_address
490     Subst["__PROD_MESSAGE__"] = prod_message
491     Subst["__CC__"] = "Cc: " + cnf["Dinstall::MyEmailAddress"]
492
493     prod_mail_message = utils.TemplateSubst(
494         Subst,cnf["Dir::Templates"]+"/process-new.prod")
495
496     # Send the prod mail if appropriate
497     if not cnf["Dinstall::Options::No-Mail"]:
498         utils.send_mail(prod_mail_message)
499
500     print "Sent proding message"
501
502 ################################################################################
503
504 def do_new(upload, session):
505     print "NEW\n"
506     files = upload.pkg.files
507     upload.check_files(not Options["No-Action"])
508     changes = upload.pkg.changes
509     cnf = Config()
510
511     # Check for a valid distribution
512     upload.check_distributions()
513
514     # Make a copy of distribution we can happily trample on
515     changes["suite"] = copy.copy(changes["distribution"])
516
517     # The main NEW processing loop
518     done = 0
519     while not done:
520         # Find out what's new
521         new = determine_new(changes, files)
522
523         if not new:
524             break
525
526         answer = "XXX"
527         if Options["No-Action"] or Options["Automatic"]:
528             answer = 'S'
529
530         (broken, note) = print_new(new, upload, indexed=0)
531         prompt = ""
532
533         if not broken and not note:
534             prompt = "Add overrides, "
535         if broken:
536             print "W: [!] marked entries must be fixed before package can be processed."
537         if note:
538             print "W: note must be removed before package can be processed."
539             prompt += "RemOve all notes, Remove note, "
540
541         prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
542
543         while prompt.find(answer) == -1:
544             answer = utils.our_raw_input(prompt)
545             m = re_default_answer.search(prompt)
546             if answer == "":
547                 answer = m.group(1)
548             answer = answer[:1].upper()
549
550         if answer in ( 'A', 'E', 'M', 'O', 'R' ) and Options["Trainee"]:
551             utils.warn("Trainees can't do that")
552             continue
553
554         if answer == 'A' and not Options["Trainee"]:
555             try:
556                 check_daily_lock()
557                 done = add_overrides (new, upload, session)
558                 new_accept(upload, Options["No-Action"], session)
559                 Logger.log(["NEW ACCEPT: %s" % (upload.pkg.changes_file)])
560             except CantGetLockError:
561                 print "Hello? Operator! Give me the number for 911!"
562                 print "Dinstall in the locked area, cant process packages, come back later"
563         elif answer == 'C':
564             check_pkg(upload)
565         elif answer == 'E' and not Options["Trainee"]:
566             new = edit_overrides (new, upload, session)
567         elif answer == 'M' and not Options["Trainee"]:
568             aborted = upload.do_reject(manual=1,
569                                        reject_message=Options["Manual-Reject"],
570                                        notes=get_new_comments(changes.get("source", ""), session=session))
571             if not aborted:
572                 upload.pkg.remove_known_changes(session=session)
573                 session.commit()
574                 Logger.log(["NEW REJECT: %s" % (upload.pkg.changes_file)])
575                 done = 1
576         elif answer == 'N':
577             edit_note(get_new_comments(changes.get("source", ""), session=session),
578                       upload, session)
579         elif answer == 'P' and not Options["Trainee"]:
580             prod_maintainer(get_new_comments(changes.get("source", ""), session=session),
581                             upload)
582             Logger.log(["NEW PROD: %s" % (upload.pkg.changes_file)])
583         elif answer == 'R' and not Options["Trainee"]:
584             confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
585             if confirm == "y":
586                 for c in get_new_comments(changes.get("source", ""), changes.get("version", ""), session=session):
587                     session.delete(c)
588                 session.commit()
589         elif answer == 'O' and not Options["Trainee"]:
590             confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
591             if confirm == "y":
592                 for c in get_new_comments(changes.get("source", ""), session=session):
593                     session.delete(c)
594                 session.commit()
595
596         elif answer == 'S':
597             done = 1
598         elif answer == 'Q':
599             end()
600             sys.exit(0)
601
602 ################################################################################
603 ################################################################################
604 ################################################################################
605
606 def usage (exit_code=0):
607     print """Usage: dak process-new [OPTION]... [CHANGES]...
608   -a, --automatic           automatic run
609   -h, --help                show this help and exit.
610   -m, --manual-reject=MSG   manual reject with `msg'
611   -n, --no-action           don't do anything
612   -t, --trainee             FTP Trainee mode
613   -V, --version             display the version number and exit"""
614     sys.exit(exit_code)
615
616 ################################################################################
617
618 def do_byhand(upload, session):
619     done = 0
620     while not done:
621         files = upload.pkg.files
622         will_install = 1
623         byhand = []
624
625         for f in files.keys():
626             if files[f]["type"] == "byhand":
627                 if os.path.exists(f):
628                     print "W: %s still present; please process byhand components and try again." % (f)
629                     will_install = 0
630                 else:
631                     byhand.append(f)
632
633         answer = "XXXX"
634         if Options["No-Action"]:
635             answer = "S"
636         if will_install:
637             if Options["Automatic"] and not Options["No-Action"]:
638                 answer = 'A'
639             prompt = "[A]ccept, Manual reject, Skip, Quit ?"
640         else:
641             prompt = "Manual reject, [S]kip, Quit ?"
642
643         while prompt.find(answer) == -1:
644             answer = utils.our_raw_input(prompt)
645             m = re_default_answer.search(prompt)
646             if answer == "":
647                 answer = m.group(1)
648             answer = answer[:1].upper()
649
650         if answer == 'A':
651             try:
652                 check_daily_lock()
653                 done = 1
654                 for f in byhand:
655                     del files[f]
656                 Logger.log(["BYHAND ACCEPT: %s" % (upload.pkg.changes_file)])
657             except CantGetLockError:
658                 print "Hello? Operator! Give me the number for 911!"
659                 print "Dinstall in the locked area, cant process packages, come back later"
660         elif answer == 'M':
661             Logger.log(["BYHAND REJECT: %s" % (upload.pkg.changes_file)])
662             upload.do_reject(manual=1, reject_message=Options["Manual-Reject"])
663             upload.pkg.remove_known_changes(session=session)
664             session.commit()
665             done = 1
666         elif answer == 'S':
667             done = 1
668         elif answer == 'Q':
669             end()
670             sys.exit(0)
671
672 ################################################################################
673
674 def check_daily_lock():
675     """
676     Raises CantGetLockError if the dinstall daily.lock exists.
677     """
678
679     cnf = Config()
680     try:
681         os.open(cnf["Process-New::DinstallLockFile"],
682                 os.O_RDONLY | os.O_CREAT | os.O_EXCL)
683     except OSError, e:
684         if e.errno == errno.EEXIST or e.errno == errno.EACCES:
685             raise CantGetLockError
686
687     os.unlink(cnf["Process-New::DinstallLockFile"])
688
689
690 @contextlib.contextmanager
691 def lock_package(package):
692     """
693     Lock C{package} so that noone else jumps in processing it.
694
695     @type package: string
696     @param package: source package name to lock
697     """
698
699     path = os.path.join(Config()["Process-New::LockDir"], package)
700     try:
701         fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
702     except OSError, e:
703         if e.errno == errno.EEXIST or e.errno == errno.EACCES:
704             user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
705             raise AlreadyLockedError, user
706
707     try:
708         yield fd
709     finally:
710         os.unlink(path)
711
712 class clean_holding(object):
713     def __init__(self,pkg):
714         self.pkg = pkg
715
716     def __enter__(self):
717         pass
718
719     def __exit__(self, type, value, traceback):
720         h = Holding()
721
722         for f in self.pkg.files.keys():
723             if os.path.exists(os.path.join(h.holding_dir, f)):
724                 os.unlink(os.path.join(h.holding_dir, f))
725
726
727 def do_pkg(changes_file, session):
728     new_queue = get_policy_queue('new', session );
729     u = Upload()
730     u.pkg.changes_file = changes_file
731     (u.pkg.changes["fingerprint"], rejects) = utils.check_signature(changes_file)
732     u.load_changes(changes_file)
733     u.pkg.directory = new_queue.path
734     u.update_subst()
735     u.logger = Logger
736     origchanges = os.path.abspath(u.pkg.changes_file)
737
738     cnf = Config()
739     bcc = "X-DAK: dak process-new"
740     if cnf.has_key("Dinstall::Bcc"):
741         u.Subst["__BCC__"] = bcc + "\nBcc: %s" % (cnf["Dinstall::Bcc"])
742     else:
743         u.Subst["__BCC__"] = bcc
744
745     files = u.pkg.files
746     for deb_filename, f in files.items():
747         if deb_filename.endswith(".udeb") or deb_filename.endswith(".deb"):
748             u.binary_file_checks(deb_filename, session)
749             u.check_binary_against_db(deb_filename, session)
750         else:
751             u.source_file_checks(deb_filename, session)
752             u.check_source_against_db(deb_filename, session)
753
754         u.pkg.changes["suite"] = copy.copy(u.pkg.changes["distribution"])
755
756     try:
757         with lock_package(u.pkg.changes["source"]):
758             with clean_holding(u.pkg):
759                 if not recheck(u, session):
760                     return
761
762                 # FIXME: This does need byhand checks added!
763                 new = determine_new(u.pkg.changes, files)
764                 if new:
765                     do_new(u, session)
766                 else:
767                     try:
768                         check_daily_lock()
769                         new_accept(u, Options["No-Action"], session)
770                     except CantGetLockError:
771                         print "Hello? Operator! Give me the number for 911!"
772                         print "Dinstall in the locked area, cant process packages, come back later"
773 #             (new, byhand) = check_status(files)
774 #             if new or byhand:
775 #                 if new:
776 #                     do_new(u, session)
777 #                 if byhand:
778 #                     do_byhand(u, session)
779 #                 (new, byhand) = check_status(files)
780
781 #             if not new and not byhand:
782 #                 try:
783 #                     check_daily_lock()
784 #                     do_accept(u)
785 #                 except CantGetLockError:
786 #                     print "Hello? Operator! Give me the number for 911!"
787 #                     print "Dinstall in the locked area, cant process packages, come back later"
788     except AlreadyLockedError, e:
789         print "Seems to be locked by %s already, skipping..." % (e)
790
791 ################################################################################
792
793 def end():
794     accept_count = SummaryStats().accept_count
795     accept_bytes = SummaryStats().accept_bytes
796
797     if accept_count:
798         sets = "set"
799         if accept_count > 1:
800             sets = "sets"
801         sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
802         Logger.log(["total",accept_count,accept_bytes])
803
804     if not Options["No-Action"] and not Options["Trainee"]:
805         Logger.close()
806
807 ################################################################################
808
809 def main():
810     global Options, Logger, Sections, Priorities
811
812     cnf = Config()
813     session = DBConn().session()
814
815     Arguments = [('a',"automatic","Process-New::Options::Automatic"),
816                  ('h',"help","Process-New::Options::Help"),
817                  ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
818                  ('t',"trainee","Process-New::Options::Trainee"),
819                  ('n',"no-action","Process-New::Options::No-Action")]
820
821     for i in ["automatic", "help", "manual-reject", "no-action", "version", "trainee"]:
822         if not cnf.has_key("Process-New::Options::%s" % (i)):
823             cnf["Process-New::Options::%s" % (i)] = ""
824
825     changes_files = apt_pkg.ParseCommandLine(cnf.Cnf,Arguments,sys.argv)
826     if len(changes_files) == 0:
827         new_queue = get_policy_queue('new', session );
828         changes_files = utils.get_changes_files(new_queue.path)
829
830     Options = cnf.SubTree("Process-New::Options")
831
832     if Options["Help"]:
833         usage()
834
835     if not Options["No-Action"]:
836         try:
837             Logger = daklog.Logger(cnf, "process-new")
838         except CantOpenError, e:
839             Options["Trainee"] = "True"
840
841     Sections = Section_Completer(session)
842     Priorities = Priority_Completer(session)
843     readline.parse_and_bind("tab: complete")
844
845     if len(changes_files) > 1:
846         sys.stderr.write("Sorting changes...\n")
847     changes_files = sort_changes(changes_files, session)
848
849     # Kill me now? **FIXME**
850     cnf["Dinstall::Options::No-Mail"] = ""
851
852     for changes_file in changes_files:
853         changes_file = utils.validate_changes_file_arg(changes_file, 0)
854         if not changes_file:
855             continue
856         print "\n" + changes_file
857
858         do_pkg (changes_file, session)
859
860     end()
861
862 ################################################################################
863
864 if __name__ == '__main__':
865     main()