+fun requestDir dname =
+ let
+ 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 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 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 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 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.\n"
+ | SOME m =>
+ case m of
+ MsgMailboxes users => (Msg.send (bio, MsgOk);
+ Vmail.Listing users)
+ | MsgError s => Vmail.Error ("Creation failed: " ^ s)
+ | _ => Vmail.Error "Unexpected server reply.\n")
+ 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 regenerate context =
+ let
+ val b = basis ()
+ val () = Tycheck.disallowExterns ()
+
+ val () = Domain.resetGlobal ()
+
+ 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
+
+ fun doUser user =
+ let
+ val _ = Domain.setUser user
+ val _ = ErrorMsg.reset ()
+
+ val dname = Config.domtoolDir user
+
+ 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
+ print ("User " ^ user ^ "'s configuration has errors!\n")
+ else
+ app eval' files
+ end
+ handle IO.Io _ => ()
+ | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n")
+ in
+ app contactNode Config.nodeIps;
+ Env.pre ();
+ app doUser (Acl.users ());
+ Env.post ()
+ 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
+