import sys, string, SambaParm from smbparm import parm_table ###################################################################### ## ## smb.conf parser class ## ## Copyright (C) Gerald Carter 2004. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see <http://www.gnu.org/licenses/>. ## ###################################################################### ##################################################################### ## multi line Samba comment class SambaComment: def __init__( self, comment ): self.comment = comment def Dump( self, stream, whitespace=None ): if not self.comment: return for line in self.comment: if whitespace: stream.write( whitespace ) stream.write( line ) stream.write( "\n" ) ##################################################################### ## string smb.conf parms class SambaParameter : ## indexs into the parm table tuples DisplayName = 0 ObjectType = 1 DefaultValue = 2 Scope = 3 ## Stores a key into the parm_table and creates an ## SambaParmXXX object to store the value def __init__( self, name, value, comment=None ): self.key = string.upper(string.strip(name)) self.comment = None assert parm_table.has_key( self.key ), "Bad parameter name! [%s]" % name self.parm = parm_table[self.key][self.ObjectType]( value ) if comment : self.comment = SambaComment( comment ) #if not self.parm.valid: # self.parm.SetValue( parm_table[self.key][self.DefaultValue] ) ## simple test for global or service parameter scope def isGlobalParm( self ) : return parm_table[self.key][Scope] ## dump <the parameter to stdout def Dump( self, stream ): if self.comment: self.comment.Dump( stream, "\t" ) stream.write( "\t%s = %s\n" % ( parm_table[self.key][self.DisplayName], self.parm.StringValue() )) ##################################################################### ## Class for parsing and modifying Smb.conf class SambaConf: def __init__( self ): self.services = {} self.valid = True self.services["GLOBAL"] = {} self.services_order = [] ## always return a non-empty line of input or None ## if we hit EOF def ReadLine( self, stream ): result = None input_str = None while True: input_str = stream.readline() ## Are we done with the file ? if len(input_str) == 0: return result ## we need one line of valid input at least ## continue around the loop again if the result ## string is empty input_str = string.strip( input_str ) if len(input_str) == 0: if not result: continue else: return result ## we have > 1` character so setup the result if not result: result = "" ## Check for comments -- terminated by \n -- no continuation if input_str[0] == '#' or input_str[0] == ';' : result = input_str break ## check for line continuation if input_str[-1] == "\\" : result += input_str[0:-1] contine ## otherwise we have a complete line result += input_str break return result ## convert the parameter name to a form suitable as a dictionary key def NormalizeParamName( self, param ): return string.upper( string.join(string.split(param), "") ) ## Open the file and parse it into a services dictionary ## if possible def ReadConfig( self, filename ): self.filename = filename try: fconfig = open( filename, "r" ) except IOError: self.valid = False return section_name = None ## the most recent seen comment is stored as an array current_comment = [] while True: str = self.ReadLine( fconfig ) if not str: break ## Check for comments if str[0] == '#' or str[0] == ';' : current_comment.append( str ) continue ## look for a next section name if str[0]=='[' and str[-1]==']' : section_name = str[1:-1] self.AddService( section_name, current_comment ) current_comment = [] continue str_list = string.split( str, "=" ) if len(str_list) != 2 : continue if not section_name : print "parameter given without section name!" break param = self.NormalizeParamName( str_list[0] ) value = string.strip(str_list[1]) self.SetServiceOption( section_name, param, value, current_comment ) self.dirty = False ## reset the comment strinf if we have one current_comment = [] fconfig.close() ## Add a parameter to the global section def SetGlobalOption( self, param, value, comment=None ) : self.SetServiceOption( "GLOBAL", param, value, comment ) ## Add a parameter to a specific service def SetServiceOption( self, servicename, param, value, comment=None ) : service = string.upper(servicename) parm = self.NormalizeParamName(param) self.services[service]['_order_'].append( parm ) self.services[service][parm] = SambaParameter( parm, value, comment ) self.dirty = True ## remove a service from the config file def DelService( self, servicename ) : service = string.upper(servicename) self.services[service] = None self.dirty = True ## remove a service from the config file def AddService( self, servicename, comment=None ) : service = string.upper(servicename) self.services[service] = {} self.services[service]['_order_'] = [] if ( comment ): self.services[service]['_comment_'] = SambaComment( comment ) self.services_order.append( service ) self.dirty = True def isService( self, servicename ): service = string.upper(servicename) return self.services.has_key( service ) ## dump a single service to stream def DumpService( self, stream, servicename ): ## comments first if self.services[servicename].has_key( '_comment_' ): self.services[servicename]['_comment_'].Dump( stream ) ## section header stream.write( "[%s]\n" % (servicename) ) ## parameter = value for parm in self.services[servicename]['_order_']: self.services[servicename][parm].Dump(stream) ## dump the config to stream def Dump( self, stream ): self.DumpService( stream, "GLOBAL" ) stream.write("\n") for section in self.services_order: ## already handled the global section if section == "GLOBAL": continue ## check for deleted sections ## if not self.services[section]: continue self.DumpService( stream, section ) stream.write( "\n" ) ## write out any changes to disk def Flush( self ): if not self.dirty: return try: fconfig = open( self.filename, "w" ) except IOError: sys.stderr.write( "ERROR!\n" ) return 1 self.Dump( fconfig ) fconfig.close() return 0 def Services( self ): service_list = [] for section in self.services.keys(): service_list.append( section ) return service_list def NumServices( self ): return len(self.Services()) def Write( self, filename ): self.filename = filename self.valid = True if not self.dirty: return self.Flush() ###################################################################### ## Unit tests ###################################################################### if __name__ == "__main__" : x = SambaConf( ) x.ReadConfig( sys.argv[1] ) if not x.valid : print "Bad file!" sys.exit(1) x.Dump( sys.stdout ) ## end of SambaConfig.py ###################################################### ###############################################################################