]> andersk Git - moira.git/commitdiff
Added WebMoira sources
authorjis <jis>
Sun, 23 Sep 2001 04:08:28 +0000 (04:08 +0000)
committerjis <jis>
Sun, 23 Sep 2001 04:08:28 +0000 (04:08 +0000)
32 files changed:
webmoira/Makefile.linux [new file with mode: 0644]
webmoira/Makefile.sparc [new file with mode: 0644]
webmoira/fileparts/addfinal.html [new file with mode: 0644]
webmoira/fileparts/addme.html [new file with mode: 0644]
webmoira/fileparts/addmember.html [new file with mode: 0644]
webmoira/fileparts/delconfirm.html [new file with mode: 0644]
webmoira/fileparts/delme.html [new file with mode: 0644]
webmoira/fileparts/delmembers.html [new file with mode: 0644]
webmoira/fileparts/displaylistinfo.html [new file with mode: 0644]
webmoira/fileparts/editme.html [new file with mode: 0644]
webmoira/fileparts/getmembers.html [new file with mode: 0644]
webmoira/fileparts/getnewlist.html [new file with mode: 0644]
webmoira/fileparts/help.html [new file with mode: 0644]
webmoira/fileparts/removemembers.html [new file with mode: 0644]
webmoira/fileparts/top.html [new file with mode: 0644]
webmoira/fileparts/updatelistinfo.html [new file with mode: 0644]
webmoira/fileparts/updatelistinfoconf.html [new file with mode: 0644]
webmoira/help.jhtml [new file with mode: 0644]
webmoira/home.gif [new file with mode: 0644]
webmoira/index.jhtml [new file with mode: 0644]
webmoira/mit.gif [new file with mode: 0644]
webmoira/mit/moira/AuthenticationError.java [new file with mode: 0644]
webmoira/mit/moira/Coder.java [new file with mode: 0644]
webmoira/mit/moira/Kticket.java [new file with mode: 0644]
webmoira/mit/moira/ListInfo.java [new file with mode: 0644]
webmoira/mit/moira/Member.java [new file with mode: 0644]
webmoira/mit/moira/MoiraConnect.java [new file with mode: 0644]
webmoira/mit/moira/MoiraException.java [new file with mode: 0644]
webmoira/mit/moira/MoiraServlet.java [new file with mode: 0644]
webmoira/moirai.c [new file with mode: 0644]
webmoira/request.jhtml [new file with mode: 0644]
webmoira/showresult.jhtml [new file with mode: 0644]

diff --git a/webmoira/Makefile.linux b/webmoira/Makefile.linux
new file mode 100644 (file)
index 0000000..c7048c8
--- /dev/null
@@ -0,0 +1,12 @@
+
+all: mit/moira/libmoirajava.so
+
+moirai.o: moirai.c mit_moira_MoiraConnectInternal.h
+       gcc -I/usr/lib/java/include -I/usr/lib/java/include/green_threads -I/usr/lib/java/include/genunix -I/u1/jis/moiradev/moira/include -I/usr/athena/include -I/u1/jis/moiradev/moira/lib -g -c moirai.c -Wall
+
+
+mit/moira/libmoirajava.so: moirai.o
+       gcc -shared -o libmoirajava.so moirai.o -L /u1/jis/moiradev/moira/lib -lmoira -L/usr/athena/lib -lhesiod -lkrb4 -lkrb5 -lcom_err -ldes425 -lcrypto -lc
+       cp -p libmoirajava.so libmoirajava_g.so
+       mv libmoirajava* mit/moira/
+
diff --git a/webmoira/Makefile.sparc b/webmoira/Makefile.sparc
new file mode 100644 (file)
index 0000000..c586b59
--- /dev/null
@@ -0,0 +1,12 @@
+
+all: mit/moira/libmoirajava.so
+
+moirai.o: moirai.c mit_moira_MoiraConnectInternal.h
+       gcc -I/usr/java/include -I/usr/java/include/solaris -I/alt/jis/moira/include -I/usr/athena/include -I/alt/jis/moira/lib -g -c moirai.c -Wall
+
+
+mit/moira/libmoirajava.so: moirai.o
+       gcc -G -o libmoirajava.so moirai.o -L /alt/jis/moira/lib -lmoira -L/usr/athena/lib -lhesiod -lkrb4 -lkrb5 -lcom_err -ldes425 -lcrypto
+       cp -p libmoirajava.so libmoirajava_g.so
+       mv libmoirajava* mit/moira/
+
diff --git a/webmoira/fileparts/addfinal.html b/webmoira/fileparts/addfinal.html
new file mode 100644 (file)
index 0000000..0dcf934
--- /dev/null
@@ -0,0 +1,4 @@
+<p>The members you are adding will receive email sent to this list
+within 24 hours.  If you are adding members to an Athena <a
+href="help.jhtml#group">group</a> with access to files in AFS, they
+immediately have access to those files.</p>
diff --git a/webmoira/fileparts/addme.html b/webmoira/fileparts/addme.html
new file mode 100644 (file)
index 0000000..968908b
--- /dev/null
@@ -0,0 +1,5 @@
+<p>You will begin receiving email from this list within 24 hours.  If
+you have added yourself to an Athena <a
+href="help.jhtml#group">group</a> with access to files in AFS, you
+will have immediate access to those files.</p>
+
diff --git a/webmoira/fileparts/addmember.html b/webmoira/fileparts/addmember.html
new file mode 100644 (file)
index 0000000..db09282
--- /dev/null
@@ -0,0 +1,28 @@
+<p>List owners may add list members of three common types:<br>
+
+<font size="-1">
+<ul>
+
+  <li><b><a href="help.jhtml#user">user</a></b> -- member with an
+Athena username.  To add a user to an Athena list, enter only member
+name to the left of the @ (e.g. add eclapton for eclapton@mit.edu)</li>
+
+
+  <li><b><a href="help.jhtml#list">list</a></b> -- member which is
+another Athena list.  To add accounts@mit.edu as a member, select
+member type list, and type accounts as the member name. </li>
+
+
+  <li><b><a href="help.jhtml#string">string</a></b> -- members with
+email addresses outside of mit.edu, such as mitalum@anotherisp.net.
+To add this email as a member, select member type string and type the
+complete address.</li>
+
+</ul>
+</font>
+</p>
+
+<p>Select the type of member you wish to add, and enter the member(s)
+you wish to add. You may enter more than one member of the <b>same
+type</b> by listing each member on its own line.</p>
+
diff --git a/webmoira/fileparts/delconfirm.html b/webmoira/fileparts/delconfirm.html
new file mode 100644 (file)
index 0000000..29176e4
--- /dev/null
@@ -0,0 +1,4 @@
+<p>The members you are deleting will stop receiving email from this
+list within 24 hours.  If you have removed members from an Athena <a
+href="help.jhtml#group">group</a> with access to files in AFS, they no
+longer have access to those files.</p>
diff --git a/webmoira/fileparts/delme.html b/webmoira/fileparts/delme.html
new file mode 100644 (file)
index 0000000..184b26e
--- /dev/null
@@ -0,0 +1 @@
+<p>You will stop receiving email from this list within 24 hours.</p>
diff --git a/webmoira/fileparts/delmembers.html b/webmoira/fileparts/delmembers.html
new file mode 100644 (file)
index 0000000..31e2f34
--- /dev/null
@@ -0,0 +1,4 @@
+<p>The members you are deleting will stop receiving email from this
+list within 24 hours.  If you are removing members from an Athena <a
+href="help.jhtml#group">group</a> with access to files in AFS, they
+will no longer have access to those files.</p>
diff --git a/webmoira/fileparts/displaylistinfo.html b/webmoira/fileparts/displaylistinfo.html
new file mode 100644 (file)
index 0000000..392333e
--- /dev/null
@@ -0,0 +1,17 @@
+<p>An Athena list has a set of characteristics that describe the
+list's functionality and purpose. The list's owner can set many of the
+list's characteristics. The characteristics include
+
+<a href="help.jhtml#description">description</a>,
+
+list type (<a href="help.jhtml#maillist">mailing list</a>, <a
+href="help.jhtml#group">group</a>),
+
+<a href="help.jhtml#admin">administrator</a>,
+
+<a href="help.jhtml#permissions">list permissions</a> (active,
+inactive, public, private, visible, hidden),
+
+and <a href="help.jhtml#lastmod">last modification</a>.
+
+</p>
diff --git a/webmoira/fileparts/editme.html b/webmoira/fileparts/editme.html
new file mode 100644 (file)
index 0000000..7f33d56
--- /dev/null
@@ -0,0 +1,11 @@
+<p>MIT community members may add themselves to any public Athena list
+and remove themselves from any Athena lists.</p>
+
+<p>Below you will see if you are currently a member of a list, and be
+prompted to change your membership.</p>
+
+<p>Be aware that Athena lists can contain other Athena mailing lists.
+If you are not a member of a list directly, you may still be on the
+list through another list that is a member (called a sublist).  If
+this is the case, you can use the <b>show list members</b> function to
+see what sublists are members of this list.</p>
diff --git a/webmoira/fileparts/getmembers.html b/webmoira/fileparts/getmembers.html
new file mode 100644 (file)
index 0000000..bc26057
--- /dev/null
@@ -0,0 +1,17 @@
+<p>The members of this list are displayed with their type. List members
+can be of four different types:
+<font size="-1">
+<ul>
+    <li><b><a href="help.jhtml#user">user</a></b> -- member with an Athena username</li>
+
+    <li><b><a href="help.jhtml#list">list</a></b> -- member which is another Athena list</li>
+
+    <li><b><a href="help.jhtml#string">string</a></b> -- members with email addresses outside of
+mit.edu, such as mitalum@anotherisp.net </li>
+
+    <li><b><a href="help.jhtml#kerbprincipal">kerberos principal</a></b> -- member who does not receive email
+sent to list, but has all other membership privileges</li>
+
+</ul>
+</font>                             
+</p>
diff --git a/webmoira/fileparts/getnewlist.html b/webmoira/fileparts/getnewlist.html
new file mode 100644 (file)
index 0000000..6313e5a
--- /dev/null
@@ -0,0 +1,11 @@
+      <p>&nbsp;</p>
+      <p>MIT community members may <a href="http://web.mit.edu/accounts/www/list.html">request 
+        a list from Athena User Accounts</a> for MIT-specific purposes. Lists 
+        may be used to distribute messages to multiple recipients, to share files 
+        in a locker, or to serve as an email alias for a particular user. Lists 
+        must be owned by a single user or another list. </p>
+      <p>Course mailing lists should be requested using the <a
+href="http://web.mit.edu/acs/clist.html"> ACS Course Mailing List Request 
+        Form</a>. </p>
+      <p>&nbsp;</p>
+      <p>&nbsp;</p>
diff --git a/webmoira/fileparts/help.html b/webmoira/fileparts/help.html
new file mode 100644 (file)
index 0000000..98e61c7
--- /dev/null
@@ -0,0 +1,134 @@
+<p><b><font size="-1"><a name="helptop"></a><a href="#manage">How to
+Manage your Athena Lists</a> | <a href="#contact">Who to Contact for
+Help</a> | <a href="#glossary">Glossary of List Management
+Terms</a></font></b></p>
+
+<p><b><font size="+1"><a name="manage"></a>How to Manage your Athena
+Lists</font></b></p>
+
+<p>Athena List Management is one of the tools list owners have to
+manage their lists. Other tools include blanche and listmaint on
+Athena. To get started, you will need MIT <a
+href="http://web.mit.edu/is/help/cert/">Personal Certificates</a> on
+your machine. Once you have them, select any function, provide a list
+name, and press enter on your keyboard, or 'Go'. Each page of the web
+interface has context sensitive help to guide you. <a
+href="#helptop"><font size="-1">[top of help]</font></a></p>
+
+<p>&nbsp; </p> <p><b><font size="+1"><a name="contact"></a>Who to
+Contact for Help</font></b></p>
+
+<p>If you do need further assistance with your lists, <a
+href="http://web.mit.edu/accounts/">Athena User Accounts</a> provides
+assistance with Athena list concerns, including creating new lists,
+modifying lists, and deleting lists. If you have trouble using this
+interface to maintain your lists, please contact Accounts at <a
+href="mailto:accounts@mit.edu">accounts@mit.edu</a>, or
+617-253-1325. <a href="#helptop"><font size="-1">[top of
+help]</font></a></p>
+
+<p>&nbsp;</p>
+
+<p><b><font size="+1"><a name="glossary"></a>Glossary of List
+Management Terms</font></b></p>
+
+<p><a name="description"></a><b>List Description</b><br> A short
+description may be specified for an Athena list. This description is
+displayed for lists which are not marked hidden if list information is
+generated by another Athena user. <a href="#helptop"><font
+size="-1">[top of help]</font></a><br> </p>
+
+<p><a name="maillist"></a><b>Mailing List</b><br> An Athena list is by
+default a mailing list. That means that the members of the list will
+be mailable through the address <em>listname</em>@mit.edu, which will
+distribute the mail the all the members of the list. Such a list may
+include email addresses for users outside of MIT. Lists can be both a
+mailing list and a group. <a href="#helptop"><font size="-1">[top of
+help]</font></a><br> </p>
+
+<p><a name="group"></a><b>Group</b><br> In addition to (or instead of)
+being a mailing list, an Athena list can also be a group. A group can
+be used as an access control list on the AFS file system, for
+example. If you wish to be able to set access permissions on an Athena
+directory or locker for the members of your list, you should choose to
+make it a group.  When a list can maintain a file space on Athena, it
+is also has a group ID number.  </p>
+
+<p>Note that only Athena users on your list will be able to take
+advantage of this feature. If you have any other members on your list,
+such as email addresses outside of MIT, they will not be able to
+access Athena file systems. <a href="#helptop"><font size="-1">[top of
+help]</font></a></p>
+
+<p><a name="admin"></a><b>List Administrator(s)</b><br> Every Athena
+mailing list must be owned by one or more
+administrators. Administrators have the power to change list
+characteristics. Administrators can be a user or another Athena
+list. <a href="#helptop"><font size="-1">[top of help]</font></a></p>
+
+<p><a name="permissions"></a><b>List Permissions</b><br> These
+describe the state of the list and what users who are not the
+administrator can do with the list. A list's permissions can be viewed
+with the &quot;display list characteristics&quot; option, and edited
+by list owners with the &quot;update list characteristics&quot;
+option.</p>
+
+<ul>
+  <li><b>active</b> -- the list is live and usable</li>
+       
+  <li><b>inactive</b> -- the list has been turned off, is still in the
+system, but mail sent will bounce and/or group permissions will not
+work</li>
+
+  <li><b>public</b> -- any user can add themselves to this list</li>
+
+  <li><b>private</b> -- only the administrator can add users to the list</li>
+
+  <li><b>visible</b> -- any user can view basic information about a
+list, such as membership and administrators</li>
+
+  <li><b>hidden</b> -- list information is hidden, but can be viewed
+by the list administrators </li>
+
+</ul>
+
+<a href="#helptop"><font size="-1">[top of help]</font></a>
+
+<p><a name="lastmod"></a><b>Last Modification</b><br> This tells you
+when the list was last changed in some way and by who. <a
+href="#helptop"><font size="-1">[top of help]</font></a></p>
+
+<p><a name="user"></a><b>Athena User</b><br> Email addresses of the
+form name@mit.edu usually indicate name is an Athena username (also
+sometimes called a Kerberos Name or MIT Network ID). To add an Athena
+user to an Athena list, you enter only member name to the left of the
+@. For example, to add eclapton@mit.edu to an Athena list, provide
+'eclapton' as the member name. <a href="#helptop"><font size="-1">[top
+of help]</font></a></p>
+
+<p><a name="list"></a><b>List</b><br> An Athena list is by default a
+mailing list. That means that the members of the list will be mailable
+through the address listname@mit.edu, which will distribute the mail
+the all the members of the list. Such a list may include email
+addresses for users outside of MIT.</p>
+
+<p>List members can be other Athena lists, of the form
+listname@mit.edu. To add an Athena list as a member of another Athena
+list, enter only the part of the address before the @ symbol. For
+example, to add accounts@mit.edu as a member, provide 'accounts' as
+the member name. <a href="#helptop"><font size="-1">[top of
+help]</font></a></p>
+
+<p><a name="string"></a><b>String</b><br> Athena lists often have
+non-MIT users and lists as members. To add an email address that
+contains an @ but is outside of mit.edu, such as
+mitalum@anotherisp.net, you add them as member type string. To add a
+string, provide the complete address, including information on both
+sides of the @. <a href="#helptop"><font size="-1">[top of
+help]</font></a></p>
+
+<p><a name="kerbprincipal"></a><b>Kerberos Principal</b><br>
+Specifying a list member as a Kerberos principal allows a member to be
+on a list without receiving any email sent to the list. This is a
+rarely used member type. <a href="#helptop"><font size="-1">[top of
+help].</font></a></p>
diff --git a/webmoira/fileparts/removemembers.html b/webmoira/fileparts/removemembers.html
new file mode 100644 (file)
index 0000000..c1c1432
--- /dev/null
@@ -0,0 +1,12 @@
+<p>List owners may delete any list members. If you wish to have a list
+deleted, please contact <a href="http://web.mit.edu/accounts/">Athena
+Accounts</a>.</p>
+
+
+<p>Check each member you wish to delete, and then 'Delete
+Selected'. For list with many members, you will see initially see ten
+members per screen, and will be able to move to the next or previous
+ten members, selecting as many members to delete as you wish. If you
+would prefer to see all the members of a list, select 'Show All' to
+display all list members. Please note that displaying all members at
+once may take a long time to load.</p>
diff --git a/webmoira/fileparts/top.html b/webmoira/fileparts/top.html
new file mode 100644 (file)
index 0000000..423e6fa
--- /dev/null
@@ -0,0 +1,24 @@
+<font size=+1>
+<p>Welcome to the Athena list management web
+interface.  This is one of <a
+href="http://web.mit.edu/answers/accounts/accounts_listmaint.html">several
+tools</a> you can use to update list membership information and
+characteristics.</p>
+
+<p>This service requires <a
+href="http://web.mit.edu/is/help/cert/">MIT Personal
+  Certificates</a>.</p>
+
+
+<p>To begin,
+<ol>
+  <li>select an option</li>
+  <li>enter a list name (e.g. accounts)</li>
+  <li>either press enter on your keyboard or select 'Go'.</li>
+</ol></p>
+
+
+<p>Feedback and questions about this service may be directed to <a
+href="mailto:web-listmaint@mit.edu">web-listmaint@mit.edu</a>.</p>
+
+</font>
diff --git a/webmoira/fileparts/updatelistinfo.html b/webmoira/fileparts/updatelistinfo.html
new file mode 100644 (file)
index 0000000..ad9ff57
--- /dev/null
@@ -0,0 +1,9 @@
+<p>An Athena list has a set of characteristics that describe the\r
+list's functionality and purpose. The list's owner may update many of\r
+the list's characteristics below, including if the list is a <a\r
+href="help.jhtml#maillist">mailing list</a>, a <a\r
+href="help.jhtml#permissions">public or private</a> list, and if it is\r
+a <a href="help.jhtml#permissions">hidden or visible</a>\r
+list.  You may also edit the list <a\r
+href="help.jhtml#admin">administrator</a> and <a\r
+href="help.jhtml#description">description</a>.\r
diff --git a/webmoira/fileparts/updatelistinfoconf.html b/webmoira/fileparts/updatelistinfoconf.html
new file mode 100644 (file)
index 0000000..b80883b
--- /dev/null
@@ -0,0 +1 @@
+<p>Updates to list characteristics will take effect within 24 hours.</p>\r
diff --git a/webmoira/help.jhtml b/webmoira/help.jhtml
new file mode 100644 (file)
index 0000000..584e547
--- /dev/null
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+               "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<!-- #BeginEditable "doctitle" --> 
+<title>help with list management</title>
+<!-- #EndEditable --> 
+</head>
+<body bgcolor="#fffff7"
+text="#222222" link="#aa3333" vlink="#666666" alink="#000000">
+
+<table width="610" border="0" cellpadding="5">
+  <tr> 
+    <td colspan="2" bgcolor="#000000"><font color="#FFFFFF" face="Arial, Helvetica, sans-serif"><b> 
+      <!-- #BeginEditable "pageheader" --><a href="index.jhtml"><img src="home.gif" width="45" height="25" align="right" border="0" alt="home"></a><font color="#FFFFFF" face="Arial, Helvetica, sans-serif" size="+1">list 
+      management services: <font color="#FFCC66">help</font></font><!-- #EndEditable --></b></font></td>
+  </tr>
+  <tr> 
+    <td rowspan="2" bgcolor="#FFCC99" width="180" valign="top"> 
+      <servlet code="mit.moira.MoiraServlet"><param name=modifier value=showform></servlet>
+        <b><font face="Arial, Helvetica, sans-serif" color="#993333">select an
+        option:</font></b> 
+        <br>
+        <table border=0>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=displaylistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">display 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=getmembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">show list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=editme>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              or remove yourself from a list</font> </b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif"><b>
+              list owners may</b></font></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=updatelistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">update 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=addmember>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=removemembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">remove 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif" color="#993333"><b>
+              enter a list name:</b></font><br>
+             <servlet code="mit.moira.MoiraServlet"><param name=modifier value=""><param name=modifier value=getlistname></servlet>
+              <input name=submit2 type=submit value="Go">
+            </td>
+          </tr>
+        </table>
+</form>
+      <b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/accounts/www/list.html">request 
+      a list</a></font></b> 
+      <p><b><font face="Arial, Helvetica, sans-serif"><servlet code=mit.moira.MoiraServlet> <param name=modifier value="showurl"> <param name=url value="help.jhtml"></servlet>help</a></font></b> 
+      </p>
+      <form method="POST" action="http://web.mit.edu/bin/cgicso">
+        <b><font face="Arial, Helvetica, sans-serif" size="-1">Forgot a username?<br>Check the MIT Directory:</font></b><font face="Arial, Helvetica, sans-serif" size="-1"><br>
+        <input name="query" size=10 maxlength=60><font size="-2">
+        <input type="submit" value="Search" name="submit"></font>
+        <a href="http://web.mit.edu/communications/directory/use.html"><br>
+        How to use the MIT Directory</a></font> 
+      </form>
+      <p><b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/"><img src="mit.gif" width="50" height="32" border="0"></a> 
+        </font></b></p>
+    </td>
+    <td width="420" valign="top"><!-- #BeginEditable "pagehelp" --> 
+    <servlet code="mit.moira.MoiraServlet">
+    <param name=modifier value=getfile>
+    <param name=operation value=help>
+    </servlet>
+
+      <!-- #EndEditable --></td>
+  </tr>
+  <tr> 
+    <td width="420" valign="top"><!-- #BeginEditable "query%20results" --><!-- #EndEditable --></td>
+  </tr>
+</table>
+<p>&nbsp;</p>
+</body>
+<!-- #EndTemplate --></html>
diff --git a/webmoira/home.gif b/webmoira/home.gif
new file mode 100644 (file)
index 0000000..9e5cac4
Binary files /dev/null and b/webmoira/home.gif differ
diff --git a/webmoira/index.jhtml b/webmoira/index.jhtml
new file mode 100644 (file)
index 0000000..ed3eebe
--- /dev/null
@@ -0,0 +1,109 @@
+<servlet code=mit.moira.MoiraServlet>
+<param name=modifier value=makesession>
+</servlet>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+               "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<!-- #BeginEditable "doctitle" --> 
+<title>athena list management</title>
+<!-- #EndEditable --> 
+</head>
+<body bgcolor="#fffff7"
+text="#222222" link="#aa3333" vlink="#666666" alink="#000000">
+
+<table width="610" border="0" cellpadding="5">
+  <tr> 
+    <td colspan="2" bgcolor="#000000"><font color="#FFFFFF" face="Arial, Helvetica, sans-serif"><b> 
+      <!-- #BeginEditable "pageheader" --><font color="#FFFFFF" face="Arial, Helvetica, sans-serif" size="+1">athena list 
+      management</font><!-- #EndEditable --></b></font></td>
+  </tr>
+  <tr> 
+    <td rowspan="2" bgcolor="#FFCC99" width="180" valign="top"> 
+      <servlet code="mit.moira.MoiraServlet"><param name=modifier value=showform></servlet>
+        <b><font face="Arial, Helvetica, sans-serif" color="#993333">select an
+        option:</font></b> 
+        <br>
+        <table border=0>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=displaylistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">display 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=getmembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">show list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=editme>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              or remove yourself from a list</font> </b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif"><b>
+              list owners may</b></font></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=updatelistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">update 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=addmember>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=removemembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">remove 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif" color="#993333"><b>
+              enter a list name:</b></font><br>
+              <input name=list value="" length=15 size=12 maxlength=40>
+              <input name=submit2 type=submit value="Go">
+            </td>
+          </tr>
+        </table>
+</form>
+      <b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/accounts/www/list.html">request 
+      a new list</a></font></b> 
+      <p><b><font face="Arial, Helvetica, sans-serif"><a href="/moira/help.jhtml">help</a></font></b> 
+      </p>
+      <form method="POST" action="http://web.mit.edu/bin/cgicso">
+        <b><font face="Arial, Helvetica, sans-serif" size="-1">Forgot a username?<br>Check the MIT Directory:</font></b><font face="Arial, Helvetica, sans-serif" size="-1"><br>
+        <input name="query" size=10 maxlength=60><font size="-2">
+        <input type="submit" value="Search" name="submit"></font>
+        <a href="http://web.mit.edu/communications/directory/use.html"><br>
+        How to use the MIT Directory</a></font> 
+      </form>
+      <p><b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/"><img src="mit.gif" width="50" height="32" border="0"></a> 
+        </font></b></p>
+    </td>
+    <td rowspan="2" valign="top"><!-- #BeginEditable "pagehelp" --> 
+       <servlet code="mit.moira.MoiraServlet">
+       <param name=modifier value=getfile>
+       <param name=operation value=top>
+       </servlet>
+      <hr>
+      <!-- #EndEditable -->
+      <!-- #BeginEditable "query%20results" --> 
+      <!-- #EndEditable --></td>
+  </tr>
+</table>
+<p>&nbsp;</p>
+</body>
+<!-- #EndTemplate --></html>
diff --git a/webmoira/mit.gif b/webmoira/mit.gif
new file mode 100644 (file)
index 0000000..6893271
Binary files /dev/null and b/webmoira/mit.gif differ
diff --git a/webmoira/mit/moira/AuthenticationError.java b/webmoira/mit/moira/AuthenticationError.java
new file mode 100644 (file)
index 0000000..43630e8
--- /dev/null
@@ -0,0 +1,8 @@
+package mit.moira;
+
+public class AuthenticationError extends Exception {
+    public AuthenticationError(String message) {
+       super(message);
+    }
+
+}
diff --git a/webmoira/mit/moira/Coder.java b/webmoira/mit/moira/Coder.java
new file mode 100644 (file)
index 0000000..8ac1819
--- /dev/null
@@ -0,0 +1,146 @@
+package mit.moira;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * Class with static functions for base64 encoding and decoding as well
+ * as other utilities.
+ */
+
+public class Coder {
+  static final char encv[] = {
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 
+    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
+  static char x2c (char [] what, int off) {
+    int digit;
+    digit = (what[off] >= 'A' ? ((what[off] & 0xdf) - 'A')+10 : (what[off] - '0'));
+    digit *= 16;
+    digit += (what[off+1] >= 'A' ? ((what[off+1] & 0xdf) - 'A')+10 : (what[off+1] - '0'));
+    return((char)digit);
+  }
+  
+  private Coder() {            // No Constructors...
+  }
+
+  /**
+   * Remove URL escape sequences
+   *
+   * @param aurl A String with potential embedded escapes.
+   * @return the String with the escapes removed.
+   */
+  public static String unescape_url(String aurl) {
+    int x, y, i;
+    char [] url = new char [aurl.length()];
+    aurl.getChars(0, aurl.length(), url, 0);
+    StringBuffer ret = new StringBuffer();
+    for (i = 0; i < url.length; i++) {
+      if (url[i] == '%') {
+       ret.append(x2c(url, i+1));
+       i += 2;
+      } else if (url[i] == '+') {
+       ret.append(' ');
+      } else ret.append(url[i]);
+    }
+    return (new String(ret));
+  }
+
+  /**
+   * Encodes a byte array in base64 characters and returns the result
+   * as a String.
+   *
+   * @param data the Byte array
+   * @return The encoded string
+   */
+
+  public static String encode(byte [] data) {
+    StringBuffer ret = new StringBuffer();
+    int len = data.length;
+    int c;
+    int p = 0;
+    int i = 0;
+    int nib1, nib2, nib3, nib4;
+    while (i < len) {
+      if ((i+3) <= len) {
+       c = (data[i++] & 0xFF);
+       c = (c << 8) | (data[i++] & 0xFF);
+       c = (c << 8) | (data[i++] & 0xFF);
+       nib1 = (c & 0x0FC0000) >>> 18;
+       nib2 = (c & 0x003F000) >>> 12;
+       nib3 = (c & 0x0000FC0) >>> 6;
+       nib4 = (c & 0x000003F);
+       ret.append(encv[nib1]);
+       ret.append(encv[nib2]);
+       ret.append(encv[nib3]);
+       ret.append(encv[nib4]);
+      } else {
+       p = len - i;
+       c = data[i++];
+       c = c << 8;
+       if (p > 1) c |= (data[i++] & 0xFF);
+       c = c << 8;
+       if (p > 2) c |= (data[i++] & 0xFF); // Should never happen
+       nib1 = (c & 0x0FC0000) >>> 18;
+       nib2 = (c & 0x003F000) >>> 12;
+       nib3 = (c & 0x0000FC0) >>> 6;
+       nib4 = (c & 0x000003F);
+       ret.append(encv[nib1]);
+       ret.append(encv[nib2]);
+       if (p == 1) ret.append('=');
+       else ret.append(encv[nib3]);
+       ret.append('=');
+      }
+    }
+    return (new String(ret));
+  }
+
+  /**
+   * Decodes a base64 encoded string and returns the decoded value
+   * in a byte array.
+   *
+   * @param s The base64 encoded string
+   * @return the byte array decoded
+   */
+  public static byte [] decode (String s) {
+    Vector v = new Vector();
+    int p = 0;
+    int i = 0;
+    int k = 0;
+    int z = 0;
+    int reg = 0;
+    for (i = 0; i < s.length(); i++) {
+      char c = s.charAt(i);
+      if (c == '/') z = 63;
+      else if (c == '+') z = 62;
+      else if ((c >= 'a') && (c <= 'z')) z = (c - 'a') + 26;
+      else if ((c >= 'A') && (c <= 'Z')) z = (c - 'A');
+      else if ((c >= '0') && (c <= '9')) z = (c - '0') + 52;
+      else if (c == '=') {
+       p++;
+       z = 0;
+      } else continue;
+      reg <<= 6;
+      reg += z;
+      k++;
+      if (k == 4) {
+       for (z = 0; z < 3; z++) {
+         v.addElement(new Integer((reg & 0xff0000) >>> 16));
+         reg = (reg << 8);
+       }
+       k = 0;
+      }
+    }
+    Enumeration e = v.elements();
+    byte [] retval = new byte[v.size() - p];
+    for(i = 0; i < v.size() - p; i++) {
+      retval[i] = ((Integer) e.nextElement()).byteValue();
+    }
+    return (retval);
+  }
+}
+      
+      
+    
diff --git a/webmoira/mit/moira/Kticket.java b/webmoira/mit/moira/Kticket.java
new file mode 100644 (file)
index 0000000..28c6f44
--- /dev/null
@@ -0,0 +1,64 @@
+package mit.moira;
+
+import java.util.Date;
+import java.io.IOException;
+
+public class Kticket implements Runnable {
+    Object LOCK;
+    String name;
+    String instance;
+    String realm;
+    boolean dostop = false;
+    Runtime r;
+    Date renewTime;
+
+    Kticket(String name, String instance, String realm, Object lock) {
+       this.name = name;
+       this.instance = instance;
+       this.realm = realm;
+       this.LOCK = lock;
+       r = Runtime.getRuntime();
+       renewTime = new Date();
+    }
+
+    public void run() {
+       for(;;) {
+           if (dostop) return;
+           Date now = new Date();
+           if (now.after(renewTime)) {
+               renew();
+               renewTime = new Date(System.currentTimeMillis() + 6*3600*1000L); // 6 hours
+           }
+           try {
+               Thread.sleep(30*1000L); // Sleep for 30 seconds
+           } catch (InterruptedException i) {
+               // Nothing to be done about it
+           }
+       }
+    }
+
+    public void renew() {
+       synchronized(LOCK) {
+           try {
+               Process p = r.exec("/usr/athena/bin/kinit -k -t /mit/jis/javahacking/moira/KEY " + name + "/" + instance + "@" + realm);
+               p.waitFor();
+           } catch (IOException e) {
+               e.printStackTrace();
+           } catch (InterruptedException i) {
+           }
+       }
+    }
+
+    public void destroy() {
+       dostop = true;
+       synchronized(LOCK) {
+           try {
+               Process p = r.exec("/usr/athena/bin/kdestroy");
+               p.waitFor();
+           } catch (IOException e) {
+               e.printStackTrace();
+           } catch (InterruptedException i) {
+           }
+       }
+    }
+}
diff --git a/webmoira/mit/moira/ListInfo.java b/webmoira/mit/moira/ListInfo.java
new file mode 100644 (file)
index 0000000..34b8ee7
--- /dev/null
@@ -0,0 +1,48 @@
+package mit.moira;
+
+import java.util.Date;
+import java.text.SimpleDateFormat;
+
+public class ListInfo {
+    String name;
+    boolean active;
+    boolean bpublic;
+    boolean hidden;
+    boolean maillist;
+    boolean grouplist;
+    String gid_original;
+    int gid;
+    String ace_type;
+    String ace_name;
+    String description;
+    Date modtime;
+    String moduser;
+    String modwith;
+    static final SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
+
+    public ListInfo(String name, String active, String spublic, String hidden, String maillist, String grouplist, String gid, String ace_type, String ace_name, String description, String modtime, String moduser, String modwith) throws MoiraException {
+       this.name = name;
+       this.active = (active.equals("1")) ? true : false;
+       this.bpublic = (spublic.equals("1")) ? true : false;
+       this.hidden = (hidden.equals("1")) ? true : false;
+       this.maillist = (maillist.equals("1")) ? true : false;
+       this.grouplist = (grouplist.equals("1")) ? true : false;
+       this.gid_original = gid;
+       try {
+           this.gid = Integer.parseInt(gid);
+       } catch (NumberFormatException e) {
+           this.gid = 0;
+       }
+       this.ace_type = ace_type;
+       this.ace_name = ace_name;
+       this.description = description;
+       try {
+           this.modtime = df.parse(modtime);
+       } catch (java.text.ParseException p) {
+           throw new MoiraException(p.getMessage());
+       }
+       this.moduser = moduser;
+       this.modwith = modwith;
+    }
+}
+
diff --git a/webmoira/mit/moira/Member.java b/webmoira/mit/moira/Member.java
new file mode 100644 (file)
index 0000000..5bbf51d
--- /dev/null
@@ -0,0 +1,23 @@
+package mit.moira;
+
+public class Member {
+    String member_type;
+    String member_id;
+
+    public Member(String mt, String mi) {
+       member_type = mt;
+       member_id = mi;
+    }
+
+    public String getMemberType() {
+       return (member_type);
+    }
+
+    public String getMemberId() {
+       return (member_id);
+    }
+
+    public String toString() {
+       return("[Member type=" + member_type + ", id=" + member_id + "]");
+    }
+}
diff --git a/webmoira/mit/moira/MoiraConnect.java b/webmoira/mit/moira/MoiraConnect.java
new file mode 100644 (file)
index 0000000..d34d185
--- /dev/null
@@ -0,0 +1,119 @@
+package mit.moira;
+
+public class MoiraConnect {
+    boolean connected = false;
+    boolean isauth = false;
+    Object LOCK;
+
+    String server = "";
+    MoiraConnect() {
+    }
+
+    public MoiraConnect(String server, Object lock) {
+       this.server = server;
+       this.LOCK = lock;
+    }
+
+    public void connect() throws MoiraException {
+       if (!connected) {
+           MoiraConnectInternal.connect(server);
+           connected = true;
+       }
+    }
+           
+    public void auth() throws MoiraException {
+       if (!isauth) {
+           synchronized(LOCK) {
+               MoiraConnectInternal.auth();
+           }
+           isauth = true;
+       }
+    }
+
+    public void proxy(String user) throws MoiraException {
+       MoiraConnectInternal.proxy(user);
+    }
+           
+    public void disconnect() {
+       if (connected) {
+           MoiraConnectInternal.disconnect();
+           connected = false;
+           isauth = false;
+       }
+    }
+
+    public ListInfo get_list_info(String list) throws MoiraException {
+       if (!connected) throw new MoiraException("Not Connected");
+       String [] args = new String[1];
+       args[0] = list;
+       Object [] retinternal = MoiraConnectInternal.mr_query("get_list_info", args);
+       if (retinternal == null) return (null);
+       if (retinternal.length == 0) return (null);
+       if (retinternal.length != 1) throw new MoiraException("get_list_info returned more then one list!");
+       String [] entry = (String []) retinternal[0];
+       return (new ListInfo(entry[0], entry[1], entry[2], entry[3], entry[4], entry[5], entry[6], entry[7], entry[8], entry[9], entry[10], entry[11], entry[12]));
+    }
+
+    void update_list_info(String list, ListInfo info) throws MoiraException {
+       if (!connected) throw new MoiraException("Not Connected");
+       String [] args = new String[11];
+       args[0] = list;
+       args[1] = info.name;
+       args[2] = info.active ? "1" : "0";
+       args[3] = info.bpublic ? "1" : "0";
+       args[4] = info.hidden ? "1" : "0";
+       args[5] = info.maillist ? "1" : "0";
+       args[6] = info.grouplist ? "1" : "0";
+       args[7] = info.gid_original;
+       args[8] = info.ace_type;
+       args[9] = info.ace_name;
+       args[10] = info.description;
+       Object [] retinternal = MoiraConnectInternal.mr_query("update_list", args);
+       return;
+    }
+
+    void delete_member_from_list(String list, String type, String member) throws MoiraException {
+       if (!connected) throw new MoiraException("Not Connected");
+       String [] args = new String[3];
+       args[0] = list;
+       args[1] = type;
+       args[2] = member;
+       Object [] retinternal = MoiraConnectInternal.mr_query("delete_member_from_list", args);
+       return;
+    }
+
+    void add_member_to_list(String list, String type, String member) throws MoiraException {
+       if (!connected) throw new MoiraException("Not Connected");
+       String [] args = new String[3];
+       args[0] = list;
+       args[1] = type;
+       args[2] = member;
+       Object [] retinternal = MoiraConnectInternal.mr_query("add_member_to_list", args);
+       return;
+    }
+
+    public Member [] get_members_of_list(String list) throws MoiraException {
+       if (!connected) throw new MoiraException("Not Connected");
+       String [] args = new String[1];
+       args[0] = list;
+       Object [] retinternal = MoiraConnectInternal.mr_query("get_members_of_list", args);
+       if (retinternal == null) return (null);
+       Member [] retval = new Member[retinternal.length];
+       for (int i = 0; i < retinternal.length; i++) {
+           String [] entry = (String []) retinternal[i];
+           retval[i] = new Member(entry[0], entry[1]);
+       }
+       return (retval);
+    }
+}
+
+class MoiraConnectInternal {
+    static native void connect(String server) throws MoiraException;
+    static native void auth() throws MoiraException;
+    static native void proxy(String user) throws MoiraException;
+    static native void disconnect();
+    static native Object [] mr_query(String command, String [] args) throws MoiraException;
+    static {
+       System.loadLibrary("moirajava");
+    }
+}
diff --git a/webmoira/mit/moira/MoiraException.java b/webmoira/mit/moira/MoiraException.java
new file mode 100644 (file)
index 0000000..87ac82f
--- /dev/null
@@ -0,0 +1,19 @@
+package mit.moira;
+
+public class MoiraException extends Exception {
+    int code = 0;
+
+    MoiraException(String mess) {
+       super(mess);
+    }
+
+    MoiraException(String mess, int code) {
+       super(mess);
+       this.code = code;
+    }
+
+    public int getCode() {
+       return (code);
+    }
+
+}
diff --git a/webmoira/mit/moira/MoiraServlet.java b/webmoira/mit/moira/MoiraServlet.java
new file mode 100644 (file)
index 0000000..eeebbe3
--- /dev/null
@@ -0,0 +1,1339 @@
+package mit.moira;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import java.io.*;
+import java.util.*;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+public class MoiraServlet extends HttpServlet {
+    static final String MOIRA_SERVER = "moira.mit.edu";
+    Object LOCK = new Object();        // Used to synchronize Moira authentication
+                               // with the ticket fetching thread
+    Kticket kt = new Kticket("jis5", "foobar", "ATHENA.MIT.EDU", LOCK);
+    boolean ktinit = false;
+    private static final int MAXDISPLAY = 10;
+    Hashtable FileParts = new Hashtable();
+    Hashtable FileTimes = new Hashtable();
+    static final SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
+    static final int KE_RD_AP_BADD = 39525414; // This is a kludge
+
+
+    public synchronized void service(HttpServletRequest request, HttpServletResponse response) {
+       System.err.println("MoiraSerlvet: Service");
+       System.err.println("---------------------");
+
+       if (!ktinit) {          // Once only initialization
+           ktinit = true;
+           Thread t = new Thread(kt);
+           t.start();
+       }
+
+       String msg = null;
+
+       Hashtable qv = new Hashtable();
+
+       String modifier = "";
+
+       try {                   // Get Parameters
+           Enumeration e = request.getParameterNames();
+           while (e.hasMoreElements()) {
+               String pname = (String)e.nextElement();
+               String [] pvaluearray = request.getParameterValues(pname);
+               String pvalue = "";
+               if (pvaluearray.length > 1) {
+                   for (int i = 0; i < pvaluearray.length; i++)
+                       pvalue += pvaluearray[i];
+               } else pvalue = pvaluearray[0];
+                   
+               System.err.println(pname + " = " + pvalue);
+               qv.put(pname, pvalue);
+           }
+
+           modifier = (String)qv.get("modifier");
+           if (modifier == null) modifier = "";
+
+           if (modifier.equals("showform")) {
+               do_showform(qv, request, response);
+               return;
+           } else if (modifier.equals("showurl")) {
+               do_showurl(qv, request, response);
+               return;
+           } else if (modifier.equals("makesession")) {
+               request.getSession(true);
+               return;
+           }
+
+           String operation = (String) qv.get("operation");
+           if (operation == null) {
+               try {
+                   DataOutputStream out = new DataOutputStream(response.getOutputStream());
+                   response.setContentType("text/html");
+                   if (modifier.equals("getfile"))
+                       out.writeBytes("You need to select an operation from the list on the left!");
+                   else if (modifier.equals("displayonly"))
+                       out.writeBytes("error");
+                   else if (modifier.equals("getlistname")) {
+                       out.writeBytes(do_getlistinput(qv, request, response));
+                   }
+                   out.flush();
+               } catch (Exception ee) {
+                   ee.printStackTrace();
+               }
+               return;
+           }
+           if (operation.charAt(operation.length()-2) == '\r') // Trim newline stuff
+               operation = operation.substring(0, operation.length() - 2);
+           if (modifier.equals("getlistname")) {
+               msg = do_getlistinput(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (modifier.equals("getfile")) {
+               msg = getfile(operation);
+           } else if (operation.equals("getmembers")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "show members";
+               } else
+                   msg = do_getmembers(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("addfinal")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "added members";
+               } else
+                   msg = do_addfinal(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("editme")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "add or remove yourself from a list";
+               } else
+                   msg = do_editme(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("removemembers")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "edit/remove members";
+               } else 
+                   msg = do_removemembers(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("delmembers")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "edit/remove members";
+               } else 
+                   msg = do_delmember(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("delconfirm")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "member deleted";
+               } else 
+                   msg = do_delconfirm(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("addmember")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "add member(s)";
+               } else
+                   msg = do_addmember(qv, request, response);
+               if (msg.equals("")) return;
+           } else if (operation.equals("addme")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "added to list";
+               } else
+                   msg = do_addremme(qv, request, response, true);
+           } else if (operation.equals("delme")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "removed from list";
+               } else
+                   msg = do_addremme(qv, request, response, false);
+           } else if (operation.equals("displaylistinfo")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "display list characteristics";
+               } else
+                   msg = do_showlistinfo(qv, request, response);
+           } else if (operation.equals("updatelistinfo")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "update list characteristics";
+               } else
+                   msg = do_updatelistinfo(qv, request, response);
+           } else if (operation.equals("updatelistinfoconf")) {
+               if (modifier.equals("displayonly")) {
+                   msg = "update list characteristics";
+               } else
+                   msg = do_updatelistinfoconf(qv, request, response);
+           } else {
+               sendError("Unimplemented Operation: " + (String)qv.get("operation"), response, true);
+               return;
+           }
+
+       } catch (AuthenticationError e) {
+           msg = "<b>" + e.getMessage() + "</b><p>\r\n";
+       } catch (Exception e) {
+           msg += "<h1>Error during Processing</h1>\r\n";
+           msg += "Please try again later</HTML>\r\n";
+           e.printStackTrace();
+       }
+    
+       try {
+           DataOutputStream out = new DataOutputStream(response.getOutputStream());
+           response.setContentType("text/html");
+           if (!MOIRA_SERVER.equals("moira.mit.edu") && modifier.equals(""))
+               out.writeBytes("<font color=red>Moira Server: " + MOIRA_SERVER + "<br></font>\r\n");
+           out.writeBytes(msg);
+           out.flush();
+       } catch (Exception e) {
+           e.printStackTrace();
+       }
+    
+    }
+    void sendError(String message, HttpServletResponse response, boolean showheader) {
+       String msg;
+       if (showheader) {
+           msg = "<HTML><TITLE>Error</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>\r\n";
+           msg += "<H1>Error</H1>\r\n";
+           msg += message;
+           msg += "<p></body></html>\r\n";
+       } else msg = "<b>" + message + "</b>";
+       try {
+           DataOutputStream out = new DataOutputStream(response.getOutputStream());
+           response.setContentType("text/html");
+           out.writeBytes(msg);
+           out.flush();
+       } catch (Exception e) {
+           e.printStackTrace();
+       }
+    }
+
+    /**
+     * Authenticate the user.
+     *
+     * @param request The HttpServlet request object
+     * @return String value of the Kerberos username who owns this request
+     * @exception AuthenticationError if authentication cannot be performed
+     */
+    String do_authentication(HttpServletRequest request) throws AuthenticationError {
+
+       // Attempt to obtain authenticated username from the session
+
+       HttpSession session = request.getSession(true);
+       String kname = (String) session.getValue("kname");
+       if (kname != null) return (kname);
+
+       String client_email = (String)request.getAttribute("org.apache.jserv.SSL_CLIENT_EMAIL");
+       if (client_email == null)
+           throw new AuthenticationError("You must use a Certificate to access this service.");
+       
+       // Need to remove the @MIT.EDU portion
+       int i = client_email.indexOf('@');
+       if (i == -1)
+           throw new AuthenticationError("Malformed or non-MIT Certificate, shouldn't happen!");
+       if (!client_email.substring(i+1).equals("MIT.EDU"))
+           throw new AuthenticationError("Certificate Email Address must end in MIT.EDU");
+       kname = client_email.substring(0, i);
+       session.putValue("kname", kname);
+       return (kname);
+    }
+
+    String do_getmembers(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       String kname = do_authentication(request);
+
+       String msg = null;
+       String arg = (String) qv.get("list");
+       if (arg == null || arg.equals("")) {
+           msg = "<p>Argument is required</p>";
+           return (msg);
+       }
+       
+       // Obtain the list of members stashed in the session object
+       // If this is the first call to do_getmembers for this list,
+       // we won't have one in which case we will obtain it from Moira
+       // below
+
+       String list = "";
+       Member [] members = null;
+       HttpSession session = request.getSession(true);
+       if (session != null) {
+           list = (String)session.getValue("list");
+           if (list == null) list = "";
+           members = (Member []) session.getValue("members");
+       }
+       if (!list.equals(arg)) // Different list from argument, new session
+           members = null;     // Force obtaining members from Moira
+
+       // Determine offset of list to view
+       int offset = 0;
+       String tmp = (String)qv.get("offset");
+       if (tmp != null)
+           offset = Integer.parseInt(tmp);
+
+       boolean showall = false;
+       if (offset < 0) showall = true;
+
+       if (members == null) {
+           MoiraConnect mc = null;
+           try {
+               mc = connect();
+               mc.proxy(kname);
+               members = mc.get_members_of_list(arg);
+               mc.disconnect();
+               mc = null;
+               if (members != null)
+                   session.putValue("members", members);
+               session.putValue("list", arg);
+           } catch (MoiraException m) {
+               try {
+                   msg = "<P><b>\r\n";
+                   msg += m.getMessage();
+                   msg += "</b></P>\r\n";
+                   msg += "<!\r\n";
+                   CharArrayWriter err = new CharArrayWriter();
+                   PrintWriter perr = new PrintWriter(err);
+                   m.printStackTrace(perr);
+                   perr.flush();
+                   msg += err.toString();
+                   msg += "!>\r\n";
+                   return (msg);
+               } catch (Exception e) {
+                   e.printStackTrace();
+               }
+           } finally {
+               if (mc != null) mc.disconnect();
+           }
+       }
+
+       // Do the actual display of the members
+       if (members ==  null)
+           return("<b>No members for list " + arg + "</b>");
+           
+       msg = "<table border=0>\r\n";
+       msg += "<tr><td>&nbsp;</td><td><table border=1 cellpadding=2>\r\n";
+       msg += "<tr><td colspan=2><b>Members of list: " + arg + "</b></td></tr>\r\n";
+       int len = offset + MAXDISPLAY;
+       if (len > members.length) len = members.length;
+       if (showall) {
+           offset = 0;
+           len = members.length;
+       }
+       for (int i = offset; i < len; i++) {
+           msg += "<tr><td>" + members[i].getMemberType() + "</td>";
+           msg += "<td>" + members[i].getMemberId() + "</td></tr>\r\n";
+       }
+       msg += "</table></td><td>&nbsp;</td></tr><tr>\r\n";
+       if ((offset > 0) && !showall) {
+           msg += "<td width=50><form method=POST action=\"" +
+               response.encodeUrl("showresult.jhtml") + "\">\r\n";
+           msg += "<input type=hidden name=operation value=getmembers>";
+           msg += "<input type=hidden name=offset value=\"" + (offset - MAXDISPLAY) + "\">\r\n";
+           msg += "<input type=hidden name=list value=\"" + arg + "\">";
+           msg += "<input type=submit value=\"Previous\"></form></td>\r\n";
+       } else
+           msg += "<td width=50>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+       if (!showall && ((offset > 0) || (members.length > len))) {
+           msg += "<td width=50><form method=POST action=\"" +
+               response.encodeUrl("showresult.jhtml") + "\">\r\n";
+           msg += "<input type=hidden name=operation value=getmembers>";
+           msg += "<input type=hidden name=offset value=\"-1\">";
+           msg += "<input type=hidden name=list value=\"" + arg + "\">";
+           msg += "<input type=submit value=\"Show All\"></form></td>\r\n";
+       } else
+           msg += "<td width=50>&nbsp;</td>";
+       if (members.length > len && !showall) {
+           msg += "<td width=50><form method=POST action=\"" +
+               response.encodeUrl("showresult.jhtml") + "\">\r\n";
+           msg += "<input type=hidden name=operation value=getmembers>";
+           msg += "<input type=hidden name=offset value=\"" + (offset + MAXDISPLAY) + "\">\r\n";
+           msg += "<input type=hidden name=list value=\"" + arg + "\">";
+           msg += "<input type=submit value=\"Next\"></form></td>\r\n";
+       } else
+           msg += "<td width=50>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+       msg += "</tr>\r\n";
+       msg += "</table>\r\n";
+       return (msg);
+    }
+
+    void do_showform(Hashtable qv, HttpServletRequest request, HttpServletResponse response) {
+       String server = request.getServerName();
+       if (server.indexOf(".") == -1)
+           server += ".mit.edu"; // MIT Specific kludge!!! XXX
+       String msg = "<form method=post action=\"" + response.encodeUrl("https://" + server + ":445/moira/showresult.jhtml") + "\">";
+       try {
+           DataOutputStream out = new DataOutputStream(response.getOutputStream());
+           response.setContentType("text/html");
+           out.writeBytes(msg);
+           out.flush();
+       } catch (Exception e) {
+           e.printStackTrace(); // Nothing else I can do here
+       }
+    }
+       
+    void do_showurl(Hashtable qv, HttpServletRequest request, HttpServletResponse response) {
+       HttpSession session = request.getSession(true); // Sigh, have to create the session here if it doesn't already exist.
+       String msg = "<a href=\"" + response.encodeUrl((String)qv.get("url"))
+           + "\">";
+       try {
+           DataOutputStream out = new DataOutputStream(response.getOutputStream());
+           response.setContentType("text/html");
+           out.writeBytes(msg);
+           out.flush();
+       } catch (Exception e) {
+           e.printStackTrace(); // Nothing else I can do here
+       }
+    }
+
+    String do_getlistinput(Hashtable qv, HttpServletRequest request, HttpServletResponse response) {
+       HttpSession session = request.getSession(false);
+       String list = null;
+       list = (String) qv.get("list");
+       if ((list == null) && (session != null)) list = (String) session.getValue("list");
+       if (list == null) list = "";
+       return("<input name=list value=\"" + list + "\" length=15 size=12 maxlength=40>\r\n");
+    }
+
+    String do_addmember(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       String msg = "";
+       String list = (String) qv.get("list");
+       if (list == null || list.equals("")) {
+           msg = "<p>Argument is required</p>";
+           return (msg);
+       }
+       
+       String kname = do_authentication(request);
+
+       HttpSession session = request.getSession(true);
+
+       MoiraConnect mc = null;
+       ListInfo li = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           li = mc.get_list_info(list);
+       } catch (MoiraException m) {
+           msg += "Error getting list info: " + m.getMessage();
+                   msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           m.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+               mc = null;
+           } catch (Exception e) {
+           }
+       }
+
+       String list_description = "";
+       if (li != null) list_description = descript(li.description);
+
+       msg += "<form method=post action=\"" +
+           response.encodeUrl("showresult.jhtml") + "\">\r\n";
+       msg += "        <table border=1 cellpadding=2>\r\n          <tr> \r\n            <td colspan=2> \r\n              <p><b>Add member(s) to list " + list + "<br>\r\n</b>Description: " + list_description + "<br>\r\n                (you may enter more than one member of the same type by listing \r\n                each member on its own line)</p>\r\n            </td>\r\n          </tr>\r\n          <tr> \r\n            <td> \r\n              <select name=type>\r\n                <option value=\"USER\" selected>user</option>\r\n                <option value=\"STRING\">string</option>\r\n                <option value=\"LIST\">list</option>\r\n              </select>\r\n            </td>\r\n            <td> \r\n              <textarea name=\"member\" cols=\"20\" rows=\"4\"></textarea>\r\n            </td>\r\n          </tr>\r\n        </table>\r\n";
+       msg += "<input type=hidden name=list value=\"" + list + "\">\r\n";
+       msg += "<input type=hidden name=operation value=addfinal>\r\n";
+       msg += "<input type=submit name=submit value=\"Add Member(s)\">\r\n";
+       session.putValue("list", list);
+       session.removeValue("members"); // In case we had them from old list
+       return (msg);
+    }
+
+    String do_editme(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       String msg = null;
+       String arg = (String) qv.get("list");
+       if (arg == null || arg.equals("")) {
+           msg = "<p>Argument is required</p>";
+           return (msg);
+       }
+       
+       String kname = do_authentication(request);
+
+       HttpSession session = request.getSession(true);
+
+       MoiraConnect mc = null;
+       boolean found = false;
+       boolean sublists = false;
+       Member [] members = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           members = mc.get_members_of_list(arg);
+           if (members != null) {
+               for (int i = 0; i < members.length; i++) {
+                   if (members[i].getMemberId().equals(kname)) {
+                       found = true;
+                       if (sublists) break;
+                   }
+                   if (members[i].getMemberType().equals("LIST")) {
+                       sublists = true;
+                       if (found) break;
+                   }
+               }
+           }
+           mc.disconnect();
+           mc = null;
+       } catch (MoiraException m) {
+           try {
+               msg = "<P><b>\r\n";
+               msg += m.getMessage();
+               msg += "</b></P>\r\n";
+               msg += "<!\r\n";
+               CharArrayWriter err = new CharArrayWriter();
+               PrintWriter perr = new PrintWriter(err);
+               m.printStackTrace(perr);
+               perr.flush();
+               msg += err.toString();
+               msg += "!>\r\n";
+               return (msg);
+           } catch (Exception e) {
+               e.printStackTrace();
+           }
+       } finally {
+           if (mc != null) mc.disconnect();
+       }
+       msg = "You are " + (found ? "" : "not ") + "a member of the list <b>" + arg + "</b>.<br>\r\n";
+       if (!found && sublists)
+           msg += "Note: You may be a member of a sublist of <b>" + arg + "</b>. You may wish to check the sublists by using the <b>show list members</b> function.<br>\r\n";
+       msg += "<form method=POST action=\"" + response.encodeUrl("showresult.jhtml") + "\">\r\n";
+       msg += "<input type=hidden name=list value=\"" + arg + "\">\r\n";
+       if (found)
+           msg += "<input type=hidden name=operation value=delme>\r\n";
+       else
+           msg += "<input type=hidden name=operation value=addme>\r\n";
+       msg += "<input name=submit type=submit value=\"" + (found ? "Remove Me" : "Add Me") + "\">\r\n";
+       msg += "</form>";
+       if (members != null) {
+           session.putValue("list", arg);
+           session.putValue("members", members);
+       }
+       return (msg);
+    }
+
+    String do_addremme(Hashtable qv, HttpServletRequest request, HttpServletResponse response, boolean add) throws AuthenticationError {
+       HttpSession session = request.getSession(false); // Better be one
+       if (session == null) {
+           return("<p>Could not proceed, has it been 30 minutes since you last interaction. If so, back up and try again.</p>");
+       }
+
+       String kname = do_authentication(request);
+
+       String msg = null;
+       String listname = (String) session.getValue("list");
+       if (listname == null) {
+           return("<p>Could not find list name (shouldn't happen).</p>");
+       }
+       MoiraConnect mc = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           if (!add) 
+               mc.delete_member_from_list(listname, "USER", kname);
+           else
+               mc.add_member_to_list(listname, "USER", kname);
+       } catch (MoiraException m) {
+           msg = "<p><b>Unable to " + (add? "add" : "remove") + " you " + (add? "to" : "from") +  " the " + listname + " list.<br>\r\n";
+           msg += "The error from Moira was: " + m.getMessage() + "</b></p>";
+           msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           m.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+       if (add) msg = "You have been added to the <b>" + listname + "</b> list.";
+       else msg = "You have been removed from the <b>" + listname + "</b> list.";
+       return (msg);
+    }
+
+    String do_removemembers(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       Delmember [] del = null;
+       String msg = null;
+       String arg = (String) qv.get("list");
+       if (arg == null || arg.equals("")) {
+           msg = "<p>Argument is required</p>";
+           return (msg);
+       }
+       
+       String kname = do_authentication(request);
+
+       HttpSession session = request.getSession(true);
+
+       MoiraConnect mc = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           Member [] members = mc.get_members_of_list(arg);
+           if (members == null) {
+               return("<P><b>No such list or empty list</b></p>.");
+           }
+           del = new Delmember[members.length];
+           for (int i = 0; i < members.length; i++) {
+               del[i] = new Delmember(members[i]);
+           }
+           mc.disconnect();
+           mc = null;
+           session.putValue("list", arg);
+           session.removeValue("members"); // In case left over from previous call
+           session.putValue("delmembers", del);
+       } catch (MoiraException m) {
+           try {
+               msg = "<P><b>\r\n";
+               msg += m.getMessage();
+               msg += "</b></P>\r\n";
+               msg += "<!\r\n";
+               CharArrayWriter err = new CharArrayWriter();
+               PrintWriter perr = new PrintWriter(err);
+               m.printStackTrace(perr);
+               perr.flush();
+               msg += err.toString();
+               msg += "!>\r\n";
+           } catch (Exception e) {
+               e.printStackTrace();
+           }
+       } finally {
+           if (mc != null) mc.disconnect();
+       }
+       return (do_remdisplay(qv, request, response, del, 0));
+    }
+
+    String do_remdisplay(Hashtable qv, HttpServletRequest request, HttpServletResponse response, Delmember [] del, int offset) {
+       // Offset = -1 means show everybody
+       boolean showall = false;
+       if (offset < 0) {
+           showall = true;
+           offset = 0;
+       }
+       String msg = "<h2>Select Members to Delete</h2>\r\n";
+       if (((offset > 0) || del.length > MAXDISPLAY) && !showall)
+           msg += "<p><font color=red>Note: Selections are remembered when you select the \"Previous\" and \"Next Buttons\".</font></p>\r\n";
+       msg += "<form method=POST action=\"" +
+           response.encodeUrl("showresult.jhtml") + "\">\r\n";
+       msg += "<table border=0><tr><td></td><td>\r\n";
+       msg += "<table border=1 cellpadding=2>\r\n";
+       int len;
+       if (showall) len = del.length;
+       else len = offset + MAXDISPLAY;
+       if (del.length < len)
+           len = del.length;
+       for (int i = offset; i < len; i++) {
+           msg += "<tr><td><input type=checkbox name=selected value=\" " + i + "\"";
+           if (del[i].marked) msg += " checked";
+           msg += "></td><td>" + del[i].member.getMemberType() + "</td>";
+           msg += "<td>" + del[i].member.getMemberId() + "</td></tr>\r\n";
+       }
+       msg += "</table></td>\r\n";
+       msg += "<td><input type=hidden name=operation value=delmembers></td></tr>\r\n";
+       if (offset > 0)
+           msg += "<td><input type=submit name=dodel value=\"Previous\"></td>";
+       else
+           msg += "<td width=50>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+       if (!showall && (del.length > MAXDISPLAY))
+           msg += "<td><input type=submit name=dodel value=\"Show All\"></td>";
+       else msg += "<td width=50>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+       if (((offset + MAXDISPLAY) < del.length) && !showall)
+           msg += "<td><input type=submit name=dodel value=\"Next\"></td></tr>\r\n";
+       else msg += "<td width=50>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr>\r\n";
+       msg += "<tr><td></td><td><input type=submit name=dodel value=\"Delete Selected\"></td><td></td></tr></table></form>\r\n";
+       HttpSession session = request.getSession(false);
+       session.putValue("offset", new Integer(offset));
+       return (msg);
+    }
+
+
+    String do_delmember(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       String kname = do_authentication(request);
+
+       HttpSession session = request.getSession(false); // Better be one
+       if (session == null) {
+           return("<p>Could not proceed, has it been 30 minutes since you last interaction. If so, back up and try again.</p>");
+       }
+
+       Delmember [] del = (Delmember []) session.getValue("delmembers");
+       if (del == null) 
+           return("<p>Could not proceed, membership list doesn't exist, this should not happen!</p>");
+       Integer OffsetO = (Integer) session.getValue("offset");
+       if (OffsetO == null) {  // Hmmm....
+           OffsetO = new Integer(0);
+       }
+       
+       int offset = OffsetO.intValue();
+
+       String dodel = (String)qv.get("dodel"); // This is the submit button
+       if (dodel == null)
+           return("<p>Could not proceed, submit button incorrect, shouldn't happen.</p>");
+
+       // Process the selections on this form
+
+       int last = offset + MAXDISPLAY;
+       if (last > del.length) last = del.length;
+       for (int i = offset; i < last; i++) // Clear marked bits for displayed
+           del[i].marked = false;          // entries
+
+       String selected = (String)qv.get("selected");
+       if (selected == null) selected = ""; // None selected
+
+       StreamTokenizer tk = new StreamTokenizer(new StringReader(selected));
+       try {
+           tk.parseNumbers();
+           while (tk.nextToken() != StreamTokenizer.TT_EOF) {
+               if (tk.ttype != StreamTokenizer.TT_NUMBER) continue;
+               int v = (int) tk.nval;
+               del[v].marked = true;
+           }
+       } catch (IOException e) {
+           e.printStackTrace();
+           return("<p>Exception while processing...</p>");
+       }
+       
+       if (dodel.equals("Next"))
+           return(do_remdisplay(qv, request, response, del, offset + MAXDISPLAY));
+       if (dodel.equals("Previous"))
+           return(do_remdisplay(qv, request, response, del, offset - MAXDISPLAY));
+       if (dodel.equals("Show All"))
+           return(do_remdisplay(qv, request, response, del, -1));
+       if (!dodel.equals("Delete Selected"))
+           return("<p>Cannot proceed, bad submit value, should not happen</p>");
+       // At this point we are going to display a list of who will be
+       // deleted and offer a confirmation.
+
+       String listname = (String) session.getValue("list");
+       if (listname == null) {
+           return("<p>Could not find list name (shouldn't happen).</p>");
+       }
+
+       // Check to see if anyone will be removed
+
+       boolean havesome = false;
+       for (int i = 0; i < del.length; i++) {
+           if (del[i].marked) {
+               havesome = true;
+               break;
+           }
+       }
+       if (!havesome)
+           return("No members selected to be removed");
+
+       // Some will be, so throw up confirmation dialog
+
+       String msg = "<h2>Please Confirm Deletion of:</h2>\r\n";
+       msg += "<table border=1 cellpadding=2>\r\n";
+       for (int i = 0; i < del.length; i++) {
+           if (del[i].marked) {
+               msg += "<tr><td>" + del[i].member.getMemberType() + "</td>";
+               msg += "<td>" + del[i].member.getMemberId() + "</td></tr>\r\n";
+           }
+       }
+       msg += "</table>\r\n";
+       msg += "<form method=POST action=\"" +
+           response.encodeUrl("showresult.jhtml") + "\">\r\n";
+       msg += "<input type=hidden name=operation value=delconfirm>\r\n";
+       msg += "<input type=submit value=\"Confirm Deletion\">\r\n";
+       msg += "</form>\r\n";
+       return (msg);
+    }
+
+    String do_delconfirm(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+
+       String kname = do_authentication(request);
+
+       String msg = "";
+
+       HttpSession session = request.getSession(false); // Better be one
+       if (session == null) {
+           return("<p>Could not proceed, has it been 30 minutes since you last interaction. If so, back up and try again.</p>");
+       }
+
+       String listname = (String)session.getValue("list");
+
+       Delmember [] del = (Delmember []) session.getValue("delmembers");
+       if (del == null)
+           return("<p>Cannot proceed, nothing to delete, shouldn't happen!</p>");
+       
+       Vector problems = new Vector();
+       Vector success = new Vector();
+
+       MoiraConnect mc = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           for (int i = 0; i < del.length; i++) {
+               try {
+                   if (del[i].marked) {
+                       mc.delete_member_from_list(listname, del[i].member.getMemberType(), del[i].member.getMemberId());
+                       success.addElement(del[i].member);
+                   }
+               } catch (MoiraException m) {
+                   problems.addElement("<tr><td>" + del[i].member.getMemberId() + "</td><td>" + m.getMessage() + "</td></tr>\r\n");
+               }
+           }
+       } catch (MoiraException m) {
+           msg = "<p><b>" + m.getMessage() + "</b></p>";
+           msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           m.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+
+       msg = "";
+       if (success.size() > 0) {
+           msg += "<h2>Deleted the following:</h2>\r\n";
+           msg += "<table border=1 cellpadding=2>\r\n";
+           for (int i = 0; i < success.size(); i++) {
+               msg += "<tr><td>" + ((Member)success.elementAt(i)).getMemberId() + "</td></tr>\r\n";
+           }
+           msg += "</table>\r\n";
+       }
+       if (problems.size() > 0) {
+           msg += "<p><h2>There were difficulties removing:</h2>\r\n";
+           msg += "<table border=1 cellpadding=2>\r\n";
+           for (int i = 0; i < problems.size(); i++) {
+               msg += (String)problems.elementAt(i);
+           }
+           msg += "</table>\r\n";
+       }
+
+       return (msg);
+    }
+
+    String do_showlistinfo(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+
+       String kname = do_authentication(request);
+
+       String list = (String)qv.get("list");
+
+       if (list == null || list.equals("")) {
+           return("Hmmm, no list specified");
+       }
+
+       HttpSession session = request.getSession(true);
+
+       String msg = "";
+       MoiraConnect mc = null;
+       ListInfo li = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           li = mc.get_list_info(list);
+       } catch (MoiraException m) {
+           msg += "Error getting list info: " + m.getMessage();
+                   msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           m.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+       if (li == null) {
+           return("Did not find list info.");
+       }
+       msg += "<b>List: " + list + "</b><br>\r\n";
+       msg += "Description: " + descript(li.description) + "<br>\r\n";
+       if (li.maillist)
+           msg += "This list is a mailing list.<br>\r\n";
+       if (li.grouplist)
+           msg += "This list is a Group and its ID number is " + li.gid + ".<br>\r\n";
+       msg += "The Administrator of this list is the " + li.ace_type + ": " + li.ace_name + ".<br>\r\n";
+       msg += "This list is: " + mkflags(li) + ".<br>\r\n";
+       msg += "Last modification by " + li.moduser + " at " + df.format(li.modtime) + " with " + li.modwith + ".<br>\r\n";
+       return (msg);
+    }
+
+    String do_updatelistinfo(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+
+       String kname = do_authentication(request);
+
+       String list = (String)qv.get("list");
+
+       if (list == null || list.equals("")) {
+           return("Hmmm, no list specified");
+       }
+
+       HttpSession session = request.getSession(true);
+
+       String msg = "";
+       MoiraConnect mc = null;
+       ListInfo li = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           li = mc.get_list_info(list);
+       } catch (MoiraException m) {
+           msg += "Error getting list info: " + m.getMessage();
+                   msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           m.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+       if (li == null) {
+           return("Did not find list info.");
+       }
+
+       session.putValue("listinfo", li); // Save for the confirmation call
+       session.putValue("list", list); // Save for side bar update
+       session.removeValue("members"); // In case left over...
+
+       msg += "<form method=POST action=\"" +
+           response.encodeUrl("showresult.jhtml") + "\">\r\n";
+       msg += "<input type=hidden name=operation value=updatelistinfoconf>\r\n";
+       msg += "<b>Update characteristics of list " + list + "</b><p>\r\n";
+       msg += "Is this list a maillist? <input type=radio name=maillist value=1 "
+           + (li.maillist ? "checked" : "") + ">Yes <input type=radio name=maillist value=0 " + (li.maillist ? "" : "checked") + "> No<br>\r\n";
+       // Note: We don't update group information here
+
+       msg += "Is this list a public list? <input type=radio name=public value=1 "
+           + (li.bpublic ? "checked" : "") + ">Yes <input type=radio name=public value=0 " + (li.bpublic ? "" : "checked") + "> No<br>\r\n";
+       msg += "Is this list a hidden list? <input type=radio name=hidden value=1 "
+           + (li.hidden ? "checked" : "") + ">Yes <input type=radio name=hidden value=0 " + (li.hidden ? "" : "checked") + "> No<p>\r\n";
+       
+       msg += "The Administrator for this list is<br>\r\n";
+       msg += "<select name=ace_type><option value=\"USER\"" +
+           (li.ace_type.equals("USER") ? " selected" : "") + ">user</option>" +
+           "<option value=\"LIST\"" + (li.ace_type.equals("LIST") ? " selected" : "") + ">list</option>" +
+           "<option value=\"KERBEROS\"" + (li.ace_type.equals("KERBEROS") ? " selected" : "") + ">kerberos</option></select>";
+       msg += " <input name=ace_name value=\"" + li.ace_name + "\"><br>\r\n";
+       msg += "Description:<br>\r\n";
+       msg += "<textarea name=description cols=60 rows=3>" + li.description + "</textarea><br>\r\n";
+       msg += "<input type=submit value=\"Make Update\"></form>\r\n";
+       return (msg);
+    }
+
+    String do_updatelistinfoconf(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+
+       String kname = do_authentication(request);
+       HttpSession session = request.getSession(false); // Better be one
+       if (session == null) {
+           return("<p>Could not proceed, has it been 30 minutes since you last interaction. If so, back up and try again.</p>");
+       }
+       
+       ListInfo li = (ListInfo)session.getValue("listinfo");
+       if (li == null) {
+           return("<p>Could not proceed, could not find orignial list info, should not happen!</p>");
+       }
+       
+       String tmp = (String)qv.get("description");
+       if (tmp != null) li.description = tmp;
+       tmp = (String)qv.get("maillist");
+       if (tmp != null) {
+           if (tmp.equals("1")) li.maillist = true;
+           else li.maillist = false;
+       }
+       tmp = (String)qv.get("public");
+       if (tmp != null) {
+           if (tmp.equals("1")) li.bpublic = true;
+           else li.bpublic = false;
+       }
+       tmp = (String)qv.get("hidden");
+       if (tmp != null) {
+           if (tmp.equals("1")) li.hidden = true;
+           else li.hidden = false;
+       }
+       tmp = (String)qv.get("ace_type");
+       if (tmp != null)
+           li.ace_type = tmp;
+
+       tmp = (String)qv.get("ace_name");
+       if (tmp != null)
+           li.ace_name = tmp;
+
+       MoiraConnect mc = null;
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           mc.update_list_info(li.name, li);
+           mc.disconnect();
+           mc = null;
+       } catch (MoiraException e) {
+           String msg;
+           if (e.getMessage().startsWith("No such list")) {
+               msg = "<p><b>Error updating " + li.name + ".<br>\r\n";
+               msg += "The list you specified as administrator does not exist.</p>\r\n";
+           } else if(e.getMessage().startsWith("No such user")) {
+               msg = "<p><b>Error updating " + li.name + ".<br>\r\n";
+               msg += "The user you specified as administrator is not known to Moira.</p>\r\n";                
+           } else 
+               msg = "<p><b>Error during update of " + li.name + ": " + e.getMessage() + "</b></p>";
+           msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           e.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+       String msg = "<b>Update of " + li.name + " succeeded</b><p>\r\n";
+       qv.put("list", li.name);
+       msg += do_showlistinfo(qv, request, response);
+       return (msg);
+    }
+
+    String do_addfinal(Hashtable qv, HttpServletRequest request, HttpServletResponse response) throws AuthenticationError {
+       HttpSession session = request.getSession(false); // Better be one
+       if (session == null) {
+           return("<p>Could not proceed, has it been 30 minutes since you last interaction. If so, back up and try again.</p>");
+       }
+
+       String kname = do_authentication(request);
+
+       String listname = (String) session.getValue("list");
+       if (listname == null) {
+           return("<p>Unable to find list name! (shouldn't happen).</p>");
+       }
+       String msg = "";
+       String member = (String)qv.get("member");
+       String type = (String)qv.get("type");
+       if (member == null || type == null || member.equals("")) {
+           return("<p>No names selected to be added!</p>");
+       }
+       MoiraConnect mc = null;
+       StreamTokenizer tk = null;
+       msg += "<table border=1 cellpadding=2>\r\n";
+       boolean addheader = false;
+       Vector warnings = new Vector();
+       Vector problems = new Vector();
+       try {
+           mc = connect();
+           mc.proxy(kname);
+           tk = new StreamTokenizer(new StringReader(member));
+           tk.wordChars('@', '@');
+           tk.wordChars('0', '9');
+           tk.wordChars('_', '_');
+           while (tk.nextToken() != StreamTokenizer.TT_EOF) {
+               if (tk.ttype != StreamTokenizer.TT_WORD) continue;
+               try {
+
+                   // Pre-process user input
+                   String [] user = canonicalize(tk.sval, type);
+
+                   mc.add_member_to_list(listname, user[1], user[0]);
+                   if (!addheader) {
+                       msg += "<tr><td>Added to list</td></tr>\r\n";
+                       addheader = true;
+                   }
+                   msg += "<tr><td>" + user[1] + "</td><td>" + user[0] + "</td></tr>\r\n";
+                   if (user.length > 2)
+                       warnings.addElement(user[2]);
+               } catch (MoiraException e) {
+                   String err = "<tr><td>" + tk.sval + "</td><td>" + e.getMessage() + "</td></tr>\r\n";
+                   problems.addElement(err);
+               }
+           }
+           if (!addheader)     // We didn't seem to be able to add anyone
+               msg += "<tr><td>No one added!</td></tr>\r\n";
+           msg += "</table>\r\n";
+           mc.disconnect();
+           mc = null;
+       } catch (MoiraException e) {
+           if (tk != null)
+               msg = "<p><b>Error adding " + tk.sval + ": " + e.getMessage() + "</b></p>";
+           else
+               msg = "<p><b>Error during add: " + e.getMessage() + "</b></p>";
+           msg += "<!\r\n";
+           CharArrayWriter err = new CharArrayWriter();
+           PrintWriter perr = new PrintWriter(err);
+           e.printStackTrace(perr);
+           perr.flush();
+           msg += err.toString();
+           msg += "!>\r\n";
+           return (msg);
+       } catch (IOException e) {
+           e.printStackTrace(); // Shouldn't happen
+       } finally {
+           try {
+               if (mc != null) mc.disconnect();
+           } catch (Exception e) {
+           }
+       }
+       if (problems.size() != 0) {
+           msg += "<p>There were difficulties adding the following users:<br>\r\n";
+           msg += "<table border=1 cellpadding=2>\r\n";
+           for (int i = 0; i < problems.size(); i++)
+               msg += problems.elementAt(i);
+           msg += "</table>\r\n";
+       }
+       if (warnings.size() != 0) {
+           msg += "<p>The following warnings were generated:<br>\r\n";
+           msg += "<table border=1 cellpadding=2>\r\n";
+           for (int i = 0; i < warnings.size(); i++)
+               msg += "<tr><td>" + warnings.elementAt(i) + "</td></tr>\r\n";
+           msg += "</table>\r\n";
+       }
+       return (msg);
+    }
+
+    String getfile(String operation) {
+       ResourceBundle bd = null;
+       try {
+           bd = ResourceBundle.getBundle("mit.moira.FileParts");
+       } catch (java.util.MissingResourceException e) {
+           return("Cannot find FileParts.properties");
+       }
+       if (bd == null) return("Cannot find FileParts.properties!");
+       String filename = null;
+       try {
+           filename = bd.getString(operation);
+       } catch (java.util.MissingResourceException e) {
+           return("Cannot find file for operation: " + operation);
+       }
+       if (filename == null) {
+           return("Cannot find file for operation: " + operation);
+       }
+       File file = new File(filename);
+       if (!file.isFile()) return("Cannot file file: " + filename);
+       long filemodtime = file.lastModified();
+       Long cachemodtime = (Long) FileTimes.get(filename);
+       if (cachemodtime == null || filemodtime > cachemodtime.longValue()) {
+           // Need to fetch the file into the cache
+           byte [] buffer = null;
+           try {
+               BufferedInputStream input = new BufferedInputStream(new FileInputStream(filename));
+               buffer = new byte[input.available()];
+               input.read(buffer);
+               input.close();
+           } catch (FileNotFoundException f) {
+                               // Should never happen given check above
+           } catch (IOException e) {
+               return("IO Error Reading: " + filename);
+           }
+           String data = new String(buffer);
+           data += "\r\n<!-- FileName: " + filename + "-->\r\n";
+           FileTimes.put(filename, new Long(filemodtime));
+           FileParts.put(filename, data);
+           return (data);
+       } else {
+           return ((String)FileParts.get(filename));
+       }
+    }
+
+    String mkflags(ListInfo li) {
+       String retval = "";
+       int state = 0;
+       if (!li.hidden) {
+           retval = "visible";
+           state = 1;
+       }
+       if (!li.bpublic) {
+           switch (state) {
+           case 0:
+               retval = "private";
+               state = 1;
+               break;
+           case 1:
+               retval = "private and " + retval;
+               state = 2;
+               break;
+           case 2:
+               retval = "private, " + retval;
+           }
+       }
+       if (li.active) {
+           switch (state) {
+           case 0:
+               retval = "active";
+               state = 1;
+               break;
+           case 1:
+               retval = "active and " + retval;
+               state = 2;
+               break;
+           case 2:
+               retval = "active, " + retval;
+           }
+       }
+       return (retval);
+    }
+
+    /**
+     * pre-process user input. Mostly this catches cases where the
+     * incorrect type is used to add someone to a list.
+     *
+     * @param user String or Userid being proposed for an add
+     * @param type Either LIST,STRING or USER
+     * @return an array of elements. The first is the user portion followed by the type and an optional warning message.
+     */
+
+    private String [] canonicalize(String user, String type) throws MoiraException {
+       int i;
+       String [] retval = null;
+       if (type.equals("STRING")) {
+           i = user.indexOf('@');
+           if (i != -1) {
+               String host = user.substring(i + 1);
+               if (host.equalsIgnoreCase("mit.edu")) {
+                   retval = new String[3];
+                   retval[0] = user.substring(0, i);
+                   retval[1] = "USER";
+                   retval[2] = "Converted " + user + " to userid " + retval[0];
+                   return (retval);
+               }
+               // Don't convert
+               retval = new String[2];
+               retval[0] = user;
+               retval[1] = "STRING";
+               return (retval);
+           } else { // No @ sign
+               throw new MoiraException("STRING (mailing list entries) must have an \'@\' in them!");
+           }
+       } else if (type.equals("USER")) {
+           i = user.indexOf('@');
+           if (i != -1) {
+               String host = user.substring(i + 1);
+               if (host.equalsIgnoreCase("mit.edu")) {
+                   retval = new String[3];
+                   retval[0] = user.substring(0, i);
+                   retval[1] = "USER";
+                   retval[2] = "Converted " + user + " to userid " + retval[0];
+                   return (retval);
+               }
+               throw new MoiraException("USER types must not have \'@\'s in them!");
+           }
+           retval = new String[2];
+           retval[0] = user;
+           retval[1] = type;
+           return (retval);
+       } else if (type.equals("LIST")) {
+           i = user.indexOf('@');
+           if (i == -1) {      // No '@' sign, just process normally
+               retval = new String[2];
+               retval[0] = user;
+               retval[1] = type;
+               return (retval);
+           } else {
+               String host = user.substring(i + 1);
+               if (host.equalsIgnoreCase("mit.edu")) { // trim mit.edu
+                   retval = new String[3];
+                   retval[0] = user.substring(0, i);
+                   retval[1] = "LIST";
+                   retval[2] = "Converted " + user + " to list named " + retval[0];
+                   return (retval);
+               } else
+                   throw new MoiraException("LIST types may not contain \'@\'s in them!");
+           } 
+       } else { // Just pass through everything else for now
+           retval = new String[2];
+           retval[0] = user;
+           retval[1] = type;
+           return (retval);
+       }
+    }
+
+    /**
+     * Remove &lt;SCRIPT&gt; tags from input String
+     *
+     * @param input String to check out
+     * @return String with &lt;SCRIPT&gt; tag removed
+     */
+    private String descript(String input) {
+       String lc = input.toLowerCase();
+       int i = lc.indexOf("<scri");
+       int j = lc.lastIndexOf("</scri");
+       int len = input.length();
+       if (i == -1) return (input); // Nothing to do.
+       else if (j == -1) {     // No closing script tag
+           return (input.substring(0, i));
+       } else {
+           return (input.substring(0, i) + input.substring(j));
+       }
+    }
+
+    /**
+     * Shutdown the servlet
+     *
+     */
+    public void destroy() {
+       super.destroy();
+       kt.destroy();
+    }
+
+
+    /**
+     * Connect to the Moira Server. Special case the error KE_RD_AP_BADD.
+     * This error occurs if the tickets we have contain the wrong IP address.
+     * This can happen if we are on a multi-homed system. If this happens,
+     * get new tickets and try again. If we exceed the loop count, throw
+     * an exception (sigh).
+     *
+     * @return A Moira Connection Object
+     * @exception MoiraException on any error
+     */
+    protected MoiraConnect connect() throws MoiraException {
+       MoiraConnect retval = null;
+       int count = 0;
+       while (count++ < 10) {  // Got to stop at some point!
+           retval = new MoiraConnect(MOIRA_SERVER, LOCK);
+           retval.connect();
+           try {
+               retval.auth();
+               return (retval); // No exception, return. yeah!
+           } catch (MoiraException m) {
+               if ((m.getCode() != KE_RD_AP_BADD) || (count > 8)) {
+                   if (retval != null) {
+                       try {
+                           retval.disconnect();
+                       } catch (Exception e) {
+                       }
+                   }
+                   throw m;    // Re-throw if not the error we expect or we are looping
+               }
+           }
+           if (retval != null) {
+               try {
+                   retval.disconnect();
+               } catch (Exception e) {
+               }
+               retval = null;
+           }
+           System.err.println("MoiraServlet: Forced Renewal...");
+           kt.renew();         // Renew tickets
+       }
+       return (null);
+    }
+
+}
+
+class Delmember {
+    boolean marked = false;
+    Member member = null;
+
+    Delmember(Member m) {
+       member = m;
+    }
+}
diff --git a/webmoira/moirai.c b/webmoira/moirai.c
new file mode 100644 (file)
index 0000000..31c0c3e
--- /dev/null
@@ -0,0 +1,298 @@
+#include "mit_moira_MoiraConnectInternal.h"
+#include <moira.h>
+#include <mr_et.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+static void throwMoiraException(JNIEnv *env, int code)
+{
+    char buffer[1024];
+    jmethodID mid;
+    jobject tothrow;
+    jclass IOE;
+    jstring jmess;
+
+    IOE = (*env)->FindClass(env, "mit/moira/MoiraException");
+    if (IOE == NULL) {
+       fprintf(stderr, "moirai: No Class\n");
+       goto die;
+    }
+    mid = (*env)->GetMethodID(env, IOE, "<init>", "(Ljava/lang/String;I)V");
+    if (mid == NULL) {
+       fprintf(stderr, "moirai: No Method\n");
+       goto die;
+    }
+    sprintf(buffer, "%s", error_message(code));
+    jmess = (*env)->NewStringUTF(env, buffer);
+    if (jmess == NULL) {
+       fprintf(stderr, "Cannot get new string\n");
+       goto die;
+    }
+    tothrow = (*env)->NewObject(env, IOE, mid, jmess, (jint) code);
+    if (tothrow == NULL) {
+       fprintf(stderr, "moirai: No Throw\n");
+       goto die;
+    }
+    (*env)->Throw(env, (jthrowable) tothrow);
+    return;
+ die:
+    abort();
+    return;
+}
+
+static void throwMoiraExceptionMess(JNIEnv *env, char *mess)
+{
+    jclass IOE = (*env)->FindClass(env, "mit/moira/MoiraException");
+    if (IOE == NULL) abort();
+    (*env)->ThrowNew(env, IOE, mess);
+    return;
+}
+
+JNIEXPORT void JNICALL Java_mit_moira_MoiraConnectInternal_connect(JNIEnv *env,
+                                                        jclass Class, jstring server) {
+    int status;
+    char buffer[1024];
+    const char *aserver = (*env)->GetStringUTFChars(env, server, 0);
+    if (strlen(aserver) > sizeof(buffer)) abort();
+    strcpy(buffer, aserver);
+    (*env)->ReleaseStringUTFChars(env, server, aserver);
+    status = mr_connect(buffer);
+    if (status != MR_SUCCESS) throwMoiraException(env, status);
+    status = mr_version(2);
+    if (status != MR_SUCCESS) {
+       if (status == MR_VERSION_LOW) return; /* This is OK */
+       else {
+           mr_disconnect();
+           throwMoiraException(env, status);
+       }
+    }
+    return;
+}
+
+JNIEXPORT void JNICALL Java_mit_moira_MoiraConnectInternal_proxy(JNIEnv *env,
+                                                      jclass Class,
+                                                      jstring jUser) {
+    int status;
+    char buffer[1024];
+    const char *user = (*env)->GetStringUTFChars(env, jUser, 0);
+    if (strlen(user) > sizeof(buffer)) abort();
+    strcpy(buffer, user);
+    (*env)->ReleaseStringUTFChars(env, jUser, user);
+    status = mr_proxy(buffer, "Java");
+    if (status != MR_SUCCESS) throwMoiraException(env, status);
+    return;
+}
+
+
+JNIEXPORT void JNICALL Java_mit_moira_MoiraConnectInternal_auth(JNIEnv *env,
+                                                     jclass Class) {
+    int status;
+    status = mr_auth("JavaInterface");
+    if (status != MR_SUCCESS) throwMoiraException(env, status);
+    return;
+}
+
+JNIEXPORT void JNICALL Java_mit_moira_MoiraConnectInternal_disconnect
+    (JNIEnv *env, jclass Class) {
+    mr_disconnect();
+    return;
+}
+       
+typedef struct ArgBlock {
+    int alloccount;
+    int count;
+    char **stringval;
+} ArgBlock;
+
+typedef struct ListBlock {
+    int alloccount;
+    int count;
+    ArgBlock **Args;
+} ListBlock;
+
+static jobjectArray ArgBlockToJavaArray(JNIEnv *env, ArgBlock *argb);
+static jobjectArray ListBlockToJavaArray(JNIEnv *env, ListBlock *lb);
+
+extern int mr_query(char *name, int argc, char **argv,
+               int (*proc)(int, char **, void *), void *hint);
+static ListBlock *NewListBlock();
+static ArgBlock *NewArgBlock();
+static void FreeListBlock(ListBlock *);
+static int StashResults(int argc, char **argv, void *callback);
+static char **ConvertJavaArgs(JNIEnv *env, jobjectArray jargs);
+static void FreeArgs(char **args);
+
+JNIEXPORT jobjectArray JNICALL Java_mit_moira_MoiraConnectInternal_mr_1query
+    (JNIEnv *env, jclass Class, jstring jcommand, jobjectArray jargs) {
+    ListBlock *listblock = NewListBlock();
+    jobjectArray retval;
+    int status;
+    const char *command;
+    char icommand[1024];
+    char **args = ConvertJavaArgs(env, jargs);
+    if (args == NULL) return (NULL); /* It probably thru an exception */
+    command = (*env)->GetStringUTFChars(env, jcommand, 0);
+    strncpy(icommand, command, sizeof(icommand));
+
+    status = mr_query(icommand, (*env)->GetArrayLength(env, jargs), args, 
+                        StashResults, listblock);
+    FreeArgs(args);                    /* Don't need them anymore */
+    if (status != MR_SUCCESS) {
+       FreeListBlock(listblock);
+       throwMoiraException(env, status);
+       return (0);
+    }
+    /*    if (listblock->count == 0) { / * No such list or empty list * /
+       FreeListBlock(listblock);
+       throwMoiraExceptionMess(env, "No Such List or Empty List");
+       return(0);
+    } */
+
+    if (listblock->count == 0) return (0);
+
+    retval = ListBlockToJavaArray(env, listblock);
+    FreeListBlock(listblock);
+    return (retval);
+
+}
+
+static ArgBlock *NewArgBlock() {
+    ArgBlock *argb = (ArgBlock *)malloc(sizeof(ArgBlock));
+    argb->alloccount = 10;
+    argb->stringval = (char **) malloc(10 * sizeof(char *));
+    argb->count = 0;
+    return (argb);
+}
+
+static void AddArg(ArgBlock *argb, char *arg) {
+    int i;
+    if (argb->alloccount <= (argb->count + 1)) {
+       char **st = malloc((2 * argb->alloccount) * sizeof(char *));
+       argb->alloccount *= 2;
+       for (i = 0; i < argb->count; i++)
+           st[i] = argb->stringval[i];
+       free(argb->stringval);
+       argb->stringval = st;
+    }
+    argb->stringval[argb->count++] = arg;
+}
+
+static ListBlock *NewListBlock() {
+    ListBlock *list = (ListBlock *)malloc(sizeof(ListBlock));
+    list->alloccount = 10;
+    list->Args = (ArgBlock **) malloc(10 * sizeof(ArgBlock *));
+    list->count = 0;
+    return (list);
+}
+
+static void FreeArgBlock(ArgBlock *argb) {
+    int i;
+    for (i = 0; i < argb->count; i++) free(argb->stringval[i]);
+    free(argb->stringval);
+    free(argb);
+}
+
+static void FreeListBlock(ListBlock *list) {
+    int i;
+    for (i = 0; i < list->count; i++) FreeArgBlock(list->Args[i]);
+    free(list->Args);
+    free(list);
+}
+
+static jobjectArray ArgBlockToJavaArray(JNIEnv *env, ArgBlock *argb) {
+    jobjectArray retval;
+    int i;
+    retval = (*env)->NewObjectArray(env, argb->count,
+                                   (*env)->FindClass(env, "java/lang/String"),
+                                   NULL);
+    for (i = 0; i < argb->count; i++) {
+       (*env)->SetObjectArrayElement(env, retval, i,
+                                     (*env)->NewStringUTF(env, argb->stringval[i]));
+    }
+    return (retval);
+}
+
+static jobjectArray ListBlockToJavaArray(JNIEnv *env, ListBlock *lb) {
+    jobjectArray retval;
+    int i;
+    retval = (*env)->NewObjectArray(env, lb->count,
+                                   (*env)->FindClass(env, "java/lang/Object"),
+                                   NULL);
+    for (i = 0; i < lb->count; i++)
+       (*env)->SetObjectArrayElement(env, retval, i,
+                                     ArgBlockToJavaArray(env, lb->Args[i]));
+    return (retval);
+}
+
+static int StashResults(int argc, char **argv, void *callback) {
+    ListBlock *list = (ListBlock *) callback;
+    ArgBlock *argb;
+    char *arg;
+    int i;
+
+    /* printf("DEBUG: StashResults argc = %d\n", argc);
+     * for (i = 0; i < argc; i++)
+     * printf("DEBUG: StashResults argv[%d] = %s\n", i, argv[i]);
+     */
+
+    while (list->alloccount <= (list->count + 1)) {
+       ArgBlock **args = (ArgBlock **) malloc(list->alloccount * 2 * sizeof(char *));
+       list->alloccount = list->alloccount * 2;
+       for(i = 0; i < list->count; i++) {
+           args[i] = list->Args[i];
+       }
+       free(list->Args);
+       list->Args = args;
+    }
+
+    argb = NewArgBlock();
+
+    for (i = 0; i < argc; i++) {
+       arg = (char *) malloc(strlen(argv[i]) + 1);
+       strcpy(arg, argv[i]);
+       AddArg(argb, arg);
+    }
+    list->Args[list->count++] = argb;
+    return (0);
+}
+
+static char **ConvertJavaArgs(JNIEnv *env, jobjectArray jargs) {
+    char **retval;
+    int i, j;
+    const char *iarg;
+    int length = (*env)->GetArrayLength(env, jargs);
+    if (length == 0) {         /* Does this happen in a non-error situation? */
+       retval = (char **) malloc(sizeof (char *));
+       retval[0] = NULL;
+       return(retval);
+    }
+    retval = (char **) malloc((length + 1) * sizeof(char *));
+    for (i = 0; i < length; i++) {
+       jobject jarg = (*env)->GetObjectArrayElement(env, jargs, i);
+       if ((*env)->ExceptionOccurred(env)) {
+           for (j = 0; j < i; j++) free(retval[j]);
+           free (retval);
+           return (NULL);
+       }
+       iarg = (*env)->GetStringUTFChars(env, jarg, 0);
+       if ((*env)->ExceptionOccurred(env)) {
+           for (j = 0; j < i; j++) free(retval[j]);
+           free (retval);
+           return (NULL);
+       }
+       retval[i] = malloc(strlen(iarg) + 1);
+       strcpy(retval[i], iarg);
+       (*env)->ReleaseStringUTFChars(env, jarg, iarg);
+    }
+    retval[length] = NULL;
+    return (retval);
+}
+
+static void FreeArgs(char **args) {
+    int i;
+    i = 0;
+    while (args[i] != NULL) free(args[i++]);
+    free(args);
+}
+
diff --git a/webmoira/request.jhtml b/webmoira/request.jhtml
new file mode 100644 (file)
index 0000000..5964c14
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+               "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<!-- #BeginEditable "doctitle" --> 
+<title>request a list</title>
+<!-- #EndEditable --> 
+</head>
+<body bgcolor="#fffff7"
+text="#222222" link="#aa3333" vlink="#666666" alink="#000000">
+
+<table width="610" border="0" cellpadding="5">
+  <tr> 
+    <td colspan="2" bgcolor="#000000"><font color="#FFFFFF" face="Arial, Helvetica, sans-serif"><b> 
+      <!-- #BeginEditable "pageheader" --><a href="index.jhtml"><img src="home.gif" width="45" height="25" align="right" border="0" alt="home"></a><font color="#FFFFFF" face="Arial, Helvetica, sans-serif" size="+1">list 
+      management services: <font color="#FFCC66">request a list</font></font><!-- #EndEditable --></b></font></td>
+  </tr>
+  <tr> 
+    <td rowspan="2" bgcolor="#FFCC99" width="180" valign="top"> 
+      <servlet code="mit.moira.MoiraServlet"><param name=modifier value=showform></servlet>
+        <b><font face="Arial, Helvetica, sans-serif" color="#993333">select an
+        option:</font></b> 
+        <br>
+        <table border=0>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=displaylistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">display 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=getmembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">show list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=editme>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              or remove yourself from a list</font> </b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif"><b>
+              list owners may</b></font></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=updatelistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">update 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=addmember>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=removemembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">remove 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif" color="#993333"><b>
+              enter a list name:</b></font><br>
+             <servlet code="mit.moira.MoiraServlet"><param name=modifier value=""><param name=modifier value=getlistname></servlet>
+              <input name=submit2 type=submit value="Go">
+            </td>
+          </tr>
+        </table>
+</form>
+      <b><font face="Arial, Helvetica, sans-serif"><servlet code=mit.moira.MoiraServlet> <param name=modifier value="showurl"> <param name=url value="request.jhtml"></servlet>request 
+      a list</a></font></b> 
+      <p><b><font face="Arial, Helvetica, sans-serif"><servlet code=mit.moira.MoiraServlet> <param name=modifier value="showurl"> <param name=url value="help.jhtml"></servlet>help</a></font></b> 
+      </p>
+      <form method="POST" action="http://web.mit.edu/bin/cgicso">
+        <b><font face="Arial, Helvetica, sans-serif" size="-1">Forgot a username?<br>Check the MIT Directory:</font></b><font face="Arial, Helvetica, sans-serif" size="-1"><br>
+        <input name="query" size=10 maxlength=60><font size="-2">
+        <input type="submit" value="Search" name="submit"></font>
+        <a href="http://web.mit.edu/communications/directory/use.html"><br>
+        How to use the MIT Directory</a></font> 
+      </form>
+      <p><b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/"><img src="mit.gif" width="50" height="32" border="0"></a> 
+        </font></b></p>
+    </td>
+    <td width="420" valign="top"><!-- #BeginEditable "pagehelp" --> 
+       <servlet code="mit.moira.MoiraServlet">
+       <param name=modifier value=getfile>
+       <param name=operation value=getnewlist>
+       </servlet>
+      <!-- #EndEditable --></td>
+  </tr>
+  <tr> 
+    <td width="420" valign="top"><!-- #BeginEditable "query%20results" --><!-- #EndEditable --></td>
+  </tr>
+</table>
+<p>&nbsp;</p>
+</body>
+<!-- #EndTemplate --></html>
diff --git a/webmoira/showresult.jhtml b/webmoira/showresult.jhtml
new file mode 100644 (file)
index 0000000..e7f3488
--- /dev/null
@@ -0,0 +1,109 @@
+<servlet code=mit.moira.MoiraServlet>
+<param name=modifier value=makesession>
+</servlet>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+               "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<title><servlet code="mit.moira.MoiraServlet"><param name=modifier value=displayonly></servlet></title>
+</head>
+<body bgcolor="#fffff7"
+text="#222222" link="#aa3333" vlink="#666666" alink="#000000">
+
+<table width="610" border="0" cellpadding="5">
+  <tr> 
+    <td colspan="2" bgcolor="#000000"><font color="#FFFFFF" face="Arial, Helvetica, sans-serif"><b> 
+      <!-- #BeginEditable "pageheader" --><a href="index.jhtml"><img src="home.gif" width="45" height="25" align="right" border="0" alt="home"></a><font size="+1">athena list 
+      management: <font color="#FFCC66"><servlet code="mit.moira.MoiraServlet"><param name=modifier value=displayonly></servlet></font></font><!-- #EndEditable --></b></font></td>
+  </tr>
+  <tr> 
+    <td rowspan="2" bgcolor="#FFCC99" width="180" valign="top"> 
+      <servlet code="mit.moira.MoiraServlet"><param name=modifier value=showform></servlet>
+        <b><font face="Arial, Helvetica, sans-serif" color="#993333">select an
+        option:</font></b> 
+        <br>
+        <table border=0>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=displaylistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">display 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=getmembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">show list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=editme>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              or remove yourself from a list</font> </b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif"><b>
+              list owners may</b></font></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=updatelistinfo>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">update 
+              list characteristics</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=addmember>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">add 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td align="center"> 
+              <input type=radio name=operation value=removemembers>
+            </td>
+            <td><b><font face="Arial, Helvetica, sans-serif">remove 
+              list members</font></b></td>
+          </tr>
+          <tr> 
+            <td colspan="2"><font face="Arial, Helvetica, sans-serif" color="#993333"><b>
+              enter a list name:</b></font><br>
+             <servlet code="mit.moira.MoiraServlet"><param name=modifier value=""><param name=modifier value=getlistname></servlet>
+              <input name=submit2 type=submit value="Go">
+            </td>
+          </tr>
+        </table>
+</form>
+      <b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/accounts/www/list.html"></servlet>request 
+      a new list</a></font></b> 
+      <p><b><font face="Arial, Helvetica, sans-serif"><servlet code=mit.moira.MoiraServlet> <param name=modifier value="showurl"> <param name=url value="help.jhtml"></servlet>help</a></font></b> 
+      </p>
+      <form method="POST" action="http://web.mit.edu/bin/cgicso">
+        <b><font face="Arial, Helvetica, sans-serif" size="-1">Forgot a username?<br>Check the MIT Directory:</font></b><font face="Arial, Helvetica, sans-serif" size="-1"><br>
+        <input name="query" size=10 maxlength=60><font size="-2">
+        <input type="submit" value="Search" name="submit"></font>
+        <a href="http://web.mit.edu/communications/directory/use.html"><br>
+        How to use the MIT Directory</a></font> 
+      </form>
+      <p><b><font face="Arial, Helvetica, sans-serif"><a href="http://web.mit.edu/"><img src="mit.gif" width="50" height="32" border="0"></a> 
+        </font></b></p>
+    </td>
+    <td rowspan="2" valign="top"><!-- #BeginEditable "pagehelp" --> 
+       <servlet code="mit.moira.MoiraServlet">
+       <param name=modifier value=getfile>
+       </servlet>
+      <hr>
+      <!-- #EndEditable -->
+      <!-- #BeginEditable "query%20results" --> 
+      <servlet code="mit.moira.MoiraServlet">
+      <param name=modifier value="">
+      </servlet>
+      <!-- #EndEditable --></td>
+  </tr>
+</table>
+<p>&nbsp;</p>
+</body>
+<!-- #EndTemplate --></html>
This page took 1.107801 seconds and 5 git commands to generate.