##// END OF EJS Templates
scripts: introduce source_format.py to fix up the module name in file headers
Mads Kiilerich -
r8549:f8971422 default
parent child Browse files
Show More
@@ -0,0 +1,24 b''
1 #!/usr/bin/env python3
2
3 # hg files 'set:!binary()&grep("^#!.*python")' 'set:**.py' | xargs scripts/source_format.py
4
5 import re
6 import sys
7
8
9 filenames = sys.argv[1:]
10
11 for fn in filenames:
12 with open(fn) as f:
13 org_content = f.read()
14
15 mod_name = fn[:-3] if fn.endswith('.py') else fn
16 mod_name = mod_name[:-9] if mod_name.endswith('/__init__') else mod_name
17 mod_name = mod_name.replace('/', '.')
18 def f(m):
19 return '"""\n%s\n%s\n' % (mod_name, '~' * len(mod_name))
20 new_content = re.sub(r'^"""\n(kallithea\..*\n)(~+\n)?', f, org_content, count=1, flags=re.MULTILINE)
21
22 if new_content != org_content:
23 with open(fn, 'w') as f:
24 f.write(new_content)
@@ -1,191 +1,191 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.bin.vcs_hooks
16 ~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Entry points for Kallithea hooking into Mercurial and Git.
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Aug 6, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import os
29 29 import sys
30 30
31 31 import mercurial.scmutil
32 32 import paste.deploy
33 33
34 34 import kallithea
35 35 import kallithea.config.application
36 36 from kallithea.lib import hooks, webutils
37 37 from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str
38 38 from kallithea.lib.vcs.backends.base import EmptyChangeset
39 39 from kallithea.lib.vcs.utils.helpers import get_scm_size
40 40 from kallithea.model import db
41 41
42 42
43 43 def repo_size(ui, repo, hooktype=None, **kwargs):
44 44 """Show size of Mercurial repository.
45 45
46 46 Called as Mercurial hook changegroup.repo_size after push.
47 47 """
48 48 size_hg, size_root = get_scm_size('.hg', safe_str(repo.root))
49 49
50 50 last_cs = repo[len(repo) - 1]
51 51
52 52 msg = ('Repository size .hg: %s Checkout: %s Total: %s\n'
53 53 'Last revision is now r%s:%s\n') % (
54 54 webutils.format_byte_size(size_hg),
55 55 webutils.format_byte_size(size_root),
56 56 webutils.format_byte_size(size_hg + size_root),
57 57 last_cs.rev(),
58 58 ascii_str(last_cs.hex())[:12],
59 59 )
60 60 ui.status(safe_bytes(msg))
61 61
62 62
63 63 def pull_action(ui, repo, **kwargs):
64 64 """Logs user pull action
65 65
66 66 Called as Mercurial hook outgoing.kallithea_pull_action.
67 67 """
68 68 hooks.log_pull_action()
69 69
70 70
71 71 def push_action(ui, repo, node, node_last, **kwargs):
72 72 """
73 73 Register that changes have been added to the repo - log the action *and* invalidate caches.
74 74 Note: This hook is not only logging, but also the side effect invalidating
75 75 caches! The function should perhaps be renamed.
76 76
77 77 Called as Mercurial hook changegroup.kallithea_push_action .
78 78
79 79 The pushed changesets is given by the revset 'node:node_last'.
80 80 """
81 81 revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
82 82 hooks.process_pushed_raw_ids(revs)
83 83
84 84
85 85 def _git_hook_environment(repo_path):
86 86 """
87 87 Create a light-weight environment for stand-alone scripts and return an UI and the
88 88 db repository.
89 89
90 90 Git hooks are executed as subprocess of Git while Kallithea is waiting, and
91 91 they thus need enough info to be able to create an app environment and
92 92 connect to the database.
93 93 """
94 94 extras = get_hook_environment()
95 95
96 96 path_to_ini_file = extras['config']
97 97 config = paste.deploy.appconfig('config:' + path_to_ini_file)
98 98 #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
99 99 kallithea.config.application.make_app(config.global_conf, **config.local_conf)
100 100
101 101 # fix if it's not a bare repo
102 102 if repo_path.endswith(os.sep + '.git'):
103 103 repo_path = repo_path[:-5]
104 104
105 105 repo = db.Repository.get_by_full_path(repo_path)
106 106 if not repo:
107 107 raise OSError('Repository %s not found in database' % repo_path)
108 108
109 109 return repo
110 110
111 111
112 112 def pre_receive(repo_path, git_stdin_lines):
113 113 """Called from Git pre-receive hook.
114 114 The returned value is used as hook exit code and must be 0.
115 115 """
116 116 # Currently unused. TODO: remove?
117 117 return 0
118 118
119 119
120 120 def post_receive(repo_path, git_stdin_lines):
121 121 """Called from Git post-receive hook.
122 122 The returned value is used as hook exit code and must be 0.
123 123 """
124 124 try:
125 125 repo = _git_hook_environment(repo_path)
126 126 except HookEnvironmentError as e:
127 127 sys.stderr.write("Skipping Kallithea Git post-receive hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
128 128 return 0
129 129
130 130 # the post push hook should never use the cached instance
131 131 scm_repo = repo.scm_instance_no_cache()
132 132
133 133 rev_data = []
134 134 for l in git_stdin_lines:
135 135 old_rev, new_rev, ref = l.strip().split(' ')
136 136 _ref_data = ref.split('/')
137 137 if _ref_data[1] in ['tags', 'heads']:
138 138 rev_data.append({'old_rev': old_rev,
139 139 'new_rev': new_rev,
140 140 'ref': ref,
141 141 'type': _ref_data[1],
142 142 'name': '/'.join(_ref_data[2:])})
143 143
144 144 git_revs = []
145 145 for push_ref in rev_data:
146 146 _type = push_ref['type']
147 147 if _type == 'heads':
148 148 if push_ref['old_rev'] == EmptyChangeset().raw_id:
149 149 # update the symbolic ref if we push new repo
150 150 if scm_repo.is_empty():
151 151 scm_repo._repo.refs.set_symbolic_ref(
152 152 b'HEAD',
153 153 b'refs/heads/%s' % safe_bytes(push_ref['name']))
154 154
155 155 # build exclude list without the ref
156 156 cmd = ['for-each-ref', '--format=%(refname)', 'refs/heads/*']
157 157 stdout = scm_repo.run_git_command(cmd)
158 158 ref = push_ref['ref']
159 159 heads = [head for head in stdout.splitlines() if head != ref]
160 160 # now list the git revs while excluding from the list
161 161 cmd = ['log', push_ref['new_rev'], '--reverse', '--pretty=format:%H']
162 162 cmd.append('--not')
163 163 cmd.extend(heads) # empty list is ok
164 164 stdout = scm_repo.run_git_command(cmd)
165 165 git_revs += stdout.splitlines()
166 166
167 167 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
168 168 # delete branch case
169 169 git_revs += ['delete_branch=>%s' % push_ref['name']]
170 170 else:
171 171 cmd = ['log', '%(old_rev)s..%(new_rev)s' % push_ref,
172 172 '--reverse', '--pretty=format:%H']
173 173 stdout = scm_repo.run_git_command(cmd)
174 174 git_revs += stdout.splitlines()
175 175
176 176 elif _type == 'tags':
177 177 git_revs += ['tag=>%s' % push_ref['name']]
178 178
179 179 hooks.process_pushed_raw_ids(git_revs)
180 180
181 181 return 0
182 182
183 183
184 184 # Almost exactly like Mercurial contrib/hg-ssh:
185 185 def rejectpush(ui, **kwargs):
186 186 """Mercurial hook to be installed as pretxnopen and prepushkey for read-only repos.
187 187 Return value 1 will make the hook fail and reject the push.
188 188 """
189 189 ex = get_hook_environment()
190 190 ui.warn(safe_bytes("Push access to %r denied\n" % ex.repository))
191 191 return 1
@@ -1,73 +1,73 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.https_fixup
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.https_fixup
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 middleware to handle https correctly
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: May 23, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28
29 29 from kallithea.lib.utils2 import asbool
30 30
31 31
32 32 class HttpsFixup(object):
33 33
34 34 def __init__(self, app, config):
35 35 self.application = app
36 36 self.config = config
37 37
38 38 def __call__(self, environ, start_response):
39 39 self.__fixup(environ)
40 40 debug = asbool(self.config.get('debug'))
41 41 is_ssl = environ['wsgi.url_scheme'] == 'https'
42 42
43 43 def custom_start_response(status, headers, exc_info=None):
44 44 if is_ssl and asbool(self.config.get('use_htsts')) and not debug:
45 45 headers.append(('Strict-Transport-Security',
46 46 'max-age=8640000; includeSubDomains'))
47 47 return start_response(status, headers, exc_info)
48 48
49 49 return self.application(environ, custom_start_response)
50 50
51 51 def __fixup(self, environ):
52 52 """
53 53 Function to fixup the environ as needed. In order to use this
54 54 middleware you should set this header inside your
55 55 proxy ie. nginx, apache etc.
56 56 """
57 57 # DETECT PROTOCOL !
58 58 if 'HTTP_X_URL_SCHEME' in environ:
59 59 proto = environ.get('HTTP_X_URL_SCHEME')
60 60 elif 'HTTP_X_FORWARDED_SCHEME' in environ:
61 61 proto = environ.get('HTTP_X_FORWARDED_SCHEME')
62 62 elif 'HTTP_X_FORWARDED_PROTO' in environ:
63 63 proto = environ.get('HTTP_X_FORWARDED_PROTO')
64 64 else:
65 65 proto = 'http'
66 66 org_proto = proto
67 67
68 68 # if we have force, just override
69 69 if asbool(self.config.get('force_https')):
70 70 proto = 'https'
71 71
72 72 environ['wsgi.url_scheme'] = proto
73 73 environ['wsgi._org_proto'] = org_proto
@@ -1,41 +1,41 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.permanent_repo_url
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.permanent_repo_url
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 middleware to handle permanent repo URLs, replacing PATH_INFO '/_123/yada' with
19 19 '/name/of/repo/yada' after looking 123 up in the database.
20 20 """
21 21
22 22
23 23 from kallithea.lib.utils import fix_repo_id_name
24 24 from kallithea.lib.utils2 import safe_bytes, safe_str
25 25
26 26
27 27 class PermanentRepoUrl(object):
28 28
29 29 def __init__(self, app, config):
30 30 self.application = app
31 31 self.config = config
32 32
33 33 def __call__(self, environ, start_response):
34 34 # Extract path_info as get_path_info does, but do it explicitly because
35 35 # we also have to do the reverse operation when patching it back in
36 36 path_info = safe_str(environ['PATH_INFO'].encode('latin1'))
37 37 if path_info.startswith('/'): # it must
38 38 path_info = '/' + fix_repo_id_name(path_info[1:])
39 39 environ['PATH_INFO'] = safe_bytes(path_info).decode('latin1')
40 40
41 41 return self.application(environ, start_response)
@@ -1,227 +1,227 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.pygrack
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.pygrack
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Python implementation of git-http-backend's Smart HTTP protocol
19 19
20 20 Based on original code from git_http_backend.py project.
21 21
22 22 Copyright (c) 2010 Daniel Dotsenko <dotsa@hotmail.com>
23 23 Copyright (c) 2012 Marcin Kuzminski <marcin@python-works.com>
24 24
25 25 This file was forked by the Kallithea project in July 2014.
26 26 """
27 27
28 28 import logging
29 29 import os
30 30 import socket
31 31 import traceback
32 32
33 33 from dulwich.server import update_server_info
34 34 from dulwich.web import GunzipFilter, LimitedInputFilter
35 35 from webob import Request, Response, exc
36 36
37 37 import kallithea
38 38 from kallithea.lib.utils2 import ascii_bytes
39 39 from kallithea.lib.vcs import get_repo, subprocessio
40 40
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class FileWrapper(object):
46 46
47 47 def __init__(self, fd, content_length):
48 48 self.fd = fd
49 49 self.content_length = content_length
50 50 self.remain = content_length
51 51
52 52 def read(self, size):
53 53 if size <= self.remain:
54 54 try:
55 55 data = self.fd.read(size)
56 56 except socket.error:
57 57 raise IOError(self)
58 58 self.remain -= size
59 59 elif self.remain:
60 60 data = self.fd.read(self.remain)
61 61 self.remain = 0
62 62 else:
63 63 data = None
64 64 return data
65 65
66 66 def __repr__(self):
67 67 return '<FileWrapper %s len: %s, read: %s>' % (
68 68 self.fd, self.content_length, self.content_length - self.remain
69 69 )
70 70
71 71
72 72 class GitRepository(object):
73 73 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
74 74 commands = ['git-upload-pack', 'git-receive-pack']
75 75
76 76 def __init__(self, repo_name, content_path):
77 77 files = set([f.lower() for f in os.listdir(content_path)])
78 78 if not (self.git_folder_signature.intersection(files)
79 79 == self.git_folder_signature):
80 80 raise OSError('%s missing git signature' % content_path)
81 81 self.content_path = content_path
82 82 self.valid_accepts = ['application/x-%s-result' %
83 83 c for c in self.commands]
84 84 self.repo_name = repo_name
85 85
86 86 def _get_fixedpath(self, path):
87 87 """
88 88 Small fix for repo_path
89 89
90 90 :param path:
91 91 """
92 92 assert path.startswith('/' + self.repo_name + '/')
93 93 return path[len(self.repo_name) + 2:].strip('/')
94 94
95 95 def inforefs(self, req, environ):
96 96 """
97 97 WSGI Response producer for HTTP GET Git Smart
98 98 HTTP /info/refs request.
99 99 """
100 100
101 101 git_command = req.GET.get('service')
102 102 if git_command not in self.commands:
103 103 log.debug('command %s not allowed', git_command)
104 104 return exc.HTTPMethodNotAllowed()
105 105
106 106 # From Documentation/technical/http-protocol.txt shipped with Git:
107 107 #
108 108 # Clients MUST verify the first pkt-line is `# service=$servicename`.
109 109 # Servers MUST set $servicename to be the request parameter value.
110 110 # Servers SHOULD include an LF at the end of this line.
111 111 # Clients MUST ignore an LF at the end of the line.
112 112 #
113 113 # smart_reply = PKT-LINE("# service=$servicename" LF)
114 114 # ref_list
115 115 # "0000"
116 116 server_advert = '# service=%s\n' % git_command
117 117 packet_len = hex(len(server_advert) + 4)[2:].rjust(4, '0').lower()
118 118 _git_path = kallithea.CONFIG.get('git_path', 'git')
119 119 cmd = [_git_path, git_command[4:],
120 120 '--stateless-rpc', '--advertise-refs', self.content_path]
121 121 log.debug('handling cmd %s', cmd)
122 122 try:
123 123 out = subprocessio.SubprocessIOChunker(cmd,
124 124 starting_values=[ascii_bytes(packet_len + server_advert + '0000')]
125 125 )
126 126 except EnvironmentError as e:
127 127 log.error(traceback.format_exc())
128 128 raise exc.HTTPExpectationFailed()
129 129 resp = Response()
130 130 resp.content_type = 'application/x-%s-advertisement' % git_command
131 131 resp.charset = None
132 132 resp.app_iter = out
133 133 return resp
134 134
135 135 def backend(self, req, environ):
136 136 """
137 137 WSGI Response producer for HTTP POST Git Smart HTTP requests.
138 138 Reads commands and data from HTTP POST's body.
139 139 returns an iterator obj with contents of git command's
140 140 response to stdout
141 141 """
142 142 _git_path = kallithea.CONFIG.get('git_path', 'git')
143 143 git_command = self._get_fixedpath(req.path_info)
144 144 if git_command not in self.commands:
145 145 log.debug('command %s not allowed', git_command)
146 146 return exc.HTTPMethodNotAllowed()
147 147
148 148 if 'CONTENT_LENGTH' in environ:
149 149 inputstream = FileWrapper(environ['wsgi.input'],
150 150 req.content_length)
151 151 else:
152 152 inputstream = environ['wsgi.input']
153 153
154 154 gitenv = dict(os.environ)
155 155 # forget all configs
156 156 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
157 157 cmd = [_git_path, git_command[4:], '--stateless-rpc', self.content_path]
158 158 log.debug('handling cmd %s', cmd)
159 159 try:
160 160 out = subprocessio.SubprocessIOChunker(
161 161 cmd,
162 162 inputstream=inputstream,
163 163 env=gitenv,
164 164 cwd=self.content_path,
165 165 )
166 166 except EnvironmentError as e:
167 167 log.error(traceback.format_exc())
168 168 raise exc.HTTPExpectationFailed()
169 169
170 170 if git_command in ['git-receive-pack']:
171 171 # updating refs manually after each push.
172 172 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
173 173 repo = get_repo(self.content_path)
174 174 if repo:
175 175 update_server_info(repo._repo)
176 176
177 177 resp = Response()
178 178 resp.content_type = 'application/x-%s-result' % git_command
179 179 resp.charset = None
180 180 resp.app_iter = out
181 181 return resp
182 182
183 183 def __call__(self, environ, start_response):
184 184 req = Request(environ)
185 185 _path = self._get_fixedpath(req.path_info)
186 186 if _path.startswith('info/refs'):
187 187 app = self.inforefs
188 188 elif req.accept.acceptable_offers(self.valid_accepts):
189 189 app = self.backend
190 190 try:
191 191 resp = app(req, environ)
192 192 except exc.HTTPException as e:
193 193 resp = e
194 194 log.error(traceback.format_exc())
195 195 except Exception as e:
196 196 log.error(traceback.format_exc())
197 197 resp = exc.HTTPInternalServerError()
198 198 return resp(environ, start_response)
199 199
200 200
201 201 class GitDirectory(object):
202 202
203 203 def __init__(self, repo_root, repo_name):
204 204 repo_location = os.path.join(repo_root, repo_name)
205 205 if not os.path.isdir(repo_location):
206 206 raise OSError(repo_location)
207 207
208 208 self.content_path = repo_location
209 209 self.repo_name = repo_name
210 210 self.repo_location = repo_location
211 211
212 212 def __call__(self, environ, start_response):
213 213 content_path = self.content_path
214 214 try:
215 215 app = GitRepository(self.repo_name, content_path)
216 216 except (AssertionError, OSError):
217 217 content_path = os.path.join(content_path, '.git')
218 218 if os.path.isdir(content_path):
219 219 app = GitRepository(self.repo_name, content_path)
220 220 else:
221 221 return exc.HTTPNotFound()(environ, start_response)
222 222 return app(environ, start_response)
223 223
224 224
225 225 def make_wsgi_app(repo_name, repo_root):
226 226 app = GitDirectory(repo_root, repo_name)
227 227 return GunzipFilter(LimitedInputFilter(app))
@@ -1,93 +1,93 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.simplegit
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.simplegit
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 SimpleGit middleware for handling Git protocol requests (push/clone etc.)
19 19 It's implemented with basic auth function
20 20
21 21 This file was forked by the Kallithea project in July 2014.
22 22 Original author and date, and relevant copyright and licensing information is below:
23 23 :created_on: Apr 28, 2010
24 24 :author: marcink
25 25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 26 :license: GPLv3, see LICENSE.md for more details.
27 27
28 28 """
29 29
30 30
31 31 import logging
32 32 import re
33 33
34 34 from kallithea.config.middleware.pygrack import make_wsgi_app
35 35 from kallithea.lib import hooks
36 36 from kallithea.lib.base import BaseVCSController, get_path_info
37 37
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)$')
43 43
44 44
45 45 cmd_mapping = {
46 46 'git-receive-pack': 'push',
47 47 'git-upload-pack': 'pull',
48 48 }
49 49
50 50
51 51 class SimpleGit(BaseVCSController):
52 52
53 53 scm_alias = 'git'
54 54
55 55 @classmethod
56 56 def parse_request(cls, environ):
57 57 path_info = get_path_info(environ)
58 58 m = GIT_PROTO_PAT.match(path_info)
59 59 if m is None:
60 60 return None
61 61
62 62 class parsed_request(object):
63 63 # See https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols#_the_smart_protocol
64 64 repo_name = m.group(1).rstrip('/')
65 65 cmd = m.group(2)
66 66
67 67 query_string = environ['QUERY_STRING']
68 68 if cmd == 'info/refs' and query_string.startswith('service='):
69 69 service = query_string.split('=', 1)[1]
70 70 action = cmd_mapping.get(service)
71 71 else:
72 72 service = None
73 73 action = cmd_mapping.get(cmd)
74 74
75 75 return parsed_request
76 76
77 77 def _make_app(self, parsed_request):
78 78 """
79 79 Return a pygrack wsgi application.
80 80 """
81 81 pygrack_app = make_wsgi_app(parsed_request.repo_name, self.basepath)
82 82
83 83 def wrapper_app(environ, start_response):
84 84 if (parsed_request.cmd == 'info/refs' and
85 85 parsed_request.service == 'git-upload-pack'
86 86 ):
87 87 # Run hooks like Mercurial outgoing.kallithea_pull_action does
88 88 hooks.log_pull_action()
89 89 # Note: push hooks are handled by post-receive hook
90 90
91 91 return pygrack_app(environ, start_response)
92 92
93 93 return wrapper_app
@@ -1,149 +1,149 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.simplehg
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.simplehg
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 SimpleHg middleware for handling Mercurial protocol requests (push/clone etc.).
19 19 It's implemented with basic auth function
20 20
21 21 This file was forked by the Kallithea project in July 2014.
22 22 Original author and date, and relevant copyright and licensing information is below:
23 23 :created_on: Apr 28, 2010
24 24 :author: marcink
25 25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 26 :license: GPLv3, see LICENSE.md for more details.
27 27
28 28 """
29 29
30 30
31 31 import logging
32 32 import os
33 33 import urllib.parse
34 34
35 35 import mercurial.hgweb
36 36
37 37 from kallithea.lib.base import BaseVCSController, get_path_info
38 38 from kallithea.lib.utils import make_ui
39 39 from kallithea.lib.utils2 import safe_bytes
40 40
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 def get_header_hgarg(environ):
46 46 """Decode the special Mercurial encoding of big requests over multiple headers.
47 47 >>> get_header_hgarg({})
48 48 ''
49 49 >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'})
50 50 'ab+c %20'
51 51 """
52 52 chunks = []
53 53 i = 1
54 54 while True:
55 55 v = environ.get('HTTP_X_HGARG_%d' % i)
56 56 if v is None:
57 57 break
58 58 chunks.append(v)
59 59 i += 1
60 60 return ''.join(chunks)
61 61
62 62
63 63 cmd_mapping = {
64 64 # 'batch' is not in this list - it is handled explicitly
65 65 'between': 'pull',
66 66 'branches': 'pull',
67 67 'branchmap': 'pull',
68 68 'capabilities': 'pull',
69 69 'changegroup': 'pull',
70 70 'changegroupsubset': 'pull',
71 71 'changesetdata': 'pull',
72 72 'clonebundles': 'pull',
73 73 'debugwireargs': 'pull',
74 74 'filedata': 'pull',
75 75 'getbundle': 'pull',
76 76 'getlfile': 'pull',
77 77 'heads': 'pull',
78 78 'hello': 'pull',
79 79 'known': 'pull',
80 80 'lheads': 'pull',
81 81 'listkeys': 'pull',
82 82 'lookup': 'pull',
83 83 'manifestdata': 'pull',
84 84 'narrow_widen': 'pull',
85 85 'protocaps': 'pull',
86 86 'statlfile': 'pull',
87 87 'stream_out': 'pull',
88 88 'pushkey': 'push',
89 89 'putlfile': 'push',
90 90 'unbundle': 'push',
91 91 }
92 92
93 93
94 94 class SimpleHg(BaseVCSController):
95 95
96 96 scm_alias = 'hg'
97 97
98 98 @classmethod
99 99 def parse_request(cls, environ):
100 100 http_accept = environ.get('HTTP_ACCEPT', '')
101 101 if not http_accept.startswith('application/mercurial'):
102 102 return None
103 103 path_info = get_path_info(environ)
104 104 if not path_info.startswith('/'): # it must!
105 105 return None
106 106
107 107 class parsed_request(object):
108 108 repo_name = path_info[1:].rstrip('/')
109 109
110 110 query_string = environ['QUERY_STRING']
111 111
112 112 action = None
113 113 for qry in query_string.split('&'):
114 114 parts = qry.split('=', 1)
115 115 if len(parts) == 2 and parts[0] == 'cmd':
116 116 cmd = parts[1]
117 117 if cmd == 'batch':
118 118 hgarg = get_header_hgarg(environ)
119 119 if not hgarg.startswith('cmds='):
120 120 action = 'push' # paranoid and safe
121 121 break
122 122 action = 'pull'
123 123 for cmd_arg in hgarg[5:].split(';'):
124 124 cmd, _args = urllib.parse.unquote_plus(cmd_arg).split(' ', 1)
125 125 op = cmd_mapping.get(cmd, 'push')
126 126 if op != 'pull':
127 127 assert op == 'push'
128 128 action = 'push'
129 129 break
130 130 else:
131 131 action = cmd_mapping.get(cmd, 'push')
132 132 break # only process one cmd
133 133
134 134 return parsed_request
135 135
136 136 def _make_app(self, parsed_request):
137 137 """
138 138 Make an hgweb wsgi application.
139 139 """
140 140 repo_name = parsed_request.repo_name
141 141 repo_path = os.path.join(self.basepath, repo_name)
142 142 baseui = make_ui(repo_path=repo_path)
143 143 hgweb_app = mercurial.hgweb.hgweb(safe_bytes(repo_path), name=safe_bytes(repo_name), baseui=baseui)
144 144
145 145 def wrapper_app(environ, start_response):
146 146 environ['REPO_NAME'] = repo_name # used by mercurial.hgweb.hgweb
147 147 return hgweb_app(environ, start_response)
148 148
149 149 return wrapper_app
@@ -1,102 +1,102 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.lib.middleware.wrapper
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 kallithea.config.middleware.wrapper
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Wrap app to measure request and response time ... all the way to the response
19 19 WSGI iterator has been closed.
20 20
21 21 This file was forked by the Kallithea project in July 2014.
22 22 Original author and date, and relevant copyright and licensing information is below:
23 23 :created_on: May 23, 2013
24 24 :author: marcink
25 25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 26 :license: GPLv3, see LICENSE.md for more details.
27 27 """
28 28
29 29 import logging
30 30 import time
31 31
32 32 from kallithea.lib.base import _get_ip_addr, get_path_info
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class Meter:
39 39
40 40 def __init__(self, start_response):
41 41 self._start_response = start_response
42 42 self._start = time.time()
43 43 self.status = None
44 44 self._size = 0
45 45
46 46 def duration(self):
47 47 return time.time() - self._start
48 48
49 49 def start_response(self, status, response_headers, exc_info=None):
50 50 self.status = status
51 51 write = self._start_response(status, response_headers, exc_info)
52 52 def metered_write(s):
53 53 self.measure(s)
54 54 write(s)
55 55 return metered_write
56 56
57 57 def measure(self, chunk):
58 58 self._size += len(chunk)
59 59
60 60 def size(self):
61 61 return self._size
62 62
63 63
64 64 class ResultIter:
65 65
66 66 def __init__(self, result, meter, description):
67 67 self._result_close = getattr(result, 'close', None) or (lambda: None)
68 68 self._next = iter(result).__next__
69 69 self._meter = meter
70 70 self._description = description
71 71
72 72 def __iter__(self):
73 73 return self
74 74
75 75 def __next__(self):
76 76 chunk = self._next()
77 77 self._meter.measure(chunk)
78 78 return chunk
79 79
80 80 def close(self):
81 81 self._result_close()
82 82 log.info("%s responded %r after %.3fs with %s bytes", self._description, self._meter.status, self._meter.duration(), self._meter.size())
83 83
84 84
85 85 class RequestWrapper(object):
86 86
87 87 def __init__(self, app, config):
88 88 self.application = app
89 89 self.config = config
90 90
91 91 def __call__(self, environ, start_response):
92 92 meter = Meter(start_response)
93 93 description = "Request from %s for %s" % (
94 94 _get_ip_addr(environ),
95 95 get_path_info(environ),
96 96 )
97 97 log.info("%s received", description)
98 98 try:
99 99 result = self.application(environ, meter.start_response)
100 100 finally:
101 101 log.info("%s responding %r after %.3fs", description, meter.status, meter.duration())
102 102 return ResultIter(result, meter, description)
@@ -1,69 +1,69 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.lib.conf
16 ~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~
17 17
18 18 Various config settings for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Mar 7, 2012
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 from kallithea.lib import pygmentsutils
29 29
30 30
31 31 # language map is also used by whoosh indexer, which for those specified
32 32 # extensions will index it's content
33 33 LANGUAGES_EXTENSIONS_MAP = pygmentsutils.get_extension_descriptions()
34 34
35 35 # Whoosh index targets
36 36
37 37 # Extensions we want to index content of using whoosh
38 38 INDEX_EXTENSIONS = list(LANGUAGES_EXTENSIONS_MAP)
39 39
40 40 # Filenames we want to index content of using whoosh
41 41 INDEX_FILENAMES = pygmentsutils.get_index_filenames()
42 42
43 43 # list of readme files to search in file tree and display in summary
44 44 # attached weights defines the search order lower is first
45 45 ALL_READMES = [
46 46 ('readme', 0), ('README', 0), ('Readme', 0),
47 47 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
48 48 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
49 49 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
50 50 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
51 51 ]
52 52
53 53 # extension together with weights to search lower is first
54 54 RST_EXTS = [
55 55 ('', 0), ('.rst', 1), ('.rest', 1),
56 56 ('.RST', 2), ('.REST', 2),
57 57 ('.txt', 3), ('.TXT', 3)
58 58 ]
59 59
60 60 MARKDOWN_EXTS = [
61 61 ('.md', 1), ('.MD', 1),
62 62 ('.mkdn', 2), ('.MKDN', 2),
63 63 ('.mdown', 3), ('.MDOWN', 3),
64 64 ('.markdown', 4), ('.MARKDOWN', 4)
65 65 ]
66 66
67 67 PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
68 68
69 69 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
@@ -1,258 +1,258 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.lib.webutils
16 ~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Helper functions that may rely on the current WSGI request, exposed in the TG2
19 19 thread-local "global" variables. It should have few dependencies so it can be
20 20 imported anywhere - just like the global variables can be used everywhere.
21 21 """
22 22
23 23 import logging
24 24 import random
25 25
26 26 from tg import request, session
27 27 from webhelpers2.html import HTML, escape, literal
28 28 from webhelpers2.html.tags import NotGiven, Option, Options, _input
29 29 from webhelpers2.html.tags import _make_safe_id_component as safeid
30 30 from webhelpers2.html.tags import checkbox, end_form
31 31 from webhelpers2.html.tags import form as insecure_form
32 32 from webhelpers2.html.tags import hidden, link_to, password, radio
33 33 from webhelpers2.html.tags import select as webhelpers2_select
34 34 from webhelpers2.html.tags import submit, text, textarea
35 35 from webhelpers2.number import format_byte_size
36 36 from webhelpers2.text import chop_at, truncate, wrap_paragraphs
37 37
38 38 import kallithea
39 39
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 # mute pyflakes "imported but unused"
45 45 assert Option
46 46 assert checkbox
47 47 assert chop_at
48 48 assert end_form
49 49 assert escape
50 50 assert format_byte_size
51 51 assert link_to
52 52 assert literal
53 53 assert password
54 54 assert radio
55 55 assert safeid
56 56 assert submit
57 57 assert text
58 58 assert textarea
59 59 assert truncate
60 60 assert wrap_paragraphs
61 61
62 62
63 63 #
64 64 # General Kallithea URL handling
65 65 #
66 66
67 67 class UrlGenerator(object):
68 68 """Emulate pylons.url in providing a wrapper around routes.url
69 69
70 70 This code was added during migration from Pylons to Turbogears2. Pylons
71 71 already provided a wrapper like this, but Turbogears2 does not.
72 72
73 73 When the routing of Kallithea is changed to use less Routes and more
74 74 Turbogears2-style routing, this class may disappear or change.
75 75
76 76 url() (the __call__ method) returns the URL based on a route name and
77 77 arguments.
78 78 url.current() returns the URL of the current page with arguments applied.
79 79
80 80 Refer to documentation of Routes for details:
81 81 https://routes.readthedocs.io/en/latest/generating.html#generation
82 82 """
83 83 def __call__(self, *args, **kwargs):
84 84 return request.environ['routes.url'](*args, **kwargs)
85 85
86 86 def current(self, *args, **kwargs):
87 87 return request.environ['routes.url'].current(*args, **kwargs)
88 88
89 89
90 90 url = UrlGenerator()
91 91
92 92
93 93 def canonical_url(*args, **kargs):
94 94 '''Like url(x, qualified=True), but returns url that not only is qualified
95 95 but also canonical, as configured in canonical_url'''
96 96 try:
97 97 parts = kallithea.CONFIG.get('canonical_url', '').split('://', 1)
98 98 kargs['host'] = parts[1]
99 99 kargs['protocol'] = parts[0]
100 100 except IndexError:
101 101 kargs['qualified'] = True
102 102 return url(*args, **kargs)
103 103
104 104
105 105 def canonical_hostname():
106 106 '''Return canonical hostname of system'''
107 107 try:
108 108 parts = kallithea.CONFIG.get('canonical_url', '').split('://', 1)
109 109 return parts[1].split('/', 1)[0]
110 110 except IndexError:
111 111 parts = url('home', qualified=True).split('://', 1)
112 112 return parts[1].split('/', 1)[0]
113 113
114 114
115 115 #
116 116 # Custom Webhelpers2 stuff
117 117 #
118 118
119 119 def html_escape(s):
120 120 """Return string with all html escaped.
121 121 This is also safe for javascript in html but not necessarily correct.
122 122 """
123 123 return (s
124 124 .replace('&', '&amp;')
125 125 .replace(">", "&gt;")
126 126 .replace("<", "&lt;")
127 127 .replace('"', "&quot;")
128 128 .replace("'", "&apos;") # Note: this is HTML5 not HTML4 and might not work in mails
129 129 )
130 130
131 131
132 132 def reset(name, value, id=NotGiven, **attrs):
133 133 """Create a reset button, similar to webhelpers2.html.tags.submit ."""
134 134 return _input("reset", name, value, id, attrs)
135 135
136 136
137 137 def select(name, selected_values, options, id=NotGiven, **attrs):
138 138 """Convenient wrapper of webhelpers2 to let it accept options as a tuple list"""
139 139 if isinstance(options, list):
140 140 option_list = options
141 141 # Handle old value,label lists ... where value also can be value,label lists
142 142 options = Options()
143 143 for x in option_list:
144 144 if isinstance(x, tuple) and len(x) == 2:
145 145 value, label = x
146 146 elif isinstance(x, str):
147 147 value = label = x
148 148 else:
149 149 log.error('invalid select option %r', x)
150 150 raise
151 151 if isinstance(value, list):
152 152 og = options.add_optgroup(label)
153 153 for x in value:
154 154 if isinstance(x, tuple) and len(x) == 2:
155 155 group_value, group_label = x
156 156 elif isinstance(x, str):
157 157 group_value = group_label = x
158 158 else:
159 159 log.error('invalid select option %r', x)
160 160 raise
161 161 og.add_option(group_label, group_value)
162 162 else:
163 163 options.add_option(label, value)
164 164 return webhelpers2_select(name, selected_values, options, id=id, **attrs)
165 165
166 166
167 167 session_csrf_secret_name = "_session_csrf_secret_token"
168 168
169 169 def session_csrf_secret_token():
170 170 """Return (and create) the current session's CSRF protection token."""
171 171 if not session_csrf_secret_name in session:
172 172 session[session_csrf_secret_name] = str(random.getrandbits(128))
173 173 session.save()
174 174 return session[session_csrf_secret_name]
175 175
176 176 def form(url, method="post", **attrs):
177 177 """Like webhelpers.html.tags.form , but automatically adding
178 178 session_csrf_secret_token for POST. The secret is thus never leaked in GET
179 179 URLs.
180 180 """
181 181 form = insecure_form(url, method, **attrs)
182 182 if method.lower() == 'get':
183 183 return form
184 184 return form + HTML.div(hidden(session_csrf_secret_name, session_csrf_secret_token()), style="display: none;")
185 185
186 186
187 187 #
188 188 # Flash messages, stored in cookie
189 189 #
190 190
191 191 class _Message(object):
192 192 """A message returned by ``pop_flash_messages()``.
193 193
194 194 Converting the message to a string returns the message text. Instances
195 195 also have the following attributes:
196 196
197 197 * ``category``: the category specified when the message was created.
198 198 * ``message``: the html-safe message text.
199 199 """
200 200
201 201 def __init__(self, category, message):
202 202 self.category = category
203 203 self.message = message
204 204
205 205
206 206 def _session_flash_messages(append=None, clear=False):
207 207 """Manage a message queue in tg.session: return the current message queue
208 208 after appending the given message, and possibly clearing the queue."""
209 209 key = 'flash'
210 210 if key in session:
211 211 flash_messages = session[key]
212 212 else:
213 213 if append is None: # common fast path - also used for clearing empty queue
214 214 return [] # don't bother saving
215 215 flash_messages = []
216 216 session[key] = flash_messages
217 217 if append is not None and append not in flash_messages:
218 218 flash_messages.append(append)
219 219 if clear:
220 220 session.pop(key, None)
221 221 session.save()
222 222 return flash_messages
223 223
224 224
225 225 def flash(message, category, logf=None):
226 226 """
227 227 Show a message to the user _and_ log it through the specified function
228 228
229 229 category: notice (default), warning, error, success
230 230 logf: a custom log function - such as log.debug
231 231
232 232 logf defaults to log.info, unless category equals 'success', in which
233 233 case logf defaults to log.debug.
234 234 """
235 235 assert category in ('error', 'success', 'warning'), category
236 236 if hasattr(message, '__html__'):
237 237 # render to HTML for storing in cookie
238 238 safe_message = str(message)
239 239 else:
240 240 # Apply str - the message might be an exception with __str__
241 241 # Escape, so we can trust the result without further escaping, without any risk of injection
242 242 safe_message = html_escape(str(message))
243 243 if logf is None:
244 244 logf = log.info
245 245 if category == 'success':
246 246 logf = log.debug
247 247
248 248 logf('Flash %s: %s', category, safe_message)
249 249
250 250 _session_flash_messages(append=(category, safe_message))
251 251
252 252
253 253 def pop_flash_messages():
254 254 """Return all accumulated messages and delete them from the session.
255 255
256 256 The return value is a list of ``Message`` objects.
257 257 """
258 258 return [_Message(category, message) for category, message in _session_flash_messages(clear=True)]
@@ -1,90 +1,90 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 kallithea.model.db
16 ~~~~~~~~~~~~~~~~~~
15 kallithea.model.userlog
16 ~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Database Models for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 08, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28
29 29 import datetime
30 30 import logging
31 31
32 32 from kallithea.lib.utils2 import get_current_authuser
33 33 from kallithea.model import db, meta
34 34
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 def action_logger(user, action, repo, ipaddr='', commit=False):
40 40 """
41 41 Action logger for various actions made by users
42 42
43 43 :param user: user that made this action, can be a unique username string or
44 44 object containing user_id attribute
45 45 :param action: action to log, should be on of predefined unique actions for
46 46 easy translations
47 47 :param repo: string name of repository or object containing repo_id,
48 48 that action was made on
49 49 :param ipaddr: optional IP address from what the action was made
50 50
51 51 """
52 52
53 53 # if we don't get explicit IP address try to get one from registered user
54 54 # in tmpl context var
55 55 if not ipaddr:
56 56 ipaddr = getattr(get_current_authuser(), 'ip_addr', '')
57 57
58 58 if getattr(user, 'user_id', None):
59 59 user_obj = db.User.get(user.user_id)
60 60 elif isinstance(user, str):
61 61 user_obj = db.User.get_by_username(user)
62 62 else:
63 63 raise Exception('You have to provide a user object or a username')
64 64
65 65 if getattr(repo, 'repo_id', None):
66 66 repo_obj = db.Repository.get(repo.repo_id)
67 67 repo_name = repo_obj.repo_name
68 68 elif isinstance(repo, str):
69 69 repo_name = repo.lstrip('/')
70 70 repo_obj = db.Repository.get_by_repo_name(repo_name)
71 71 else:
72 72 repo_obj = None
73 73 repo_name = ''
74 74
75 75 user_log = db.UserLog()
76 76 user_log.user_id = user_obj.user_id
77 77 user_log.username = user_obj.username
78 78 user_log.action = action
79 79
80 80 user_log.repository = repo_obj
81 81 user_log.repository_name = repo_name
82 82
83 83 user_log.action_date = datetime.datetime.now()
84 84 user_log.user_ip = ipaddr
85 85 meta.Session().add(user_log)
86 86
87 87 log.info('Logging action:%s on %s by user:%s ip:%s',
88 88 action, repo, user_obj, ipaddr)
89 89 if commit:
90 90 meta.Session().commit()
@@ -1,13 +1,14 b''
1 1 #!/bin/sh
2 2
3 3 # Convenience script for running various idempotent source code cleanup scripts
4 4
5 5 set -e
6 6 set -x
7 7
8 8 scripts/docs-headings.py
9 9 scripts/generate-ini.py
10 10 scripts/whitespacecleanup.sh
11 hg files 'set:!binary()&grep("^#!.*python")' 'set:**.py' | xargs scripts/source_format.py
11 12
12 13 hg files 'set:!binary()&grep("^#!.*python")' 'set:**.py' | xargs scripts/pyflakes
13 14 echo "no blocking problems found by $0"
General Comments 0
You need to be logged in to leave comments. Login now