eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/zeroconf/__init__.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # zeroconf.py - zeroconf support for Mercurial
       
     2 #
       
     3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 '''discover and advertise repositories on the local network
       
     9 
       
    10 Zeroconf-enabled repositories will be announced in a network without
       
    11 the need to configure a server or a service. They can be discovered
       
    12 without knowing their actual IP address.
       
    13 
       
    14 To allow other people to discover your repository using run
       
    15 :hg:`serve` in your repository::
       
    16 
       
    17   $ cd test
       
    18   $ hg serve
       
    19 
       
    20 You can discover Zeroconf-enabled repositories by running
       
    21 :hg:`paths`::
       
    22 
       
    23   $ hg paths
       
    24   zc-test = http://example.com:8000/test
       
    25 '''
       
    26 
       
    27 import socket, time, os
       
    28 
       
    29 import Zeroconf
       
    30 from mercurial import ui, hg, encoding, util
       
    31 from mercurial import extensions
       
    32 from mercurial.hgweb import hgweb_mod
       
    33 from mercurial.hgweb import hgwebdir_mod
       
    34 
       
    35 # publish
       
    36 
       
    37 server = None
       
    38 localip = None
       
    39 
       
    40 def getip():
       
    41     # finds external-facing interface without sending any packets (Linux)
       
    42     try:
       
    43         s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
       
    44         s.connect(('1.0.0.1', 0))
       
    45         ip = s.getsockname()[0]
       
    46         return ip
       
    47     except:
       
    48         pass
       
    49 
       
    50     # Generic method, sometimes gives useless results
       
    51     try:
       
    52         dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
       
    53         if not dumbip.startswith('127.') and ':' not in dumbip:
       
    54             return dumbip
       
    55     except (socket.gaierror, socket.herror):
       
    56         dumbip = '127.0.0.1'
       
    57 
       
    58     # works elsewhere, but actually sends a packet
       
    59     try:
       
    60         s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
       
    61         s.connect(('1.0.0.1', 1))
       
    62         ip = s.getsockname()[0]
       
    63         return ip
       
    64     except:
       
    65         pass
       
    66 
       
    67     return dumbip
       
    68 
       
    69 def publish(name, desc, path, port):
       
    70     global server, localip
       
    71     if not server:
       
    72         ip = getip()
       
    73         if ip.startswith('127.'):
       
    74             # if we have no internet connection, this can happen.
       
    75             return
       
    76         localip = socket.inet_aton(ip)
       
    77         server = Zeroconf.Zeroconf(ip)
       
    78 
       
    79     hostname = socket.gethostname().split('.')[0]
       
    80     host = hostname + ".local"
       
    81     name = "%s-%s" % (hostname, name)
       
    82 
       
    83     # advertise to browsers
       
    84     svc = Zeroconf.ServiceInfo('_http._tcp.local.',
       
    85                                name + '._http._tcp.local.',
       
    86                                server = host,
       
    87                                port = port,
       
    88                                properties = {'description': desc,
       
    89                                              'path': "/" + path},
       
    90                                address = localip, weight = 0, priority = 0)
       
    91     server.registerService(svc)
       
    92 
       
    93     # advertise to Mercurial clients
       
    94     svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
       
    95                                name + '._hg._tcp.local.',
       
    96                                server = host,
       
    97                                port = port,
       
    98                                properties = {'description': desc,
       
    99                                              'path': "/" + path},
       
   100                                address = localip, weight = 0, priority = 0)
       
   101     server.registerService(svc)
       
   102 
       
   103 class hgwebzc(hgweb_mod.hgweb):
       
   104     def __init__(self, repo, name=None, baseui=None):
       
   105         super(hgwebzc, self).__init__(repo, name=name, baseui=baseui)
       
   106         name = self.reponame or os.path.basename(self.repo.root)
       
   107         path = self.repo.ui.config("web", "prefix", "").strip('/')
       
   108         desc = self.repo.ui.config("web", "description", name)
       
   109         publish(name, desc, path,
       
   110                 util.getport(self.repo.ui.config("web", "port", 8000)))
       
   111 
       
   112 class hgwebdirzc(hgwebdir_mod.hgwebdir):
       
   113     def __init__(self, conf, baseui=None):
       
   114         super(hgwebdirzc, self).__init__(conf, baseui=baseui)
       
   115         prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
       
   116         for repo, path in self.repos:
       
   117             u = self.ui.copy()
       
   118             u.readconfig(os.path.join(path, '.hg', 'hgrc'))
       
   119             name = os.path.basename(repo)
       
   120             path = (prefix + repo).strip('/')
       
   121             desc = u.config('web', 'description', name)
       
   122             publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
       
   123 
       
   124 # listen
       
   125 
       
   126 class listener(object):
       
   127     def __init__(self):
       
   128         self.found = {}
       
   129     def removeService(self, server, type, name):
       
   130         if repr(name) in self.found:
       
   131             del self.found[repr(name)]
       
   132     def addService(self, server, type, name):
       
   133         self.found[repr(name)] = server.getServiceInfo(type, name)
       
   134 
       
   135 def getzcpaths():
       
   136     ip = getip()
       
   137     if ip.startswith('127.'):
       
   138         return
       
   139     server = Zeroconf.Zeroconf(ip)
       
   140     l = listener()
       
   141     Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
       
   142     time.sleep(1)
       
   143     server.close()
       
   144     for value in l.found.values():
       
   145         name = value.name[:value.name.index('.')]
       
   146         url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
       
   147                                   value.properties.get("path", "/"))
       
   148         yield "zc-" + name, url
       
   149 
       
   150 def config(orig, self, section, key, default=None, untrusted=False):
       
   151     if section == "paths" and key.startswith("zc-"):
       
   152         for name, path in getzcpaths():
       
   153             if name == key:
       
   154                 return path
       
   155     return orig(self, section, key, default, untrusted)
       
   156 
       
   157 def configitems(orig, self, section, untrusted=False):
       
   158     repos = orig(self, section, untrusted)
       
   159     if section == "paths":
       
   160         repos += getzcpaths()
       
   161     return repos
       
   162 
       
   163 def defaultdest(orig, source):
       
   164     for name, path in getzcpaths():
       
   165         if path == source:
       
   166             return name.encode(encoding.encoding)
       
   167     return orig(source)
       
   168 
       
   169 extensions.wrapfunction(ui.ui, 'config', config)
       
   170 extensions.wrapfunction(ui.ui, 'configitems', configitems)
       
   171 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
       
   172 hgweb_mod.hgweb = hgwebzc
       
   173 hgwebdir_mod.hgwebdir = hgwebdirzc