Author: Isomer <Isomer@undernet.org>
[ircu2.10.12-pk.git] / tools / convert-conf.py
1 #!/usr/bin/env python
2 #
3 # IRC - Internet Relay Chat, tools/convert-conf.py
4 # Copyright (C) 2002 Alex Badea <vampire@p16.pub.ro>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 1, or (at your option)
9 # any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #
20 #
21 # Configuration file converter from 2.10.11 to 2.10.12 format
22 # Usage:
23 #   convert-conf.py < old.conf > new.conf
24 #
25 # $Id: convert-conf.py,v 1.5 2005-04-17 02:18:55 isomer Exp $
26 #
27
28 import sys
29 from string import *
30 import re
31
32 if len(sys.argv) > 1:
33         f = open(sys.argv[1], "r")
34 else:
35         f = sys.stdin
36
37 connects = {}
38 jupes = []
39 feats = [ ("OPLEVELS","FALSE")]
40 opers = []
41 quarintines = []
42
43 useable_features = [
44         "LOG", "DOMAINNAME", "RELIABLE_CLOCK", "BUFFERPOOL", 
45         "HAS_FERGUNSON_FLUSHER", "CLIENT_FLOOD", "SERVER_PORT", "NODEFAULTMOTD",
46         "MOTD_BANNER", "KILL_IPMISMATCH", "IDLE_FROM_MSG", "HUB", 
47         "WALLOPS_OPER_ONLY", "NODNS", "RANDOM_SEED", "DEFAULT_LIST_PARAM",
48         "NICKNAMEHISTORYLENGTH", "NETWORK", "HOST_HIDING", "HIDDEN_HOST",
49         "HIDDEN_IP", "KILLCHASETIMELIMIT", "MAXCHANNELSPERUSER", "NICKLEN",
50         "AVBANLEN", "MAXBANS", "MAXSILES", "HANGONGOODLINK", "HANGONRETRYDELAY",
51         "CONNECTTIMEOUT", "MAXIMUM_LINKS", "PINGFREQUENCY", "CONNECTFREQUENCY",
52         "DEFAULTMAXSENDQLENGTH", "GLINEMAXUSERCOUNT", "MPATH", "RPATH", "PPATH",
53         "TOS_SERVER", "TOS_CLIENT", "POLLS_PER_LOOP", "IRCD_RES_TIMEOUT",
54         "IRCD_RES_RETRIES", "AUTH_TIMEOUT", "IPCHECK_CLONE_LIMIT", 
55         "IPCHECK_CLONE_PERIOD", "IPCHECK_CLONE_DELAY", "CONFIG_OPERCMDS", 
56         "OPLEVELS", "LOCAL_CHANNELS", "ANNOUNCE_INVITES", "HIS_SNOTICES",
57         "HIS_DEBUG_OPER_ONLY", "HIS_WALLOPS", "HIS_MAP", "HIS_LINKS", 
58         "HIS_TRACE", "HIS_STATS_a", "HIS_STATS_c", "HIS_STATS_d", "HIS_STATS_e",
59         "HIS_STATS_f", "HIS_STATS_g", "HIS_STATS_i", "HIS_STATS_j", 
60         "HIS_STATS_J", "HIS_STATS_k", "HIS_STATS_l", "HIS_STATS_L",
61         "HIS_STATS_m", "HIS_STATS_M", "HIS_STATS_o", "HIS_STATS_p",
62         "HIS_STATS_q", "HIS_STATS_r", "HIS_STATS_R", "HIS_STATS_t",
63         "HIS_STATS_T", "HIS_STATS_u", "HIS_STATS_U", "HIS_STATS_v",
64         "HIS_STATS_v", "HIS_STATS_w", "HIS_STATS_x", "HIS_STATS_z",
65         "HIS_WHOIS_SERVERNAME", "HIS_WHOIS_IDLETIME", "HIS_WHOIS_LOCALCHAN",
66         "HIS_WHO_SERVERNAME", "HIS_WHO_HOPCOUNT"," HIS_BANWHO", "HIS_KILLWHO",
67         "HIS_REWRITE", "HIS_REMOTE", "HIS_NETSPLIT", "HIS_SERVERNAME", 
68         "HIS_SERVERINFO", "HIS_URLSERVERS"
69         ]
70
71 # [ "old feature" => ( local oper priv, global oper priv ) ]
72 # None means don't add this
73 feature_to_priv = {
74         "UNLIMIT_OPER_QUERY" : ("unlimit_query","unlimit_query"),
75         "OPER_WALK_THROUGH_LMODES" : (None, "walk_lchan"),
76         "NO_OPER_DEOP_LCHAN" : (None, "deop_lchan"),
77         }
78
79 def qstr(s):
80         return replace(s,'"','\\"')
81
82 def istr(s):
83         return str(int(strip(s)))
84         
85
86 def do_uline(parts):
87         print "Uworld {"
88         print "\tname = \"%s\";" % qstr(parts[1])
89         print "};"
90         print
91         if len(parts[2]):
92                 for i in split(parts[2],","):
93                         jupes.append(i)
94
95 def do_hline(parts):
96         if not connects.has_key(lower(parts[3])):
97                 connects[lower(parts[3])]={
98                         "name" : lower(parts[3])
99                 }
100         connects[lower(parts[3])]["hub"] = parts[1]
101
102 def do_lline(parts):
103         if not connects.has_key(lower(parts[3])):
104                 connects[lower(parts[3])]={
105                         "name" : lower(parts[3])
106                 }
107         del connects[lower(parts[3])]["hub"]
108
109 def do_pline(parts):
110         print "Port {"
111         print "\tport = %s;" % istr(parts[4])
112         if len(parts[1]):
113                 print "\tmask = \"%s\";" % qstr(parts[1])
114         if len(parts[2]):
115                 print "\tvhost = \"%s\";" % qstr(parts[2])
116         if count(parts[3], 'S'):
117                 print "\tserver = yes;"
118         if count(parts[3], 'H'):
119                 print "\thidden = yes;"
120         print "};"
121         print
122
123 def do_fline(parts):
124         feats.append((parts[1], parts[2]))
125
126 def do_kline(parts):
127         if len(parts)!=4:
128                 sys.stderr.write("WARNING: Wrong number of parameters on line %i\n" % lno)
129                 return
130         letter,host,reason,user=parts
131         print "Kill {"
132         if host[:2]=="$R":
133                 if host=="$R":
134                         sys.stderr.write("WARNING: Empty realname kline on line %i\n" % lno)
135                 if user!="*":
136                         print '\thost = "%s@*";' % qstr(user)
137                 print "\trealname = \"%s\";" % qstr(host[2:])
138         else:
139                 print "\thost = \"%s@%s\";" % (qstr(user),qstr(host))
140         if reason[:1]=="!":
141                 print "\tfile = \"%s\";" % qstr(reason[1:])
142         else:
143                 print "\treason = \"%s\";" % qstr(reason)
144         print "};"
145         print
146
147 def do_iline(parts):
148         if len(parts)!=6:
149                 sys.stderr.write("WARNING: I:line doesn't have enough fields on line %i\n" % lno)
150                 return
151         iline,ip,password,hostname,dummy,clss = parts
152         for i in [0,1]:
153                 mask = [ip,hostname][i]
154                 # Ignore things that aren't masks
155                 if "." not in mask and "*" not in mask and "@" not in mask:
156                         continue
157                 if "@" in mask:
158                         user,host = split(mask,"@")
159                 else:
160                         user,host = "",mask
161                 if i==0 and not re.match("^[0-9\.\*]+$",host):
162                         sys.stderr.write("WARNING: Bad IP mask in line %s (%s)\n" % (lno,repr(mask)))
163                         continue
164                 print "Client {"
165                 if re.match("^[1-9][1-9]?$",password):
166                         print "\tmaxlinks = %s;" % int(password)
167                 elif password:
168                         print "\tpassword = \"%s\";" % qstr(password)
169                 print "\tclass = \"%s\";" % clss
170                 if i == 0:
171                         print "\tip = \"%s\";" % qstr(host)
172                 else:
173                         print "\thost = \"%s\";" % qstr(host)
174                 if user!="":
175                         print "\tusername = \"%s\";" % qstr(user)
176                 print "};"
177                 print
178
179 def do_cline(parts):
180         name=lower(parts[3])
181         if not connects.has_key(name):
182                 connects[name]={}
183         connects[name]["host"]=parts[1]
184         connects[name]["password"]=parts[2]
185         connects[name]["name"]=parts[3]
186         if parts[4].strip()!="":
187                 connects[name]["port"]=parts[4]
188         connects[name]["class"]=parts[5]
189
190 def do_qline(parts):
191         print "Quarintine {"
192         print '\t"%s" = "%s";' % (qstr(parts[1]),qstr(parts[2]))
193         print "}"
194
195 def do_oline(parts):
196         opers.append(parts)
197
198 cvtmap = {
199         'M': ('General', ('name', 'vhost', 'description', '', '!numeric'), ''),
200         'A': ('Admin', ('location', 'contact', 'contact'), ''),
201         'Y': ('Class', ('name', '!pingfreq', '!connectfreq', '!maxlinks', '!sendq'), ''),
202         'I': do_iline,
203         'T': ('motd', ('host', 'file'), ''),
204         'U': do_uline,
205         'H': do_hline,
206         'L': do_lline,
207         'K': do_kline,
208         'k': do_kline,
209         'C': do_cline,
210         'D': ('CRULE', ('server', '', 'rule'), '\tall = yes;'),
211         'd': ('CRULE', ('server', '', 'rule'), ''),
212         'O': do_oline,
213         'o': do_oline,
214         'Q': ('Quarintine', ('channel','reason', '', '', ''), ''),
215         'P': do_pline,
216         'F': do_fline
217 }
218
219 lno=0
220 for line in f.readlines():
221         lno=lno+1
222         line = strip(line)
223         if line=="":
224                 continue
225         if line[0]=="#":
226                 print "#"+line
227                 continue
228         print "#",line
229         parts = split(line, ":")
230         parts=['']
231         # This statemachine is pretty much directly stolen from ircu
232         # to give an "authentic" parser :)
233         state=0 # normal
234         quoted=0
235         for i in line:
236                 if state==0:
237                         if i=="\\":
238                                 state=1 # escaped
239                         elif i=='"':
240                                 quoted=not quoted
241                         elif i==':':
242                                 if quoted:
243                                         parts[-1]=parts[-1]+i
244                                 else:
245                                         parts.append("")
246                         elif i=='#':
247                                 break
248                         else:
249                                 parts[-1]=parts[-1]+i
250                 elif state==1:
251                         if i in "bfnrtv":
252                                 parts[-1]=parts[-1]+"\b\f\n\r\t\v"[index("bfnrtv",i)]
253                         else:
254                                 parts[-1]=parts[-1]+i
255                         state=0
256         if quoted:
257                 sys.stderr.write("WARNING: No closing quote on line %i\n"%lno)
258         if not len(parts):
259                 continue
260         if not cvtmap.has_key(parts[0]):
261                 print "#Unknown:",line
262                 continue
263         if callable(cvtmap[parts[0]]):
264                 cvtmap[parts[0]](parts)
265                 continue
266         (block, items, extra) = cvtmap[parts[0]]
267
268         print block, "{"
269         idx = 1
270         for item in items:
271                 if idx >= len(parts):
272                         break
273                 if len(parts[idx]) and len(item):
274                         if item[0] == '!':
275                                 print "\t%s = %s;" % (item[1:], istr(parts[idx]))
276                         else:
277                                 print "\t%s = \"%s\";" % (item, qstr(parts[idx]))
278                 idx = idx + 1
279         if len(extra):
280                 print extra
281         print "};"
282         print
283
284 if len(opers):
285         for i in opers:
286                 print 'Operator {'
287                 print '\tname = "%s";' % qstr(i[3])
288                 print '\thost = "%s";' % qstr(i[1])
289                 print '\tpassword = "%s";' % qstr(i[2])
290                 print '\tclass = "%s";' % qstr(i[5])
291                 if i[0]=='O':
292                         print '\tlocal = no;'
293                 else:
294                         print '\tlocal = yes;'
295                 for j in feats:
296                         if (j[0].startswith("LOCOP_") and i[0]=='o'):
297                                 print "#",i
298                                 if j[1].lower()=="true":
299                                         print '\t%s = yes;'% (j[0][6:].lower())
300                                 else:
301                                         print '\t%s = no;' % (j[0][6:].lower())
302                         if (j[0].startswith("OPER_") and i[0]=='O'):
303                                 if j[1].lower()=="true":
304                                         print '\t%s = yes;'% (j[0][5:].lower())
305                                 else:
306                                         print '\t%s = no;' % (j[0][5:].lower())
307                         if feature_to_priv.has_key(j[0]):
308                                 if i[0]=="o" and feature_to_priv[j[0]][0]:
309                                         if j[1].lower()=="true":
310                                                 print '\t%s = yes;' % feature_to_priv[j[0]][0]
311                                         else:
312                                                 print '\t%s = yes;' % feature_to_priv[j[0]][0]
313                                 if i[0]=="O" and feature_to_priv[j[0]][1]:
314                                         if j[1].lower()=="true":
315                                                 print '\t%s = yes;' % feature_to_priv[j[0]][1]
316                                         else:
317                                                 print '\t%s = yes;' % feature_to_priv[j[0]][1]
318                 print '};'
319                 print
320
321 if len(jupes):
322         print "Jupe {"
323         for nick in jupes:
324                 print "\tnick = \"%s\";" % qstr(nick)
325         print "};"
326         print
327
328 if len(connects.keys()):
329         for i in connects.keys():
330                 print "Connect {"
331                 print "\tname = \"%s\";" % qstr(connects[i]["name"])
332                 print "\thost = \"%s\";" % qstr(connects[i]["host"])
333                 print "\tpassword = \"%s\";" % qstr(connects[i]["password"])
334                 if connects[i]["port"]:
335                         print "\tport = %s;" % connects[i]["port"]
336                 print "\tclass = \"%s\";" % qstr(connects[i]["class"])
337                 if connects[i].has_key("hub"):
338                         print "\thub = \"%s\";" % qstr(connects[i]["hub"])
339                 else:
340                         print "\tleaf;"
341                 print "};"
342                 print
343
344 if len(feats):
345         print "features {"
346         for (name, value) in feats:
347                 if name in useable_features:
348                         print "\t\"%s\" = \"%s\";" % (qstr(name), qstr(value))
349                 else:
350                         if feature_to_priv.has_key(name):
351                                 print '# Option converted to privilege "%s"' % \
352                                         qstr(feature_to_priv[name][1])
353                         elif name.startswith("LOCOP_"):
354                                 print "# Option converted to locop privilege"
355                         elif name.startswith("OPER_"):
356                                 print "# Option converted to oper privilege"
357                         else:
358                                 print "# Option no longer exists in 2.10.12"
359                         print "#\t\"%s\" = \"%s\";" % (qstr(name), qstr(value))
360         print "};"
361         print