Mailing list subscription management (discuss and misc)
authorAdam Chlipala <adamc@hcoop.net>
Tue, 26 Apr 2005 19:33:11 +0000 (19:33 +0000)
committerAdam Chlipala <adamc@hcoop.net>
Tue, 26 Apr 2005 19:33:11 +0000 (19:33 +0000)
TODO
mailman/.cvsignore [new file with mode: 0644]
mailman/Makefile [new file with mode: 0644]
mailman/portalsub.sml [new file with mode: 0644]
pref.mlt
pref.sig
pref.sml

diff --git a/TODO b/TODO
index c1fc465..b4d7664 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,7 +1,2 @@
-Member data
-       - Summarize these with publicly accessible static pages
-
 Specific requests
        - Join, with display of pending applications for all members to read
-
-Manage mailing list subscriptions
\ No newline at end of file
diff --git a/mailman/.cvsignore b/mailman/.cvsignore
new file mode 100644 (file)
index 0000000..7ed5368
--- /dev/null
@@ -0,0 +1 @@
+portalsub
\ No newline at end of file
diff --git a/mailman/Makefile b/mailman/Makefile
new file mode 100644 (file)
index 0000000..e99f98d
--- /dev/null
@@ -0,0 +1,2 @@
+portalsub:
+       mlton portalsub.sml
\ No newline at end of file
diff --git a/mailman/portalsub.sml b/mailman/portalsub.sml
new file mode 100644 (file)
index 0000000..f781964
--- /dev/null
@@ -0,0 +1,59 @@
+fun isMember (list, addr) =
+    let
+       val proc = Unix.execute ("/usr/sbin/find_member", ["-l", list, addr])
+    in
+       (case TextIO.inputLine (Unix.textInstreamOf proc) of
+            NONE => false
+          | _ => true)
+       before ignore (Unix.reap proc)
+    end
+
+fun isIdent ch = Char.isLower ch orelse Char.isDigit ch
+
+fun validHost s =
+    size s > 0 andalso size s < 20 andalso List.all isIdent (String.explode s)
+                                                 
+fun validDomain s =
+    size s > 0 andalso size s < 100 andalso List.all validHost (String.fields (fn ch => ch = #".") s)
+
+fun validEmailUser s =
+    size s > 0 andalso size s < 50 andalso List.all
+                                              (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+")
+                                              (String.explode s)
+
+fun validEmail s =
+    (case String.fields (fn ch => ch = #"@") s of
+        [user, host] => validEmailUser user andalso validDomain host
+       | _ => false)
+
+fun main () =
+    case CommandLine.arguments () of
+       [list, cmd, addr] =>
+       if list <> "hcoop-discuss" andalso list <> "hcoop-misc" then
+           (print "Bad mailing list name\n";
+            OS.Process.failure)
+       else if not (validEmail addr) then
+           (print "Invalid e-mail address\n";
+            OS.Process.failure)
+       else (case cmd of
+                 "check" =>
+                 if isMember (list, addr) then
+                     OS.Process.success
+                 else
+                     OS.Process.failure
+               | "add" =>
+                 if isMember (list, addr) then
+                     OS.Process.success
+                 else
+                     OS.Process.system (String.concat ["echo ", addr, " | /usr/sbin/add_members -r - ", list])
+               | "rm" =>
+                 if isMember (list, addr) then
+                     OS.Process.system (String.concat ["/usr/sbin/remove_members ", list, " ", addr])
+                 else
+                     OS.Process.success
+               | _ => (print ("Invalid command " ^ cmd ^ "\n");
+                       OS.Process.failure))
+      | _ => (print "Bad command-line arguments\n";
+             OS.Process.failure)
+
+val _ = OS.Process.exit (main ())
\ No newline at end of file
index f4ba6fb..0f908d6 100644 (file)
--- a/pref.mlt
+++ b/pref.mlt
@@ -1,4 +1,6 @@
 <% val you = Init.getUserId ();
+val yourname = Init.getUserName ();
+val youremail = yourname ^ "@hcoop.net";
 
 @header [("title", ["Member preferences"])];
 
@@ -7,7 +9,21 @@ if $"cmd" = "mod" then
                Pref.setDirectory you
        else
                Pref.unsetDirectory you
-       end
+       end;
+
+       if not (iff $"discuss" = "on" then
+                       Pref.subscribe ("hcoop-discuss", youremail)
+                   else
+                       Pref.unsubscribe ("hcoop-discuss", youremail)) then
+               %><h3><b>Error setting <tt>hcoop-discuss</tt> status</b></h3><%
+       end;
+
+       if not (iff $"misc" = "on" then
+                       Pref.subscribe ("hcoop-misc", youremail)
+                   else
+                       Pref.unsubscribe ("hcoop-misc", youremail)) then
+               %><h3><b>Error setting <tt>hcoop-misc</tt> status</b></h3><%
+       end;
 
        %><h3><b>Preferences updated</b></h3><%
 end %>
@@ -15,7 +31,9 @@ end %>
 <form action="pref">
 <input type="hidden" name="cmd" value="mod">
 <table>
-<tr> <td align="right"><input type="checkbox" name="dir"<% if Pref.hasDirectory you then %> checked<% end %>></td> <td>Include me in the public member directory</td> </tr>
+<tr> <td align="right"><input type="checkbox" name="dir"<% if Pref.hasDirectory you then %> checked<% end %>></td> <td>Include me in the public member directory.</td> </tr>
+<tr> <td align="right"><input type="checkbox" name="discuss"<% if Pref.subscribed ("hcoop-discuss", youremail) then %> checked<% end %>></td> <td>Include me on the <tt>hcoop-discuss</tt> mailing list. <i>(On-topic discussion and sporadically high volume)</i></td> </tr>
+<tr> <td align="right"><input type="checkbox" name="misc"<% if Pref.subscribed ("hcoop-misc", youremail) then %> checked<% end %>></td> <td>Include me on the <tt>hcoop-misc</tt> mailing list. <i>(Off-topic)</i></td> </tr>
 <tr> <td><input type="submit" value="Save"></td> </tr>
 </table>
 
index 1bf518a..e3cb7d7 100644 (file)
--- a/pref.sig
+++ b/pref.sig
@@ -3,4 +3,8 @@ sig
     val hasDirectory : int -> bool
     val setDirectory : int -> unit
     val unsetDirectory : int -> unit
+
+    val subscribed : string * string -> bool
+    val subscribe : string * string -> bool
+    val unsubscribe : string * string -> bool
 end
\ No newline at end of file
index 9deb6fe..8476410 100644 (file)
--- a/pref.sml
+++ b/pref.sml
@@ -17,4 +17,22 @@ fun setDirectory usr =
 fun unsetDirectory usr =
     ignore (C.dml (getDb ()) ($`DELETE FROM DirectoryPref WHERE usr = ^(C.intToSql usr)`))
 
+fun subscribed (list, address) = OS.Process.isSuccess (OS.Process.system (String.concat
+                                                                        ["/usr/bin/sudo /usr/local/bin/portalsub ",
+                                                                         list,
+                                                                         " check ",
+                                                                         address]))
+
+fun subscribe (list, address) = OS.Process.isSuccess (OS.Process.system (String.concat
+                                                                        ["/usr/bin/sudo /usr/local/bin/portalsub ",
+                                                                         list,
+                                                                         " add ",
+                                                                         address]))
+
+fun unsubscribe (list, address) = OS.Process.isSuccess (OS.Process.system (String.concat
+                                                                              ["/usr/bin/sudo /usr/local/bin/portalsub ",
+                                                                               list,
+                                                                               " rm ",
+                                                                               address]))
+
 end
\ No newline at end of file