Rationale ^^^^^^^^^ The rationale for this proposal is summarized in the following points: * No module should care where the configuration actually lies and the user should be free to use any means to configure their pbx, ranging from simple flat configuration files, via XML, to a distributed enterprise-wide LDAP directory that has configuration data for lots of pbxes. It isn't even necessary that all parts of the configuration come from the same backend. * The configuration should be powerful enough to be extensible to future needs of future modules without having to add specialized configuration handling for those modules. * Common configuration information should be specified in one place only, not once for each module that needs it. The most common example is users and their associated passwords, pin-codes and email addresses. * The configuration should have a well defined structure. * The system should support different realms so one can support virtual hosts on a single pbx, or have configuration data for different pbxes in the same place. The dialplan is a special-case that doesn't fit exactly within this model due to it's enormous complexity. Therefore the dialplan is not specified in this proposal. Configuration system description ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Hierarchical structure for the configuration space. This gives us a very high degree of flexibility and extensibility. Support for multiple realms with configuration data gives us the possibility to have configuration data for multiple pbxes in the same system. The ability to specify multiple realms when searching for a configuration item gives us a very flexible way to organize the configuration and share items that are common to all pbxes. A possibility of having references in the configuration that point to another object in the tree at another position and thereby including the same subtree in multiple places was considered, but it would cause an increase in complexity that is probably not motivated since the same effect can be had using multiple realms as defined above. Parameters are accessed by an identifier that uniquely identifies an attribute in the configuration object. Getting the SIP hostname for user "demo" might be done something like this: user = opbx_conf_getobject(realm, "User.demo"); opbx_conf_getstring(user, "SIP.HostName", hostname); A user object can then be extended by adding new subobjects to it and keeping the information grouped within that subobject. We could even have an API that allows a module to add items to a user that is already configured, if that module has some special knowledge of the user. This would complicate the configuration system a bit, since user data couldn't be directly read from a backend anymore as some parts of mit might not be coming from that backend. Configuration hierarchies could even be organized into realms to allow a form of virtual hosting. However if this is desirable support from it should be included from the beginning. A minimal user object might only contain the username to identify the user as all other information is optional. A problem with this approach would be that the channels would have to know that user configuration may be in more than one place. They would either have to be taught to look for peer information in the channels own part of the hierarchy as well as the user part of the hierarchy. It might be a good thing to have a special syntax for dialstrings that refer to a user's phone and make a distinction between user phones and connections to other pbxes, telcos etc. A typical user might look like this: Username: demo Extension: 4242 PersonalInfo -> Name: Demo User EMail: demo@example.com PhoneNumber: 123456789 FaxNumber: 987654321 AuthInfo -> Password: abc123 PIN: 1234 SIP -> Hostname: semilocal.host HostIP: 127.0.0.2 NAT: YES T38Capable: NO Codecs -> Allowed: alaw,ulaw,gsm Priority: alaw,ulaw IAX2 -> Hostname: semilocal.host HostIP: 127.0.0.2 Codecs -> Allowed: alaw,ulaw,ilbc,speex,gsm Priority: alaw,ulaw,speex Transfer: NO Initial API draft ^^^^^^^^^^^^^^^^^ The configuration is organised hierarchically in the form of a tree. Each point in a tree has an identifier associated with it. The identifiers are NOT case sensitive. To specify a full path in a tree identifiers for the different branches are separated by periods '.' . Below is an example of an identifier that refers to the email address of the user demo in the default realm: default.Users.demo.PersonalInfo.EMail An identifier doesn't have to be specified from the root of the configuration tree if the root can be specified in some other manner. If an identifier specifies a complete path from the root it is called a Fully Qualified Identifier (FQI), otherwise it is a Relative Identifier (RI). realm = opbx_conf_getrealm(realm_name); The function returns a realm handle that refers to the current configuration realm. The realms are used to separate different configurations accessible within the pbx. This is to allow for a simple form of virtual hosting. Initially only the realm "default" is defined, until the pbx has full support for different realms. realm = opbx_conf_appendrealm(realm, realm_name); Sometimes it is necessary to search more than one realm for configuration information. Some configurations may have a different realm for each pbx since each pbx has a unique configuration, but one common realm for user accounts since all pbxes need to share user data. This function appends the specified realm to the current realm identifier. The realms are searched in the order they are specified. Example: realm = opbx_conf_getrealm("pbx1"); realm = opbx_conf_appendrealm(realm, "users"); obj1 = opbx_conf_getobj(realm, "SIP.Stuff"); obj2 = opbx_conf_getobj(realm, "User.demo"); In the above example requests are made for the objects identified by "SIP.Stuff" and "User.demo". The configuration API searches the realms in turn for those objects and returns the first one found. In the example specified this will result in the obj1 to be found in the first realm, since it is defined in the specific pbx configuration. obj2 will be found in the second realm since it is the object that contains user account information for user 'demo'. obj = opbx_conf_getobject(root, identifier); Returns an object that refers to the specified point in the configuration tree relative to the 'root' parameter. If the conf parameter is NULL the identifier must refer to a configuration entity from the root of the config tree. Getting the SIP configuration object from the default realm could be done in the following manner: def_realm = opbx_conf_realm("default");' sip_obj = opbx_conf_object(def_realm, "SIP"); Or if a complete identifier is specified with: sip_obj = opbx_conf_object(NULL, "default.SIP"); obj = opbx_conf_search(root, identifier, value); Searches the configuration for the Relative Identifier specified as 'RI'. This could for example be used by a SIP stack to find a user that has a particular peer address: peer = opbx_conf_search(NULL, "SIP.HostIP", "127.0.0.2") fqi = opbx_conf_getfqi(obj); Given a configuration object this function returns the Fully Qualified Identifier associated with this object. obj = opbx_conf_getparent(obj); Giben a configuration object this function returns the parent object that contains this object, or NULL if this is the root. list = opbx_conf_listattribs(obj); Lists the names of all attributes defined in this object. list = opbx_conf_listsubs(obj); Lists the names of all subobjects contained within this object. err = opbx_conf_getstring(root, identifier, string); Gets the string value contained in the attribute refered to by identifier. Returns NULL if the value exists and is a string and an error code otherwise. err = opbx_conf_getint(root, identifier, int); Gets the integer value contained in the attribute refered to by identifier. Returns NULL if the value exists and is an integer and an error code otherwise. err = opbx_conf_getbool(root, identifier, bool); Gets the boolean value contained in the attribute refered to by identifier. Returns NULL if the value exists and is an boolean and an error code otherwise.