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 ######################################################
###############################################################################