c70c23118a14d560fc12e658bbad7ad20262ef10
[samba.git] / examples / scripts / python / SambaConfig.py
1 import sys, string, SambaParm
2 from smbparm import parm_table
3
4 ######################################################################
5 ##
6 ##  smb.conf parser class
7 ##
8 ##  Copyright (C) Gerald Carter                2004.
9 ##
10 ##  This program is free software; you can redistribute it and/or modify
11 ##  it under the terms of the GNU General Public License as published by
12 ##  the Free Software Foundation; either version 2 of the License, or
13 ##  (at your option) any later version.
14 ##
15 ##  This program is distributed in the hope that it will be useful,
16 ##  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ##  GNU General Public License for more details.
19 ##
20 ##  You should have received a copy of the GNU General Public License
21 ##  along with this program; if not, write to the Free Software
22 ##  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ##
24 ######################################################################
25
26
27 #####################################################################
28 ## multi line Samba comment
29 class SambaComment:
30
31         def __init__( self, comment ):
32                 self.comment = comment
33                 
34         def Dump( self, stream, whitespace=None ):
35                 if not self.comment:
36                         return
37                 for line in self.comment:
38                         if whitespace:
39                                 stream.write( whitespace )
40                         stream.write( line )
41                         stream.write( "\n" )
42                 
43         
44 #####################################################################
45 ## string smb.conf parms
46 class SambaParameter :
47
48         ## indexs into the parm table tuples
49         DisplayName  = 0
50         ObjectType   = 1
51         DefaultValue = 2
52         Scope        = 3
53
54         ## Stores a key into the parm_table and creates an
55         ## SambaParmXXX object to store the value
56         def __init__( self, name, value, comment=None ):
57                 self.key = string.upper(string.strip(name))
58                 self.comment = None
59                 assert parm_table.has_key( self.key ), "Bad parameter name! [%s]" % name
60                 self.parm = parm_table[self.key][self.ObjectType]( value )
61                 if comment :
62                         self.comment = SambaComment( comment )
63                         
64                 #if not self.parm.valid:
65                 #       self.parm.SetValue( parm_table[self.key][self.DefaultValue] )
66
67         ## simple test for global or service parameter scope
68         def isGlobalParm( self ) :
69                 return parm_table[self.key][Scope]
70
71         ## dump <the parameter to stdout
72         def Dump( self, stream ):
73                 if self.comment:
74                         self.comment.Dump( stream, "\t" )
75                 stream.write( "\t%s = %s\n" % ( parm_table[self.key][self.DisplayName], self.parm.StringValue() ))
76
77
78 #####################################################################
79 ## Class for parsing and modifying Smb.conf 
80 class SambaConf:
81                 
82         def __init__( self ):
83                 self.services = {}
84                 self.valid = True
85                 self.services["GLOBAL"] = {}
86                 self.services_order = []
87                 
88         
89         ## always return a non-empty line of input or None
90         ## if we hit EOF
91         def ReadLine( self, stream ):
92                 result = None
93                 input_str = None
94                 
95                 while True:
96                         input_str = stream.readline()
97                 
98                         ## Are we done with the file ?
99                         
100                         if len(input_str) == 0:
101                                 return result
102                         
103                         ## we need one line of valid input at least
104                         ## continue around the loop again if the result
105                         ## string is empty
106                         
107                         input_str = string.strip( input_str )
108                         if len(input_str) == 0:
109                                 if not result:
110                                         continue
111                                 else:
112                                         return result
113                         
114                         ## we have > 1` character so setup the result
115                         if not result: 
116                                 result = ""
117                         
118                         ## Check for comments -- terminated by \n -- no continuation
119                         
120                         if input_str[0] == '#' or input_str[0] == ';' :
121                                 result = input_str
122                                 break
123                         
124                         ## check for line continuation                  
125                         
126                         if input_str[-1] == "\\" :
127                                 result += input_str[0:-1]
128                                 contine
129
130                         ## otherwise we have a complete line
131                         result += input_str
132                         break
133
134                 return result
135                 
136         ## convert the parameter name to a form suitable as a dictionary key    
137         def NormalizeParamName( self, param ):
138                 return string.upper( string.join(string.split(param), "") )
139                 
140         ## Open the file and parse it into a services dictionary
141         ## if possible
142         def ReadConfig( self, filename ):
143                 self.filename = filename
144
145                 try:
146                         fconfig = open( filename, "r" )
147                 except IOError:
148                         self.valid = False
149                         return
150
151                 section_name = None
152                 
153                 ## the most recent seen comment is stored as an array
154                 current_comment = []
155                 
156                 while True:
157                 
158                         str = self.ReadLine( fconfig )
159                         if not str:
160                                 break
161
162                         ## Check for comments
163                         if str[0] == '#' or str[0] == ';' :
164                                 current_comment.append( str )
165                                 continue
166
167                         ## look for a next section name
168                         if str[0]=='[' and str[-1]==']' :
169                                 section_name = str[1:-1]
170                                 self.AddService( section_name, current_comment )
171                                 current_comment = []
172                                 continue
173
174                         str_list = string.split( str, "=" )
175
176                         if len(str_list) != 2 :
177                                 continue
178
179                         if not section_name :
180                                 print "parameter given without section name!"
181                                 break
182
183                         param = self.NormalizeParamName( str_list[0] )
184                         value = string.strip(str_list[1])
185
186                         self.SetServiceOption( section_name, param, value, current_comment )
187                         self.dirty = False
188                         
189                         ## reset the comment strinf if we have one
190                         current_comment = []
191                         
192                 fconfig.close()
193
194         ## Add a parameter to the global section
195         def SetGlobalOption( self, param, value, comment=None ) :
196                 self.SetServiceOption( "GLOBAL", param, value, comment )
197
198         ## Add a parameter to a specific service
199         def SetServiceOption( self, servicename, param, value, comment=None ) :
200                 service = string.upper(servicename)
201                 parm = self.NormalizeParamName(param)
202                 self.services[service]['_order_'].append( parm )
203                 self.services[service][parm] = SambaParameter( parm, value, comment )
204                 self.dirty = True
205
206         ## remove a service from the config file
207         def DelService( self, servicename ) :
208                 service = string.upper(servicename)
209                 self.services[service] = None
210                 self.dirty = True
211                 
212         ## remove a service from the config file
213         def AddService( self, servicename, comment=None ) :
214                 service = string.upper(servicename)
215
216                 self.services[service] = {}
217                 self.services[service]['_order_'] = []
218
219                 if ( comment ):
220                         self.services[service]['_comment_'] = SambaComment( comment )
221
222                 self.services_order.append( service )
223
224                 self.dirty = True
225                 
226         def isService( self, servicename ):
227                 service = string.upper(servicename)
228                 return self.services.has_key( service )
229                 
230         ## dump a single service to stream
231         def DumpService( self, stream, servicename ):
232         
233                 ## comments first 
234                 if self.services[servicename].has_key( '_comment_' ):
235                         self.services[servicename]['_comment_'].Dump( stream )
236                         
237                 ## section header
238                 stream.write( "[%s]\n" % (servicename) )
239                 
240                 ## parameter = value
241                 for parm in self.services[servicename]['_order_']:
242                         self.services[servicename][parm].Dump(stream)
243         
244         ## dump the config to stream
245         def Dump( self, stream ):
246                 self.DumpService( stream, "GLOBAL" )
247                 stream.write("\n")
248                 
249                 for section in self.services_order:
250                         ## already handled the global section
251                         if section == "GLOBAL": 
252                                 continue
253                                 
254                         ## check for deleted sections ##
255                         if not self.services[section]:
256                                 continue
257                                 
258                         self.DumpService( stream, section )
259                         stream.write( "\n" )
260                         
261         ## write out any changes to disk
262         def Flush( self ):
263                 if not self.dirty: 
264                         return
265                         
266                 try:
267                         fconfig = open( self.filename, "w" )
268                 except IOError:
269                         sys.stderr.write( "ERROR!\n" ) 
270                         return 1
271                         
272                 self.Dump( fconfig )
273                 fconfig.close()
274                 return 0
275                 
276         def Services( self ):
277                 service_list = []
278                 for section in self.services.keys():
279                         service_list.append( section )
280                         
281                 return service_list
282                 
283         def NumServices( self ):
284                 return len(self.Services())
285                 
286         def Write( self, filename ):
287                 self.filename = filename
288                 self.valid = True
289
290                 if not self.dirty:
291                         return
292                 
293                 self.Flush()
294                         
295                 
296
297 ######################################################################
298 ## Unit tests
299 ######################################################################
300
301 if __name__ == "__main__" :
302
303         x = SambaConf( )
304         x.ReadConfig( sys.argv[1] )
305         if not x.valid :
306                 print "Bad file!"
307                 sys.exit(1)
308
309         x.Dump( sys.stdout )
310         
311         
312         
313 ## end of SambaConfig.py ######################################################
314 ###############################################################################
315