Adding domain description
[hcoop/domtool2.git] / src / domain.sml
index ed26bcd..67ff449 100644 (file)
@@ -1,5 +1,5 @@
 (* HCoop Domtool (http://hcoop.sourceforge.net/)
- * Copyright (c) 2006, Adam Chlipala
+ * Copyright (c) 2006-2007, Adam Chlipala
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -48,16 +48,26 @@ fun your_groups () = !your_grps
 val your_pths = ref SS.empty
 fun your_paths () = !your_pths
 
+val world_readable = SS.addList (SS.empty, Config.worldReadable)
+val readable_pths = ref SS.empty
+fun readable_paths () = !readable_pths
+
 fun setUser user =
-    (usr := user;
-     your_doms := Acl.class {user = getUser (),
-                            class = "domain"};
-     your_usrs := Acl.class {user = getUser (),
-                            class = "user"};
-     your_grps := Acl.class {user = getUser (),
-                            class = "group"};
-     your_pths := Acl.class {user = getUser (),
-                            class = "path"})
+    let
+       val () = usr := user
+
+       val your_paths = Acl.class {user = getUser (),
+                                   class = "path"}
+    in
+       your_doms := Acl.class {user = getUser (),
+                               class = "domain"};
+       your_usrs := Acl.class {user = getUser (),
+                               class = "user"};
+       your_grps := Acl.class {user = getUser (),
+                               class = "group"};
+       your_pths := your_paths;
+       readable_pths := SS.union (your_paths, world_readable)
+    end
 
 fun validIp s =
     case map Int.fromString (String.fields (fn ch => ch = #".") s) of
@@ -80,11 +90,13 @@ fun validNode s = List.exists (fn s' => s = s') nodes
 fun yourDomain s = SS.member (your_domains (), s)
 fun yourUser s = SS.member (your_users (), s)
 fun yourGroup s = SS.member (your_groups (), s)
-fun yourPath path =
+fun checkPath paths path =
     List.all (fn s => s <> "..") (String.fields (fn ch => ch = #"/") path)
     andalso CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"/"
                                     orelse ch = #"-" orelse ch = #"_") path
-    andalso SS.exists (fn s' => path = s' orelse String.isPrefix (s' ^ "/") path) (your_paths ())
+    andalso SS.exists (fn s' => path = s' orelse String.isPrefix (s' ^ "/") path) (paths ())
+val yourPath = checkPath your_paths
+val readablePath = checkPath readable_paths
 
 fun yourDomainHost s =
     yourDomain s
@@ -114,10 +126,12 @@ val validGroup = validUser
 
 val _ = Env.type_one "no_spaces"
                     Env.string
-                    (CharVector.all (fn ch => not (Char.isSpace ch)))
+                    (CharVector.all (fn ch => Char.isPrint ch andalso not (Char.isSpace ch)
+                                              andalso ch <> #"\"" andalso ch <> #"'"))
 val _ = Env.type_one "no_newlines"
                     Env.string
-                    (CharVector.all (fn ch => ch <> #"\n" andalso ch <> #"\r"))
+                    (CharVector.all (fn ch => Char.isPrint ch andalso ch <> #"\n" andalso ch <> #"\r"
+                                              andalso ch <> #"\"" andalso ch <> #"'"))
 
 val _ = Env.type_one "ip"
        Env.string
@@ -159,6 +173,10 @@ val _ = Env.type_one "your_path"
        Env.string
        yourPath
 
+val _ = Env.type_one "readable_path"
+       Env.string
+       readablePath
+
 val _ = Env.type_one "node"
        Env.string
        validNode
@@ -772,4 +790,96 @@ fun homedirOf uname =
 
 fun homedir () = homedirOf (getUser ())
 
+type subject = {node : string, domain : string}
+
+val describers : (subject -> string) list ref = ref []
+
+fun registerDescriber f = describers := f :: !describers
+
+fun describeOne arg = String.concat (map (fn f => f arg) (!describers))
+
+val line = "-------------------------------\n"
+val dline = "===============================\n"
+
+fun describe dom =
+    String.concat (List.mapPartial
+                      (fn node =>
+                          case describeOne {node = node, domain = dom} of
+                              "" => NONE
+                            | s =>
+                              SOME (String.concat [dline, "Node ", node, "\n", dline, "\n", s]))
+                      nodes)
+
+datatype description =
+        Filename of { filename : string, heading : string}
+       | Extension of { extension : string, heading : string -> string }
+                       
+fun considerAll ds {node, domain} =
+    let
+       val ds = map (fn d => (d, ref [])) ds
+
+       val path = Config.resultRoot
+       val jdf = OS.Path.joinDirFile
+       val path = jdf {dir = path, file = node}
+       val path = foldr (fn (more, path) => jdf {dir = path, file = more})
+                        path (String.tokens (fn ch => ch = #".") domain)
+    in
+       if Posix.FileSys.access (path, []) then
+           let
+               val dir = Posix.FileSys.opendir path
+
+               fun loop () =
+                   case Posix.FileSys.readdir dir of
+                       NONE => ()
+                     | SOME fname =>
+                       let
+                           fun readFile entries =
+                               let
+                                   val fname = OS.Path.joinDirFile {dir = path,
+                                                                    file = fname}
+
+                                   val inf = TextIO.openIn fname
+
+                                   fun loop entries =
+                                       case TextIO.inputLine inf of
+                                           NONE => entries
+                                         | SOME line => loop (line :: entries)
+                               in
+                                   loop entries
+                                   before TextIO.closeIn inf
+                               end
+                       in
+                           app (fn (d, entries) =>
+                                   case d of
+                                       Filename {filename, heading} =>
+                                       if fname = filename then
+                                           entries := "\n" :: readFile ("\n" :: line :: ":\n" :: heading :: line :: !entries)
+                                       else
+                                           ()
+                                     | Extension {extension, heading} =>
+                                       let
+                                           val {base, ext} = OS.Path.splitBaseExt fname
+                                       in
+                                           case ext of
+                                               NONE => ()
+                                             | SOME extension' =>
+                                               if extension' = extension then
+                                                   entries := "\n" :: readFile ("\n" :: line :: ":\n" :: heading base :: line :: !entries)
+                                               else
+                                                   ()
+                                       end) ds;
+                           loop ()
+                       end
+           in
+               loop ();
+               Posix.FileSys.closedir dir;
+               String.concat (List.concat (map (fn (_, entries) => rev (!entries)) ds))
+           end
+       else
+           ""
+    end
+
+val () = registerDescriber (considerAll [Filename {filename = "soa",
+                                                  heading = "DNS SOA"}])
+
 end