add sysctl configuration to remove every debugging capabilities
[freebsd-maintainer-scripts/.git] / getpatch
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Copyright (c) 2012 Sofian Brabez <sbz@FreeBSD.org>
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #
27 # $FreeBSD: head/Tools/scripts/getpatch 362667 2014-07-23 12:14:32Z sbz $
28 #
29 # MAINTAINER=   sbz@FreeBSD.org
30
31 import argparse
32 import codecs
33 import os
34 import re
35 import ssl
36 import sys
37 if sys.version_info.major == 3:
38     import urllib.request as urllib2
39 else:
40     import urllib2
41
42 """
43 FreeBSD getpatch handles Gnats and Bugzilla patch attachments
44 """
45
46
47 def create_ssl_context(cafile):
48     if os.path.exists(cafile):
49         return ssl.create_default_context(cafile=cafile)
50     else:
51         return ssl._create_unverified_context()
52
53
54 class GetPatch(object):
55
56     def __init__(self, pr, category='ports'):
57         self.pr = pr
58         self.category = category
59         self.patchs = []
60         self.url = ""
61         self.patch = ""
62         self.output_stdout = False
63         self.default_locale = sys.getdefaultencoding()
64         self.ssl_context = create_ssl_context('/usr/local/etc/ssl/cert.pem')
65
66     def fetch(self, *largs, **kwargs):
67         raise NotImplementedError()
68
69     def write(self, filename, data):
70         if filename.endswith(('.patch', '.txt')):
71             filename = filename[:filename.rindex('.')]+'.diff'
72         f = codecs.open(filename, encoding=self.default_locale, mode='w')
73         f.write(data.decode(self.default_locale))
74         f.close()
75         self.out("[+] %s created" % filename)
76
77     def get(self, only_last=False, output_stdout=False):
78         self.output_stdout = output_stdout
79         self.fetch(self.pr, category=self.category)
80
81         if len(self.patchs) == 0:
82             self.out("[-] No patch found")
83             sys.exit(1)
84
85         if only_last:
86             self.patchs = [self.patchs.pop()]
87
88         for patch in self.patchs:
89             url = patch['url']
90             p = patch['name']
91
92             data = urllib2.urlopen(url, context=self.ssl_context).read()
93
94             if self.output_stdout:
95                 sys.stdout.write(data.decode(self.default_locale))
96             else:
97                 self.write(p, data)
98
99     def add_patch(self, url, name):
100         self.patchs.append({'url': url, 'name': name})
101
102     def out(self, s):
103         if not self.output_stdout:
104             print(s)
105
106
107 class GnatsGetPatch(GetPatch):
108
109     URL_BASE = 'https://www.freebsd.org/cgi'
110     URL = '%s/query-pr.cgi?pr=' % URL_BASE
111     REGEX = r'<b>Download <a href="([^"]*)">([^<]*)</a>'
112
113     def __init__(self, pr, category):
114         GetPatch.__init__(self, pr, category)
115
116     def fetch(self, *largs, **kwargs):
117         category = kwargs['category']
118         target = ("%s/%s" % (category, self.pr), "%s" % self.pr)[category == '']
119         self.out("[+] Fetching patch for pr %s" % target)
120         pattern = re.compile(self.REGEX)
121         u = urllib2.urlopen(self.URL+'%s' % target, context=self.ssl_context)
122         data = u.read()
123         if data is None:
124             self.out("[-] No patch found")
125             sys.exit(1)
126
127         for patchs in re.findall(pattern, str(data)):
128             self.add_patch(patchs[0], patchs[1])
129
130
131 class BzGetPatch(GetPatch):
132
133     URL_BASE = 'https://bugs.freebsd.org/bugzilla/'
134     URL_SHOW = '%s/show_bug.cgi?id=' % URL_BASE
135     REGEX_URL = r'<a href="([^<]+)">Details</a>'
136     REGEX = r'<div class="details">([^ ]+) \(text/plain(?:; charset=[-\w]+)?\)'
137
138     def __init__(self, pr, category):
139         GetPatch.__init__(self, pr, category)
140
141     def _get_patch_name(self, url):
142         match = re.search(self.REGEX, urllib2.urlopen(url, context=self.ssl_context).read())
143         if match is None:
144             return None
145         return match.group(1)
146
147     def _get_patch_urls(self, data):
148         patch_urls = {}
149         for url in re.findall(self.REGEX_URL, data):
150             url = '%s/%s' % (self.URL_BASE, url)
151             file_name = self._get_patch_name(url)
152             if file_name is None:
153                 self.out("[-] Could not determine the patch file name in %s. "
154                          "Skipping." % url)
155                 continue
156             download_url = url[:url.find('&')]
157             patch_urls[download_url] = file_name
158         return patch_urls
159
160     def fetch(self, *largs, **kwargs):
161         category = kwargs['category']
162         self.out("[+] Fetching patch for pr %s/%s" % (category, self.pr))
163         u = urllib2.urlopen(self.URL_SHOW+'%s' % self.pr, context=self.ssl_context)
164         data = u.read()
165
166         if data is None:
167             self.out("[-] No patch found")
168             sys.exit(1)
169
170         patch_urls = self._get_patch_urls(data)
171         if not patch_urls:
172             self.out("[-] No patch found")
173             sys.exit(1)
174
175         for url, file_name in patch_urls.iteritems():
176             self.add_patch(url, file_name)
177
178
179 def main():
180
181     parser = argparse.ArgumentParser(
182             description='Gets patch attachments from a Bug Tracking System'
183     )
184     parser.add_argument('pr', metavar='pr', type=str, nargs=1,
185             help='Pr id number')
186     parser.add_argument('--mode', type=str, choices=['gnats', 'bz'],
187             default='bz', help='available modes to retrieve patch')
188     parser.add_argument('--last', action='store_true',
189             help='only retrieve the latest iteration of a patch')
190     parser.add_argument('--stdout', action='store_true',
191             help='dump patch on stdout')
192
193     if len(sys.argv) == 1:
194         parser.print_help()
195         sys.exit(1)
196
197     args = parser.parse_args()
198
199     category = ""
200     pr = str(args.pr[0])
201
202     if pr and '/' in pr:
203         category, pr = pr.split('/')
204
205     Clazz = globals()['%sGetPatch' % args.mode.capitalize()]
206     gp = Clazz(pr, category)
207     gp.get(only_last=args.last, output_stdout=args.stdout)
208
209 if __name__ == '__main__':
210     main()