dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  -*- LPC -*-  */
/*
 * $Id: mailer.c,v 1.27 2003/03/19 20:44:14 ceres Exp taffyd $
 */

/**
 * The mailer handler object.  This is used to send mail and do oyther
 * stuff related to mail.
 *  The new mailer object ! *shiver*
 *  Thanks to Wodan and Pinkfish for ideas and help.
 *  By Turrican@Discworld, May 1995.
 * @author Turrican
 * @started May 1995
 *
 * @change 1-10-96, Turrican
 *  Changed the way Ccs and including yourself get done
 * @change 17-09-97, Olorin
 *  Swapped 'r' and 'R' to make don't sent to CC's the default use
 */

#include <mime.h>
#include <mail.h>
#include <localtime.h>
#include <player_handler.h>

#define FOLDER_H "/obj/handlers/folder_handler.c"
#define MAIL_PATH "/save/mail/"
#define COLS (int)owner->query_cols()
#define ROWS (int)owner->query_rows()
#undef CONVERTER

nosave class mail_header *folder;
nosave int *deleted, *newish, no_menu, last_read, full_header;
nosave string current, to, subject, cc, mailrc;
nosave mixed *do_this_last;
nosave object owner;
nosave string *ignore = ({
  "email-version",
  "x-lines",
  "expires",
  "transport-options",
  "x-mailer",
  "errors-to",
  "lines",
  "priority",
  "newsgroups",
  "default-options",
  "auto-forward-count",
  "ua-message-id",
  "approved",
  "followup-to",
  "message-version",
  "message-id",
  "message-service",
  "message-type",
  "sender",
  "end-of-header",
  "content-type",
  "path",
  "report-version",
  "submitted-by",
  "message-protocol",
  "posting-number",
  "x-postmark",
  "apparently-to",
  "organization",
  "posted-date",
  "return-receipt-to",
  "keywords",
  "x-sun-charset",
  "summary",
  "in-reply-to",
  "reply-to",
  "distribution",
  "sent-by",
  "company",
  "references",
  "mts-message-id",
  "x-sequence",
  "via",
  "content-charset",
  "status",
  "confirmed-by",
  "ua-content-id",
  "content-length",
  "mime-version",
  "auto-forwarded-from",
  "content-transfer-encoding",
  "received",
  "end-of-protocol",
  "x-uidl",
  "precedence",
  "return-path"
});
string *folder_names;

/* PROTOTYPES */

varargs protected void rm_message(string input);
protected void unrm_message(string input);
private void print_message(int number);
varargs private void reply_message(int input, int flag);
private void save_me();
private int load_me();
void dest_me();
private void read_messages(string fname);
protected void read_loop(string input);
private void prompt();
protected void change_folder(string input);
varargs private void delete_it(string bonk, int last, string newish_folder);
varargs protected void move_message(string input, int flag);
private void save_message(string input);
protected void get_recipient(string input);
varargs protected void get_subject(string input, int number, int flag);
varargs protected void main_menu(string fname, int flag, int *range);
protected void forward_message(string input);
void finish_write_message(string input);
string format_date(int x);
private int *expand_range(string str);
private int valid_name(string str);
void read_mail(string str, string sub);
private void forward_email(int number);
private void write_message();
private void check_external_mail();

/* BEGIN */

private void create() {
    mapping aliases;

    seteuid("Mailer");
    no_menu = full_header = 0;
    last_read = -1;
    newish = ({ });
    folder = ({ });
    folder_names = ({ "inbox" });
    deleted = ({ });
    current = "";
    if (clonep()) {
        owner = this_player();
        if (owner) {
            aliases = owner->query_aliases();
            if (aliases) {
                mailrc = aliases[".mailrc"];
            }
        }
    } else {
        check_external_mail();
    }
} /* create() */

#define HEADER_NAME 1
#define HEADER_VAL  2

private string folder_filename(string name) {
    return MAIL_PATH+name[0..0] + "/"  + name;
}

private string strip_header(string message) {
    mixed *ra;
    int i;
    string header;

    if ((i = strsrch(message, "\n\n")) == -1) {
        return message;
    }
    header = message[0..i];
    message = message[i+1..];
    ra = reg_assoc(header,
      ({ "^[!-9;-~]+:", "((\n?[ \t])+[^\n]*(\n|$))+" }),
      ({ HEADER_NAME, HEADER_VAL }));
    for (i = 1; i + 2 < sizeof(ra[0]); i += 2) {
        if (ra[1][i] == HEADER_NAME && ra[1][i+2] == HEADER_VAL) {
            if (member_array(replace_string(lower_case(ra[0][i]), ":", ""),
                ignore) != -1) {
                ra[0][i] = ra[0][i+2] = "";
            }
        }
    }
    return implode(ra[0], "") + message;
}

private void check_external_mail() {
    string *dir, fname, mess, t, ccs;
    class mail_message msg = new(class mail_message);
    class mime_header hdr;

    dir = unguarded((: get_dir, EXTERNAL_MAIL_PATH :));
    if (!dir) {
        call_out((: check_external_mail :), 60);
        return;
    }
    foreach (fname in dir) {
        mess = unguarded((: read_file, EXTERNAL_MAIL_PATH + fname :));
        mess = replace(mess, ({ "\r\n", "\n", "\t", "        " }));
        hdr = MIME->parse_headers(mess);
        if (!hdr) {
            unguarded((: rm, EXTERNAL_MAIL_PATH + fname :));
            continue;
        }
        msg->from = hdr->header_m["from"];
        msg->subject = hdr->header_m["subject"];
        t = hdr->header_m["to"];
        if (!t) {
            continue;
        }
        msg->to = explode(t, ",");
        ccs = hdr->header_m["cc"];
        if (!ccs) {
            ccs = "";
        }
        msg->cc = explode(ccs, ",");
        msg->body = mess;
        FOLDER_H->add_it(msg, 1);
        unguarded((: rm, EXTERNAL_MAIL_PATH + fname :));
    }
    call_out((: check_external_mail :), 60);
} /* check_external_mail() */

/**
 * This method sets the call back fuinction to use when
 * the mailer has finished. ({ ob, func })
 * @param bing the call back function
 */
void set_do_this_last(mixed *bing) { do_this_last = bing; }
/**
 * This method returns the call back fuinction to use when
 * the mailer has finished. ({ ob, func })
 * @return the call back function
 */
mixed *query_do_this_last() { return do_this_last; }

/* For backwards compatibility. */
/**
 * This method allows a message to be mailed.  It checks the previous
 * object to make sure it is one of the allowed set to
 * do mailing.
 * @param t who it is to
 * @param from who it is from
 * @param sub the subject of the message
 * @param ccs the carbon copy recipients
 * @param body the body of the message
 * @param only_to only mail to the to address
 * @param flag prevent this_player() from getting the messages if flag != 0
 * @example
 * MAIL_HANDLER->do_mail_message("pinkfish", "gumboot, killer tomato":,
 *                               "About the tomatoes", "",
 *                   "The grass ius greener yesterday,.\nYours\nGumboot.");
 */
varargs int do_mail_message(string t, string from, string sub, string ccs,
  string body, int, string only_to, int flag) {
    string *cc_e, *goto;
    class mail_message msg;

    if(file_name(previous_object())[0..12] != "/secure/login" &&
       file_name(previous_object())[0..13] != "/secure/nlogin" &&
       file_name(previous_object())[0..13] != "/obj/handlers/" &&
       file_name(previous_object())[0..4] != "/www/" &&
       file_name(previous_object())[0..11] != "/net/daemon/" &&
       file_name(previous_object())[0..11] != "/global/lord" &&
       file_name(previous_object())[0..14] != "/global/creator" &&
       file_name(previous_object())[0..18] != "/global/auto_mailer" &&
       file_name(previous_object())[0..12] != "/cmds/creator" &&
       file_name(previous_object())[0..9] != "/cmds/lord" &&
       file_name(previous_object()) != "/d/am/buildings/post/parcel" &&
       file_name(previous_object()) != "/d/am/buildings/apex/admin_office" &&
       file_name(previous_object()) != "/d/am/bookkeepers/weichert_office" &&
       file_name(previous_object()) != "/d/ram/ohulan/market/post_office" &&
       file_name(previous_object()) != "/d/ram/interview" &&
       file_name(previous_object()) != "/d/forn/utils/interview" &&
       file_name(previous_object()) != "/d/am/buildings/flintwick/lawyer_office" &&
       file_name(previous_object()) != "/d/am/buildings/council/court" &&
       file_name(previous_object()) != "/d/klatch/djel/city/palace/council_court" &&
       file_name(previous_object()) != "/d/guilds/error_tracker" &&
       file_name(previous_object())[0..25] != "/d/ram/ohulan/market/post2") { 
        printf("MAILER: illegal access (%O).\n", file_name(previous_object()));
        return 0;
    }
    if ((file_name(previous_object())[0..11] == "/global/lord" ||
        file_name(previous_object())[0..14] == "/global/creator") &&
        (lower_case(from) != (string)this_player()->query_name())) {
        printf("MAILER: illegal acces.\n");
        return 0;
    }
    if (!ccs) {
        ccs = "";
    }
    cc_e = explode(ccs, ",")-({""});
    goto = explode(t, ",")-({""});
    if (only_to) {
        goto = explode(only_to, ",")-({""});
    }
    msg = new(class mail_message);
    msg->to = goto;
    msg->cc = cc_e;
    msg->body = "From " + from + " " + ctime(time()) +
    "\nDate: " + format_date(time()) +
    "\nFrom: " + from +
    "\nTo: " + t +
    "\nSubject: " + sub +
    "\n" + (sizeof(cc_e)?"Cc: "+ ccs+ "\n":"")+
    "\n" + body;
    msg->from = lower_case(from);
    msg->subject = sub;
    FOLDER_H->add_it(msg, flag);
    return 1;
} /* do_mail_message() */

/**
 * This method returns the mail information which is placed into the
 * the finger command.
 * @param pname the name of the player
 * @return the function mail string
 */
string finger_mail(string pname) {
    return FOLDER_H->finger_mail(lower_case(pname));
} /* finger_mail() */

/**
 * This method returns a string saying if the player has new mail or not.
 * This is what is used when the player first logs on.
 * @param pname the name of the player
 * @return the new mail string
 * @example
 * str = MAIL_HANDLER->new_mail(this_player()->query_name());
 */
string new_mail(string pname) {
    return FOLDER_H->check_mail(lower_case(pname));
} /* new_mail() */

/**
 * This method prints the prompt which is used in the main mail loop.
 */
private void prompt() {
    printf("\nCommand (h for main menu): ");
} /* prompt() */

/**
 * This method is the main entry point to the mailer.  It is
 * what is called to start up the system when a mailer is used.
 * @example
 * mailer = clone_object(MAIL_HANDLER);
 * mailer->read_mail();
 */
void read_mail(string str, string sub) {
    if (this_player()->query_property("guest")) {
        write("Sorry, mailer access is not allowed for guests.\n");
        if (do_this_last && objectp(do_this_last[0])) {
            call_other(do_this_last[0], do_this_last[1], do_this_last[2]);
        } else {
            dest_me();
        }
        return;
    }

    if (!load_me()) {
        if (do_this_last && objectp(do_this_last[0])) {
            call_other(do_this_last[0], do_this_last[1], do_this_last[2]);
        } else {
            dest_me();
        }
        return;
    }
    if (str) {
        no_menu = 1;
        if (!sub) {
            get_recipient(str);
        } else {
            to = str;
            get_subject(sub);
        }
        return;
    }
    MAIL_TRACK->add_mailer(this_object(), owner->query_name()); 
    main_menu("inbox");
    return;
} /* read_mail() */

/** @ignore yes */
varargs private void main_menu(string fname, int flag, int *range) {
    int i, size, offs, cols, fromcols, statcols;

    cols = COLS;
    printf("%|=*s", cols, "" + mud_name() + " mailer system version 2.0\n\n");
    if (!strlen(fname)) {
        fname = "inbox";
    }
    current = fname;
    if (!flag) {
        read_messages(current);
        last_read = -1;
        deleted = ({ });
    }
    if (!range) {
        range = ({ });
    }
    newish = ({ });
    size = sizeof(folder);
    for (i = 0; i < size; i++) {
        if (folder[i]->status == "N") {
            newish += ({ i });
        }
    }
    if (sizeof(newish) && newish[0] != 0 && last_read == -1) {
        last_read = newish[0]-1;
    }
    if (size && !sizeof(newish) && last_read == -1) {
        last_read = size-1;
    }
    printf(owner->fix_string(sprintf("%s%|=*s%s","%^CYAN%^",cols,
          "Folder "+current+" with "+
          (size?""+size:"no")+
          " message"+(size == 1?"":"s")+".\n\n",
          "%^RESET%^")));
    if (!sizeof(range)) {
        i = size - ROWS + 11;
        if (i < 0) {
            i = 0;
        }
        range = expand_range(sprintf("%d-%d", i, size));
    }
    size = sizeof(range);
    if (size) {
        offs = range[0]-1;
    } else {
        offs = 0;
    }
    statcols = cols / 3;
    fromcols = 2 * cols / 3 - 8;
    for (i = 0; i<size; i++) {
        printf(owner->
          fix_string(sprintf("%s%-6s %-*.*s %-*.*s%s\n",
              (last_read == i+offs?
                "%^REVERSE%^":""), 
              (last_read == i+offs?">":" ")+
              folder[i+offs]->status+
              " " + (i+offs+1), statcols, statcols, "From: " +
              folder[i+offs]->from, fromcols, fromcols, 
              "Subject: "+
              replace_string(terminal_colour(folder[i+offs]->subject,
                  ([ ])), "%", "%%"),
              "%^RESET%^")));
    }
    printf("%|=*s", cols, "\n  You can use any of the following commands by "
      "entering the first character;\nd)elete or u)ndelete mail,  m)ail a "
      "message,  r)eply or f)orward mail,  q)uit,\n"
      "> = move messages, c)hange folder, i)ndex of folders, ? = help\n"
      "To read next unread message, press <return>.\n");
    printf("Command: ");
    input_to("read_loop");
} /* main_menu() */

/** @ignore yes */
protected void read_loop(string input) {
    int i, num;
    string s1, s2, comm, *tmp;

    /*
     * ok this should (theoreticaly) do some clever things and get the
     * message number being reffered to out of the junk.  But then again,
     * maybe not.
     */
    num = 0;
    i = 0;
    if (sscanf(input, "%d%s", num, input) != 2) {
        comm = "";
        if (sscanf(input, "%s %d %s", s1, num, s2) == 3) {
            i = 1;
            input = s1 + " " + s2;
        }
    }
    if (sscanf(input, "%s %s", comm, input) != 2) {
        comm = input;
        input = "";
    }
    if (i)
        input = ""+num+" "+input;
    switch(comm) {
    case "q" :  if (current == "inbox") {
            if (sizeof(folder) && 
              (sizeof(folder) != sizeof(newish)) && 
              (sizeof(deleted) != sizeof(folder))) {
                printf( "\nMove read message(s) to \"received\" folder? "
                  "(y/[n]): ");
                input_to("get_yesno");
                break; 
            }
        }
        delete_it(current, 1);
        break;
    case "d" :
        if (input != "") {
            rm_message(input);
            break;
        }
        printf( "\nDelete which messages (number or range): " );
        input_to("rm_message");
        break; 
    case "u" :
        if (input != "") {
            unrm_message(input);
            break;
        }
        printf( "\nUndelete which messages (number or range): " );
        input_to("unrm_message");
        break;
    case "c" :
        if (input != "") {
            change_folder(input);
            break;
        }
        printf( "\nChange to which folder (= for inbox): " );
        input_to("change_folder");
        break;
    case "m" :
        if (input != "") {
            get_recipient(input);
            break;
        } 
        printf( "\nRecipient: " );
        input_to("get_recipient");
        break;
    case "R" :
        reply_message(num);
        break;
    case "r" :
        reply_message(num, 1);
        break;
    case "$" :
        main_menu(current);
        break;
    case "f" :
        if (input != "") {
            forward_message(input);
            break;
        }
        printf("\nForward a message.  Ranges are not supported.\n"
          "Enter the number of the message and the name(s) of the "
          "recipient(s), separated by a comma: ");
        input_to("forward_message");
        break;
    case "F" :
        forward_email(num);
        break;
    case "i" :
        printf("\nHere's the index of your folders:\n");
        printf("\n%-#*s\n", COLS, implode(folder_names, "\n")); 
        prompt();
        input_to("read_loop");
        break;
    case "L" :
        if (full_header) {
            printf("\nOkay, now showing abbreviated header.\n");
            full_header = 0;
        } else {
            printf("\nOkay, now showing full header.\n");
            full_header = 1;
        }
        prompt();
        input_to("read_loop");
        break;
    case "l" :
        if (input != "") {
            if (!MAIL_TRACK->query_list(input)) {
                printf( "\nSorry, list "+input+" does not exist.\n" );
                prompt();
                input_to("read_loop");
                break;
            }
            printf( "\nMembers of list "+ input +":\n" );
            tmp = MAIL_TRACK->query_members(input);
        } else {
            printf( "\nCurrently available mailing lists:\n" );
            tmp = MAIL_TRACK->query_mailing_lists();
        }
        printf("\n%-#*s\n", COLS, implode(tmp,"\n"));
        prompt();
        input_to("read_loop");
        break;
    case ">" :
        if (!PLAYER_HANDLER->test_user(owner->query_name())) {
            printf( "You can't do that as a guest.\n" );
            break;
        }
        if (input != "") {
            move_message(input);
            break;
        }
        printf("\nMove message to folder.\nEnter the number "
          "(or range) of the message and the name of the folder, "
          "separated by a comma: ");
        input_to("move_message");
        break;
    case "h" :  
        if (input != "") {
            main_menu(current, 1, expand_range(input));
            break;
        }
        main_menu(current, 1);
        break;
    case "s" :
        save_message(input);
        break;
    case "?" :
        owner->set_finish_func("finish_print");
        comm = read_file("/doc/helpdir/mailer");
        owner->more_string(comm);
        break;
    case "" :
        if (num) {
            print_message(num-1);
            last_read = num-1;
            break;
        }
        if (newish == ({ })) {
            printf("\nNo more new messages in folder.\n");
            prompt();
            input_to("read_loop");
            break;
        }
        last_read = newish[0];
        print_message(newish[0]);
        break;
    default :
        printf("\nUnknown command.  Use \"?\" for help.\n");
        prompt();
        input_to("read_loop");
        break; 
    }
} /* read_loop() */

/** @ignore yes */
protected void get_yesno(string input) {
    int bing, i;

    if (!input || (input == "")) {
        printf("No.\n");
        delete_it(current, 1);
        return;
    }
    if (lower_case(input) == "n") {
        delete_it(current, 1);
        return;
    }
    if (lower_case(input) == "y") {
        bing = sizeof(folder);
        for (i = 0; i < bing; i++) {
            if ((member_array(i, newish) == -1) && 
              (member_array(folder[i]->number, deleted) == -1)) {
                move_message(""+(i+1)+" received", 1); 
            }
        }
    }
    delete_it(current, 1);
    return;
} /* get_yesno() */

private void forward_email(int number) {
    int i;
    string email, body;

    if (!number) {
        if (!(i = sizeof(folder))) {
            printf("No messages in folder.\n");
            prompt();
            input_to("read_loop");
            return;
        }
        if (newish != ({ })) {
            number = newish[0];
            if (number == 0) {
                printf("All your messages are still unread: aborting.\n");
                prompt();
                input_to("read_loop");
                return;
            }
        }
        else if (last_read > -1)
            number = last_read+1;
        else 
            number = i;
    }
    if (sizeof(folder) < number) {
        printf("Oh dear.  No message with that number.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    number--;
    if (!(email = PLAYER_HANDLER->test_email(owner->query_name()))) {
        printf("Sorry, your E-mail address is not set.  Use chfn or email "
          "to set it.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if (email[0] == ':') {
        email = email[1..];
    }
    body = FOLDER_H->load_message(owner->query_name(), current,
      folder[number]->number);
    if (body) {
        SMTP->eventSendMail(email, owner->query_name(), body);
        printf("Message #%d forwarded to your E-mail address.\n", number + 1);
    }
    prompt();
    input_to("read_loop");
} /* forward_mail() */

private string rewrite_local(string rcpt) {
    string tmpr;

    if ((tmpr = FOLDER_H->check_local(rcpt))) {
        return tmpr;
    }
    return rcpt;
} /* rewrite_local() */

varargs private void reply_message(int number, int flag) {
    int i;
    string body;
    class mime_header hdr;

    if (!number) {
        if (!(i = sizeof(folder))) {
            printf("No messages in folder.\n");
            prompt();
            input_to("read_loop");
            return;
        }
        if (newish != ({ })) {
            number = newish[0];
            if (number == 0) {
                printf("All your messages are still unread: aborting.\n");
                prompt();
                input_to("read_loop");
                return;
            }
        }
        else if (last_read > -1)
            number = last_read+1;
        else 
            number = i;
    }
    if (sizeof(folder) < number) {
        printf("Oh dear.  No message with that number.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    number--;
    to = folder[number]->from;
    body = FOLDER_H->load_message(owner->query_name(), current,
      folder[number]->number);
    if (!body) {
        prompt();
        input_to("read_loop");
        return;
    }
    hdr = MIME->parse_headers(body);
    if (!hdr) {
        prompt();
        input_to("read_loop");
        return;
    }
    if (hdr->header_m["reply-to"]) {
        to = hdr->header_m["reply-to"];
    }
    if (!flag) {
        string tmp;

        cc = hdr->header_m["cc"];
        tmp = hdr->header_m["to"];
        if (tmp) {
            if (cc) {
                cc += "," + tmp;
            } else {
                cc = tmp;
            }
        }
        if (cc == "") {
            cc = 0;
        }
        if (cc) {
            cc = implode(map(MIME->get_email_addrs(cc)[0],
                (: rewrite_local($1) :))-({ owner->query_name(), to }),
              ",");
        }
    }
    printf("Include original message? (y/[n]/q) ");
    input_to("finish_reply_message", 0, number);
} /* reply_message() */

/** @ignore yes */
protected void finish_reply_message(string input, int number) {
    int flag, i;
    string s1;

    if (!input || input == "" || lower_case(input)[0] != 'y') {
        if (lower_case(input)[0] == 'q') {
            cc = 0;
            to = "";
            printf("Aborting.\n");
            prompt();
            input_to("read_loop");
            return;
        }
        printf( "No.\n" );
    } else flag = 1;
    subject = folder[number]->subject;
    if (sscanf(subject, "Re:#%d %s", i, s1) != 2)
        subject = "Re:#1 " + subject;
    else 
        subject = sprintf("Re:#%d %s", (i+1), s1);
    printf("Press return for a subject of \"%s\"\nSubject: ", subject);
    if (flag)
        input_to("get_subject", 0, ++number, 1);
    else
        input_to("get_subject");
} /* finish_reply_message() */

/** @ignore yes */
protected void change_folder(string input) {
    if (!input || input == "") {
        prompt();
        input_to("read_loop");
        return;
    }
    if (input == "=") {
        delete_it(current, 0, "inbox");
        return;
    }
    if (member_array(input, folder_names) == -1) {
        printf("No folder named %s.\n", input);
        prompt();
        input_to("read_loop");
        return;
    }
    delete_it(current, 0, input);
} /* change_folder() */

/** @ignore yes */
protected void get_recipient(string input) {
    string *str, *full, nn;
    int i;
    mixed *addrs;

    if (!input || input == "") {
        printf("No recipient given: aborting.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    addrs = MIME->get_email_addrs(input);
    str = addrs[0];
    full = addrs[1];
    for (i=0;i<sizeof(str);i++){
        nn = (string)owner->expand_nickname(str[i]);
        if (str[i] != nn) {
            str[i] = full[i] = nn;
        }
        if (!valid_name(str[i])) {
            printf("%s is not a valid recipient.\n", str[i]);
            str = delete(str, i, 1);
            full = delete(full, i--, 1); 
        }
    }
    if (sizeof(full))
        to = implode(full, ","); 
    else {
        if (no_menu) {
            delete_it("inbox", 1);
            return;
        }
        prompt();
        input_to("read_loop");
        return;
    }
    subject = "";
    printf("Subject: ");
    input_to("get_subject");
} /* get_recipient() */

/** @ignore yes */
varargs protected void get_subject(string input, int number, int flag) {
    if ((!input || input == "") && (!subject || subject == "")) {
        printf("No subject given: aborting.\n");
        if (no_menu) {
            delete_it("inbox", 1);
            return;
        }
        prompt();
        input_to("read_loop");
        return;
    }
    if (input && input != "")
        subject = input;
    printf("Cc: ");
    input_to("get_cc", 0 , number, flag);
} /* get_subject() */

private int valid_name(string str) {
    string mud;

    str = lower_case(str);
    if (!sscanf(str, "%s@%s", str, mud) || 
      lower_case(mud) == lower_case(mud_name())) {
        /* local...  */
        return (int)PLAYER_HANDLER->test_user(str) ||
        (int)MAIL_TRACK->query_list(str);
    }
    return 1;
} /* valid_name() */  

/** @ignore yes */
varargs protected void get_cc(string input, int number, int flag) {
    string *str, body, *full, nn;
    int i;
    mixed *addrs;

    if (!input || input == "**" || input == "") {
        if (number) {
            body = FOLDER_H->load_message(owner->query_name(), current, 
              folder[number-1]->number);
            if (!body) {
                prompt();
                input_to("read_loop");
                return;
            }
            if (!flag) {
                finish_write_message("> " + replace_string(body, "\n", "\n> ") + "\n");
                return;
            }
            owner->do_edit("> " + replace_string(body, "\n", "\n> ") + "\n", 
              "finish_write_message");
            return;
        }
        write_message();
        return;
    }
    addrs = MIME->get_email_addrs(input);
    str = addrs[0];
    full = addrs[1];
    for (i = 0; i < sizeof(str); i++) {
        nn = (string)owner->expand_nickname(str[i]);
        if (str[i] != nn) {
            str[i] = full[i] = nn;
        }
        if (!valid_name(str[i])) {
            printf("%s is not a valid recipient.\n", str[i]);
            str = delete(str, i, 1);
            full = delete(full, i--, 1);
        }
    }
    if (cc)
        cc += "," + implode(full, ",");
    else
        cc = implode(full, ",");
    printf("Cc: ");
    input_to("get_cc", 0, number, flag);
}  /* get_cc() */

private void write_message() {
    owner->do_edit(0, "finish_write_message");
} /* write_message() */

/** @ignore yes */
void finish_write_message(string input) {
    if (!input || input == "") {
        if (no_menu) {
            delete_it("inbox", 1);
            return;
        }
        prompt();
        input_to("read_loop");
        return;
    }
    printf("Cc: ");
    input_to("get_cc_after", 0, input);
} /* finish_write_message() */

/** @ignore yes */
protected void get_cc_after(string input, string body) {
    mixed *goto, cc_e, *addrs;
    class mail_message msg;
    string *str, *full, nn;
    int i;

    if (!input || input == "**" || input == "") {
        body += owner->append_signature();
        FOLDER_H->mark_read(owner->query_name(), current, newish);
        goto = explode(to, ",") - ({ "" });
        if (cc && cc != "") {
            cc_e = explode(cc, ",") - ({ "" });
        } else {
            cc_e = ({ });
        }
        msg = new(class mail_message);
        msg->to = goto;
        msg->cc = cc_e;
        msg->body = "From " + owner->query_name() + " " + ctime(time()) +
        "\nDate: " + format_date(time()) +
        "\nFrom: " + owner->query_name() +
        "\nTo: " + to +
        "\nSubject: " + subject +
        "\n" + (sizeof(cc_e)?"Cc: " + cc + "\n":"") +
        "\n" + body;
        msg->from = owner->query_name();
        msg->subject = subject;
        FOLDER_H->add_it(msg, 0);
        to = "";
        cc = 0;
        subject = "";
        printf("Message sent.\n");
        if (no_menu) {
            delete_it("inbox", 1);
            return;
        }
        prompt();
        input_to("read_loop");
        return;
    }
    addrs = MIME->get_email_addrs(input);
    str = addrs[0];
    full = addrs[1];
    for (i = 0; i < sizeof(str); i++) {
        nn = (string)owner->expand_nickname(str[i]);
        if (str[i] != nn) {
            str[i] = full[i] = nn;
        }
        if (!valid_name(str[i])) {
            printf("%s is not a valid recipient.\n", str[i]);
            str = delete(str, i, 1);
            full = delete(full, i--, 1);
        }
    }
    if (cc)
        cc += "," + implode(full, ",");
    else
        cc = implode(full, ",");
    printf("Cc: ");
    input_to("get_cc_after", 0, body);
} /* get_cc_after() */

varargs private void delete_it(string fname, int last, string newish_folder) {
    if (!fname)
        fname = current;
    if (deleted != ({ })) {
        printf("Delete message(s)? ([y]/n) ");
        input_to("finish_delete_it", 0, fname, last, newish_folder);
        return;
    }
    if (last) {
        FOLDER_H->mark_read(owner->query_name(), current, newish);
        save_me();
        if (do_this_last && objectp(do_this_last[0]))
            call_other(do_this_last[0], do_this_last[1], do_this_last[2]);
        else
            dest_me();
        return;
    }
    FOLDER_H->mark_read(owner->query_name(), current, newish);
    if (!newish_folder || newish_folder == "")
        main_menu(fname);
    else
        main_menu(newish_folder);
} /* delete_it() */

/** @ignore yes */
protected void finish_delete_it(string input, string fname, int last, 
  string newish_folder) {
    if (lower_case(input) == "n" || (input && input != "" && 
        lower_case(input) != "y")) {
        printf("Keeping message(s).\n");
        deleted = ({ });
        if (last) {
            FOLDER_H->mark_read(owner->query_name(), current, newish);
            save_me();
            if (do_this_last && objectp(do_this_last[0]))
                call_other(do_this_last[0], do_this_last[1], do_this_last[2]);
            else
                dest_me();
            return;
        }
        FOLDER_H->mark_read(owner->query_name(), current, newish);
        if (!newish_folder || newish_folder == "")
            main_menu(fname);
        else
            main_menu(newish_folder);
        return;
    }
    printf("Deleting message(s).\n");
    FOLDER_H->mark_read(owner->query_name(), current, newish);
    FOLDER_H->delete_it(owner->query_name(), fname, deleted);
    deleted = ({ });
    save_me();
    if (last) {
        if (do_this_last && objectp(do_this_last[0]))
            call_other(do_this_last[0], do_this_last[1], do_this_last[2]);
        else
            dest_me();
        return;
    }
    if (!newish_folder || newish_folder == "")
        main_menu(fname);
    else
        main_menu(newish_folder);
    return;
} /* finish_delete_it() */

/** @ignore yes */
varargs protected void rm_message(string input, int flag) {
    int i, tmp;
    int *range;

    if (!input || input == "") {
        prompt();
        input_to("read_loop");
        return;
    }
    range = expand_range(input);
    tmp = sizeof(range);
    for (i = 0; i < tmp; i++) {
        if (member_array(folder[range[i]-1]->number, 
            deleted) == -1) {
            newish -= ({ range[i]-1 });
            deleted += ({ folder[range[i]-1]->number });
            folder[range[i]-1]->status = "D";
            printf("Marked message %d as deleted.\n", range[i]);
        }
    }
    if (!flag) {
        prompt();
        input_to("read_loop");
    }
} /* rm_message() */

/** @ignore yes */
protected void unrm_message(string input) {
    int i, tmp;
    int *range;

    if (!input || input == "") {
        prompt();
        input_to("read_loop");
        return;
    }
    range = expand_range(input);
    tmp = sizeof(range);
    for (i = 0; i < tmp; i++) {
        if (member_array(folder[range[i]-1]->number, 
            deleted) > -1) {
            deleted -= ({ folder[range[i]-1]->number });
            folder[range[i]-1]->status = "U";
            printf("Undeleted message %d.\n", range[i]);
        }
    }
    prompt();
    input_to("read_loop");
} /* unrm_message() */

/** @ignore yes */
protected void forward_message(string input) {
    int number, i;
    string *str, *full, nn;
    mixed *addrs;

    if (!input || input == "") {
        prompt();
        input_to("read_loop");
        return;
    }
    if (sscanf(input, "%d %s", number, to) != 2) {
        printf("Wrong syntax (ranges are not supported with forward).\n");
        prompt();
        input_to("read_loop");
        return;
    } 
    if (number > sizeof(folder) || !number) {
        printf("Oh dear. No message with that number.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    addrs = MIME->get_email_addrs(to);
    str = addrs[0];
    full = addrs[1];
    for (i = 0; i < sizeof(str); i++) {
        nn = (string)owner->expand_nickname(str[i]);
        if (str[i] != nn) {
            str[i] = full[i] = nn;
        }
        if (!valid_name(str[i])) {
            printf("%s is not a valid recipient.\n", str[i]);
            str = delete(str, i, 1);
            full = delete(full, i--, 1);
        }
    }
    if (sizeof(full)) {
        to = implode(full, ",");
    } else {
        prompt();
        input_to("read_loop");
        return;
    }
    cc = 0;
    printf("Edit outgoing message? (y/[n]) ");
    input_to("edit_it", 0, number);
} /* forward_message() */

/** @ignore yes */
protected void edit_it(string input, int number) {
    subject = folder[number-1]->subject + " (fwd)";
    if (!input || input == "")
        printf( "No.\n" );
    printf("Press return for a subject of \"%s\"\nSubject: ", subject);
    if (!strlen(input) || (lower_case(input) == "n"))
        input_to("get_subject", 0, number);
    else if (lower_case(input) == "y")
        input_to("get_subject", 0, number, 1);
    return;
} /* edit_it() */

/** @ignore yes */
varargs protected void move_message(string input, int flag) {
    int *range, i, bing;
    string to_folder, number;

    if (!input || input == "") {
        prompt();
        input_to("read_loop");
        return;
    }
    if (!sscanf(input, "%s %s", number, to_folder)) {
        printf("Wrong syntax.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if (to_folder == current) {
        printf("Destination folder is the same as source folder: not moved.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if (to_folder == "=")
        to_folder = "inbox";
    range = expand_range(number);
    bing = sizeof(range);
    FOLDER_H->mark_read(owner->query_name(), current, newish);
    for (i = 0; i < bing; i++) { 
        int status;

        if (member_array(to_folder, folder_names) == -1) { 
            if (FOLDER_H->can_create_folder(owner->query_name(), to_folder)) {
                folder_names += ({ to_folder });
            } else {
                printf("Cannot create a folder called %s, please choose "
                    "another another name.\n", to_folder);
                break; 
            }
        }
        deleted += ({ folder[range[i]-1]->number });
        status = FOLDER_H->move_it(owner->query_name(), current, to_folder,
          folder[range[i]-1]->number);
        folder[range[i]-1]->status = "D";
        switch(status) {
        case 2:
          printf("Folder full!\n");
        case 1:
          printf("Marked message %d as deleted.\n", range[i]);
          break;
        case 0:
          if(!flag)
            printf("Saved message %d to folder %s.\n", range[i], to_folder);
        }
    }
    if (!flag) {
        prompt();
        input_to("read_loop");
    }
} /* move_message() */

private void save_message(string input) {
    int *range, i, bing, ret;
    string to_file, number, body, err;

    if (!wizardp(owner)) {
        printf("Sorry, only creators can save mail to files.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if (!input || input == "") {
        if (last_read != -1)
            input = sprintf("%d ~/mbox",(last_read+1));
        else {
            printf("No messages in folder or all messages still unread.\n");
            prompt();
            input_to("read_loop");
            return;
        } 
    }
    if (!sscanf(input, "%s %s", number, to_file)) {
        printf("Wrong syntax.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    seteuid(geteuid(owner));
    to_file = (string)owner->get_path(to_file);
    if (!master()->valid_write(to_file, owner, "write_file")) {
        printf("You cannot write to that file.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if ((i=file_size(to_file)) > 0)
        printf("Appending to existing file.\n");
    else if (i == -2) {
        printf("That is a directory.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    range = expand_range(number);
    bing = sizeof(range);
    i = 0;
    for (i = 0; i < bing; i++) {
        body = FOLDER_H->load_message(owner->query_name(), current, 
          folder[range[i]-1]->number);
        if (!body) {
            continue;
        }
        err = catch(ret = write_file(to_file, body + "\n\n"));
        if (err) {
            printf("Writing to file failed: %s\n", err);
        } else if (!ret) {
            printf("Writing to file failed.\n");
        } else {
            printf("Saved message %d to %s.\n", range[i], to_file);
        }
    }
    seteuid("Mailer");
    prompt();
    input_to("read_loop");
} /* save_message() */

private void print_message(int number) {
    string body;

    if (number > sizeof(folder)-1 || number < 0) {
        printf("No message with that number.\n");
        prompt();
        input_to("read_loop");
        return;
    }
    if (folder[number]->status == "N") {
        newish -= ({ number });
        folder[number]->status = " ";
    }
    body = FOLDER_H->load_message(owner->query_name(), current, 
      folder[number]->number);
    if (!body) {
        rm_message(""+(number+1));
        return;
    }
    owner->set_finish_func("finish_print");
    printf("\n\nMessage %d\n", (number + 1));
    owner->more_string((full_header?body:strip_header(body)),
      "Message "+(number+1));
} /* print_message() */

/** @ignore yes */
void finish_print() {
    printf("\n");
    prompt();
    input_to("read_loop");
} /* finish_print() */

private void save_me() {
    if (!PLAYER_HANDLER->test_user(owner->query_name())) {
        return;
    }
    if (current != "inbox") {
        if (FOLDER_H->check_empty(owner->query_name(), current)) {
            folder_names -= ({ current });
        }
    }
    unguarded((: save_object, folder_filename(owner->query_name()) :));
} /* save_me() */

private int load_me() {
#ifdef CONVERTER
    if ("/obj/handlers/converter"->query_busy(owner->query_name())) {
        printf( "You cannot use the mailer now, your mail is being converted.\n" );
        return 0;
    }
#endif
    unguarded((: restore_object, folder_filename(owner->query_name()) :));
    if (!folder_names) {
        folder_names = ({ "inbox" });
    }
    return 1;
} /* load_me() */

/**
 * This method returns the list of folders associated with the player.
 * @param pname the player name
 * @return an array containing the folder names
 */
string *query_folders(string pname) {
    unguarded((: restore_object, folder_filename(pname) :));
    if (!folder_names)
        return ({});
    return folder_names;
} /* query_folders() */

private void read_messages(string fname) {
    folder = FOLDER_H->get_messages(owner->query_name(), fname);
} /* read_messages() */

/** @ignore yes */
void dest_me() {
    MAIL_TRACK->delete_mailer(this_object());
    destruct(this_object());
} /* dest_me() */

/**
 * This formats the date as needed by the mailer object.
 * @param x the date to format
 * @return the new date
 */
string format_date(int x) {
    string str;
    string mon;
    mixed *tm;

    if (x<0 || !intp(x))
        return "Bad time";

    tm = localtime(x);
    str = DAYS[tm[LT_WDAY]];
    mon = MONTHS[tm[LT_MON]];
    str = sprintf("%s, %d %s %d %02d:%02d %s", str, tm[LT_MDAY], mon,
      tm[LT_YEAR], tm[LT_HOUR], tm[LT_MIN], tm[LT_ZONE]);
    return str;
} /* format_date() */

private int *expand_range(string str) {
    int *ms, i, start, end;

    if (!str)
        str = "";
    if (!sizeof(folder))
        return ({ });
    str = replace_string(str, " ", "");
    ms = ({ });
    if (sscanf(str, "%*sall%*s")==2) {
        int size = sizeof(folder) + 1;
        for (i = 1; i < size; i++) {
            ms += ({ i });
        }
        return ms;
    }
    while (sscanf(str, "%d%s", start, str) == 2) {
        if (start < 1)
            start = 1;
        if (start > sizeof(folder))
            start = sizeof(folder);
        if (str && str != "" && (str[0] == '-' || ((strlen(str) > 1) &&
              str[0..1] == ".."))) {
            sscanf(str, "%*(-|(..))%d%s", end, str);
            if (end >= start) {
                if (end > sizeof(folder))
                    end = sizeof(folder);
                for (i = start; i <= end; i++)
                    if (member_array(i, ms) == -1)
                        ms += ({ i });
            }
        } else
        if (member_array(start, ms) == -1)
            ms += ({ start });
        sscanf(str, ",%s", str);
    }
    return ms;
} /* expand_range() */