--- /dev/null
+
+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/
+
--- /dev/null
+
+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/
+
--- /dev/null
+<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>
--- /dev/null
+<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>
+
--- /dev/null
+<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>
+
--- /dev/null
+<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>
--- /dev/null
+<p>You will stop receiving email from this list within 24 hours.</p>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+ <p> </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> </p>
+ <p> </p>
--- /dev/null
+<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> </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> </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 "display list characteristics" option, and edited
+by list owners with the "update list characteristics"
+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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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
--- /dev/null
+<p>Updates to list characteristics will take effect within 24 hours.</p>\r
--- /dev/null
+<!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> </p>
+</body>
+<!-- #EndTemplate --></html>
--- /dev/null
+<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> </p>
+</body>
+<!-- #EndTemplate --></html>
--- /dev/null
+package mit.moira;
+
+public class AuthenticationError extends Exception {
+ public AuthenticationError(String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+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);
+ }
+}
+
+
+
--- /dev/null
+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) {
+ }
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
+
--- /dev/null
+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 + "]");
+ }
+}
--- /dev/null
+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");
+ }
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+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> </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> </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> </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> </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> </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> </td>";
+ if (!showall && (del.length > MAXDISPLAY))
+ msg += "<td><input type=submit name=dodel value=\"Show All\"></td>";
+ else msg += "<td width=50> </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> </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 <SCRIPT> tags from input String
+ *
+ * @param input String to check out
+ * @return String with <SCRIPT> 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;
+ }
+}
--- /dev/null
+#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);
+}
+
--- /dev/null
+<!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> </p>
+</body>
+<!-- #EndTemplate --></html>
--- /dev/null
+<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> </p>
+</body>
+<!-- #EndTemplate --></html>