+fun requestDir dname =
+ let
+ val _ = if Posix.FileSys.access (dname, []) then
+ ()
+ else
+ (print ("Can't access " ^ dname ^ ".\n");
+ print "Did you mean to run domtool on a specific file, instead of asking for all\n";
+ print "files in your ~/.domtool directory?\n";
+ OS.Process.exit OS.Process.failure)
+
+ val _ = ErrorMsg.reset ()
+
+ val (user, bio) = requestBio (fn () => checkDir dname)
+
+ val b = basis ()
+
+ val dir = Posix.FileSys.opendir dname
+
+ fun loop files =
+ case Posix.FileSys.readdir dir of
+ NONE => (Posix.FileSys.closedir dir;
+ files)
+ | SOME fname =>
+ if notTmp fname then
+ loop (OS.Path.joinDirFile {dir = dname,
+ file = fname}
+ :: files)
+ else
+ loop files
+
+ val files = loop []
+ val (_, files) = Order.order (SOME b) files
+
+ val _ = if !ErrorMsg.anyErrors then
+ raise ErrorMsg.Error
+ else
+ ()
+
+ val codes = map (fn fname =>
+ let
+ val inf = TextIO.openIn fname
+
+ fun loop lines =
+ case TextIO.inputLine inf of
+ NONE => String.concat (rev lines)
+ | SOME line => loop (line :: lines)
+ in
+ loop []
+ before TextIO.closeIn inf
+ end) files
+ in
+ if !ErrorMsg.anyErrors then
+ ()
+ else
+ (Msg.send (bio, MsgMultiConfig codes);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Configuration succeeded.\n"
+ | MsgError s => print ("Configuration failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio)
+ end
+ handle ErrorMsg.Error => ()
+
+fun requestPing () =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ OpenSSL.close bio;
+ OS.Process.success
+ end
+ handle _ => OS.Process.failure
+
+fun requestShutdown () =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgShutdown);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Shutdown begun.\n"
+ | MsgError s => print ("Shutdown failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestSlavePing () =
+ let
+ val (_, bio) = requestSlaveBio ()
+ in
+ OpenSSL.close bio;
+ OS.Process.success
+ end
+ handle _ => OS.Process.failure
+
+fun requestSlaveShutdown () =
+ let
+ val (_, bio) = requestSlaveBio ()
+ in
+ Msg.send (bio, MsgShutdown);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Shutdown begun.\n"
+ | MsgError s => print ("Shutdown failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestGrant acl =
+ let
+ val (user, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgGrant acl);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Grant succeeded.\n"
+ | MsgError s => print ("Grant failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestRevoke acl =
+ let
+ val (user, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRevoke acl);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Revoke succeeded.\n"
+ | MsgError s => print ("Revoke failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestListPerms user =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgListPerms user);
+ (case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ NONE)
+ | SOME m =>
+ case m of
+ MsgPerms perms => SOME perms
+ | MsgError s => (print ("Listing failed: " ^ s ^ "\n");
+ NONE)
+ | _ => (print "Unexpected server reply.\n";
+ NONE))
+ before OpenSSL.close bio
+ end
+
+fun requestWhoHas perm =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgWhoHas perm);
+ (case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ NONE)
+ | SOME m =>
+ case m of
+ MsgWhoHasResponse users => SOME users
+ | MsgError s => (print ("whohas failed: " ^ s ^ "\n");
+ NONE)
+ | _ => (print "Unexpected server reply.\n";
+ NONE))
+ before OpenSSL.close bio
+ end
+
+fun requestRegen () =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRegenerate);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Regeneration succeeded.\n"
+ | MsgError s => print ("Regeneration failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestRegenTc () =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRegenerateTc);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "All configuration validated.\n"
+ | MsgError s => print ("Configuration validation failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestRmdom dom =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRmdom dom);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Removal succeeded.\n"
+ | MsgError s => print ("Removal failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestRmuser user =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRmuser user);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Removal succeeded.\n"
+ | MsgError s => print ("Removal failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestDbUser dbtype =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgCreateDbUser dbtype);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Your user has been created.\n"
+ | MsgError s => print ("Creation failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestDbPasswd rc =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgDbPasswd rc);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Your password has been changed.\n"
+ | MsgError s => print ("Password set failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestDbTable p =
+ let
+ val (user, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgCreateDbTable p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("Your database " ^ user ^ "_" ^ #dbname p ^ " has been created.\n")
+ | MsgError s => print ("Creation failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestDbDrop p =
+ let
+ val (user, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgDropDb p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("Your database " ^ user ^ "_" ^ #dbname p ^ " has been dropped.\n")
+ | MsgError s => print ("Drop failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestDbGrant p =
+ let
+ val (user, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgGrantDb p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("You've been granted all allowed privileges to database " ^ user ^ "_" ^ #dbname p ^ ".\n")
+ | MsgError s => print ("Grant failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestListMailboxes domain =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgListMailboxes domain);
+ (case Msg.recv bio of
+ NONE => Vmail.Error "Server closed connection unexpectedly."
+ | SOME m =>
+ case m of
+ MsgMailboxes users => (Msg.send (bio, MsgOk);
+ Vmail.Listing users)
+ | MsgError s => Vmail.Error ("Listing failed: " ^ s)
+ | _ => Vmail.Error "Unexpected server reply.")
+ before OpenSSL.close bio
+ end
+
+fun requestNewMailbox p =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgNewMailbox p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("A mapping for " ^ #user p ^ "@" ^ #domain p ^ " has been created.\n")
+ | MsgError s => print ("Creation failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestPasswdMailbox p =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgPasswdMailbox p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("The password for " ^ #user p ^ "@" ^ #domain p ^ " has been changed.\n")
+ | MsgError s => print ("Set failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestRmMailbox p =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgRmMailbox p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("The mapping for mailbox " ^ #user p ^ "@" ^ #domain p ^ " has been deleted.\n")
+ | MsgError s => print ("Remove failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestSaQuery addr =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgSaQuery addr);
+ (case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgSaStatus b => (print ("SpamAssassin filtering for " ^ addr ^ " is "
+ ^ (if b then "ON" else "OFF") ^ ".\n");
+ Msg.send (bio, MsgOk))
+ | MsgError s => print ("Query failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n")
+ before OpenSSL.close bio
+ end
+
+fun requestSaSet p =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgSaSet p);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("SpamAssassin filtering for " ^ #1 p ^ " is now "
+ ^ (if #2 p then "ON" else "OFF") ^ ".\n")
+ | MsgError s => print ("Set failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestSmtpLog domain =
+ let
+ val (_, bio) = requestBio (fn () => ())
+
+ val _ = Msg.send (bio, MsgSmtpLogReq domain)
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => ()
+ | MsgSmtpLogRes line => (print line;
+ loop ())
+ | MsgError s => print ("Log search failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n"
+ in
+ loop ();
+ OpenSSL.close bio
+ end
+
+fun requestMysqlFixperms () =
+ let
+ val (_, bio) = requestBio (fn () => ())
+ in
+ Msg.send (bio, MsgMysqlFixperms);
+ case Msg.recv bio of
+ NONE => print "Server closed connection unexpectedly.\n"
+ | SOME m =>
+ case m of
+ MsgOk => print "Permissions granted.\n"
+ | MsgError s => print ("Failed: " ^ s ^ "\n")
+ | _ => print "Unexpected server reply.\n";
+ OpenSSL.close bio
+ end
+
+fun requestApt {node, pkg} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QApt pkg))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgYes => (print "Package is installed.\n";
+ OS.Process.success)
+ | MsgNo => (print "Package is not installed.\n";
+ OS.Process.failure)
+ | MsgError s => (print ("APT query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun requestCron {node, uname} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QCron uname))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgYes => (print "User has cron permissions.\n";
+ OS.Process.success)
+ | MsgNo => (print "User does not have cron permissions.\n";
+ OS.Process.failure)
+ | MsgError s => (print ("Cron query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun requestFtp {node, uname} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QFtp uname))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgYes => (print "User has FTP permissions.\n";
+ OS.Process.success)
+ | MsgNo => (print "User does not have FTP permissions.\n";
+ OS.Process.failure)
+ | MsgError s => (print ("FTP query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun requestTrustedPath {node, uname} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QTrustedPath uname))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgYes => (print "User has trusted path restriction.\n";
+ OS.Process.success)
+ | MsgNo => (print "User does not have trusted path restriction.\n";
+ OS.Process.failure)
+ | MsgError s => (print ("Trusted path query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun requestSocketPerm {node, uname} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QSocket uname))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgSocket p => (case p of
+ Any => print "Any\n"
+ | Client => print "Client\n"
+ | Server => print "Server\n"
+ | Nada => print "Nada\n";
+ OS.Process.success)
+ | MsgError s => (print ("Socket permission query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun requestFirewall {node, uname} =
+ let
+ val (user, context) = requestContext (fn () => ())
+ val bio = OpenSSL.connect (context, if node = Config.masterNode then
+ dispatcher
+ else
+ Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
+
+ val _ = Msg.send (bio, MsgQuery (QFirewall uname))
+
+ fun loop () =
+ case Msg.recv bio of
+ NONE => (print "Server closed connection unexpectedly.\n";
+ OS.Process.failure)
+ | SOME m =>
+ case m of
+ MsgFirewall ls => (app (fn s => (print s; print "\n")) ls;
+ OS.Process.success)
+ | MsgError s => (print ("Firewall query failed: " ^ s ^ "\n");
+ OS.Process.failure)
+ | _ => (print "Unexpected server reply.\n";
+ OS.Process.failure)
+ in
+ loop ()
+ before OpenSSL.close bio
+ end
+
+fun regenerate context =
+ let
+ val _ = ErrorMsg.reset ()
+
+ val b = basis ()
+ val () = Tycheck.disallowExterns ()
+
+ val () = Domain.resetGlobal ()
+
+ val ok = ref true
+
+ fun contactNode (node, ip) =
+ if node = Config.defaultNode then
+ Domain.resetLocal ()
+ else let
+ val bio = OpenSSL.connect (context,
+ ip
+ ^ ":"
+ ^ Int.toString Config.slavePort)
+ in
+ Msg.send (bio, MsgRegenerate);
+ case Msg.recv bio of
+ NONE => print "Slave closed connection unexpectedly\n"
+ | SOME m =>
+ case m of
+ MsgOk => print ("Slave " ^ node ^ " pre-regeneration finished\n")
+ | MsgError s => print ("Slave " ^ node
+ ^ " returned error: " ^
+ s ^ "\n")
+ | _ => print ("Slave " ^ node
+ ^ " returned unexpected command\n");
+ OpenSSL.close bio
+ end
+ handle OpenSSL.OpenSSL s => print ("OpenSSL error: " ^ s ^ "\n")
+
+ fun doUser user =
+ let
+ val _ = Domain.setUser user
+ val _ = ErrorMsg.reset ()
+
+ val dname = Config.domtoolDir user
+ in
+ if Posix.FileSys.access (dname, []) then
+ let
+ val dir = Posix.FileSys.opendir dname
+
+ fun loop files =
+ case Posix.FileSys.readdir dir of
+ NONE => (Posix.FileSys.closedir dir;
+ files)
+ | SOME fname =>
+ if notTmp fname then
+ loop (OS.Path.joinDirFile {dir = dname,
+ file = fname}
+ :: files)
+ else
+ loop files
+
+ val files = loop []
+ val (_, files) = Order.order (SOME b) files
+ in
+ if !ErrorMsg.anyErrors then
+ (ErrorMsg.reset ();
+ print ("User " ^ user ^ "'s configuration has errors!\n"))
+ else
+ app eval' files
+ end
+ else
+ ()
+ end
+ handle IO.Io {name, function, ...} =>
+ (print ("IO error processing user " ^ user ^ ": " ^ function ^ ": " ^ name ^ "\n");
+ ok := false)
+ | OS.SysErr (s, _) => (print ("System error processing user " ^ user ^ ": " ^ s ^ "\n");
+ ok := false)
+ | ErrorMsg.Error => (ErrorMsg.reset ();
+ print ("User " ^ user ^ " had a compilation error.\n");
+ ok := false)
+ | _ => (print "Unknown exception during regeneration!\n";
+ ok := false)
+ in
+ app contactNode Config.nodeIps;
+ Env.pre ();
+ app doUser (Acl.users ());
+ Env.post ();
+ !ok
+ end
+
+fun regenerateTc context =
+ let
+ val _ = ErrorMsg.reset ()
+
+ val b = basis ()
+ val () = Tycheck.disallowExterns ()
+
+ val () = Domain.resetGlobal ()
+
+ val ok = ref true
+
+ fun doUser user =
+ let
+ val _ = Domain.setUser user
+ val _ = ErrorMsg.reset ()
+
+ val dname = Config.domtoolDir user
+ in
+ if Posix.FileSys.access (dname, []) then
+ let
+ val dir = Posix.FileSys.opendir dname
+
+ fun loop files =
+ case Posix.FileSys.readdir dir of
+ NONE => (Posix.FileSys.closedir dir;
+ files)
+ | SOME fname =>
+ if notTmp fname then
+ loop (OS.Path.joinDirFile {dir = dname,
+ file = fname}
+ :: files)
+ else
+ loop files
+
+ val files = loop []
+ val (_, files) = Order.order (SOME b) files
+ in
+ if !ErrorMsg.anyErrors then
+ (ErrorMsg.reset ();
+ print ("User " ^ user ^ "'s configuration has errors!\n");
+ ok := false)
+ else
+ app (ignore o check) files
+ end
+ else
+ ()
+ end
+ handle IO.Io _ => ()
+ | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n")
+ | ErrorMsg.Error => (ErrorMsg.reset ();
+ print ("User " ^ user ^ " had a compilation error.\n"))
+ | _ => print "Unknown exception during -tc regeneration!\n"
+ in
+ app doUser (Acl.users ());
+ !ok
+ end
+
+fun rmuser user =
+ let
+ val doms = Acl.class {user = user, class = "domain"}
+ val doms = List.filter (fn dom =>
+ case Acl.whoHas {class = "domain", value = dom} of
+ [_] => true
+ | _ => false) (StringSet.listItems doms)
+ in
+ Acl.rmuser user;
+ Domain.rmdom doms
+ end
+
+fun now () = Date.toString (Date.fromTimeUniv (Time.now ()))
+
+fun answerQuery q =
+ case q of
+ QApt pkg => if Apt.installed pkg then MsgYes else MsgNo
+ | QCron user => if Cron.allowed user then MsgYes else MsgNo
+ | QFtp user => if Ftp.allowed user then MsgYes else MsgNo
+ | QTrustedPath user => if TrustedPath.query user then MsgYes else MsgNo
+ | QSocket user => MsgSocket (SocketPerm.query user)
+ | QFirewall user => MsgFirewall (Firewall.query user)
+
+fun describeQuery q =
+ case q of
+ QApt pkg => "Requested installation status of package " ^ pkg
+ | QCron user => "Asked about cron permissions for user " ^ user
+ | QFtp user => "Asked about FTP permissions for user " ^ user
+ | QTrustedPath user => "Asked about trusted path settings for user " ^ user
+ | QSocket user => "Asked about socket permissions for user " ^ user
+ | QFirewall user => "Asked about firewall rules for user " ^ user
+