tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
/*
 *	    /std/board/bboard.c
 *	
 *	generic bulletin board object
 *
 *
 *	Truilkan, ????	allow read/write permissions and assignment of
 *			projects, rewritten to use a mapping.
 *	Zak, 930119	added 'followup', 'mailnote'
 *   	Shemp, 930224	added maker stuff
 *	Zak, 930225	made it range from 1..x instead of 0..x-1
 *	Shemp, 930226	made the note removed message reflect the
 *			actually note removed instead of note-1
 *	Shemp, 930228	added function to enable an admin to "lock" a
 *			note so that it doesn't get removed with the
 *			automatic removal of aged notes.
 *	Zak, 930309	changed to new output format. Optimised a bit
 *			too. Also checks before mkdir({save,attic}_dir).
 *			Store all `set'able things in temp vars, so
 *			they're not lost during the restore_object()
 *			in set_save_file
 *	Zak, 930314	enhanced locking/unlocking of notes, won't
 *			archive locked notes.
 *	Zak, 930320	only messages[] is non-static, num_messages
 *			is calculated at each restore_object()
 *	Zak, 930321	set_default_long_text() to update your
 *			bboard's default long.text.
 *	Watcher, 930322	Switched name recognition to use link to
 *			prevent impersonation with monster bodies
 *	Zak, 930413	hacked to use my new mailer.
 *	Telsin, 930526	added archive_board(), does what you think.
 *			several other mods to have the board archive
 *			at each reset.
 *	Zak, 930530	unique_name() now calls get_oid(). Put in
 *			define for IVORY - undefine if using new TMI
 *			mudlib. isghost() prints a message & returns
 *			0 if player is a ghost. Archiving occurs at
 *			each post now - not on reset.
 *	Zak, 930618	added 'read next' capabilities, as well as
 *			`date of newest note in short()'. Reformatted
 *			code
 *	Zak, 930620	implemented parse_num - given appropriate args
 *			will automagically do `current', `next', etc
 *			parsing for you. It calls isghost() as well...
 *	Zak, 930621	added `note <num', `help board', save_obj &
 *			set_cur_note in each operation, show curnote
 *			in short(). Moved help text to separate file.
 *			removed curnote from short... too much hassle.
 *	Zak, 930623	current note is now just a temp variable. It
 *			only `permanently' stores the newest note that
 *			you've read. Added `read new'. Modified short()
 *			to reflect `x new notes of'. removed `last mod'
 *			tackyness. (Thanx to Telsin for ideas...)
 *	Zak, 930625	changed _CUR & _NEW to different variables, to
 *			avoid a bug in /std/objects/prop.c.
 *			Also fixed initial setting of cur note
 *			in init().
 *	Zak, 930626	installed on TMI-2, removed kludge for prop bug.
 *			reformatted short ([x notes, y new]) now..
 *  Pallando, 940722    Stopped chain of Re: Re: Re: Re: Re: Re:
 * Blue, 940908	Fixed mapping passed to send_mail by mailnote,
 *			changed time() key from "time" to "date".  Thanks
 *			to Baccara for noticing the problem.
//Fixed the followup on MAX_NOTES problem Leto somewhere 1194
 * Blue, 941128    No longer archives locked notes.
 */

/*
 * Please mail all bugs, suggestions, ideas to zak@tmi-2, or
 * internet mail <zak@rmit.edu.au>
 */
 
#include <move.h>
#include <mudlib.h>
#include <daemons.h>
#include <mailer.h>
#include <board.h>

inherit OBJECT;

	// undef for TMI 1.0 based mudlibs (with link_data())
#undef IVORY	

	// various defines.
#define BBOARD_HELP	"/std/board/board.help"
#define VERSION		"bboard v3.0.2z, 930626"


	// these 2 are overridden by set_max_messages & set_carryover
#define MAX_MESG	30	// max # notes before archiving
#define MIN_MESG	10	// min # notes to leave on board

#ifdef IVORY
#  define QNAME		query("name")
#  define QCAPNAME	query("cap_name")
#else
#  define QNAME		link_data("name")
#  define QCAPNAME	link_data("cap_name")
#endif

// ([messages]) contains "title", "poster", "time", "doer", "body",
// "locked", "id".  (Don't maps make great substitutes for structures? :)

mapping		*messages;
int		id_ref;

static string	attic_dir, save_dir;
static mapping	title;
static string	file, location, maker, board_set, second_arg;
static int	num_messages, max_messages, min_messages, carryover;


int
query_prevent_shadow()
{
    return 1;
} // query_prevent_shadow

string
query_info(int i, string field)
{
    if (i < num_messages && i >= 0)
	return messages[i][field];
    else
	return "";
}

//
// these valid_* functions are designed to be masked by the one inheriting
//

int
valid_note_read(int i)
{
    return 1;
}

int
valid_post()
{
if (getuid(this_player())=="guest") 
  {
   notify_fail("Due to abuse, Guest is no longer allowed to post.\n");
   return 0;
  }
    return 1;
}

int
valid_assign()
{
    return 0;
}

int
valid_remove(int i)
{
    string poster;

    if ( messages[i]["locked"] != geteuid(this_player()) &&
	 !adminp(geteuid(this_player())) &&
	     (string)this_player()->QCAPNAME != messages[i]["poster"]
	     && (string)this_player()->QNAME != maker ) {
	    notify_fail("You can only remove your own notes.\n");
	    return 0;
    }
    return 1;
} // valid_remove

int
valid_lock()
{
    return (adminp(geteuid(this_player())));
}

//
// end valid_* maskable code.
//

void
set_carryover(int t)
{
    if (t < 0)
	    t = 0;
    carryover = t;
}

int
query_carryover() {
    if (!carryover)
	    carryover = MIN_MESG;
    return carryover;
}

int
query_max_messages()
{
    if (!max_messages)
	    max_messages = MAX_MESG;
    return max_messages;
}

void
set_max_messages(int num)
{ 
    if (num < 0)
	    num = 0; 
    max_messages = num;
}

private static void
set_maker(string o)
{
    maker = lower_case(o);
}

int 
query_num_messages() { return num_messages; }

string
query_maker()
{
    return maker;
}

void
debug_info()
{
    printf("board_set is %s\n", board_set);
    printf("cur note is %d\n", QUERY_CUR);
    printf("new note is %d\n", QUERY_NEW);
}

int convert_id( int idx ) {
  int i;

  i = num_messages;
  while( i-- ) {
    if( messages[i]["id"] == idx ) return i;
    if( messages[i]["id"] < idx ) return -1;
  }
  return 0;
}

string
get_time(int t)
{
    return extract(ctime(t), 0, 9);
}

string
headers()
{
    string tmp;
    int i, c;

    if (!num_messages)
	    return "";
    tmp = "";
    i = -1;
    c = QUERY_CUR;
    if (!c)
	c = -1;
    while ( ++i < num_messages ) {
	    tmp += sprintf("%3d %s %-12s (%s)%s %s%s\n",
		(i + 1),
		c == messages[i]["id"] ? ">" : " ",
		messages[i]["poster"],
		get_time(messages[i]["time"]),
		(messages[i]["locked"] ? "*" : " "),
		messages[i]["title"],
		(messages[i]["doer"]
			? "  <" + messages[i]["doer"] + ">" : "" ));
    }
    return tmp;
} // headers

string
query_long()
{
    return ( sprintf("%sThe %s has %s note%s:\n%s",
		(string)query("long.text"),
		(string)query("short.text"),
		(num_messages == 0 ? "no" : "" + num_messages),
		(num_messages == 1 ? "" : "s"), 
		headers()
		));
} // query_long

int query_num_new() {
	int i, n, c;

	n = QUERY_NEW;
	i = num_messages;
	c = 0;
  if( !intp( n ) ) return c;
	while (i--) {
		if (messages[i]["id"] <= n) break;
		c++;
	}
	return c;
}

string
query_short()
{
	int c;
    string ns;
	c = query_num_new();
    if (c)
        ns = sprintf(", %d new", c);
    else
	ns = "";
    return (sprintf("%s [%d note%s%s]",
		(string)query("short.text"), num_messages,
		(num_messages == 1 ? "" : "s"), ns));
} // query_short

void
set_save_dir(string s)
{
    save_dir = s + "/";
    if (file_size(save_dir) == -1)
	    mkdir(save_dir);
}

string
query_save_dir(string s)
{
    return save_dir;
}

void
set_attic_dir(string s)
{
    attic_dir = s + "/";
    if (file_size(attic_dir) == -1)
	    mkdir(attic_dir);
}

string
query_attic_dir()
{
    return attic_dir;
}

void
set_location(string l)
{
    location = l;
#ifdef IVORY
    base::move(l);
#else
    ob::move(l);
#endif
}

int
move(mixed dest)
{
    return MOVE_NOT_ALLOWED;
}

void
archive_board()
{
    int i,m;
    string *tmpa, temp_file;
    mapping *temp_messages, *save_messages;
 
    tmpa = explode(file,"/");
    temp_file = attic_dir + tmpa[sizeof(tmpa) - 1] + "_" + time();
    temp_messages = messages;
    messages = ({ });
    save_messages = ({ });
    m = num_messages-query_carryover();
    for (i = 0; i < m; i++) {
        if (temp_messages[i]["locked"]) {
            save_messages += ({ temp_messages[i] });
        }
        else {
            messages += ({ temp_messages[i] });
        }
    }
    save_messages += temp_messages[m..num_messages-1];
    save_object(temp_file);
    messages = save_messages;
    num_messages = sizeof(messages);
    save_object(file);
} // archive_board

void
set_save_file(string s)
{
    int i;
    file = save_dir + s;
#ifndef IVORY
    seteuid(geteuid(previous_object()));
#endif
    restore_object(file);
    num_messages = sizeof(messages);
    if (num_messages>=query_max_messages())
	    archive_board();
    for (i = 0; i < num_messages; i++) {
	    if (!messages[i]["id"])
		messages[i]["id"] = ++id_ref;
    }
    board_set = replace_string(file, "/", "_");
} // set_save_file

mixed query_save_file() {
	return file;
}

void
set_default_long_text()
{
    set("long.text", @LONG
This is a bulletin board. For information on how to use it, use `help board'.
LONG
);
    set("silent_look", 1);
}

void
create()
{
#ifndef IVORY
    seteuid(getuid(this_object()));
#endif
    set_max_messages(MAX_MESG);
    set_carryover(MIN_MESG);
    set_attic_dir("/attic/boards");
    set("short", "@@query_short");
    set("long", "@@query_long");
    set("short.text", "A bulletin board");
    set_default_long_text();
    set("id",({"board","bulletin board"}));
    set("silent_look", 1);
    messages = ({ });
    num_messages = 0;
    id_ref = 0;
    title = ([]);
    board_set = "board/default";	// in case you forget set_save_file()
} // create

void
init()
{
    add_action("new_post","post");
    add_action("read", "read");
    add_action("remove_msg", "remove");
    add_action("edit_note","edit");
    add_action("assign","assign");
    add_action("followup_note", "followup");
    add_action("mail_note", "mailnote");
    add_action("lock_note", "mlock");
    add_action("unlock_note", "munlock");
    add_action("note", "note");
    add_action("help", "help");
//    add_action("comment", "comment");
    if (!QUERY_CUR)
	    SET_CURID(QUERY_NEW);
} // init

string
unique_name()
{
    return "/tmp/bb_" + getoid(this_player());
}

int
isghost()
{
    if (this_player()->query("ghost")) {
	    notify_fail("Your ghostly hands pass right through the board.\n");
	    return 1;
    }
}

// command argument parsing routine below

#define PN_OPT		1	// 2nd arg is optional
#define PN_NEED		2	// 2nd arg is necessary
#define PN_CUR		4	// `current' is a valid note num
#define PN_NEXT		8	// `next' is a valid note num
#define PN_NEW		16	// `new' is a valid note num

varargs int
parse_num(string str, string usage, int arg2type, string usg2)
{
    int i, c;
    string t; 
    usage = "Usage: " + usage + " <num> " + (usg2 ? usg2 : "") + "\n";
    second_arg = 0;
    if (isghost())
	    return -1;
    if (!str) {
	    notify_fail(usage);
	    return -1;
    }
    if (str != "board" &&
          (present(str, environment(this_object())) ||
           !undefinedp(environment(this_object())->query("item_desc/"+str)))) {
        notify_fail("Maybe you should look at it?\n");
        return -1;
    }
    if ((arg2type & PN_NEED) && sscanf(str,"%s %s", t, second_arg) != 2) {
	    notify_fail(usage);		// compulsory 2nd arg
	    return -1;
    }
    if ((arg2type & PN_OPT) && (sscanf(str, "%s %s", t, second_arg) != 2))
	    t = str;
    if (!t)
	    t = str;
    if (  (t == "next" && !(arg2type & PN_NEXT))
	||(t == "current" && !(arg2type & PN_CUR))
	||(t == "new" && !(arg2type & PN_NEW)) ) {
	    notify_fail(usage);
	    return -1;
    }
    if (t == "next" || t == "current" || t == "new") {
	if (t == "new")
	    c = QUERY_NEW;
	else
	    c = QUERY_CUR;
	if (!c) {
	// Added by Inspiral.
		write( "Reading note 1.\n" );
// Below commented out by Inspiral.
	}
	if (!num_messages) {
	    notify_fail("There aren't any messages on this board.\n");
	    return -1;
	}
	i = num_messages;
	while (i--)
	    if (messages[i]["id"] <= c)
		break;
	if (t == "current") {
	    if (messages[i]["id"] != c) {
		notify_fail("Current note invalid.\n");
		return -1;
	    }
	} else {	// t == `new' or `next'
	    if (++i == num_messages) {
		if (t == "next")
		    notify_fail("At last note.\n");
		else
		    notify_fail("No new notes.\n");
		return -1;
	    }
	}
	return i;
    } else {	    	// just a number
	if (sscanf(t, "%d", i) != 1) {
	    notify_fail(usage);
	    return -1;
	}
	if (i < 1 || i > num_messages) {
	    notify_fail("No note with that number.\n");
	    return -1;
	}
	return i-1;
    }
} // parse_num

//
// actual commands follow
//

int
help(string str)
{
    if (str != "board")
	return 0;
    this_player()->more( BBOARD_HELP );
    return 1;
}

int
assign(string str)
{
    int i;

    i = parse_num(str, "assign", PN_NEED + PN_CUR, "<name>");
    if (i == -1)
	return 0;
    if (!valid_assign())
	return 0;
    messages[i]["doer"] = capitalize(second_arg);
    SET_CUR(i);
    save_object(file);
    return 1;
} // assign

int
lock_note(string str)
{
    int i;

    i = parse_num(str, "mlock", PN_CUR);
    if (i == -1)
	return 0;
    if (!valid_lock()) {
	notify_fail("Permission denied - you may not lock that note.\n");
	return 0;
    }
    messages[i]["locked"] = geteuid(this_player());
    printf("Note %s locked.\n", str);	// XXX: could check if already locked.
    SET_CUR(i);
    save_object(file);
    return 1;
} // lock_note

int
unlock_note(string str)
{
    int i;

    i = parse_num(str, "munlock", PN_CUR);
    if (i == -1)
	return 0;
    if (!valid_lock()) {
	notify_fail("Permission denied - you may not unlock that note.\n");
	return 0;
    }
    messages[i]["locked"] = 0;		// XXX: should check if locked or not
    printf("Note %s unlocked.\n", str);
    SET_CUR(i);
    save_object(file);
    return 1;
} // unlock_note

int
comment(string str)
{
    int i;

    i = parse_num(str, "comment", PN_CUR);
    if (i == -1)
	return 0;
    printf("This function does nothing at the moment.\n");
    return 1;
} // comment

int
new_post(string str)
{
    string hed;
    if (isghost())
	return 0;
    if (!valid_post())
	return 0;
    if (!str)
	str = "< no title >";
    if(title[this_player()])
	printf("Warning! You are already marked as posting!\n");

    if (num_messages >= query_max_messages()) {
	archive_board();
	/* check if space for new messages was made by archive_board() */
	if (num_messages >= query_max_messages()) {
	    printf("Too many messages.  You must remove some first.\n");
	    return 1;
	}
    }
    if (strlen(str) > 50) {
	notify_fail("Title is too long.\n");
	return 0;
    }
    title[this_player()] = str;
    printf("Editing note.\n");
    hed = unique_name();
    rm(hed);
    this_player()->edit(hed, "postit", this_object());
    return 1;
} // new_post

void
abort()
{
    rm ((string)this_player()->query_edit_filename());
    map_delete(title,this_player());
}

varargs int
postit(string mtitle)
{
    string filename;
    mapping msg;

    filename = (string)this_player()->query_edit_filename();
    msg = allocate_mapping(4);
    msg["title"] = mtitle ? mtitle : title[this_player()];
    msg["poster"] = capitalize((string)this_player()->QCAPNAME);
    msg["time"] = time();
    msg["body"] = ( read_file( filename ) );
    msg["id"] = ++id_ref;
    rm(filename);
    this_player()->set_edit_filename("");
    if (!msg["body"])
	return 0;
    messages += ({ msg });
    printf("Posted.\n");
    num_messages++;
    map_delete(title,this_player());
    // XXX: don't set current as your own post...
    save_object(file);
    return 1;
} // postit

int
read(string str)
{
    int i;
    string tmp;
   
    if (id(str)) {
	write(query_long());
	return 1;
    }
    if (str == "version") {
	printf("BBoard version: %s\n", VERSION);
	return 1;
    }
    i = parse_num(str, "read", PN_CUR + PN_NEXT + PN_NEW);
    if (i == -1)
	return 0;
    if (this_object()->valid_note_read(i)) {
	printf("Note %3d  %-12s (%s)  %s%s\n",
		(i + 1),
		messages[i]["poster"],
		get_time(messages[i]["time"]),
		messages[i]["title"],
		(messages[i]["doer"]
			? "  <" + messages[i]["doer"] + ">" : "" ));
        if (QUERY_NEW < messages[i]["id"])
	    SET_NEW(i);
	SET_CUR(i);
	this_player()->more(({""}) + explode(messages[i]["body"],"\n"));
	return 1;
    }
    return 0;
} // read

mixed
read_text(string str)
{
    int i;
    string tmp;
   
    if (id(str)) {
	tmp = query_long();
	return tmp;
    }
    if (str == "version") {
	tmp = sprintf("BBoard version: %s\n", VERSION);
	return tmp;
    }
    i = parse_num(str, "read", PN_CUR + PN_NEXT + PN_NEW);
    if (i == -1)
	return 0;
    if (this_object()->valid_note_read(i)) {
	tmp = sprintf("Note %3d  %-12s (%s)  %s%s\n",
		(i + 1),
		messages[i]["poster"],
		get_time(messages[i]["time"]),
		messages[i]["title"],
		(messages[i]["doer"]
			? "  <" + messages[i]["doer"] + ">" : "" ));
        if (QUERY_NEW < messages[i]["id"])
	    SET_NEW(i);
	SET_CUR(i);
  tmp += messages[i]["body"];
	return tmp;
    }
    return 0;
} // read

int
remove_msg(string str)
{
    string poster,tmp, *tmpmsg;
    int i,j;
   
    i = parse_num(str, "remove", PN_CUR);
    if (i == -1)
	return 0;
    if (!valid_remove(i))
	return 0;
    messages = messages[0..(i-1)]+messages[(i+1) .. (sizeof(messages) - 1)];
    printf("Note %d removed.\n", i+1);
    num_messages--;
    save_object(file);
    return 1;
} // remove_msg


static int	orig_number;

int
edit_note(string str)
{
    int i, tmp, j, m;
    string *lines, fl;
   
    i = parse_num(str, "edit", PN_CUR);
    if (i == -1)
	return 0;
    if (messages[i]["locked"]) {
	notify_fail("That message is locked\n");
	return 0;
    }
    if (!adminp(geteuid(this_player())) &&
	messages[i]["poster"] != capitalize((string)this_player()->QCAPNAME)) {
	notify_fail("You may only edit your own messages.\n");
	return 0;
    }
    fl = unique_name();
    rm(fl);
    write_file(fl, messages[i]["body"]);
    orig_number = i;
    printf("Editing: %s\n", messages[i]["title"]);
    this_player()->edit(fl,"edit_stop_ed",this_object());
    return 1;
} // edit_note

void
edit_stop_ed()
{
    SET_CUR(orig_number);
    messages[orig_number]["body"] =
	( read_file((string)this_player()->query_edit_filename()) );
    printf("Note %d edited.\n", orig_number + 1);
    abort();
    save_object(file);
} // edit_stop_ed

int
followup_note(string str)
{
    string *lines;
    int i;

    if (!valid_post())
	return 0;

 if ( to_int(str) == MAX_MESG -1 ) {
   archive_board();
}
    i = parse_num(str, "followup", PN_CUR + PN_OPT, "[<title>]");
    if (i == -1)
	return 0;

    if (num_messages >= query_max_messages()) {
	archive_board();
	/* check if space for new messages was made by archive_board() */
	if (num_messages >= query_max_messages()) {
	    printf("Too many messages.  You must remove some first.\n");
	    return 1;
	}
    }
    if(title[this_player()])
	printf("Warning! You are already marked as posting!\n");
    if (!second_arg) {
	second_arg = messages[i]["title"];
	if (second_arg[0..3] != "Re: ")
	    second_arg = "Re: " + second_arg;
    }
    if (strlen(second_arg) > 50) {
	notify_fail("Title is too long.\n");
	return 0;
    }
    title[this_player()] = second_arg;
    printf("Following up to note %d as '%s'\n", i +  1, second_arg);
    lines = explode(messages[i]["body"],"\n");
    second_arg = unique_name();
    rm(second_arg);
    write_file(second_arg, sprintf("On %s, %s wrote:\n> %s\n",
		    get_time(messages[i]["time"]),
		    messages[i]["poster"],
		    implode(lines, "\n> ")));
    this_player()->edit(second_arg,"postit",this_object());
    SET_CUR(i);
    return 1;
} // followup_note

int
mail_note(string str)
{
    int i;
    string myname, msg, *r;

    i = parse_num(str, "mailnote", PN_CUR);
    if (i == -1)
	return 0;
    msg = sprintf("Title: %s\nFrom:  %s\nDate:  %s\n%s\n%s\n",
		    messages[i]["title"],
		    messages[i]["poster"],
		    get_time(messages[i]["time"]),
		    (messages[i]["doer"]
			? "Doer:  " + messages[i]["doer"] + "\n" : "" ),
		    messages[i]["body"]
		);

    myname = (string)this_player()->QNAME;
#if 1		// XXX: change the following if using the old mailer
    r = (string *)MAILER_D->send_mail( ([ "from" : myname, "to" : ({ myname }), 
		       "subject" : "Note '" + messages[i]["title"] + "'",
		       "date" : time(), "message" : msg ]) );
#else
    r = (string *)MAILER_D->send_mail(
	    ({myname}), ({}), (string) this_player()->QNAME,
	    "Note: " + messages[i]["title"], time(), msg );
#endif
    if (sizeof(r) != 1)
	printf("Something went wrong with the mailing\n");
    else {
	printf("Note '%s' mailed to you.\n",  messages[i]["title"]);
	MAILER_D->flush_files();
    }
    SET_CUR(i);
    return 1;
} // mail_note

int
note(string str)
{
    int i;
    if (!str)
	str = "current";
    i = parse_num(str, "note", PN_CUR + PN_NEXT);
    if (i == -1)
	return 0;
    SET_CUR(i);
    printf("Current note set to %d.\n", i+1);
    return 1;
} // note

void
clean_up()
{
    return;
} // clean_up

void set_id_ref(int n) {
    if (!adminp(previous_object()))  return;
    id_ref = n;
}
int query_id_ref() { return id_ref; }