# HG changeset patch # User Pat Downey # Date 1264092934 0 # Node ID 6e81c130aa29d0acbea59eaeea1864d3247b7ec6 # Parent 47849267b4d1f10342870d8e8d8e90653260e53b Add initial version of blacklist mercurial extension for use to exclude and manage known bad changesets from mercurial repositories. diff -r 47849267b4d1 -r 6e81c130aa29 hg_hooks/blacklist/COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hg_hooks/blacklist/COPYING Thu Jan 21 16:55:34 2010 +0000 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -r 47849267b4d1 -r 6e81c130aa29 hg_hooks/blacklist/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hg_hooks/blacklist/README Thu Jan 21 16:55:34 2010 +0000 @@ -0,0 +1,34 @@ +hg blacklist + +manage repository changeset blacklist + + This extension is used to manage a blacklist for the repository. + Can blacklist changesets by changeset id, and regular expressions against + the user field of a changeset and also a changesets file list. + + Current rules can be viewed using the [-l|--list] operation. + + Each modification to a blacklist is logged. These can be viewed using the + --auditlog operation. + + Each time a changeset is blocked/denied it's logged. These can be viewed + using the --blocklog operation. + + Types of changeset blacklist rules can be defined implicitly or explicitly: + + If a rule definition contains between 12 and 40 hexadecimal characters + it is assumed to be a rule matched against changeset id. Can be set + explicitly set with the -n flag to the --add operation. + + If a rule definition contains a '@' it is assumed to be a rule matched + against a changeset's user property. Can be set explicitly with + the -u flag to the --add operation. + + Otherwise the rule is assumed to be matched against a changeset's file + list. Can be set explicitly with the -f flag to the --add operation. + + When this extension is enabled a hook is also added to the + 'pretxnchangegroup' action that will block any incoming changesets + (via pull/push/unbundle) if they are blacklisted. + It won't block any local commits. + diff -r 47849267b4d1 -r 6e81c130aa29 hg_hooks/blacklist/blacklist.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hg_hooks/blacklist/blacklist.py Thu Jan 21 16:55:34 2010 +0000 @@ -0,0 +1,542 @@ +#!/usr/bin/env python +# +# Copyright (C) 2010 Mozilla Foundation +# Copyright (C) 2010 Symbian Foundation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Initial Contributors: +# Pat Downey +# +# Contributors: +# Your name here? +# +# Description: +# +# An extension to mercurial that adds the ability to specify a blacklist for +# a repository. That is to deny a changeset from being pushed/pulled/unbundled +# if it matches one of a set of patterns. +# +# At present it can deny nodes based on their changeset id, a regular expression +# matched against the user field, or a regular expression matched against the +# changeset's file list. +# +# Note: With the regular expression rules, if you want to match a string anywhere +# with in a string, e.g. create a rule against files within directories called +# 'internal' the rule would need to be ..*/internal/.*'. That is you need to be +# explicit in specifying a set of any characters otherwise it will perform a +# direct string comparison. # +# +# Requires sqlite extension (included in python 2.5 onwards) +# * Available for python 2.4 in python-sqlite2 package on RHEL5.2+ +# +# Ideas for implementation came strongly from: +# http://hg.mozilla.org/users/bsmedberg_mozilla.com/hghooks/file/tip/mozhghooks/pushlog.py +# +# + +'''manage repository changeset blacklist + +''' + +from mercurial import demandimport + +demandimport.disable() +try: + import sqlite3 as sqlite +except ImportError: + from pysqlite2 import dbapi2 as sqlite +demandimport.enable() + +import binascii +import os +import os.path +import re +import stat +import sys +import time +from datetime import datetime + + +# changeset identifier 12-40 hex chars +NODE_RE='^[0-9|a-f]{12,40}' + + + + +def blacklist(ui,repo,*args,**opts): + '''manage repository changeset blacklist + + This extension is used to manage a blacklist for the repository. + Can blacklist changesets by changeset id, and regular expressions against + the user field of a changeset and also a changesets file list. + + Current rules can be viewed using the [-l|--list] operation. + + Each modification to a blacklist is logged. These can be viewed using the + --auditlog operation. + + Each time a changeset is blocked/denied it's logged. These can be viewed + using the --blocklog operation. + + Types of changeset blacklist rules can be defined implicitly or explicitly: + + If a rule definition contains between 12 and 40 hexadecimal characters + it is assumed to be a rule matched against changeset id. Can be set + explicitly set with the -n flag to the --add operation. + + If a rule definition contains a '@' it is assumed to be a rule matched + against a changeset's user property. Can be set explicitly with + the -u flag to the --add operation. + + Otherwise the rule is assumed to be matched against a changeset's file + list. Can be set explicitly with the -f flag to the --add operation. + + When this extension is enabled a hook is also added to the + 'pretxnchangegroup' action that will block any incoming changesets + (via pull/push/unbundle) if they are blacklisted. + It won't block any local commits. + ''' + conn = openconn(ui, repo ) + if 'list' in opts and opts['list'] : + listblacklistrule(ui,conn,args,opts) + elif 'blocklog' in opts and opts['blocklog'] : + listblacklistblocklog(ui,conn,args,opts) + elif 'auditlog' in opts and opts['auditlog'] : + listblacklistauditlog(ui,conn,args,opts) + elif 'enable' in opts and opts['enable'] : + enableblacklistrule(ui,conn,args,opts) + elif 'disable' in opts and opts['disable'] : + disableblacklistrule(ui,conn,args,opts) + elif 'remove' in opts and opts['remove'] : + removeblacklistrule(ui,conn,args,opts) + elif 'add' in opts and opts['add'] : + addblacklistrule(ui,conn,args,opts) + else : + ui.warn( 'invalid operation specified\n' ) + + conn.close( ) + +####### Database setup methods +# this part derived from mozilla's pushlog.py hook +def openconn(ui,repo): + blacklistdb = os.path.join(repo.path, 'blacklist.db') + createdb = False + if not os.path.exists(blacklistdb): + createdb = True + conn = sqlite.connect(blacklistdb) + if not createdb and not schemaexists(conn): + createdb = True + if createdb: + createblacklistdb(ui,conn) + st = os.stat(blacklistdb) + os.chmod(blacklistdb, st.st_mode | stat.S_IWGRP) + + return conn + +# Derived from mozilla's pushlog hook +def schemaexists(conn): + return 3 == conn.execute("SELECT COUNT(*) FROM SQLITE_MASTER WHERE name IN ( ?, ?, ?)" , ['blacklist_rule','blacklist_auditlog','blacklist_blocklog']).fetchone()[0] + +# Derived from mozilla's pushlog hook +def createblacklistdb(ui,conn): + # record of different blacklist rule, type should be either 'node' or 'file' or 'user' + # 'node' - compare pattern with changeset identifier + # 'file' - used as regular expression against changeset file manifest + # 'user' - used as regular expression against changeset author/user + # (id,pattern,type,enabled) + conn.execute("CREATE TABLE IF NOT EXISTS blacklist_rule (id INTEGER PRIMARY KEY AUTOINCREMENT, pattern TEXT, type TEXT, enabled INTEGER,comment TEXT)") + conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS blacklist_rule_idx ON blacklist_rule (pattern,type)" ) + + # records additions and modifications to the blacklist_rule table + # (id, operation, rule_id, user, date, comment) + conn.execute("CREATE TABLE IF NOT EXISTS blacklist_auditlog (id INTEGER PRIMARY KEY AUTOINCREMENT, operation TEXT, rule_id INTEGER, user TEXT, date INTEGER, comment TEXT)") + conn.execute("CREATE INDEX IF NOT EXISTS blacklist_auditlog_rule_idx ON blacklist_auditlog (rule_id)" ) + + # log attempted pushes and the http users trying to push a blocked changeset + # (id,rule_id,cset_id, cset_user, cset_desc, user,date) + conn.execute("CREATE TABLE IF NOT EXISTS blacklist_blocklog (id INTEGER PRIMARY KEY AUTOINCREMENT, rule_id INTEGER, cset_id TEXT, cset_user TEXT, cset_desc TEXT, user TEXT, date INTEGER)") + conn.execute("CREATE INDEX IF NOT EXISTS blacklist_blocklog_rule_idx ON blacklist_blocklog (rule_id)" ) + + conn.commit() + + +# Methods for extension commands +def __getblacklistruletype( ui, pattern, opts ): + type=None + + if opts['nodeType'] : + type = 'node' + elif opts['fileType'] : + type = 'file' + elif opts['userType'] : + type = 'user' + + # try and work out type of blacklist if none specified + # default to regexp + if type == None : + if re.match( NODE_RE, pattern ) : + type = 'node' + elif '@' in pattern : + type = 'user' + else : + type = 'file' + ui.note( 'type implicitly set to \'%s\'\n' % type ) + + return type + +def addblacklistrule(ui,conn,args,opts): + ret = 1 + if len(args) == 1 : + createrule = True + pattern = args[0] + + type = __getblacklistruletype( ui, pattern, opts ) + + if type == 'node' : + # if pattern has been specified as a node type + # check that pattern is a valid node + if not re.match( NODE_RE, pattern ) : + ui.warn( 'node should be 12 or 40 characters.\n' ) + createrule = False + + if createrule : + comment = None + + if 'desc' in opts and opts['desc'] : + if opts['desc'] != '' : + comment = opts['desc'] + + insertblacklistrule(ui,conn,pattern,type, comment=comment) + else : + ui.warn( 'missing pattern argument.\n' ) + + return ret + +def removeblacklistrule(ui,conn,args,opts): + if len(args) == 1 : + deleteblacklistrule(ui,conn,args[0]) + else : + ui.warn( 'rule id argument required.\n' ) + + return 0 + +def disableblacklistrule(ui,conn,args,opts): + if len(args) == 1 : + updateblacklistrule(ui,conn,args[0],False) + else : + ui.warn( 'rule id argument required.\n' ) + + return 0 + +def enableblacklistrule(ui,conn,args,opts): + if len(args) == 1 : + updateblacklistrule(ui,conn,args[0],True) + else : + ui.warn( 'rule id argument required.\n' ) + + return 0 + +def listblacklistrule(ui,conn,args,opts): + if len(args) in (0,1) : + res = selectblacklistrule(ui,conn,args) + + printblacklist(ui,res) + else : + ui.warn( 'too many arguments.\n' ) + + return 0 + +def listblacklistauditlog(ui,conn,args,opts): + if len(args) in (0,1) : + res = selectblacklistauditlog(ui, conn, args ) + + printauditlog(ui,res) + else : + ui.warn( 'too many arguments.\n' ) + + return 0 + +def listblacklistblocklog(ui,conn,args,opts): + if len(args) in (0,1) : + res = selectblacklistblocklog(ui, conn, args ) + + printblocklog(ui,res) + else : + ui.warn( 'too many arguments.\n' ) + + return 0 + +def insertblacklistaudit(ui, conn, operation, rule_id, comment=None ): + user = __getenvuser( ) + audit_date = int(time.time()) + + audit_sql = 'INSERT INTO blacklist_auditlog ( operation, rule_id, user, date, comment ) VALUES ( ?, ?, ?, ?, ? )' + conn.execute( audit_sql, (operation, rule_id, user, audit_date, comment ) ) + +def insertblacklistrule(ui, conn, pattern, type, enabled=True, comment=None): + rule_sql = 'INSERT INTO blacklist_rule ( pattern, type, enabled,comment ) VALUES ( ?, ?, ?, ? )' + + res = conn.execute( rule_sql, (pattern,type,enabled,comment) ) + rule_id= res.lastrowid + + insertblacklistaudit(ui, conn, 'add', rule_id,comment=comment ) + + conn.commit( ) + +def __getenvuser( ): + # look at REMOTE_USER first + # then look at LOGUSER + # then look at USER + for e in ['REMOTE_USER','SUDO_USER','LOGUSER','USER'] : + if e in os.environ : + user = '%s:%s' %( e, os.environ.get( e ) ) + break + + return user + +def insertblacklistblocklog( ui, conn, rule, ctx ): + # (id, rule_id, user, date) + rule_id=rule[0] + + log_user = __getenvuser( ) + audit_date = int(time.time()) + + ctx_node = binascii.hexlify(ctx.node()) + ctx_user = ctx.user() + ctx_desc = ctx.description() + + log_sql = 'INSERT INTO blacklist_blocklog (rule_id,user,date,cset_id,cset_user,cset_desc) VALUES (?,?,?,?,?,?)' + conn.execute( log_sql, (rule_id,log_user,audit_date, ctx_node, ctx_user, ctx_desc)) + conn.commit() + +def updateblacklistrule(ui, conn, rule_id, enabled): + rule_sql = 'UPDATE blacklist_rule SET enabled=? WHERE id=?' + + conn.execute( rule_sql, [enabled, rule_id] ) + + insertblacklistaudit(ui, conn, 'update', rule_id, 'enabled=%s' % enabled ) + + conn.commit( ) + +def deleteblacklistrule(ui, conn, rule_id ): + if rule_id != None : + res = selectblacklistrule(ui, conn, rule_id ) + processed = False + for (id,pattern,type,enabled,comment) in res : + comment = 'deleted: pattern=%s, type=%s, enabled=%s' % (pattern, type, enabled) + + rule_sql = 'DELETE FROM blacklist_rule WHERE id=?' + conn.execute( rule_sql, [rule_id] ) + + insertblacklistaudit(ui, conn, 'delete', rule_id, comment) + + conn.commit( ) + processed = True + + if not processed : + ui.warn( 'no matching blacklist rule found with id %s\n' % rule_id ) + else : + ui.warn( 'no rule id specified\n' ) + +def selectblacklistrule(ui, conn, rule_id ): + # (id, operation, rule_id, user, date, comment) + if rule_id : + rule_sql = 'SELECT id,pattern,type,enabled,comment FROM blacklist_rule WHERE id=? ORDER BY id ASC' + res = conn.execute( rule_sql, rule_id ) + else : + rule_sql = 'SELECT id,pattern,type,enabled,comment FROM blacklist_rule ORDER BY id ASC' + res = conn.execute( rule_sql ) + + return res + +def selectblacklistauditlog(ui,conn,rule_id=None) : + # (id, operation, rule_id, user, date, comment) + if rule_id : + rule_sql = 'SELECT id,operation,rule_id,user,date,comment FROM blacklist_auditlog WHERE rule_id=? ORDER BY date ASC' + res = conn.execute( rule_sql, rule_id ) + else : + rule_sql = 'SELECT id,operation,rule_id,user,date,comment FROM blacklist_auditlog ORDER BY date ASC' + res = conn.execute( rule_sql ) + + return res + +def selectblacklistblocklog(ui,conn,rule_id=None) : + # (id, rule_id, node, user, date) + if rule_id : + rule_sql = 'SELECT id,rule_id,cset_id,cset_user,cset_desc,user,date FROM blacklist_blocklog WHERE rule_id=? ORDER BY date ASC' + res = conn.execute( rule_sql, rule_id ) + else : + rule_sql = 'SELECT id,rule_id,cset_id,cset_user,cset_desc,user,date FROM blacklist_blocklog ORDER BY date ASC' + res = conn.execute( rule_sql ) + + return res + +def printblacklist(ui,res): + for r in res : + (id,pattern,type,enabled,comment) = r + + if enabled == 1 : + enabled = True + elif enabled == 0 : + enabled = False + + ui.write( 'rule: %d:%s\n' % (id,type) ) + ui.write( 'pattern: %s\n' % pattern ) + ui.write( 'enabled: %s\n' % enabled ) + if comment : + ui.write( 'comment: %s\n' % comment ) + ui.write( '\n' ) + +def printauditlog(ui,res): + for r in res : + (id, operation, rule_id, user, date, comment) = r + + date = datetime.utcfromtimestamp(date).isoformat() + + if not comment : + comment = '' + + ui.write( 'date: %s\n' % date ) + ui.write( 'operation: %s\n' % operation ) + ui.write( 'user: %s\n' % user ) + ui.write( 'rule: %s\n' % rule_id ) + ui.write( 'comment: %s\n' % comment ) + ui.write( '\n' ) + +def printblocklog(ui,res): + for r in res : + (id, rule_id, cset_id, cset_user, cset_desc, user, date) = r + + date = datetime.utcfromtimestamp(date).isoformat() + + ui.write( 'cset: %s\n' % cset_id ) + ui.write( 'cset-user: %s\n' % cset_user) + ui.write( 'cset-desc: %s\n' % cset_desc ) + ui.write( 'rule: %s\n' % rule_id ) + ui.write( 'date: %s\n' % date ) + ui.write( 'user: %s\n' % user ) + + ui.write( '\n' ) + + +# Hook specific functions follow + +def excludecsetbyfile(ctx,pattern): + exclude = False + + file_re = re.compile( '%s' % pattern, re.I ) + for f in ctx.files() : + if file_re.match( f ) : + exclude = True + break + + return exclude + +def excludecsetbynode(ctx,pattern): + exclude = False + + node = binascii.hexlify(ctx.node()) + + if node.startswith( pattern ) : + exclude = True + + return exclude + +def excludecsetbyuser(ctx,pattern): + exclude = False + userStr = ctx.user() + + user_re = re.compile( '^.*%s.*$' % pattern, re.I ) + if user_re.match( userStr ) : + exclude = True + + return exclude + +def excludeblacklistcset(ui,conn,ctx): + + bl_sql = 'SELECT id,pattern,type FROM blacklist_rule WHERE enabled=1' + res = conn.execute( bl_sql ) + + (exclude,rule) = (False,None) + + for (id,pattern,type) in res : + if type == 'node' : + exclude = excludecsetbynode(ctx,pattern) + elif type == 'user' : + exclude = excludecsetbyuser(ctx,pattern) + elif type == 'file' : + exclude = excludecsetbyfile(ctx,pattern) + else : + ui.warn('unrecognised rule type \'%s\'' % type ) + + if exclude : + rule = (id,pattern,type) + break + + return (exclude,rule) + +# The hook method that is used to block bad changesets from being introduced +# to the current repository +def pretxnchangegroup(ui,repo,hooktype,node,**args): + start = repo[node].rev() + end = len(repo) + + conn = openconn(ui, repo ) + + blocked = False + + for rev in xrange(start, end): + ctx = repo[rev] + (blocked,rule) = excludeblacklistcset( ui, conn, ctx) + + if blocked : + insertblacklistblocklog( ui, conn, rule, ctx ) + (id,pattern,type) = rule + ui.write( 'blocked: cset %s in changegroup blocked by blacklist\n' % str(ctx) ) + ui.write( 'blocked-reason: %s matched against \'%s\'\n' % ( type,pattern )) + break + + conn.close( ) + + return blocked + +def setupblacklisthook(ui): + ui.setconfig('hooks', 'pretxnchangegroup.blacklist', pretxnchangegroup) + +def reposetup(ui,repo): + # print 'in blacklist reposetup' + setupblacklisthook( ui ) + +def uisetup(ui): + # print 'in blacklist uisetup' + setupblacklisthook( ui ) + +cmdtable = { + 'blacklist': (blacklist, + [ + ('l','list',None,'list blacklist entries'), + ('','blocklog',None,'list blocked changesets'), + ('','auditlog',None,'show audit log for blacklist'), + ('a','add',None,'add node to blacklist'), + ('d','disable',None,'disable blacklist rule'), + ('e','enable',None,'enable blacklist rule'), + ('r','remove',None,'remove node from blacklist'), + ('n','nodeType',False,'parse argument as node to blacklist'), + ('f','fileType',False,'parse argument as file path to blacklist'), + ('u','userType',False,'parse argument as user regexp to blacklist'), + ('','desc','','comment to attach to rule')], + "") + }