2 rcs_id('$Id: WikiGroup.php,v 1.13 2004-03-08 18:17:09 rurban Exp $');
4 Copyright 2003, 2004 $ThePhpWikiProgrammingTeam
6 This file is part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 // For now we provide no default memberhsip method. This might change.
24 // (!defined('GROUP_METHOD')) define('GROUP_METHOD', "WIKIPAGE");
26 if (!defined('GROUP_METHOD') or
27 !in_array(GROUP_METHOD,
28 array('NONE','WIKIPAGE','DB','FILE','LDAP')))
29 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
31 /* Special group names for ACL */
32 define('GROUP_EVERY', _("Every"));
33 define('GROUP_ANONYMOUS', _("Anonymous Users"));
34 define('GROUP_BOGOUSERS', _("Bogo Users"));
35 define('GROUP_HASHOMEPAGE', _("HasHomePage"));
36 define('GROUP_SIGNED', _("Signed Users"));
37 define('GROUP_AUTHENTICATED', _("Authenticated Users"));
38 define('GROUP_ADMIN', _("Administrators"));
39 define('GROUP_OWNER', _("Owner"));
40 define('GROUP_CREATOR', _("Creator"));
43 * WikiGroup is an abstract class to provide the base functions for determining
46 * WikiGroup is an abstract class with three functions:
47 * <ol><li />Provide the static method getGroup with will return the proper
49 * <li />Provide an interface for subclasses to implement.
50 * <li />Provide fallover methods (with error msgs) if not impemented in subclass.
52 * Do not ever instantiate this class use: $group = &WikiGroup::getGroup($request);
53 * This will instantiate the proper subclass.
54 * @author Joby Walker <zorloc@imperium.org>
59 /** The global WikiRequest object */
61 /** Array of groups $username is confirmed to belong to */
65 * Initializes a WikiGroup object which should never happen. Use:
66 * $group = &WikiGroup::getGroup($request);
67 * @param object $request The global WikiRequest object -- ignored.
69 function WikiGroup(&$request){
74 * Gets the current username and erases $this->membership if is different than
75 * the stored $this->username
76 * @return string Current username.
78 function _getUserName(){
79 $request = &$this->request;
80 $user = $request->getUser();
81 $username = $user->getID();
82 if ($username != $this->username) {
83 $this->membership = array();
84 $this->username = $username;
90 * Static method to return the WikiGroup subclass used in this wiki. Controlled
91 * by the constant GROUP_METHOD.
92 * @param object $request The global WikiRequest object.
93 * @return object Subclass of WikiGroup selected via GROUP_METHOD.
95 function getGroup($request){
96 switch (GROUP_METHOD){
98 return new GroupNone($request);
101 return new GroupWikiPage($request);
104 return new GroupDB($request);
107 return new GroupFile($request);
110 return new GroupLDAP($request);
113 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
114 return new WikiGroup($request);
118 /* ACL PagePermissions will need those special groups based on the User status only */
119 function specialGroup($group){
120 return in_array($group,$this->specialGroups());
123 function specialGroups(){
134 * Determines if the current user is a member of a group.
136 * This method is an abstraction. The group is ignored, an error is sent, and
137 * false (not a member of the group) is returned.
138 * @param string $group Name of the group to check for membership (ignored).
139 * @return boolean True if user is a member, else false (always false).
141 function isMember($group){
142 if ($this->specialGroup($group)) {
143 $request = &$this->request;
144 $user = $request->getUser();
146 case GROUP_EVERY: return true;
147 case GROUP_ANONYMOUS: return ! $user->isSignedIn();
148 case GROUP_BOGOUSERS: return isa($user,'_BogoUser');
149 case GROUP_SIGNED: return $user->isSignedIn();
150 case GROUP_AUTHENTICATED: return $user->isAuthenticated();
151 case GROUP_ADMIN: return $user->isAdmin();
153 trigger_error(__sprintf("Undefined method %s for special group %s",
158 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
159 'isMember',GROUP_METHOD),
166 * Determines all of the groups of which the current user is a member.
168 * This method is an abstraction. An error is sent and an empty
170 * @return array Array of groups to which the user belongs (always empty).
172 function getAllGroupsIn(){
173 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
174 'getAllGroupsIn',GROUP_METHOD),
179 function _allUsers() {
180 static $users = array();
184 /* WikiPage users: */
185 $dbh = $this->request->getDbh();
186 $page_iter = $dbh->getAllPages();
187 while ($page = $page_iter->next()) {
188 if ($page->isUserPage())
189 $users[] = $page->_pagename;
192 /* WikiDB users from prefs (not from users): */
193 $dbi = _PassUser::getAuthDbh();
194 if ($GLOBALS['DBAuthParams']['pref_select']) {
196 $sql = str_replace(' prefs ',' userid ',$GLOBALS['DBAuthParams']['pref_select']);
197 $sql = preg_replace('/WHERE.*/i','',$sql);
198 if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
199 $db_result = $dbi->Execute($sql);
200 $users = array_merge($users,$db_result->GetArray());
201 } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
202 $users = array_merge($users,$dbi->getCol($sql));
209 * Determines all of the members of a particular group.
211 * This method is an abstraction. The group is ignored, an error is sent,
212 * and an empty array is returned
213 * @param string $group Name of the group to get the full membership list of.
214 * @return array Array of usernames that have joined the group (always empty).
216 function getMembersOf($group){
217 if ($this->specialGroup($group)) {
218 $request = &$this->request;
219 $all = $this->_allUsers();
224 case GROUP_ANONYMOUS:
226 case GROUP_BOGOUSERS:
227 foreach ($all as $u) {
228 if (isWikiWord($user)) $users[] = $u;
232 foreach ($all as $u) {
233 $user = WikiUser($u);
234 if ($user->isSignedIn()) $users[] = $u;
237 case GROUP_AUTHENTICATED:
238 foreach ($all as $u) {
239 $user = WikiUser($u);
240 if ($user->isAuthenticated()) $users[] = $u;
244 foreach ($all as $u) {
245 $user = WikiUser($u);
246 if ($user->isAdmin()) $users[] = $u;
250 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
251 'getMembersOf',GROUP_METHOD),
255 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
256 'getMembersOf',GROUP_METHOD),
262 * Add the current or specified user to a group.
264 * This method is an abstraction. The group and user are ignored, an error
265 * is sent, and false (not added) is always returned.
266 * @param string $group User added to this group.
267 * @param string $user Username to add to the group (default = current user).
268 * @return bool On true user was added, false if not.
270 function setMemberOf($group, $user = false){
271 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
272 'setMemberOf',GROUP_METHOD),
278 * Remove the current or specified user to a group.
280 * This method is an abstraction. The group and user are ignored, and error
281 * is sent, and false (not removed) is always returned.
282 * @param string $group User removed from this group.
283 * @param string $user Username to remove from the group (default = current user).
284 * @return bool On true user was removed, false if not.
286 function removeMemberOf($group, $user = false){
287 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
288 'removeMemberOf',GROUP_METHOD),
295 * GroupNone disables all Group funtionality
297 * All of the GroupNone functions return false or empty values to indicate failure or
298 * no results. Use GroupNone if group controls are not desired.
299 * @author Joby Walker <zorloc@imperium.org>
301 class GroupNone extends WikiGroup{
306 * Ignores the parameter provided.
307 * @param object $request The global WikiRequest object - ignored.
309 function GroupNone(&$request){
314 * Determines if the current user is a member of a group.
316 * The group is ignored and false (not a member of the group) is returned.
317 * @param string $group Name of the group to check for membership (ignored).
318 * @return boolean True if user is a member, else false (always false).
320 function isMember($group){
321 if ($this->specialGroup($group)) {
322 return WikiGroup::isMember($group);
329 * Determines all of the groups of which the current user is a member.
331 * The group is ignored and an empty array (a member of no groups) is returned.
332 * @param string $group Name of the group to check for membership (ignored).
333 * @return array Array of groups to which the user belongs (always empty).
335 function getAllGroupsIn(){
340 * Determines all of the members of a particular group.
342 * The group is ignored and an empty array (a member of no groups) is returned.
343 * @param string $group Name of the group to check for membership (ignored).
344 * @return array Array of groups user belongs to (always empty).
346 function getMembersOf($group){
353 * GroupWikiPage provides group functionality via pages within the Wiki.
355 * GroupWikiPage is the Wiki way of managing a group. Every group will have
356 * a page. To modify the membership of the group, one only needs to edit the
357 * membership list on the page.
358 * @author Joby Walker <zorloc@imperium.org>
360 class GroupWikiPage extends WikiGroup{
365 * Initializes the three superclass instance variables
366 * @param object $request The global WikiRequest object.
368 function GroupWikiPage(&$request){
369 $this->request = &$request;
370 $this->username = $this->_getUserName();
371 //$this->username = null;
372 $this->membership = array();
376 * Determines if the current user is a member of a group.
378 * To determine membership in a particular group, this method checks the
379 * superclass instance variable $membership to see if membership has
380 * already been determined. If not, then the group page is parsed to
381 * determine membership.
382 * @param string $group Name of the group to check for membership.
383 * @return boolean True if user is a member, else false.
385 function isMember($group){
386 if ($this->specialGroup($group))
387 return WikiGroup::isMember($group);
389 $request = $this->request;
390 //$username = $this->_getUserName();
391 if (isset($this->membership[$group])) {
392 return $this->membership[$group];
394 $group_page = $request->getPage($group);
395 if ($this->_inGroupPage($group_page)) {
396 $this->membership[$group] = true;
399 $this->membership[$group] = false;
404 * Private method to take a WikiDB_Page and parse to determine if the
405 * current_user is a member of the group.
406 * @param object $group_page WikiDB_Page object for the group's page
407 * @return boolean True if user is a member, else false.
410 function _inGroupPage($group_page){
411 $group_revision = $group_page->getCurrentRevision();
412 if ($group_revision->hasDefaultContents()) {
413 $group = $group_page->getName();
414 trigger_error(sprintf(_("Group %s does not exist"),$group), E_USER_WARNING);
417 $contents = $group_revision->getContent();
418 $match = '/^\s*[\*\#]+\s*' . $this->username . '\s*$/';
419 foreach ($contents as $line){
420 if (preg_match($match, $line)) {
428 * Determines all of the groups of which the current user is a member.
430 * Checks the root Group page ('CategoryGroup') for the list of all groups,
431 * then checks each group to see if the current user is a member.
432 * @param string $group Name of the group to check for membership.
433 * @return array Array of groups to which the user belongs.
435 function getAllGroupsIn(){
436 $request = &$this->request;
437 //$username = $this->_getUserName();
438 $membership = array();
440 $specialgroups = $this->specialGroups();
441 foreach ($specialgroups as $group) {
442 $this->membership[$group] = $this->isMember($group);
445 $dbh = &$request->getDbh();
446 $master_page = $request->getPage(_("CategoryGroup"));
447 $master_list = $master_page->getLinks(true);
448 while ($group_page = $master_list->next()){
449 $group = $group_page->getName();
450 $this->membership[$group] = $this->_inGroupPage($group_page);
452 foreach ($this->membership as $g => $bool) {
453 if ($bool) $membership[] = $g;
459 * Determines all of the members of a particular group.
461 * Checks a group's page to return all the current members. Currently this
462 * method is disabled and triggers an error and returns an empty array.
463 * @param string $group Name of the group to get the full membership list of.
464 * @return array Array of usernames that have joined the group (always empty).
466 function getMembersOf($group){
467 if ($this->specialGroup($group))
468 return WikiGroup::getMembersOf($group);
470 trigger_error("GroupWikiPage::getMembersOf is not yet implimented",
474 * Waiting for a reliable way to check if a string is a username.
475 $request = $this->request;
477 $group_page = $request->getPage($group);
478 $group_revision = $group_page->getCurrentRevision();
479 if ($group_revision->hasDefaultContents()) {
480 trigger_error("Group $group does not exist", E_USER_WARNING);
483 $contents = $group_revision->getContent();
484 $match = '/^(\s*[\*\#]+\s*)(\w+)(\s*)$/';
486 foreach($contents as $line){
488 if(preg_match($match, $line, $matches)){
489 $members[] = $matches[2];
498 * GroupDb is configured by $DbAuthParams[] statements
502 class GroupDb extends WikiGroup {
504 var $_is_member, $_group_members, $_user_groups;
508 * @param object $request The global WikiRequest object.
510 function GroupDb(&$request){
511 global $DBAuthParams;
512 $this->request = &$request;
513 $this->username = $this->_getUserName();
514 //$this->membership = array();
516 if (empty($DBAuthParams['group_members']) or
517 empty($DBAuthParams['user_groups']) or
518 empty($DBAuthParams['is_member'])) {
519 trigger_error(_("No or not enough GROUP_DB SQL statements defined"), E_USER_WARNING);
522 _PassUser::getAuthDbh();
523 $this->_is_member = $this->_auth_dbi->prepare(str_replace(array('"$userid"','"$groupname"'),array('?','?'),$DBAuthParams['is_member']));
524 $this->_group_members = $this->_auth_dbi->prepare(str_replace('"$groupname"','?',$DBAuthParams['group_members']));
525 $this->_user_groups = $this->_auth_dbi->prepare(str_replace('"$userid"','?',$DBAuthParams['user_groups']));
529 * Determines if the current user is a member of a database group.
531 * @param string $group Name of the group to check for membership.
532 * @return boolean True if user is a member, else false.
534 function isMember($group) {
535 if ($this->specialGroup($group))
536 return WikiGroup::isMember($group);
538 $request = $this->request;
539 $username = $this->_getUserName();
540 if (isset($this->membership[$group])) {
541 return $this->membership[$group];
543 $dbh = _PassUser::getAuthDbh();
544 $db_result = $dbh->execute($this->_is_member,array($username,$group));
545 if ($db_result->numRows() > 0) {
546 $this->membership[$group] = true;
549 $this->membership[$group] = false;
554 * Determines all of the groups of which the current user is a member.
556 * then checks each group to see if the current user is a member.
557 * @param string $group Name of the group to check for membership.
558 * @return array Array of groups to which the user belongs.
560 function getAllGroupsIn(){
561 $request = &$this->request;
562 $username = $this->_getUserName();
563 $membership = array();
565 $specialgroups = $this->specialGroups();
566 foreach ($specialgroups as $group) {
567 if ($this->isMember($group)) {
568 $membership[] = $group;
571 $dbh = _PassUser::getAuthDbh();
572 $db_result = $dbh->execute($this->_user_groups,$username);
573 if ($db_result->numRows() > 0) {
574 while (list($group) = $db_result->fetchRow()) {
575 $membership[] = $group;
578 $this->membership = $membership;
583 * Determines all of the members of a particular group.
585 * Checks a group's page to return all the current members. Currently this
586 * method is disabled and triggers an error and returns an empty array.
587 * @param string $group Name of the group to get the full membership list of.
588 * @return array Array of usernames that have joined the group.
590 function getMembersOf($group){
591 $request = &$this->request;
592 $username = $this->_getUserName();
595 $dbh = _PassUser::getAuthDbh();
596 $db_result = $dbh->execute($this->_group_members,$group);
597 if ($db_result->numRows() > 0) {
598 while (list($userid) = $db_result->fetchRow()) {
599 $members[] = $userid;
607 * GroupFile is configured by AUTH_GROUP_FILE
608 * groupname: user1 user2 ...
612 class GroupFile extends WikiGroup {
617 * @param object $request The global WikiRequest object.
619 function GroupFile(&$request){
620 $this->request = &$request;
621 $this->username = null;
622 $this->membership = array();
624 if (!defined('AUTH_GROUP_FILE')) {
625 trigger_error(_("AUTH_GROUP_FILE not defined"), E_USER_WARNING);
628 if (!file_exists(AUTH_GROUP_FILE)) {
629 trigger_error(sprintf(_("Cannot open AUTH_GROUP_FILE %s"), AUTH_GROUP_FILE), E_USER_WARNING);
632 require 'lib/pear/File_Passwd.php';
633 $this->_file = File_Passwd($file);
637 * Determines if the current user is a member of a group.
639 * To determine membership in a particular group, this method checks the
640 * superclass instance variable $membership to see if membership has
641 * already been determined. If not, then the group file is parsed to
642 * determine membership.
643 * @param string $group Name of the group to check for membership.
644 * @return boolean True if user is a member, else false.
646 function isMember($group) {
647 if ($this->specialGroup($group))
648 return WikiGroup::isMember($group);
650 $request = $this->request;
651 $username = $this->_getUserName();
652 if (isset($this->membership[$group])) {
653 return $this->membership[$group];
656 foreach ($this->_file->users[] as $g => $u) {
657 $users = explode(' ',$u);
658 if (in_array($username,$users)) {
659 $this->membership[$group] = true;
663 $this->membership[$group] = false;
668 * Determines all of the groups of which the current user is a member.
670 * then checks each group to see if the current user is a member.
671 * @param string $group Name of the group to check for membership.
672 * @return array Array of groups to which the user belongs.
674 function getAllGroupsIn(){
675 $username = $this->_getUserName();
676 $membership = array();
678 $specialgroups = $this->specialGroups();
679 foreach ($specialgroups as $group) {
680 if ($this->isMember($group)) {
681 $this->membership[$group] = true;
682 $membership[] = $group;
685 foreach ($this->_file->users[] as $group => $u) {
686 $users = explode(' ',$u);
687 if (in_array($username,$users)) {
688 $this->membership[$group] = true;
689 $membership[] = $group;
692 $this->membership = $membership;
697 * Determines all of the members of a particular group.
699 * Return all the current members.
700 * @param string $group Name of the group to get the full membership list of.
701 * @return array Array of usernames that have joined the group.
703 function getMembersOf($group){
704 if ($this->specialGroup($group)) {
705 trigger_error(__sprintf("Undefined method %s for special group %s",
706 'getMembersOf',$group),
709 if (!empty($this->_file->users[$group])) {
710 return explode(' ',$this->_file->users[$group]);
718 * Ldap is configured in index.php
722 class GroupLdap extends WikiGroup {
727 * @param object $request The global WikiRequest object.
729 function GroupLdap(&$request){
730 $this->request = &$request;
731 $this->username = null;
732 $this->membership = array();
734 if (!defined("LDAP_AUTH_HOST")) {
735 trigger_error(_("LDAP_AUTH_HOST not defined"), E_USER_WARNING);
738 if (! function_exists('ldap_connect')) {
740 if (! function_exists('ldap_connect')) {
741 trigger_error(_("No LDAP in this PHP version"), E_USER_WARNING);
745 if (!defined("LDAP_BASE_DN"))
746 define("LDAP_BASE_DN",'');
747 $this->base_dn = LDAP_BASE_DN;
748 if (strstr("ou=",LDAP_BASE_DN))
749 $this->base_dn = preg_replace("/(ou=\w+,)?()/","\$2",LDAP_BASE_DN);
753 * Determines if the current user is a member of a group.
756 * @param string $group Name of the group to check for membership.
757 * @return boolean True if user is a member, else false.
759 function isMember($group) {
760 if ($this->specialGroup($group))
761 return WikiGroup::isMember($group);
763 if (isset($this->membership[$group])) {
764 return $this->membership[$group];
766 $request = $this->request;
767 $username = $this->_getUserName();
769 $this->membership[$group] = in_array($username,$this->getMembersOf($group));
770 return $this->membership[$group];
774 * Determines all of the groups of which the current user is a member.
776 * @param string $group Name of the group to check for membership.
777 * @return array Array of groups to which the user belongs.
779 function getAllGroupsIn(){
780 $request = &$this->request;
781 $username = $this->_getUserName();
782 $membership = array();
784 $specialgroups = $this->specialGroups();
785 foreach ($specialgroups as $group) {
786 if ($this->isMember($group)) {
787 $this->membership[$group] = true;
788 $membership[] = $group;
791 if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
792 $r = @ldap_bind($ldap); // this is an anonymous bind
793 $sr = ldap_search($ldap, "ou=Users,".$this->base_dn,"uid=$username");
794 $info = ldap_get_entries($ldap, $sr);
795 for ($i = 0; $i < $info["count"]; $i++) {
796 if ($info[$i]["gidnumber"]["count"]) {
797 $gid = $info[$i]["gidnumber"][0];
798 $sr2 = ldap_search($ldap, "ou=Groups,".$this->base_dn,"gidNumber=$gid");
799 $info2 = ldap_get_entries($ldap, $sr2);
801 $membership[] = $info2[0]["cn"][0];
806 $this->membership = $membership;
811 * Determines all of the members of a particular group.
813 * Return all the members of the given group. LDAP just returns the gid of each user
814 * @param string $group Name of the group to get the full membership list of.
815 * @return array Array of usernames that have joined the group.
817 function getMembersOf($group){
819 if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
820 $r = @ldap_bind($ldap); // this is an anonymous bind
821 $sr = ldap_search($ldap, "ou=Groups,".$this->base_dn,"cn=$group");
822 $info = ldap_get_entries($ldap, $sr);
823 for ($i = 0; $i < $info["count"]; $i++) {
824 $gid = $info[$i]["gidnumber"][0];
825 $sr2 = ldap_search($ldap, "ou=Users,".$this->base_dn,"gidNumber=$gid");
826 $info2 = ldap_get_entries($ldap, $sr2);
827 for ($j = 0; $j < $info2["count"]; $j++) {
828 $members[] = $info2[$j]["cn"][0];
837 // $Log: not supported by cvs2svn $
838 // Revision 1.12 2004/02/23 21:30:25 rurban
839 // more PagePerm stuff: (working against 1.4.0)
840 // ACL editing and simplification of ACL's to simple rwx------ string
843 // Revision 1.11 2004/02/07 10:41:25 rurban
844 // fixed auth from session (still double code but works)
846 // fixed DbPassUser upgrade and policy=old
849 // Revision 1.10 2004/02/03 09:45:39 rurban
850 // LDAP cleanup, start of new Pref classes
852 // Revision 1.9 2004/02/01 09:14:11 rurban
853 // Started with Group_Ldap (not yet ready)
854 // added new _AuthInfo plugin to help in auth problems (warning: may display passwords)
855 // fixed some configurator vars
856 // renamed LDAP_AUTH_SEARCH to LDAP_BASE_DN
857 // changed PHPWIKI_VERSION from 1.3.8a to 1.3.8pre
858 // USE_DB_SESSION defaults to true on SQL
859 // changed GROUP_METHOD definition to string, not constants
860 // changed sample user DBAuthParams from UPDATE to REPLACE to be able to
861 // create users. (Not to be used with external databases generally, but
862 // with the default internal user table)
864 // fixed the IndexAsConfigProblem logic. this was flawed:
865 // scripts which are the same virtual path defined their own lib/main call
866 // (hmm, have to test this better, phpwiki.sf.net/demo works again)
868 // Revision 1.8 2004/01/27 23:23:39 rurban
869 // renamed ->Username => _userid for consistency
870 // renamed mayCheckPassword => mayCheckPass
871 // fixed recursion problem in WikiUserNew
872 // fixed bogo login (but not quite 100% ready yet, password storage)
874 // Revision 1.7 2004/01/26 16:52:40 rurban
875 // added GroupDB and GroupFile classes
877 // Revision 1.6 2003/12/07 19:29:11 carstenklapp
878 // Code Housecleaning: fixed syntax errors. (php -l *.php)
880 // Revision 1.5 2003/02/22 20:49:55 dairiki
881 // Fixes for "Call-time pass by reference has been deprecated" errors.
883 // Revision 1.4 2003/01/21 04:02:39 zorloc
884 // Added Log entry and page footer.
891 // c-hanging-comment-ender-p: nil
892 // indent-tabs-mode: nil