accel.pl knows about the accelerators added by winctrls.c:prefslist().
[u/mdw/putty] / contrib / kh2reg.py
1 #! /usr/bin/env python
2
3 # $Id: kh2reg.py,v 1.1 2002/03/10 22:00:06 jacob Exp $
4 # Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY
5 # host keys in a Windows .REG file (double-click to install).
6 # usage: hosts2reg.py known_hosts1 2 3 4 ... > hosts.reg
7 # Line endings are someone else's problem as is traditional.
8 # Developed for Python 1.5.2.
9
10 import fileinput
11 import base64
12 import struct
13 import string
14 import re
15 import sys
16
17 def mungestr(s):
18 "Duplicate of PuTTY's mungestr() in winstore.c:1.10 for Registry keys"
19 candot = 0
20 r = ""
21 for c in s:
22 if c in ' \*?%~' or ord(c)<ord(' ') or (c == '.' and not candot):
23 r = r + ("%%%02X" % ord(c))
24 else:
25 r = r + c
26 candot = 1
27 return r
28
29 def strtolong(s):
30 "Convert arbitrary-length big-endian binary data to a Python long"
31 bytes = struct.unpack(">%luB" % len(s), s)
32 return reduce ((lambda a, b: (long(a) << 8) + long(b)), bytes)
33
34 def longtohex(n):
35 """Convert long int to lower-case hex.
36
37 Ick, Python (at least in 1.5.2) doesn't appear to have a way to
38 turn a long int into an unadorned hex string -- % gets upset if the
39 number is too big, and raw hex() uses uppercase (sometimes), and
40 adds unwanted "0x...L" around it."""
41
42 plain=string.lower(re.match(r"0x([0-9A-Fa-f]*)l?$", hex(n), re.I).group(1))
43 return "0x" + plain
44
45 # Output REG file header.
46 sys.stdout.write("""REGEDIT4
47
48 [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys]
49 """)
50
51 # Now process all known_hosts input.
52 for line in fileinput.input():
53
54 try:
55 # Remove leading/trailing whitespace (should zap CR and LF)
56 line = string.strip (line)
57
58 # Skip blanks and comments
59 if line == '' or line[0] == '#':
60 raise "Skipping input line"
61
62 # Split line on spaces.
63 fields = string.split (line, ' ')
64
65 # Common fields
66 hostpat = fields[0]
67 magicnumbers = [] # placeholder
68 keytype = "" # placeholder
69
70 # Grotty heuristic to distinguish known_hosts from known_hosts2:
71 # is second field entirely decimal digits?
72 if re.match (r"\d*$", fields[1]):
73
74 # Treat as SSH1-type host key.
75 # Format: hostpat bits10 exp10 mod10 comment...
76 # (PuTTY doesn't store the number of bits.)
77 magicnumbers = map (long, fields[2:4])
78 keytype = "rsa"
79
80 else:
81
82 # Treat as SSH2-type host key.
83 # Format: hostpat keytype keyblob64 comment...
84 sshkeytype, blob = fields[1], base64.decodestring (fields[2])
85
86 # 'blob' consists of a number of
87 # uint32 N (big-endian)
88 # uint8[N] field_data
89 subfields = []
90 while blob:
91 sizefmt = ">L"
92 (size,) = struct.unpack (sizefmt, blob[0:4])
93 size = int(size) # req'd for slicage
94 (data,) = struct.unpack (">%lus" % size, blob[4:size+4])
95 subfields.append(data)
96 blob = blob [struct.calcsize(sizefmt) + size : ]
97
98 # The first field is keytype again, and the rest we can treat as
99 # an opaque list of bignums (same numbers and order as stored
100 # by PuTTY). (currently embedded keytype is ignored entirely)
101 magicnumbers = map (strtolong, subfields[1:])
102
103 # Translate key type into something PuTTY can use.
104 if sshkeytype == "ssh-rsa": keytype = "rsa2"
105 elif sshkeytype == "ssh-dss": keytype = "dss"
106 else:
107 raise "Unknown SSH key type", sshkeytype
108
109 # Now print out one line per host pattern, discarding wildcards.
110 for host in string.split (hostpat, ','):
111 if re.search (r"[*?!]", host):
112 sys.stderr.write("Skipping wildcard host pattern '%s'\n"
113 % host)
114 continue
115 else:
116 # Slightly bizarre registry key format: 'type@port:hostname'
117 # As far as I know, the input never specifies a port.
118 port = 22
119 # XXX: does PuTTY do anything useful with literal IP[v4]s?
120 key = keytype + ("@%d:%s" % (port, host))
121 value = string.join (map (longtohex, magicnumbers), ',')
122 # XXX: worry about double quotes?
123 sys.stdout.write("\"%s\"=\"%s\"\n" % (mungestr(key), value))
124
125 except "Unknown SSH key type", k:
126 sys.stderr.write("Unknown SSH key type '%s', skipping\n" % k)
127 except "Skipping input line":
128 pass