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