Files @ 75b0d3fd6303
Branch filter:

Location: kallithea/scripts/docs-headings.py

Mads Kiilerich
ssh: handle IPv6 ssh connections

Performing ssh actions towards Kallithea via an IPv6 connection gave the
following error:

$ hg incoming ssh://kallithea@example.com/repo
remote: Traceback (most recent call last):
remote: File ".../bin/kallithea-cli", line 11, in <module>
remote: load_entry_point('Kallithea', 'console_scripts', 'kallithea-cli')()
remote: File ".../python2.7/site-packages/click/core.py", line 764, in __call__
remote: return self.main(*args, **kwargs)
remote: File ".../python2.7/site-packages/click/core.py", line 717, in main
remote: rv = self.invoke(ctx)
remote: File ".../python2.7/site-packages/click/core.py", line 1137, in invoke
remote: return _process_result(sub_ctx.command.invoke(sub_ctx))
remote: File ".../python2.7/site-packages/click/core.py", line 956, in invoke
remote: return ctx.invoke(self.callback, **ctx.params)
remote: File ".../python2.7/site-packages/click/core.py", line 555, in invoke
remote: return callback(*args, **kwargs)
remote: File ".../kallithea/bin/kallithea_cli_base.py", line 79, in runtime_wrapper
remote: return annotated(*args, **kwargs)
remote: File ".../kallithea/bin/kallithea_cli_ssh.py", line 74, in ssh_serve
remote: vcs_handler.serve(user_id, key_id, client_ip)
remote: File ".../kallithea/lib/vcs/backends/ssh.py", line 65, in serve
remote: self.authuser = AuthUser.make(dbuser=dbuser, ip_addr=client_ip)
remote: File ".../kallithea/lib/auth.py", line 407, in make
remote: if not check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
remote: File ".../kallithea/lib/auth.py", line 860, in check_ip_access
remote: if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
remote: File ".../kallithea/lib/ipaddr.py", line 76, in IPAddress
remote:
remote: ValueError: '2' does not appear to be an IPv4 or IPv6 address
abort: no suitable response from remote hg!


This was caused by IPv4-exclusive parsing of the SSH_CONNECTION variable.
With an IPv6 address starting with '2a02:1810:', only the first '2' would
survive.

According to 'man 1 ssh':

SSH_CONNECTION Identifies the client and server ends of the con‐
nection. The variable contains four space-sepa‐
rated values: client IP address, client port num‐
ber, server IP address, and server port number.


So, the client IP address will be the first space-separated word, regardless
of IPv4 or IPv6. Use that knowledge without further parsing.

(commit message by Thomas De Schampheleire)
#!/usr/bin/env python2

"""
Consistent formatting of rst section titles
"""

import re
import subprocess


spaces = [
    (0, 1), # we assume this is a over-and-underlined header
    (2, 1),
    (1, 1),
    (1, 0),
    (1, 0),
    ]

# http://sphinx-doc.org/rest.html :
#   for the Python documentation, this convention is used which you may follow:
#   # with overline, for parts
#   * with overline, for chapters
#   =, for sections
#   -, for subsections
#   ^, for subsubsections
#   ", for paragraphs
pystyles = ['#', '*', '=', '-', '^', '"']

# match on a header line underlined with one of the valid characters
headermatch = re.compile(r'''\n*(.+)\n([][!"#$%&'()*+,./:;<=>?@\\^_`{|}~-])\2{2,}\n+''', flags=re.MULTILINE)


def main():
    filenames = subprocess.check_output(['hg', 'loc', 'set:**.rst+kallithea/i18n/how_to']).splitlines()
    for fn in filenames:
        print 'processing %s' % fn
        s = open(fn).read()

        # find levels and their styles
        lastpos = 0
        styles = []
        for markup in headermatch.findall(s):
            style = markup[1]
            if style in styles:
                stylepos = styles.index(style)
                if stylepos > lastpos + 1:
                    print 'bad style %r with level %s - was at %s' % (style, stylepos, lastpos)
            else:
                stylepos = len(styles)
                if stylepos > lastpos + 1:
                    print 'bad new style %r - expected %r' % (style, styles[lastpos + 1])
                else:
                    styles.append(style)
            lastpos = stylepos

        # remove superfluous spacing (may however be restored by header spacing)
        s = re.sub(r'''(\n\n)\n*''', r'\1', s, flags=re.MULTILINE)

        if styles:
            newstyles = pystyles[pystyles.index(styles[0]):]

            def subf(m):
                title, style = m.groups()
                level = styles.index(style)
                before, after = spaces[level]
                newstyle = newstyles[level]
                return '\n' * (before + 1) + title + '\n' + newstyle * len(title) + '\n' * (after + 1)
            s = headermatch.sub(subf, s)

        # remove superfluous spacing when headers are adjacent
        s = re.sub(r'''(\n.+\n([][!"#$%&'()*+,./:;<=>?@\\^_`{|}~-])\2{2,}\n\n\n)\n*''', r'\1', s, flags=re.MULTILINE)
        # fix trailing space and spacing before link sections
        s = s.strip() + '\n'
        s = re.sub(r'''\n+((?:\.\. _[^\n]*\n)+)$''', r'\n\n\n\1', s)

        open(fn, 'w').write(s)

    print subprocess.check_output(['hg', 'diff'] + filenames)

if __name__ == '__main__':
    main()