Windows

Create a Web configuration INI file

Discover how you can implement your own INI file for Web application configuration information. The INI file can provide a central repository for this information to which you can provide a Web front-end to make configuration changes.

Application-specific configuration information has historically been stored in INI files, which are plain-text files that contain key/value data within sections. In Windows, the registry has replaced INI files for configuration information. However, a lot of applications still use INI files.

In a Web application, configuration information is sometimes hard-coded, leaving the configuration information scattered and difficult to maintain. If you implement your own INI file for Web application configuration information, it provides a central repository for this information to which you can provide a Web front-end to make configuration changes.

INI files usually contain information in the following format:

 [Section]
key=value

Also, the Windows' API includes some functions for getting and storing INI information. Two of these functions are getPrivateProfileString() and writePrivateProfileString(). These functions wrap up the steps to export and import configuration data to/from an INI file. You can mimic this same functionality in your INI files; but instead of using the INI format as above, you can implement an XML-based INI file.

In the following example, I'll create a class in ASP to wrap up the functionality for collecting and writing configuration information. The class will contain two methods, getPrivateProfileString() and writePrivateProfileString(), through which I'll mimic the interface of the two Windows' API functions. Here's the code:

Class CINI
Private m_dom

Public Function getApplicationPath()
    Dim sURL
    sURL = Request.ServerVariables("URL")
    getApplicationPath = Mid(sURL, 1, InStr(2, sURL, "/"))
End Function

Public Function getPrivateProfileString(sDomain, sKeyName, sDefault,
 sFileName)
    Dim m_result
    m_result = ""
    If m_dom.load(sFileName) Then
        Dim domain_node
        Set domain_node = m_dom.selectSingleNode("//domain[@domain_name='"
& LCase(sDomain) & "']")
        If domain_node Is Nothing Then
            m_result = sDefault
            Err.Raise vbObjectError, "", "no domain"
        Else
            Dim node
            Set node = domain_node.selectSingleNode(sKeyName)
            If node Is Nothing Then
                m_result = sDefault
                Err.Raise vbObjectError, "", "no node"
            Else
                m_result = Unescape(node.text)
            End If
            Set node = Nothing
        End If
        Set domain_node = Nothing
    Else
        m_result = sDefault
        Err.Raise vbObjectError, "", "File Error: Cannot open or find " &
sFileName
 & "."
    End If
    getPrivateProfileString = m_result
End Function

Public Function writePrivateProfileString(sDomain, sKeyName, sString,
sFileName)
    If m_dom.load(sFileName) Then
        Dim domain_node, node
        Set domain_node =
 m_dom.selectSingleNode("//domain[@domain_name='" & LCase(sDomain) &
 "']")
        If domain_node Is Nothing Then
            Dim domain_attr, map
            Set domain_node = m_dom.createNode(1, "domain", "")
            Set domain_attr = m_dom.createNode(2, "domain_name", "")            
            domain_attr.text = LCase(sDomain)
            Set map = domain_node.Attributes
            map.setNamedItem domain_attr
            Set domain_node =
m_dom.documentElement.appendChild(domain_node)
            Set map = Nothing
            Set domain_attr = Nothing
        End If
        Set node = domain_node.selectSingleNode(sKeyName)
        If node Is Nothing Then
            Set node = m_dom.createNode(1, sKeyName, "")
            Set node = domain_node.appendChild(node)
        End If
        node.text = Escape(sString)
        If m_dom.save(sFileName) <> 0 Then _
            Err.Raise vbObjectError, "CToolbox::writePrivateProfileString(.., "
&
 sFileName & ")", "Cannot save file for some reason."
        Set domain_node = Nothing
        Set node = Nothing
    Else
        Err.Raise vbObjectError, "", "File Error: Cannot open or find " &
sFileName
 & "."
    End If
End Function

Private Sub Class_Initialize
    Set m_dom = Server.CreateObject("MSXML2.DOMDocument")
    m_dom.async = False
End Sub

Private Sub Class_Terminate
    Set m_dom = Nothing
End Sub

End Class

The first thing to note is that application information will be stored in the following XML format:

<root>
    <domain domain_name="my_domain">
        <key>value</key>
    </domain>
</root>

From this example, you can see that information is stored relative to a domain. The domain is like the Section part of the INI file so you can store information specific to the Web application. Web applications exist under a given domain, which you can find at the root of the HTTP environment variable "URL."

After reviewing the code, you'll see that I included a simple little function called getApplicationPath() to get the domain name of the current script. If you look at the method writePrivateProfileString(), you'll see that the method expects four parameters: sDomain, sKeyName, sString, and sFileName. This method loads the XML file specified by the sFileName parameter. Once it loads successfully, it searches for the domain node by the sDomain parameter. It does this by selecting the node based on the XPath query "domain[@domain_name='sDomain']". The sDomain parameter is converted to lowercase to avoid problems with case sensitivity. If the domain node doesn't exist, a domain node is created, the domain_name attribute value is set, and the domain node is appended to the documentElement of the XML document. The domain node is then used to create the key/value pairs.

A sKeyName node is created in the XML document. The only drawback to sKeyName is that it must be a qualified name, which means that it must abide by the rules of XML convention for qualified names. When the node is created, it's appended to the domain node. The sKeyName node's text is then set to the escaped sString parameter value, which is escaped to avoid possible special characters from causing parsing errors in the XML document. Once all the changes occur, the XML document is saved back to sFileName.

The getPrivateProfileString() method also accepts four parameters: sDomain, sKeyName, sDefault, and sFileName. The XML document is loaded from sFileName. The domain node is located using the same logic as writePrivateProfileString(). If the domain node is found, the sKeyName node is located, and, upon location, the return value of the method is the text of the sKeyName node. If the sKeyName node isn't located, the return value is the value of the sDefault parameter.

In order to utilize this functionality, you must provide explicit permission for file access to the INI file. There are several ways you can do this. One way to do this is to set the anonymous user of the accessing page to an account with the appropriate privileges. Another way is to allow read/write access privileges of the INI file to the IIS anonymous user account, which is usually IUSR_[COMPUTERNAME]. And yet a third way is to restrict the Web page's usage through Basic or NTLM authentication (note that Basic authentication isn't recommended).

You can also use this logic to create the same functionality in other languages. However, the specifics of the DOM Document interface are different for different languages, as is the language syntax.

Keep your developer skills sharp by automatically signing up for TechRepublic's free Web Development Zone newsletter, delivered each Tuesday.

0 comments

Editor's Picks