DIRKRB-499 A complete krb5 conf file parser. Contributed by Yan.
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
new file mode 100644
index 0000000..a7654dc
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
@@ -0,0 +1,216 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.common;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A parser to parse krb5.conf format file.
+ */
+public class Krb5Parser {
+    private File krb5conf;
+    /**
+     * The variable items regards section name as a key of Map, and
+     * contents of a section as a value with Object type.
+     * In specific, the value is a recursive Map type, which can be
+     * in the form of both Map<String, String> and Map<String, Object>,
+     * depending on contents of the section.
+     */
+    private Map<String, Object> items;
+
+    public Krb5Parser(File confFile) {
+        krb5conf = confFile;
+        items = null;
+    }
+
+    /**
+     * Load the krb5.conf into a member variable, which is a Map.
+     * @throws IOException
+     */
+    public void load() throws IOException {
+        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(krb5conf)));
+        items = new HashMap<String, Object>();
+
+        String originLine = br.readLine();
+        while (originLine != null) {
+            String line = originLine.trim();
+            /*parse through comments*/
+            if (line.startsWith("#")) {
+                originLine = br.readLine();
+            }   else if (line.startsWith("[")) {
+                insertSections(line, br, items);
+                originLine = br.readLine();
+            }   else {
+                throw new RuntimeException("Unable to parse:" + originLine);
+            }
+        }
+        br.close();
+    }
+
+    /**
+     * Get all the names of sections in a list.
+     * @return a list of section names.
+     */
+    public List<String> getSections() {
+        List<String> al = new ArrayList<String>(items.keySet());
+        return al;
+    }
+
+    /**
+     * Get the contents of a section given the section name.
+     * @param sectionName the name of a section
+     * @return a Map of section contents
+     */
+    public Map<String, Object> getSection(String sectionName) {
+        Map<String, Object> sections = (HashMap) items.get(sectionName);
+        return sections;
+    }
+
+    /**
+     * Print all the meaningful contents of the krb5.conf on the console.
+     * Comments are ignored.
+     * Hierarchy is considered.
+     * Attention that the order of sections and the order inside a section
+     * will be different to the original file, due to the use of HashMap.
+     */
+    public void dump() {
+        printSection(items);
+    }
+
+    private void insertSections(String line, BufferedReader br, Map<String, Object> items) throws IOException {
+        while (line.startsWith("[")) {
+            String sectionName = line.substring(1, line.length() - 1);
+            Map<String, Object> entries = new HashMap<String, Object>();
+            line = br.readLine();
+            if (line != null) {
+                line = line.trim();
+                line = insertEntries(line, br, entries);
+                items.put(sectionName, entries);
+            }
+            /*line has been modified after the recursive.*/
+            if (line == null) {
+                /*the end of file*/
+                break;
+            }
+        }
+    }
+
+    /**
+     * recursively go through the key-value pairs of a section
+     * */
+    private String insertEntries(String line, BufferedReader br, Map<String, Object> entries) throws IOException {
+        if (line == null) {
+            return line;
+        }
+        if (line.startsWith("[")) {
+            return line;
+        }
+        if (line.startsWith("}")) {
+            line = br.readLine();
+            if (line != null) {
+                line = line.trim();
+            }
+            return line;
+        }
+        if (line.length() == 0) {
+            line = br.readLine();
+            if (line != null) {
+                line = line.trim();
+                line = insertEntries(line, br, entries);
+            }
+            return line;
+        }
+        /*some special cases above*/
+        String[] kv = line.split("=");
+        if (kv.length > 2) {
+            throw new RuntimeException("Unable to parse:" + line);
+        }
+        kv[0] = kv[0].trim();
+        kv[1] = kv[1].trim();
+
+        if (kv[1].startsWith("{")) {
+            Map<String, Object> meValue = new HashMap<String, Object>();
+            line = br.readLine();
+            if (line != null) {
+                line = line.trim();
+                line = insertEntries(line, br, meValue);
+                entries.put(kv[0], meValue);
+                line = insertEntries(line, br, entries);
+            }
+        }   else {
+            entries.put(kv[0], kv[1]);
+            line = br.readLine();
+            if (line != null) {
+                line = line.trim();
+                line = insertEntries(line, br, entries);
+            }
+        }
+        return line;
+    }
+
+    private void printSection(Map<String, Object> map) {
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String key = (String) entry.getKey();
+            Object value = entry.getValue();
+            System.out.println("[" + key + "]");
+
+            if (value instanceof Map) {
+                int count = 0;
+                printEntry((Map) value, count);
+            }   else {
+                throw new RuntimeException("Unable to print contents of [" + key + "]");
+            }
+        }
+    }
+
+    private void printEntry(Map<String, Object> map, int count) {
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String key = (String) entry.getKey();
+            Object value = entry.getValue();
+            for (int i = 0; i < count; i++) {
+                System.out.print("\t");
+            }
+            if (value instanceof String) {
+                System.out.println(key + " = " + (String) value);
+            }
+            if (value instanceof Map) {
+                System.out.println(key + " = {");
+                printEntry((Map) value, count + 1);
+                for (int i = 0; i < count; i++) {
+                    System.out.print("\t");
+                }
+                System.out.println("}");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
new file mode 100644
index 0000000..b11ad16
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
@@ -0,0 +1,55 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb;
+
+import org.apache.kerby.kerberos.kerb.common.Krb5Parser;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A unit test for Krb5Parser.
+ * Checked the number of sections, the name of
+ * sections, and contents of a section.
+ */
+public class Krb5ParserTest {
+
+    @Test
+    public void test() throws IOException {
+        URL url = Krb5ParserTest.class.getResource("/krb5.conf");
+        Krb5Parser k = new Krb5Parser(new File(url.getFile()));
+        k.load();
+
+        assertThat(k.getSections().size()).isEqualTo(4);
+        assertThat(k.getSections().contains("libdefaults")).isTrue();
+
+        assertThat(k.getSection("libdefaults").get("dns_lookup_kdc")).isEqualTo("false");
+        assertThat(k.getSection("realms").get("ATHENA.MIT.EDU") instanceof Map).isTrue();
+        Map<String, Object> m1 = (Map) k.getSection("realms").get("ATHENA.MIT.EDU");
+        assertThat(m1.get("v4_instance_convert") instanceof  Map).isTrue();
+        Map<String, Object> m2 = (Map) m1.get("v4_instance_convert");
+        assertThat(m2.get("mit")).isEqualTo("mit.edu");
+    }
+}
diff --git a/kerby-kerb/kerb-common/src/test/resources/krb5.conf b/kerby-kerb/kerb-common/src/test/resources/krb5.conf
new file mode 100644
index 0000000..1a5b35e
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/test/resources/krb5.conf
@@ -0,0 +1,59 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[libdefaults]
+  default_realm = KRB.COM
+  kdc_host = kdc-server.example.com
+  kdc_realm = TEST.COM
+  dns_lookup_kdc = false
+  dns_lookup_realm = false
+  allow_weak_crypto = true
+  ticket_lifetime = 86400
+  renew_lifetime = 604800
+  forwardable = true
+  permitted_enctypes = des-cbc-crc aes128-cts-hmac-sha1-96
+  clockskew = 300
+  proxiable = true
+  default_tgs_enctypes = des-cbc-crc
+  default_tkt_enctypes = des-cbc-crc
+[realms]
+  ATHENA.MIT.EDU = {
+        admin_server = KERBEROS.MIT.EDU
+        default_domain = MIT.EDU
+        v4_instance_convert = {
+            mit = mit.edu
+            lithium = lithium.lcs.mit.edu
+        }
+    }
+    ANDREW.CMU.EDU = {
+      admin_server = vice28.fs.andrew.cmu.edu
+    }
+  GNU.ORG = {
+    kdc = kerberos.gnu.org
+    kdc = kerberos-2.gnu.org
+    admin_server = kerberos.gnu.org
+ }
+[domain_realm]
+  .mit.edu = ATHENA.MIT.EDU
+    mit.edu = ATHENA.MIT.EDU
+    .media.mit.edu = MEDIA-LAB.MIT.EDU
+    media.mit.edu = MEDIA-LAB.MIT.EDU
+    .ucsc.edu = CATS.UCSC.EDU
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
\ No newline at end of file